From 9f4235770588a87df2d2346d6581e66bbed1d2cc Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Wed, 10 Feb 2021 09:06:42 +1000 Subject: [PATCH] Improved handling of grantor access to organizations after takeover (refactored) (#1134) * Revert "Only return policy in TakeoverResponse if Owner" This reverts commit b20e6f5e856411df061d9ecc6c7504532e59e556. * Revert "Return grantor policy info in TakeoverResponse" This reverts commit 204217a5e0ef9c5febb1940ab8eb7ed3e81af9d9. * Add endpoint to get grantor policies on takeover --- .../Controllers/EmergencyAccessController.cs | 16 +++++++++++--- .../Response/EmergencyAccessResponseModel.cs | 4 +--- src/Core/Services/IEmergencyAccessService.cs | 3 ++- .../Implementations/EmergencyAccessService.cs | 21 ++++++++++++++++--- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/Api/Controllers/EmergencyAccessController.cs b/src/Api/Controllers/EmergencyAccessController.cs index 594cea142..dffdfcd6d 100644 --- a/src/Api/Controllers/EmergencyAccessController.cs +++ b/src/Api/Controllers/EmergencyAccessController.cs @@ -5,6 +5,7 @@ using Bit.Core.Exceptions; using Bit.Core.Models.Api; using Bit.Core.Models.Api.Request; using Bit.Core.Models.Api.Response; +using Bit.Core.Models.Table; using Bit.Core.Repositories; using Bit.Core.Services; using Microsoft.AspNetCore.Authorization; @@ -60,7 +61,16 @@ namespace Bit.Api.Controllers var result = await _emergencyAccessService.GetAsync(new Guid(id), userId.Value); return new EmergencyAccessGranteeDetailsResponseModel(result); } - + + [HttpGet("{id}/policies")] + public async Task> Policies(string id) + { + var user = await _userService.GetUserByPrincipalAsync(User); + var policies = await _emergencyAccessService.GetPoliciesAsync(new Guid(id), user); + var responses = policies.Select(policy => new PolicyResponseModel(policy)); + return new ListResponseModel(responses); + } + [HttpPut("{id}")] [HttpPost("{id}")] public async Task Put(string id, [FromBody]EmergencyAccessUpdateRequestModel model) @@ -136,8 +146,8 @@ namespace Bit.Api.Controllers public async Task Takeover(string id) { var user = await _userService.GetUserByPrincipalAsync(User); - var (result, grantor, policy) = await _emergencyAccessService.TakeoverAsync(new Guid(id), user); - return new EmergencyAccessTakeoverResponseModel(result, grantor, policy); + var (result, grantor) = await _emergencyAccessService.TakeoverAsync(new Guid(id), user); + return new EmergencyAccessTakeoverResponseModel(result, grantor); } [HttpPost("{id}/password")] diff --git a/src/Core/Models/Api/Response/EmergencyAccessResponseModel.cs b/src/Core/Models/Api/Response/EmergencyAccessResponseModel.cs index 6515d1023..4545f8261 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, ICollection policy, string obj = "emergencyAccessTakeover") : base(obj) + public EmergencyAccessTakeoverResponseModel(EmergencyAccess emergencyAccess, User grantor, string obj = "emergencyAccessTakeover") : base(obj) { if (emergencyAccess == null) { @@ -94,13 +94,11 @@ 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 c8a1a4614..aa28d0021 100644 --- a/src/Core/Services/IEmergencyAccessService.cs +++ b/src/Core/Services/IEmergencyAccessService.cs @@ -20,7 +20,8 @@ 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, ICollection)> TakeoverAsync(Guid id, User initiatingUser); + Task> GetPoliciesAsync(Guid id, User requestingUser); + Task<(EmergencyAccess, User)> 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 7afd5654a..c05cea2f3 100644 --- a/src/Core/Services/Implementations/EmergencyAccessService.cs +++ b/src/Core/Services/Implementations/EmergencyAccessService.cs @@ -239,7 +239,7 @@ namespace Bit.Core.Services await _mailService.SendEmergencyAccessRecoveryRejected(emergencyAccess, NameOrEmail(rejectingUser), grantee.Email); } - public async Task<(EmergencyAccess, User, ICollection)> TakeoverAsync(Guid id, User requestingUser) + public async Task> GetPoliciesAsync(Guid id, User requestingUser) { var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(id); @@ -253,9 +253,24 @@ namespace Bit.Core.Services 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; + var policies = isOrganizationOwner ? await _policyRepository.GetManyByUserIdAsync(grantor.Id) : null; - return (emergencyAccess, grantor, policy); + return policies; + } + + public async Task<(EmergencyAccess, User)> TakeoverAsync(Guid id, User requestingUser) + { + var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(id); + + if (emergencyAccess == null || emergencyAccess.GranteeId != requestingUser.Id || + emergencyAccess.Status != EmergencyAccessStatusType.RecoveryApproved) + { + throw new BadRequestException("Emergency Access not valid."); + } + + var grantor = await _userRepository.GetByIdAsync(emergencyAccess.GrantorId); + + return (emergencyAccess, grantor); } public async Task PasswordAsync(Guid id, User requestingUser, string newMasterPasswordHash, string key)