mirror of
https://github.com/bitwarden/server.git
synced 2024-11-25 12:45:18 +01:00
[AC-621] Added possibility of adding users through SCIM to an Organization without a confirmed Owner (#2846)
* [AC-621] Added possibility of adding users through SCIM to an Organization without a confirmed Owner * [AC-621] Passing EventSystemUser argument for HasConfirmedOwnersExceptAsync in user delete actions by SCIM * [AC-624] Removed EventSystemUser parameter from IOrganizationService.HasConfirmedOwnersExceptAsync * [AC-621] Added IProviderUserRepository.GetManyOrganizationDetailsByOrganizationAsync * [AC-621] Updated OrganizationService.HasConfirmedOwnersExceptAsync to use IProviderUserRepository.GetManyOrganizationDetailsByOrganizationAsync to check for any confirmed provider users * [AC-621] Removed unused EventSystemUser parameters * [AC-621] Refactored ProviderUserRepository.GetManyByOrganizationAsync to return ProviderUser objects * [AC-621] Removed default parameter value for Status
This commit is contained in:
parent
db8e82ff03
commit
04e18ee8e7
@ -18,4 +18,5 @@ public interface IProviderUserRepository : IRepository<ProviderUser, Guid>
|
||||
Task DeleteManyAsync(IEnumerable<Guid> userIds);
|
||||
Task<IEnumerable<ProviderUserPublicKey>> GetManyPublicKeysByProviderUserAsync(Guid providerId, IEnumerable<Guid> Ids);
|
||||
Task<int> GetCountByOnlyOwnerAsync(Guid userId);
|
||||
Task<ICollection<ProviderUser>> GetManyByOrganizationAsync(Guid organizationId, ProviderUserStatusType? status = null);
|
||||
}
|
||||
|
@ -993,7 +993,7 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
}
|
||||
|
||||
var (organizationUsers, events) = await SaveUsersSendInvitesAsync(organizationId, invites);
|
||||
var (organizationUsers, events) = await SaveUsersSendInvitesAsync(organizationId, invites, systemUser: null);
|
||||
|
||||
await _eventService.LogOrganizationUserEventsAsync(events);
|
||||
|
||||
@ -1003,7 +1003,7 @@ public class OrganizationService : IOrganizationService
|
||||
public async Task<List<OrganizationUser>> InviteUsersAsync(Guid organizationId, EventSystemUser systemUser,
|
||||
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites)
|
||||
{
|
||||
var (organizationUsers, events) = await SaveUsersSendInvitesAsync(organizationId, invites);
|
||||
var (organizationUsers, events) = await SaveUsersSendInvitesAsync(organizationId, invites, systemUser);
|
||||
|
||||
await _eventService.LogOrganizationUserEventsAsync(events.Select(e => (e.Item1, e.Item2, systemUser, e.Item3)));
|
||||
|
||||
@ -1011,7 +1011,7 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
|
||||
private async Task<(List<OrganizationUser> organizationUsers, List<(OrganizationUser, EventType, DateTime?)> events)> SaveUsersSendInvitesAsync(Guid organizationId,
|
||||
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites)
|
||||
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites, EventSystemUser? systemUser)
|
||||
{
|
||||
var organization = await GetOrgById(organizationId);
|
||||
var initialSeatCount = organization.Seats;
|
||||
@ -1040,7 +1040,7 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
|
||||
var invitedAreAllOwners = invites.All(i => i.invite.Type == OrganizationUserType.Owner);
|
||||
if (!invitedAreAllOwners && !await HasConfirmedOwnersExceptAsync(organizationId, new Guid[] { }))
|
||||
if (!invitedAreAllOwners && !await HasConfirmedOwnersExceptAsync(organizationId, new Guid[] { }, includeProvider: true))
|
||||
{
|
||||
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
||||
}
|
||||
@ -1596,7 +1596,7 @@ public class OrganizationService : IOrganizationService
|
||||
throw new BadRequestException("Only owners can delete other owners.");
|
||||
}
|
||||
|
||||
if (!await HasConfirmedOwnersExceptAsync(organizationId, new[] { organizationUserId }))
|
||||
if (!await HasConfirmedOwnersExceptAsync(organizationId, new[] { organizationUserId }, includeProvider: true))
|
||||
{
|
||||
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
||||
}
|
||||
@ -1700,7 +1700,7 @@ public class OrganizationService : IOrganizationService
|
||||
bool hasOtherOwner = confirmedOwnersIds.Except(organizationUsersId).Any();
|
||||
if (!hasOtherOwner && includeProvider)
|
||||
{
|
||||
return (await _currentContext.ProviderIdForOrg(organizationId)).HasValue;
|
||||
return (await _providerUserRepository.GetManyByOrganizationAsync(organizationId, ProviderUserStatusType.Confirmed)).Any();
|
||||
}
|
||||
return hasOtherOwner;
|
||||
}
|
||||
@ -2272,7 +2272,7 @@ public class OrganizationService : IOrganizationService
|
||||
throw new BadRequestException("Already revoked.");
|
||||
}
|
||||
|
||||
if (!await HasConfirmedOwnersExceptAsync(organizationUser.OrganizationId, new[] { organizationUser.Id }))
|
||||
if (!await HasConfirmedOwnersExceptAsync(organizationUser.OrganizationId, new[] { organizationUser.Id }, includeProvider: true))
|
||||
{
|
||||
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
||||
}
|
||||
|
@ -160,4 +160,17 @@ public class ProviderUserRepository : Repository<ProviderUser, Guid>, IProviderU
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<ProviderUser>> GetManyByOrganizationAsync(Guid organizationId, ProviderUserStatusType? status = null)
|
||||
{
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryAsync<ProviderUser>(
|
||||
"[dbo].[ProviderUser_ReadByOrganizationIdStatus]",
|
||||
new { OrganizationId = organizationId, Status = status },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return results.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -180,4 +180,19 @@ public class ProviderUserRepository :
|
||||
.CountAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<ProviderUser>> GetManyByOrganizationAsync(Guid organizationId, ProviderUserStatusType? status = null)
|
||||
{
|
||||
using (var scope = ServiceScopeFactory.CreateScope())
|
||||
{
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
var query = from pu in dbContext.ProviderUsers
|
||||
join po in dbContext.ProviderOrganizations
|
||||
on pu.ProviderId equals po.ProviderId
|
||||
where po.OrganizationId == organizationId &&
|
||||
(status == null || pu.Status == status)
|
||||
select pu;
|
||||
return await query.ToArrayAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
CREATE PROCEDURE [dbo].[ProviderUser_ReadByOrganizationIdStatus]
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@Status TINYINT
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
PU.*
|
||||
FROM
|
||||
[dbo].[ProviderUserView] PU
|
||||
INNER JOIN [dbo].[ProviderOrganizationView] as PO
|
||||
ON PU.[ProviderId] = PO.[ProviderId]
|
||||
WHERE
|
||||
PO.[OrganizationId] = @OrganizationId
|
||||
AND (@Status IS NULL OR PU.[Status] = @Status)
|
||||
END
|
@ -6,7 +6,9 @@ using Bit.Core.Auth.Models.Data;
|
||||
using Bit.Core.Auth.Repositories;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Entities.Provider;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Enums.Provider;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Models.Data;
|
||||
@ -1415,4 +1417,56 @@ public class OrganizationServiceTests
|
||||
await eventService.Received()
|
||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored, eventSystemUser);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task HasConfirmedOwnersExcept_WithConfirmedOwner_ReturnsTrue(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
||||
.Returns(new List<OrganizationUser> { owner });
|
||||
|
||||
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organization.Id, new List<Guid>(), true);
|
||||
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task HasConfirmedOwnersExcept_ExcludingConfirmedOwner_ReturnsFalse(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
||||
.Returns(new List<OrganizationUser> { owner });
|
||||
|
||||
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organization.Id, new List<Guid> { owner.Id }, true);
|
||||
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task HasConfirmedOwnersExcept_WithInvitedOwner_ReturnsFalse(Organization organization, [OrganizationUser(OrganizationUserStatusType.Invited, OrganizationUserType.Owner)] OrganizationUser owner, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
||||
.Returns(new List<OrganizationUser> { owner });
|
||||
|
||||
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organization.Id, new List<Guid>(), true);
|
||||
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(true)]
|
||||
[BitAutoData(false)]
|
||||
public async Task HasConfirmedOwnersExcept_WithConfirmedProviderUser_IncludeProviderTrue_ReturnsTrue(bool includeProvider, Organization organization, ProviderUser providerUser, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
providerUser.Status = ProviderUserStatusType.Confirmed;
|
||||
|
||||
sutProvider.GetDependency<IProviderUserRepository>()
|
||||
.GetManyByOrganizationAsync(organization.Id, ProviderUserStatusType.Confirmed)
|
||||
.Returns(new List<ProviderUser> { providerUser });
|
||||
|
||||
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organization.Id, new List<Guid>(), includeProvider);
|
||||
|
||||
Assert.Equal(includeProvider, result);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,18 @@
|
||||
CREATE OR ALTER PROCEDURE [dbo].[ProviderUser_ReadByOrganizationIdStatus]
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@Status TINYINT
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
PU.*
|
||||
FROM
|
||||
[dbo].[ProviderUserView] PU
|
||||
INNER JOIN [dbo].[ProviderOrganizationView] as PO
|
||||
ON PU.[ProviderId] = PO.[ProviderId]
|
||||
WHERE
|
||||
PO.[OrganizationId] = @OrganizationId
|
||||
AND (@Status IS NULL OR PU.[Status] = @Status)
|
||||
END
|
||||
GO
|
Loading…
Reference in New Issue
Block a user