11 changed files with 256 additions and 223 deletions
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
using Bit.Core.Entities; |
||||
using Bit.Core.Enums; |
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; |
||||
|
||||
public interface IRevokeOrganizationUserCommand |
||||
{ |
||||
Task RevokeUserAsync(OrganizationUser organizationUser, Guid? revokingUserId); |
||||
Task RevokeUserAsync(OrganizationUser organizationUser, EventSystemUser systemUser); |
||||
Task<List<Tuple<OrganizationUser, string>>> RevokeUsersAsync(Guid organizationId, |
||||
IEnumerable<Guid> organizationUserIds, Guid? revokingUserId); |
||||
} |
||||
@ -0,0 +1,135 @@
@@ -0,0 +1,135 @@
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; |
||||
using Bit.Core.Context; |
||||
using Bit.Core.Entities; |
||||
using Bit.Core.Enums; |
||||
using Bit.Core.Exceptions; |
||||
using Bit.Core.Platform.Push; |
||||
using Bit.Core.Repositories; |
||||
using Bit.Core.Services; |
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers; |
||||
|
||||
public class RevokeOrganizationUserCommand( |
||||
IEventService eventService, |
||||
IPushNotificationService pushNotificationService, |
||||
IOrganizationUserRepository organizationUserRepository, |
||||
ICurrentContext currentContext, |
||||
IHasConfirmedOwnersExceptQuery hasConfirmedOwnersExceptQuery) |
||||
: IRevokeOrganizationUserCommand |
||||
{ |
||||
public async Task RevokeUserAsync(OrganizationUser organizationUser, Guid? revokingUserId) |
||||
{ |
||||
if (revokingUserId.HasValue && organizationUser.UserId == revokingUserId.Value) |
||||
{ |
||||
throw new BadRequestException("You cannot revoke yourself."); |
||||
} |
||||
|
||||
if (organizationUser.Type == OrganizationUserType.Owner && revokingUserId.HasValue && |
||||
!await currentContext.OrganizationOwner(organizationUser.OrganizationId)) |
||||
{ |
||||
throw new BadRequestException("Only owners can revoke other owners."); |
||||
} |
||||
|
||||
await RepositoryRevokeUserAsync(organizationUser); |
||||
await eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked); |
||||
|
||||
if (organizationUser.UserId.HasValue) |
||||
{ |
||||
await pushNotificationService.PushSyncOrgKeysAsync(organizationUser.UserId.Value); |
||||
} |
||||
} |
||||
|
||||
public async Task RevokeUserAsync(OrganizationUser organizationUser, |
||||
EventSystemUser systemUser) |
||||
{ |
||||
await RepositoryRevokeUserAsync(organizationUser); |
||||
await eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked, |
||||
systemUser); |
||||
|
||||
if (organizationUser.UserId.HasValue) |
||||
{ |
||||
await pushNotificationService.PushSyncOrgKeysAsync(organizationUser.UserId.Value); |
||||
} |
||||
} |
||||
|
||||
private async Task RepositoryRevokeUserAsync(OrganizationUser organizationUser) |
||||
{ |
||||
if (organizationUser.Status == OrganizationUserStatusType.Revoked) |
||||
{ |
||||
throw new BadRequestException("Already revoked."); |
||||
} |
||||
|
||||
if (!await hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(organizationUser.OrganizationId, |
||||
new[] { organizationUser.Id }, includeProvider: true)) |
||||
{ |
||||
throw new BadRequestException("Organization must have at least one confirmed owner."); |
||||
} |
||||
|
||||
await organizationUserRepository.RevokeAsync(organizationUser.Id); |
||||
organizationUser.Status = OrganizationUserStatusType.Revoked; |
||||
} |
||||
|
||||
public async Task<List<Tuple<OrganizationUser, string>>> RevokeUsersAsync(Guid organizationId, |
||||
IEnumerable<Guid> organizationUserIds, Guid? revokingUserId) |
||||
{ |
||||
var orgUsers = await organizationUserRepository.GetManyAsync(organizationUserIds); |
||||
var filteredUsers = orgUsers.Where(u => u.OrganizationId == organizationId) |
||||
.ToList(); |
||||
|
||||
if (!filteredUsers.Any()) |
||||
{ |
||||
throw new BadRequestException("Users invalid."); |
||||
} |
||||
|
||||
if (!await hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(organizationId, organizationUserIds)) |
||||
{ |
||||
throw new BadRequestException("Organization must have at least one confirmed owner."); |
||||
} |
||||
|
||||
var deletingUserIsOwner = false; |
||||
if (revokingUserId.HasValue) |
||||
{ |
||||
deletingUserIsOwner = await currentContext.OrganizationOwner(organizationId); |
||||
} |
||||
|
||||
var result = new List<Tuple<OrganizationUser, string>>(); |
||||
|
||||
foreach (var organizationUser in filteredUsers) |
||||
{ |
||||
try |
||||
{ |
||||
if (organizationUser.Status == OrganizationUserStatusType.Revoked) |
||||
{ |
||||
throw new BadRequestException("Already revoked."); |
||||
} |
||||
|
||||
if (revokingUserId.HasValue && organizationUser.UserId == revokingUserId) |
||||
{ |
||||
throw new BadRequestException("You cannot revoke yourself."); |
||||
} |
||||
|
||||
if (organizationUser.Type == OrganizationUserType.Owner && revokingUserId.HasValue && |
||||
!deletingUserIsOwner) |
||||
{ |
||||
throw new BadRequestException("Only owners can revoke other owners."); |
||||
} |
||||
|
||||
await organizationUserRepository.RevokeAsync(organizationUser.Id); |
||||
organizationUser.Status = OrganizationUserStatusType.Revoked; |
||||
await eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked); |
||||
if (organizationUser.UserId.HasValue) |
||||
{ |
||||
await pushNotificationService.PushSyncOrgKeysAsync(organizationUser.UserId.Value); |
||||
} |
||||
|
||||
result.Add(Tuple.Create(organizationUser, "")); |
||||
} |
||||
catch (BadRequestException e) |
||||
{ |
||||
result.Add(Tuple.Create(organizationUser, e.Message)); |
||||
} |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
} |
||||
@ -0,0 +1,83 @@
@@ -0,0 +1,83 @@
|
||||
using Bit.Core.AdminConsole.Entities; |
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers; |
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; |
||||
using Bit.Core.Context; |
||||
using Bit.Core.Entities; |
||||
using Bit.Core.Enums; |
||||
using Bit.Core.Platform.Push; |
||||
using Bit.Core.Repositories; |
||||
using Bit.Core.Services; |
||||
using Bit.Core.Test.AutoFixture.OrganizationUserFixtures; |
||||
using Bit.Test.Common.AutoFixture; |
||||
using Bit.Test.Common.AutoFixture.Attributes; |
||||
using NSubstitute; |
||||
using Xunit; |
||||
|
||||
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers; |
||||
|
||||
[SutProviderCustomize] |
||||
public class RevokeOrganizationUserCommandTests |
||||
{ |
||||
|
||||
[Theory, BitAutoData] |
||||
public async Task RevokeUser_Success( |
||||
Organization organization, |
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner, |
||||
[OrganizationUser] OrganizationUser organizationUser, |
||||
SutProvider<RevokeOrganizationUserCommand> sutProvider) |
||||
{ |
||||
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider); |
||||
|
||||
await sutProvider.Sut.RevokeUserAsync(organizationUser, owner.Id); |
||||
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>() |
||||
.Received(1) |
||||
.RevokeAsync(organizationUser.Id); |
||||
await sutProvider.GetDependency<IEventService>() |
||||
.Received(1) |
||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked); |
||||
await sutProvider.GetDependency<IPushNotificationService>() |
||||
.Received(1) |
||||
.PushSyncOrgKeysAsync(organizationUser.UserId!.Value); |
||||
} |
||||
|
||||
[Theory, BitAutoData] |
||||
public async Task RevokeUser_WithEventSystemUser_Success( |
||||
Organization organization, |
||||
[OrganizationUser] OrganizationUser organizationUser, |
||||
EventSystemUser eventSystemUser, |
||||
SutProvider<RevokeOrganizationUserCommand> sutProvider) |
||||
{ |
||||
RestoreRevokeUser_Setup(organization, null, organizationUser, sutProvider); |
||||
|
||||
await sutProvider.Sut.RevokeUserAsync(organizationUser, eventSystemUser); |
||||
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>() |
||||
.Received(1) |
||||
.RevokeAsync(organizationUser.Id); |
||||
await sutProvider.GetDependency<IEventService>() |
||||
.Received(1) |
||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked, eventSystemUser); |
||||
await sutProvider.GetDependency<IPushNotificationService>() |
||||
.Received(1) |
||||
.PushSyncOrgKeysAsync(organizationUser.UserId!.Value); |
||||
} |
||||
|
||||
private void RestoreRevokeUser_Setup( |
||||
Organization organization, |
||||
OrganizationUser? requestingOrganizationUser, |
||||
OrganizationUser targetOrganizationUser, |
||||
SutProvider<RevokeOrganizationUserCommand> sutProvider) |
||||
{ |
||||
if (requestingOrganizationUser != null) |
||||
{ |
||||
requestingOrganizationUser.OrganizationId = organization.Id; |
||||
} |
||||
targetOrganizationUser.OrganizationId = organization.Id; |
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(organization.Id).Returns(requestingOrganizationUser != null && requestingOrganizationUser.Type is OrganizationUserType.Owner); |
||||
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>() |
||||
.HasConfirmedOwnersExceptAsync(organization.Id, Arg.Any<IEnumerable<Guid>>()) |
||||
.Returns(true); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue