1
0
mirror of https://github.com/bitwarden/server.git synced 2025-05-23 20:41:04 -05:00
bitwarden/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/RevokeNonCompliantOrganizationUserCommand.cs
Thomas Rittson 5b3d3d6e20
CommandResult and ValidationResult tweaks (#5772)
* Simplify and align CommandResult and ValidationResult.
  In particular, 1 error per Failure/Invalid.

* Move these files to a common namespace

* Remove unused code
2025-05-09 10:46:49 +10:00

113 lines
5.0 KiB
C#

using Bit.Core.AdminConsole.Models.Data;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Requests;
using Bit.Core.AdminConsole.Utilities.Commands;
using Bit.Core.Enums;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Repositories;
using Bit.Core.Services;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
public class RevokeNonCompliantOrganizationUserCommand(IOrganizationUserRepository organizationUserRepository,
IEventService eventService,
IHasConfirmedOwnersExceptQuery confirmedOwnersExceptQuery,
TimeProvider timeProvider) : IRevokeNonCompliantOrganizationUserCommand
{
public const string ErrorCannotRevokeSelf = "You cannot revoke yourself.";
public const string ErrorOnlyOwnersCanRevokeOtherOwners = "Only owners can revoke other owners.";
public const string ErrorUserAlreadyRevoked = "User is already revoked.";
public const string ErrorOrgMustHaveAtLeastOneOwner = "Organization must have at least one confirmed owner.";
public const string ErrorInvalidUsers = "Invalid users.";
public const string ErrorRequestedByWasNotValid = "Action was performed by an unexpected type.";
public async Task<CommandResult> RevokeNonCompliantOrganizationUsersAsync(RevokeOrganizationUsersRequest request)
{
var validationResult = await ValidateAsync(request);
if (validationResult.HasErrors)
{
return validationResult;
}
await organizationUserRepository.RevokeManyByIdAsync(request.OrganizationUsers.Select(x => x.Id));
var now = timeProvider.GetUtcNow();
switch (request.ActionPerformedBy)
{
case StandardUser:
await eventService.LogOrganizationUserEventsAsync(
request.OrganizationUsers.Select(x => GetRevokedUserEventTuple(x, now)));
break;
case SystemUser { SystemUserType: not null } loggableSystem:
await eventService.LogOrganizationUserEventsAsync(
request.OrganizationUsers.Select(x =>
GetRevokedUserEventBySystemUserTuple(x, loggableSystem.SystemUserType.Value, now)));
break;
}
return validationResult;
}
private static (OrganizationUserUserDetails organizationUser, EventType eventType, DateTime? time) GetRevokedUserEventTuple(
OrganizationUserUserDetails organizationUser, DateTimeOffset dateTimeOffset) =>
new(organizationUser, EventType.OrganizationUser_Revoked, dateTimeOffset.UtcDateTime);
private static (OrganizationUserUserDetails organizationUser, EventType eventType, EventSystemUser eventSystemUser, DateTime? time) GetRevokedUserEventBySystemUserTuple(
OrganizationUserUserDetails organizationUser, EventSystemUser systemUser, DateTimeOffset dateTimeOffset) => new(organizationUser,
EventType.OrganizationUser_Revoked, systemUser, dateTimeOffset.UtcDateTime);
private async Task<CommandResult> ValidateAsync(RevokeOrganizationUsersRequest request)
{
if (!PerformedByIsAnExpectedType(request.ActionPerformedBy))
{
return new CommandResult(ErrorRequestedByWasNotValid);
}
if (request.ActionPerformedBy is StandardUser user
&& request.OrganizationUsers.Any(x => x.UserId == user.UserId))
{
return new CommandResult(ErrorCannotRevokeSelf);
}
if (request.OrganizationUsers.Any(x => x.OrganizationId != request.OrganizationId))
{
return new CommandResult(ErrorInvalidUsers);
}
if (!await confirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(
request.OrganizationId,
request.OrganizationUsers.Select(x => x.Id)))
{
return new CommandResult(ErrorOrgMustHaveAtLeastOneOwner);
}
return request.OrganizationUsers.Aggregate(new CommandResult(), (result, userToRevoke) =>
{
if (IsAlreadyRevoked(userToRevoke))
{
result.ErrorMessages.Add($"{ErrorUserAlreadyRevoked} Id: {userToRevoke.Id}");
return result;
}
if (NonOwnersCannotRevokeOwners(userToRevoke, request.ActionPerformedBy))
{
result.ErrorMessages.Add($"{ErrorOnlyOwnersCanRevokeOtherOwners}");
return result;
}
return result;
});
}
private static bool PerformedByIsAnExpectedType(IActingUser entity) => entity is SystemUser or StandardUser;
private static bool IsAlreadyRevoked(OrganizationUserUserDetails organizationUser) =>
organizationUser is { Status: OrganizationUserStatusType.Revoked };
private static bool NonOwnersCannotRevokeOwners(OrganizationUserUserDetails organizationUser,
IActingUser actingUser) =>
actingUser is StandardUser { IsOrganizationOwnerOrProvider: false } && organizationUser.Type == OrganizationUserType.Owner;
}