mirror of
https://github.com/bitwarden/server
synced 2025-12-15 15:53:59 +00:00
* Allow for binning of comb IDs by date and value
* Introduce notification hub pool
* Replace device type sharding with comb + range sharding
* Fix proxy interface
* Use enumerable services for multiServiceNotificationHub
* Fix push interface usage
* Fix push notification service dependencies
* Fix push notification keys
* Fixup documentation
* Remove deprecated settings
* Fix tests
* PascalCase method names
* Remove unused request model properties
* Remove unused setting
* Improve DateFromComb precision
* Prefer readonly service enumerable
* Pascal case template holes
* Name TryParse methods TryParse
* Apply suggestions from code review
Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com>
* AllClients is a set of clients and must be deduplicated
* Fix registration start time
* Add logging to initialization of a notification hub
* more logging
* Add lower level logging for hub settings
* Log when connection is resolved
* Improve log message
* Log pushes to notification hub
* temporarily elevate log messages for visibility
* Log in multi-service when relaying to another push service
* Revert to more reasonable logging free of user information
* Fixup merge
Deleting user was extracted to a command in #4803, this updates that work to use just the device ids as I did elsewhere in abd67e8ec
* Do not use bouncy castle exception types
* Add required services for logging
---------
Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com>
Co-authored-by: bnagawiecki <107435978+bnagawiecki@users.noreply.github.com>
181 lines
7.1 KiB
C#
181 lines
7.1 KiB
C#
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.Repositories;
|
|
using Bit.Core.Services;
|
|
|
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
|
|
|
public class RemoveOrganizationUserCommand : IRemoveOrganizationUserCommand
|
|
{
|
|
private readonly IDeviceRepository _deviceRepository;
|
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
|
private readonly IEventService _eventService;
|
|
private readonly IPushNotificationService _pushNotificationService;
|
|
private readonly IPushRegistrationService _pushRegistrationService;
|
|
private readonly ICurrentContext _currentContext;
|
|
private readonly IHasConfirmedOwnersExceptQuery _hasConfirmedOwnersExceptQuery;
|
|
|
|
public RemoveOrganizationUserCommand(
|
|
IDeviceRepository deviceRepository,
|
|
IOrganizationUserRepository organizationUserRepository,
|
|
IEventService eventService,
|
|
IPushNotificationService pushNotificationService,
|
|
IPushRegistrationService pushRegistrationService,
|
|
ICurrentContext currentContext,
|
|
IHasConfirmedOwnersExceptQuery hasConfirmedOwnersExceptQuery)
|
|
{
|
|
_deviceRepository = deviceRepository;
|
|
_organizationUserRepository = organizationUserRepository;
|
|
_eventService = eventService;
|
|
_pushNotificationService = pushNotificationService;
|
|
_pushRegistrationService = pushRegistrationService;
|
|
_currentContext = currentContext;
|
|
_hasConfirmedOwnersExceptQuery = hasConfirmedOwnersExceptQuery;
|
|
}
|
|
|
|
public async Task RemoveUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId)
|
|
{
|
|
var organizationUser = await _organizationUserRepository.GetByIdAsync(organizationUserId);
|
|
ValidateDeleteUser(organizationId, organizationUser);
|
|
|
|
await RepositoryDeleteUserAsync(organizationUser, deletingUserId);
|
|
|
|
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed);
|
|
}
|
|
|
|
public async Task RemoveUserAsync(Guid organizationId, Guid organizationUserId, EventSystemUser eventSystemUser)
|
|
{
|
|
var organizationUser = await _organizationUserRepository.GetByIdAsync(organizationUserId);
|
|
ValidateDeleteUser(organizationId, organizationUser);
|
|
|
|
await RepositoryDeleteUserAsync(organizationUser, null);
|
|
|
|
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed, eventSystemUser);
|
|
}
|
|
|
|
public async Task RemoveUserAsync(Guid organizationId, Guid userId)
|
|
{
|
|
var organizationUser = await _organizationUserRepository.GetByOrganizationAsync(organizationId, userId);
|
|
ValidateDeleteUser(organizationId, organizationUser);
|
|
|
|
await RepositoryDeleteUserAsync(organizationUser, null);
|
|
|
|
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed);
|
|
}
|
|
|
|
public async Task<List<Tuple<OrganizationUser, string>>> RemoveUsersAsync(Guid organizationId,
|
|
IEnumerable<Guid> organizationUsersId,
|
|
Guid? deletingUserId)
|
|
{
|
|
var orgUsers = await _organizationUserRepository.GetManyAsync(organizationUsersId);
|
|
var filteredUsers = orgUsers.Where(u => u.OrganizationId == organizationId)
|
|
.ToList();
|
|
|
|
if (!filteredUsers.Any())
|
|
{
|
|
throw new BadRequestException("Users invalid.");
|
|
}
|
|
|
|
if (!await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(organizationId, organizationUsersId))
|
|
{
|
|
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
|
}
|
|
|
|
var deletingUserIsOwner = false;
|
|
if (deletingUserId.HasValue)
|
|
{
|
|
deletingUserIsOwner = await _currentContext.OrganizationOwner(organizationId);
|
|
}
|
|
|
|
var result = new List<Tuple<OrganizationUser, string>>();
|
|
var deletedUserIds = new List<Guid>();
|
|
foreach (var orgUser in filteredUsers)
|
|
{
|
|
try
|
|
{
|
|
if (deletingUserId.HasValue && orgUser.UserId == deletingUserId)
|
|
{
|
|
throw new BadRequestException("You cannot remove yourself.");
|
|
}
|
|
|
|
if (orgUser.Type == OrganizationUserType.Owner && deletingUserId.HasValue && !deletingUserIsOwner)
|
|
{
|
|
throw new BadRequestException("Only owners can delete other owners.");
|
|
}
|
|
|
|
await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_Removed);
|
|
|
|
if (orgUser.UserId.HasValue)
|
|
{
|
|
await DeleteAndPushUserRegistrationAsync(organizationId, orgUser.UserId.Value);
|
|
}
|
|
result.Add(Tuple.Create(orgUser, ""));
|
|
deletedUserIds.Add(orgUser.Id);
|
|
}
|
|
catch (BadRequestException e)
|
|
{
|
|
result.Add(Tuple.Create(orgUser, e.Message));
|
|
}
|
|
|
|
await _organizationUserRepository.DeleteManyAsync(deletedUserIds);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private void ValidateDeleteUser(Guid organizationId, OrganizationUser orgUser)
|
|
{
|
|
if (orgUser == null || orgUser.OrganizationId != organizationId)
|
|
{
|
|
throw new NotFoundException("User not found.");
|
|
}
|
|
}
|
|
|
|
private async Task RepositoryDeleteUserAsync(OrganizationUser orgUser, Guid? deletingUserId)
|
|
{
|
|
if (deletingUserId.HasValue && orgUser.UserId == deletingUserId.Value)
|
|
{
|
|
throw new BadRequestException("You cannot remove yourself.");
|
|
}
|
|
|
|
if (orgUser.Type == OrganizationUserType.Owner)
|
|
{
|
|
if (deletingUserId.HasValue && !await _currentContext.OrganizationOwner(orgUser.OrganizationId))
|
|
{
|
|
throw new BadRequestException("Only owners can delete other owners.");
|
|
}
|
|
|
|
if (!await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(orgUser.OrganizationId, new[] { orgUser.Id }, includeProvider: true))
|
|
{
|
|
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
|
}
|
|
}
|
|
|
|
await _organizationUserRepository.DeleteAsync(orgUser);
|
|
|
|
if (orgUser.UserId.HasValue)
|
|
{
|
|
await DeleteAndPushUserRegistrationAsync(orgUser.OrganizationId, orgUser.UserId.Value);
|
|
}
|
|
}
|
|
|
|
private async Task<IEnumerable<string>> GetUserDeviceIdsAsync(Guid userId)
|
|
{
|
|
var devices = await _deviceRepository.GetManyByUserIdAsync(userId);
|
|
return devices
|
|
.Where(d => !string.IsNullOrWhiteSpace(d.PushToken))
|
|
.Select(d => d.Id.ToString());
|
|
}
|
|
|
|
private async Task DeleteAndPushUserRegistrationAsync(Guid organizationId, Guid userId)
|
|
{
|
|
var devices = await GetUserDeviceIdsAsync(userId);
|
|
await _pushRegistrationService.DeleteUserRegistrationOrganizationAsync(devices,
|
|
organizationId.ToString());
|
|
await _pushNotificationService.PushSyncOrgKeysAsync(userId);
|
|
}
|
|
}
|