mirror of
https://github.com/bitwarden/server.git
synced 2025-05-23 20:41:04 -05:00

* Simplify and align CommandResult and ValidationResult. In particular, 1 error per Failure/Invalid. * Move these files to a common namespace * Remove unused code
113 lines
5.0 KiB
C#
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;
|
|
}
|