diff --git a/src/Api/Controllers/EmergencyAccessController.cs b/src/Api/Controllers/EmergencyAccessController.cs index ebe2e59f03..594cea1428 100644 --- a/src/Api/Controllers/EmergencyAccessController.cs +++ b/src/Api/Controllers/EmergencyAccessController.cs @@ -136,8 +136,8 @@ namespace Bit.Api.Controllers public async Task Takeover(string id) { var user = await _userService.GetUserByPrincipalAsync(User); - var (result, grantor) = await _emergencyAccessService.TakeoverAsync(new Guid(id), user); - return new EmergencyAccessTakeoverResponseModel(result, grantor); + var (result, grantor, policy) = await _emergencyAccessService.TakeoverAsync(new Guid(id), user); + return new EmergencyAccessTakeoverResponseModel(result, grantor, policy); } [HttpPost("{id}/password")] diff --git a/src/Core/Models/Api/Response/EmergencyAccessResponseModel.cs b/src/Core/Models/Api/Response/EmergencyAccessResponseModel.cs index 4545f82616..6515d1023d 100644 --- a/src/Core/Models/Api/Response/EmergencyAccessResponseModel.cs +++ b/src/Core/Models/Api/Response/EmergencyAccessResponseModel.cs @@ -84,7 +84,7 @@ namespace Bit.Core.Models.Api.Response public class EmergencyAccessTakeoverResponseModel : ResponseModel { - public EmergencyAccessTakeoverResponseModel(EmergencyAccess emergencyAccess, User grantor, string obj = "emergencyAccessTakeover") : base(obj) + public EmergencyAccessTakeoverResponseModel(EmergencyAccess emergencyAccess, User grantor, ICollection policy, string obj = "emergencyAccessTakeover") : base(obj) { if (emergencyAccess == null) { @@ -94,11 +94,13 @@ namespace Bit.Core.Models.Api.Response KeyEncrypted = emergencyAccess.KeyEncrypted; Kdf = grantor.Kdf; KdfIterations = grantor.KdfIterations; + Policy = policy?.Select(policy => new PolicyResponseModel(policy)); } public int KdfIterations { get; private set; } public KdfType Kdf { get; private set; } public string KeyEncrypted { get; private set; } + public IEnumerable Policy { get; private set; } } public class EmergencyAccessViewResponseModel : ResponseModel diff --git a/src/Core/Services/IEmergencyAccessService.cs b/src/Core/Services/IEmergencyAccessService.cs index b156d518c4..c8a1a46142 100644 --- a/src/Core/Services/IEmergencyAccessService.cs +++ b/src/Core/Services/IEmergencyAccessService.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; using Bit.Core.Enums; using Bit.Core.Models.Api.Response; @@ -19,7 +20,7 @@ namespace Bit.Core.Services Task InitiateAsync(Guid id, User initiatingUser); Task ApproveAsync(Guid id, User approvingUser); Task RejectAsync(Guid id, User rejectingUser); - Task<(EmergencyAccess, User)> TakeoverAsync(Guid id, User initiatingUser); + Task<(EmergencyAccess, User, ICollection)> TakeoverAsync(Guid id, User initiatingUser); Task PasswordAsync(Guid id, User user, string newMasterPasswordHash, string key); Task SendNotificationsAsync(); Task HandleTimedOutRequestsAsync(); diff --git a/src/Core/Services/Implementations/EmergencyAccessService.cs b/src/Core/Services/Implementations/EmergencyAccessService.cs index 6721603631..7afd5654a1 100644 --- a/src/Core/Services/Implementations/EmergencyAccessService.cs +++ b/src/Core/Services/Implementations/EmergencyAccessService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Bit.Core.Enums; using Bit.Core.Exceptions; @@ -17,32 +18,41 @@ namespace Bit.Core.Services public class EmergencyAccessService : IEmergencyAccessService { private readonly IEmergencyAccessRepository _emergencyAccessRepository; + private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IUserRepository _userRepository; private readonly ICipherRepository _cipherRepository; + private readonly IPolicyRepository _policyRepository; private readonly IMailService _mailService; private readonly IUserService _userService; private readonly IDataProtector _dataProtector; private readonly GlobalSettings _globalSettings; private readonly IPasswordHasher _passwordHasher; + private readonly IOrganizationService _organizationService; public EmergencyAccessService( IEmergencyAccessRepository emergencyAccessRepository, + IOrganizationUserRepository organizationUserRepository, IUserRepository userRepository, ICipherRepository cipherRepository, + IPolicyRepository policyRepository, IMailService mailService, IUserService userService, IPasswordHasher passwordHasher, IDataProtectionProvider dataProtectionProvider, - GlobalSettings globalSettings) + GlobalSettings globalSettings, + IOrganizationService organizationService) { _emergencyAccessRepository = emergencyAccessRepository; + _organizationUserRepository = organizationUserRepository; _userRepository = userRepository; _cipherRepository = cipherRepository; + _policyRepository = policyRepository; _mailService = mailService; _userService = userService; _passwordHasher = passwordHasher; _dataProtector = dataProtectionProvider.CreateProtector("EmergencyAccessServiceDataProtector"); _globalSettings = globalSettings; + _organizationService = organizationService; } public async Task InviteAsync(User invitingUser, string email, EmergencyAccessType type, int waitTime) @@ -229,7 +239,7 @@ namespace Bit.Core.Services await _mailService.SendEmergencyAccessRecoveryRejected(emergencyAccess, NameOrEmail(rejectingUser), grantee.Email); } - public async Task<(EmergencyAccess, User)> TakeoverAsync(Guid id, User requestingUser) + public async Task<(EmergencyAccess, User, ICollection)> TakeoverAsync(Guid id, User requestingUser) { var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(id); @@ -240,8 +250,12 @@ namespace Bit.Core.Services } var grantor = await _userRepository.GetByIdAsync(emergencyAccess.GrantorId); - - return (emergencyAccess, grantor); + + var grantorOrganizations = await _organizationUserRepository.GetManyByUserAsync(grantor.Id); + var isOrganizationOwner = grantorOrganizations.Any(organization => organization.Type == OrganizationUserType.Owner); + var policy = isOrganizationOwner ? await _policyRepository.GetManyByUserIdAsync(grantor.Id) : null; + + return (emergencyAccess, grantor, policy); } public async Task PasswordAsync(Guid id, User requestingUser, string newMasterPasswordHash, string key) @@ -261,6 +275,16 @@ namespace Bit.Core.Services // Disable TwoFactor providers since they will otherwise block logins grantor.SetTwoFactorProviders(new Dictionary()); await _userRepository.ReplaceAsync(grantor); + + // Remove grantor from all organisations unless Owner + var orgUser = await _organizationUserRepository.GetManyByUserAsync(grantor.Id); + foreach (var o in orgUser) + { + if (o.Type != OrganizationUserType.Owner) + { + await _organizationService.DeleteUserAsync(o.OrganizationId, grantor.Id); + } + } } public async Task SendNotificationsAsync()