Files
server/src/Core/Auth/Services/Implementations/EmergencyAccessService.cs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

433 lines
18 KiB
C#
Raw Normal View History

using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Auth.Entities;
[PM-1188] Server owner auth migration (#2825) * [PM-1188] add sso project to auth * [PM-1188] move sso api models to auth * [PM-1188] fix sso api model namespace & imports * [PM-1188] move core files to auth * [PM-1188] fix core sso namespace & models * [PM-1188] move sso repository files to auth * [PM-1188] fix sso repo files namespace & imports * [PM-1188] move sso sql files to auth folder * [PM-1188] move sso test files to auth folders * [PM-1188] fix sso tests namespace & imports * [PM-1188] move auth api files to auth folder * [PM-1188] fix auth api files namespace & imports * [PM-1188] move auth core files to auth folder * [PM-1188] fix auth core files namespace & imports * [PM-1188] move auth email templates to auth folder * [PM-1188] move auth email folder back into shared directory * [PM-1188] fix auth email names * [PM-1188] move auth core models to auth folder * [PM-1188] fix auth model namespace & imports * [PM-1188] add entire Identity project to auth codeowners * [PM-1188] fix auth orm files namespace & imports * [PM-1188] move auth orm files to auth folder * [PM-1188] move auth sql files to auth folder * [PM-1188] move auth tests to auth folder * [PM-1188] fix auth test files namespace & imports * [PM-1188] move emergency access api files to auth folder * [PM-1188] fix emergencyaccess api files namespace & imports * [PM-1188] move emergency access core files to auth folder * [PM-1188] fix emergency access core files namespace & imports * [PM-1188] move emergency access orm files to auth folder * [PM-1188] fix emergency access orm files namespace & imports * [PM-1188] move emergency access sql files to auth folder * [PM-1188] move emergencyaccess test files to auth folder * [PM-1188] fix emergency access test files namespace & imports * [PM-1188] move captcha files to auth folder * [PM-1188] fix captcha files namespace & imports * [PM-1188] move auth admin files into auth folder * [PM-1188] fix admin auth files namespace & imports - configure mvc to look in auth folders for views * [PM-1188] remove extra imports and formatting * [PM-1188] fix ef auth model imports * [PM-1188] fix DatabaseContextModelSnapshot paths * [PM-1188] fix grant import in ef * [PM-1188] update sqlproj * [PM-1188] move missed sqlproj files * [PM-1188] move auth ef models out of auth folder * [PM-1188] fix auth ef models namespace * [PM-1188] remove auth ef models unused imports * [PM-1188] fix imports for auth ef models * [PM-1188] fix more ef model imports * [PM-1188] fix file encodings
2023-04-14 13:25:56 -04:00
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Auth.Models.Data;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
[PM-1188] Server owner auth migration (#2825) * [PM-1188] add sso project to auth * [PM-1188] move sso api models to auth * [PM-1188] fix sso api model namespace & imports * [PM-1188] move core files to auth * [PM-1188] fix core sso namespace & models * [PM-1188] move sso repository files to auth * [PM-1188] fix sso repo files namespace & imports * [PM-1188] move sso sql files to auth folder * [PM-1188] move sso test files to auth folders * [PM-1188] fix sso tests namespace & imports * [PM-1188] move auth api files to auth folder * [PM-1188] fix auth api files namespace & imports * [PM-1188] move auth core files to auth folder * [PM-1188] fix auth core files namespace & imports * [PM-1188] move auth email templates to auth folder * [PM-1188] move auth email folder back into shared directory * [PM-1188] fix auth email names * [PM-1188] move auth core models to auth folder * [PM-1188] fix auth model namespace & imports * [PM-1188] add entire Identity project to auth codeowners * [PM-1188] fix auth orm files namespace & imports * [PM-1188] move auth orm files to auth folder * [PM-1188] move auth sql files to auth folder * [PM-1188] move auth tests to auth folder * [PM-1188] fix auth test files namespace & imports * [PM-1188] move emergency access api files to auth folder * [PM-1188] fix emergencyaccess api files namespace & imports * [PM-1188] move emergency access core files to auth folder * [PM-1188] fix emergency access core files namespace & imports * [PM-1188] move emergency access orm files to auth folder * [PM-1188] fix emergency access orm files namespace & imports * [PM-1188] move emergency access sql files to auth folder * [PM-1188] move emergencyaccess test files to auth folder * [PM-1188] fix emergency access test files namespace & imports * [PM-1188] move captcha files to auth folder * [PM-1188] fix captcha files namespace & imports * [PM-1188] move auth admin files into auth folder * [PM-1188] fix admin auth files namespace & imports - configure mvc to look in auth folders for views * [PM-1188] remove extra imports and formatting * [PM-1188] fix ef auth model imports * [PM-1188] fix DatabaseContextModelSnapshot paths * [PM-1188] fix grant import in ef * [PM-1188] update sqlproj * [PM-1188] move missed sqlproj files * [PM-1188] move auth ef models out of auth folder * [PM-1188] fix auth ef models namespace * [PM-1188] remove auth ef models unused imports * [PM-1188] fix imports for auth ef models * [PM-1188] fix more ef model imports * [PM-1188] fix file encodings
2023-04-14 13:25:56 -04:00
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Tokens;
using Bit.Core.Vault.Models.Data;
using Bit.Core.Vault.Repositories;
using Bit.Core.Vault.Services;
using Microsoft.AspNetCore.Identity;
[PM-1188] Server owner auth migration (#2825) * [PM-1188] add sso project to auth * [PM-1188] move sso api models to auth * [PM-1188] fix sso api model namespace & imports * [PM-1188] move core files to auth * [PM-1188] fix core sso namespace & models * [PM-1188] move sso repository files to auth * [PM-1188] fix sso repo files namespace & imports * [PM-1188] move sso sql files to auth folder * [PM-1188] move sso test files to auth folders * [PM-1188] fix sso tests namespace & imports * [PM-1188] move auth api files to auth folder * [PM-1188] fix auth api files namespace & imports * [PM-1188] move auth core files to auth folder * [PM-1188] fix auth core files namespace & imports * [PM-1188] move auth email templates to auth folder * [PM-1188] move auth email folder back into shared directory * [PM-1188] fix auth email names * [PM-1188] move auth core models to auth folder * [PM-1188] fix auth model namespace & imports * [PM-1188] add entire Identity project to auth codeowners * [PM-1188] fix auth orm files namespace & imports * [PM-1188] move auth orm files to auth folder * [PM-1188] move auth sql files to auth folder * [PM-1188] move auth tests to auth folder * [PM-1188] fix auth test files namespace & imports * [PM-1188] move emergency access api files to auth folder * [PM-1188] fix emergencyaccess api files namespace & imports * [PM-1188] move emergency access core files to auth folder * [PM-1188] fix emergency access core files namespace & imports * [PM-1188] move emergency access orm files to auth folder * [PM-1188] fix emergency access orm files namespace & imports * [PM-1188] move emergency access sql files to auth folder * [PM-1188] move emergencyaccess test files to auth folder * [PM-1188] fix emergency access test files namespace & imports * [PM-1188] move captcha files to auth folder * [PM-1188] fix captcha files namespace & imports * [PM-1188] move auth admin files into auth folder * [PM-1188] fix admin auth files namespace & imports - configure mvc to look in auth folders for views * [PM-1188] remove extra imports and formatting * [PM-1188] fix ef auth model imports * [PM-1188] fix DatabaseContextModelSnapshot paths * [PM-1188] fix grant import in ef * [PM-1188] update sqlproj * [PM-1188] move missed sqlproj files * [PM-1188] move auth ef models out of auth folder * [PM-1188] fix auth ef models namespace * [PM-1188] remove auth ef models unused imports * [PM-1188] fix imports for auth ef models * [PM-1188] fix more ef model imports * [PM-1188] fix file encodings
2023-04-14 13:25:56 -04:00
namespace Bit.Core.Auth.Services;
2022-08-29 16:06:55 -04:00
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 ICipherService _cipherService;
private readonly IMailService _mailService;
private readonly IUserService _userService;
private readonly GlobalSettings _globalSettings;
private readonly IPasswordHasher<User> _passwordHasher;
private readonly IOrganizationService _organizationService;
private readonly IDataProtectorTokenFactory<EmergencyAccessInviteTokenable> _dataProtectorTokenizer;
2022-08-29 16:06:55 -04:00
public EmergencyAccessService(
IEmergencyAccessRepository emergencyAccessRepository,
IOrganizationUserRepository organizationUserRepository,
IUserRepository userRepository,
ICipherRepository cipherRepository,
IPolicyRepository policyRepository,
ICipherService cipherService,
IMailService mailService,
IUserService userService,
IPasswordHasher<User> passwordHasher,
GlobalSettings globalSettings,
IOrganizationService organizationService,
IDataProtectorTokenFactory<EmergencyAccessInviteTokenable> dataProtectorTokenizer)
{
_emergencyAccessRepository = emergencyAccessRepository;
_organizationUserRepository = organizationUserRepository;
_userRepository = userRepository;
_cipherRepository = cipherRepository;
_policyRepository = policyRepository;
_cipherService = cipherService;
_mailService = mailService;
_userService = userService;
_passwordHasher = passwordHasher;
_globalSettings = globalSettings;
_organizationService = organizationService;
_dataProtectorTokenizer = dataProtectorTokenizer;
2022-08-29 16:06:55 -04:00
}
public async Task<EmergencyAccess> InviteAsync(User invitingUser, string email, EmergencyAccessType type, int waitTime)
2022-08-29 16:06:55 -04:00
{
if (!await _userService.CanAccessPremium(invitingUser))
{
throw new BadRequestException("Not a premium user.");
}
if (type == EmergencyAccessType.Takeover && invitingUser.UsesKeyConnector)
{
throw new BadRequestException("You cannot use Emergency Access Takeover because you are using Key Connector.");
}
var emergencyAccess = new EmergencyAccess
{
GrantorId = invitingUser.Id,
Email = email.ToLowerInvariant(),
Status = EmergencyAccessStatusType.Invited,
Type = type,
WaitTimeDays = waitTime,
CreationDate = DateTime.UtcNow,
RevisionDate = DateTime.UtcNow,
2022-08-29 16:06:55 -04:00
};
await _emergencyAccessRepository.CreateAsync(emergencyAccess);
await SendInviteAsync(emergencyAccess, NameOrEmail(invitingUser));
2022-08-29 16:06:55 -04:00
return emergencyAccess;
}
public async Task<EmergencyAccessDetails> GetAsync(Guid emergencyAccessId, Guid userId)
2022-08-29 16:06:55 -04:00
{
var emergencyAccess = await _emergencyAccessRepository.GetDetailsByIdGrantorIdAsync(emergencyAccessId, userId);
if (emergencyAccess == null)
2022-08-29 16:06:55 -04:00
{
throw new BadRequestException("Emergency Access not valid.");
}
return emergencyAccess;
2022-08-29 16:06:55 -04:00
}
public async Task ResendInviteAsync(User invitingUser, Guid emergencyAccessId)
2022-08-29 16:06:55 -04:00
{
var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(emergencyAccessId);
if (emergencyAccess == null || emergencyAccess.GrantorId != invitingUser.Id ||
emergencyAccess.Status != EmergencyAccessStatusType.Invited)
{
throw new BadRequestException("Emergency Access not valid.");
2022-08-29 16:06:55 -04:00
}
await SendInviteAsync(emergencyAccess, NameOrEmail(invitingUser));
}
public async Task<EmergencyAccess> AcceptUserAsync(Guid emergencyAccessId, User user, string token, IUserService userService)
2022-08-29 16:06:55 -04:00
{
var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(emergencyAccessId);
if (emergencyAccess == null)
2022-08-29 16:06:55 -04:00
{
throw new BadRequestException("Emergency Access not valid.");
}
if (!_dataProtectorTokenizer.TryUnprotect(token, out var data) && data.IsValid(emergencyAccessId, user.Email))
2022-08-29 14:53:16 -04:00
{
throw new BadRequestException("Invalid token.");
}
if (emergencyAccess.Status == EmergencyAccessStatusType.Accepted)
2022-08-29 16:06:55 -04:00
{
throw new BadRequestException("Invitation already accepted. You will receive an email when the grantor confirms you as an emergency access contact.");
2022-08-29 16:06:55 -04:00
}
else if (emergencyAccess.Status != EmergencyAccessStatusType.Invited)
{
throw new BadRequestException("Invitation already accepted.");
}
if (string.IsNullOrWhiteSpace(emergencyAccess.Email) ||
!emergencyAccess.Email.Equals(user.Email, StringComparison.InvariantCultureIgnoreCase))
2022-08-29 16:06:55 -04:00
{
throw new BadRequestException("User email does not match invite.");
}
var granteeEmail = emergencyAccess.Email;
emergencyAccess.Status = EmergencyAccessStatusType.Accepted;
emergencyAccess.GranteeId = user.Id;
emergencyAccess.Email = null;
var grantor = await userService.GetUserByIdAsync(emergencyAccess.GrantorId);
await _emergencyAccessRepository.ReplaceAsync(emergencyAccess);
await _mailService.SendEmergencyAccessAcceptedEmailAsync(granteeEmail, grantor.Email);
return emergencyAccess;
2022-08-29 16:06:55 -04:00
}
public async Task DeleteAsync(Guid emergencyAccessId, Guid grantorId)
2022-08-29 16:06:55 -04:00
{
var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(emergencyAccessId);
if (emergencyAccess == null || (emergencyAccess.GrantorId != grantorId && emergencyAccess.GranteeId != grantorId))
2022-08-29 16:06:55 -04:00
{
throw new BadRequestException("Emergency Access not valid.");
}
await _emergencyAccessRepository.DeleteAsync(emergencyAccess);
}
public async Task<EmergencyAccess> ConfirmUserAsync(Guid emergencyAcccessId, string key, Guid confirmingUserId)
2022-08-29 16:06:55 -04:00
{
var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(emergencyAcccessId);
if (emergencyAccess == null || emergencyAccess.Status != EmergencyAccessStatusType.Accepted ||
emergencyAccess.GrantorId != confirmingUserId)
2022-08-29 16:06:55 -04:00
{
throw new BadRequestException("Emergency Access not valid.");
}
var grantor = await _userRepository.GetByIdAsync(confirmingUserId);
if (emergencyAccess.Type == EmergencyAccessType.Takeover && grantor.UsesKeyConnector)
{
throw new BadRequestException("You cannot use Emergency Access Takeover because you are using Key Connector.");
}
var grantee = await _userRepository.GetByIdAsync(emergencyAccess.GranteeId.Value);
emergencyAccess.Status = EmergencyAccessStatusType.Confirmed;
emergencyAccess.KeyEncrypted = key;
emergencyAccess.Email = null;
await _emergencyAccessRepository.ReplaceAsync(emergencyAccess);
await _mailService.SendEmergencyAccessConfirmedEmailAsync(NameOrEmail(grantor), grantee.Email);
return emergencyAccess;
2022-08-29 16:06:55 -04:00
}
public async Task SaveAsync(EmergencyAccess emergencyAccess, User savingUser)
2022-08-29 16:06:55 -04:00
{
if (!await _userService.CanAccessPremium(savingUser))
2022-08-29 16:06:55 -04:00
{
throw new BadRequestException("Not a premium user.");
}
if (emergencyAccess.GrantorId != savingUser.Id)
{
throw new BadRequestException("Emergency Access not valid.");
}
if (emergencyAccess.Type == EmergencyAccessType.Takeover)
{
var grantor = await _userService.GetUserByIdAsync(emergencyAccess.GrantorId);
if (grantor.UsesKeyConnector)
{
throw new BadRequestException("You cannot use Emergency Access Takeover because you are using Key Connector.");
}
2022-08-29 16:06:55 -04:00
}
await _emergencyAccessRepository.ReplaceAsync(emergencyAccess);
}
public async Task InitiateAsync(Guid id, User initiatingUser)
2022-08-29 16:06:55 -04:00
{
var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(id);
if (emergencyAccess == null || emergencyAccess.GranteeId != initiatingUser.Id ||
emergencyAccess.Status != EmergencyAccessStatusType.Confirmed)
2022-08-29 16:06:55 -04:00
{
throw new BadRequestException("Emergency Access not valid.");
2022-08-29 14:53:16 -04:00
}
var grantor = await _userRepository.GetByIdAsync(emergencyAccess.GrantorId);
2022-08-29 16:06:55 -04:00
if (emergencyAccess.Type == EmergencyAccessType.Takeover && grantor.UsesKeyConnector)
{
throw new BadRequestException("You cannot takeover an account that is using Key Connector.");
2022-08-29 16:06:55 -04:00
}
2021-12-16 15:35:09 +01:00
var now = DateTime.UtcNow;
emergencyAccess.Status = EmergencyAccessStatusType.RecoveryInitiated;
emergencyAccess.RevisionDate = now;
emergencyAccess.RecoveryInitiatedDate = now;
emergencyAccess.LastNotificationDate = now;
await _emergencyAccessRepository.ReplaceAsync(emergencyAccess);
2021-12-16 15:35:09 +01:00
await _mailService.SendEmergencyAccessRecoveryInitiated(emergencyAccess, NameOrEmail(initiatingUser), grantor.Email);
2022-08-29 16:06:55 -04:00
}
public async Task ApproveAsync(Guid id, User approvingUser)
2022-08-29 16:06:55 -04:00
{
var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(id);
if (emergencyAccess == null || emergencyAccess.GrantorId != approvingUser.Id ||
emergencyAccess.Status != EmergencyAccessStatusType.RecoveryInitiated)
{
throw new BadRequestException("Emergency Access not valid.");
2022-08-29 16:06:55 -04:00
}
emergencyAccess.Status = EmergencyAccessStatusType.RecoveryApproved;
await _emergencyAccessRepository.ReplaceAsync(emergencyAccess);
var grantee = await _userRepository.GetByIdAsync(emergencyAccess.GranteeId.Value);
await _mailService.SendEmergencyAccessRecoveryApproved(emergencyAccess, NameOrEmail(approvingUser), grantee.Email);
2022-08-29 16:06:55 -04:00
}
public async Task RejectAsync(Guid id, User rejectingUser)
2022-08-29 16:06:55 -04:00
{
var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(id);
if (emergencyAccess == null || emergencyAccess.GrantorId != rejectingUser.Id ||
(emergencyAccess.Status != EmergencyAccessStatusType.RecoveryInitiated &&
emergencyAccess.Status != EmergencyAccessStatusType.RecoveryApproved))
{
throw new BadRequestException("Emergency Access not valid.");
}
emergencyAccess.Status = EmergencyAccessStatusType.Confirmed;
await _emergencyAccessRepository.ReplaceAsync(emergencyAccess);
var grantee = await _userRepository.GetByIdAsync(emergencyAccess.GranteeId.Value);
await _mailService.SendEmergencyAccessRecoveryRejected(emergencyAccess, NameOrEmail(rejectingUser), grantee.Email);
2022-08-29 16:06:55 -04:00
}
public async Task<ICollection<Policy>> GetPoliciesAsync(Guid id, User requestingUser)
2022-08-29 16:06:55 -04:00
{
var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(id);
if (!IsValidRequest(emergencyAccess, requestingUser, EmergencyAccessType.Takeover))
2022-08-29 14:53:16 -04:00
{
throw new BadRequestException("Emergency Access not valid.");
2022-08-29 16:06:55 -04:00
}
var grantor = await _userRepository.GetByIdAsync(emergencyAccess.GrantorId);
var grantorOrganizations = await _organizationUserRepository.GetManyByUserAsync(grantor.Id);
var isOrganizationOwner = grantorOrganizations.Any<OrganizationUser>(organization => organization.Type == OrganizationUserType.Owner);
var policies = isOrganizationOwner ? await _policyRepository.GetManyByUserIdAsync(grantor.Id) : null;
return policies;
}
public async Task<(EmergencyAccess, User)> TakeoverAsync(Guid id, User requestingUser)
2022-08-29 16:06:55 -04:00
{
var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(id);
if (!IsValidRequest(emergencyAccess, requestingUser, EmergencyAccessType.Takeover))
{
throw new BadRequestException("Emergency Access not valid.");
2022-08-29 16:06:55 -04:00
}
var grantor = await _userRepository.GetByIdAsync(emergencyAccess.GrantorId);
if (emergencyAccess.Type == EmergencyAccessType.Takeover && grantor.UsesKeyConnector)
2022-08-29 16:06:55 -04:00
{
throw new BadRequestException("You cannot takeover an account that is using Key Connector.");
2022-08-29 16:06:55 -04:00
}
return (emergencyAccess, grantor);
2022-08-29 16:06:55 -04:00
}
public async Task PasswordAsync(Guid id, User requestingUser, string newMasterPasswordHash, string key)
2022-08-29 16:06:55 -04:00
{
var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(id);
2021-12-14 15:05:07 +00:00
if (!IsValidRequest(emergencyAccess, requestingUser, EmergencyAccessType.Takeover))
{
throw new BadRequestException("Emergency Access not valid.");
2022-08-29 16:06:55 -04:00
}
2021-12-14 15:05:07 +00:00
var grantor = await _userRepository.GetByIdAsync(emergencyAccess.GrantorId);
await _userService.UpdatePasswordHash(grantor, newMasterPasswordHash);
grantor.RevisionDate = DateTime.UtcNow;
grantor.LastPasswordChangeDate = grantor.RevisionDate;
grantor.Key = key;
// Disable TwoFactor providers since they will otherwise block logins
grantor.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>());
await _userRepository.ReplaceAsync(grantor);
2022-08-29 14:53:16 -04:00
// Remove grantor from all organizations unless Owner
var orgUser = await _organizationUserRepository.GetManyByUserAsync(grantor.Id);
foreach (var o in orgUser)
{
if (o.Type != OrganizationUserType.Owner)
{
await _organizationService.RemoveUserAsync(o.OrganizationId, grantor.Id);
}
}
2022-08-29 16:06:55 -04:00
}
2022-08-29 14:53:16 -04:00
public async Task SendNotificationsAsync()
{
var toNotify = await _emergencyAccessRepository.GetManyToNotifyAsync();
foreach (var notify in toNotify)
{
var ea = notify.ToEmergencyAccess();
ea.LastNotificationDate = DateTime.UtcNow;
await _emergencyAccessRepository.ReplaceAsync(ea);
2022-08-29 14:53:16 -04:00
var granteeNameOrEmail = string.IsNullOrWhiteSpace(notify.GranteeName) ? notify.GranteeEmail : notify.GranteeName;
await _mailService.SendEmergencyAccessRecoveryReminder(ea, granteeNameOrEmail, notify.GrantorEmail);
}
2022-08-29 16:06:55 -04:00
}
2022-08-29 14:53:16 -04:00
public async Task HandleTimedOutRequestsAsync()
2022-08-29 16:06:55 -04:00
{
var expired = await _emergencyAccessRepository.GetExpiredRecoveriesAsync();
2022-08-29 16:06:55 -04:00
foreach (var details in expired)
{
var ea = details.ToEmergencyAccess();
ea.Status = EmergencyAccessStatusType.RecoveryApproved;
await _emergencyAccessRepository.ReplaceAsync(ea);
2022-08-29 14:53:16 -04:00
var grantorNameOrEmail = string.IsNullOrWhiteSpace(details.GrantorName) ? details.GrantorEmail : details.GrantorName;
var granteeNameOrEmail = string.IsNullOrWhiteSpace(details.GranteeName) ? details.GranteeEmail : details.GranteeName;
2022-08-29 14:53:16 -04:00
await _mailService.SendEmergencyAccessRecoveryApproved(ea, grantorNameOrEmail, details.GranteeEmail);
await _mailService.SendEmergencyAccessRecoveryTimedOut(ea, granteeNameOrEmail, details.GrantorEmail);
2022-08-29 14:53:16 -04:00
}
2022-08-29 16:06:55 -04:00
}
2022-08-29 14:53:16 -04:00
public async Task<EmergencyAccessViewData> ViewAsync(Guid id, User requestingUser)
2022-08-29 16:06:55 -04:00
{
var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(id);
2022-08-29 16:06:55 -04:00
if (!IsValidRequest(emergencyAccess, requestingUser, EmergencyAccessType.View))
2022-08-29 14:53:16 -04:00
{
throw new BadRequestException("Emergency Access not valid.");
2022-08-29 14:53:16 -04:00
}
var ciphers = await _cipherRepository.GetManyByUserIdAsync(emergencyAccess.GrantorId, withOrganizations: false);
2022-08-29 16:06:55 -04:00
return new EmergencyAccessViewData
{
EmergencyAccess = emergencyAccess,
Ciphers = ciphers,
};
}
public async Task<AttachmentResponseData> GetAttachmentDownloadAsync(Guid id, Guid cipherId, string attachmentId, User requestingUser)
2022-08-29 16:06:55 -04:00
{
var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(id);
2022-08-29 16:06:55 -04:00
if (!IsValidRequest(emergencyAccess, requestingUser, EmergencyAccessType.View))
2022-08-29 14:53:16 -04:00
{
throw new BadRequestException("Emergency Access not valid.");
}
2022-08-29 16:06:55 -04:00
var cipher = await _cipherRepository.GetByIdAsync(cipherId, emergencyAccess.GrantorId);
return await _cipherService.GetAttachmentDownloadDataAsync(cipher, attachmentId);
2022-08-29 16:06:55 -04:00
}
private async Task SendInviteAsync(EmergencyAccess emergencyAccess, string invitingUsersName)
2022-08-29 16:06:55 -04:00
{
var token = _dataProtectorTokenizer.Protect(new EmergencyAccessInviteTokenable(emergencyAccess, _globalSettings.OrganizationInviteExpirationHours));
await _mailService.SendEmergencyAccessInviteEmailAsync(emergencyAccess, invitingUsersName, token);
2022-08-29 16:06:55 -04:00
}
private string NameOrEmail(User user)
2022-08-29 16:06:55 -04:00
{
return string.IsNullOrWhiteSpace(user.Name) ? user.Email : user.Name;
2022-08-29 16:06:55 -04:00
}
[PM-1969] Spellcheck other (#2878) * Fix typo in error message: 'Unkown' -> 'Unknown' * Fix typos in error message * Fix typo in example text: 'licence' -> 'license' * Fix typo in validation: 'Ooganization' -> 'Organization' * Fix typo in text string: 'compatibilty' -> 'compatibility' * Fix typo: 'ProviderDisllowedOrganizationTypes' -> 'ProviderDisallowedOrganizationTypes' * Fix typo: 'NSubstitueVersion' -> 'NSubstituteVersion' * Fix typo: 'CreateIntialInvite' -> 'CreateInitialInvite' * Fix typo: '_queuryScheme' -> '_queryScheme' * Fix typo: 'GetApplicationCacheServiceBusSubcriptionName' -> 'GetApplicationCacheServiceBusSubscriptionName' * Fix typo: 'metaDataRespository' -> 'metaDataRepository' * Fix typo: 'cipherAttachements' -> 'cipherAttachments' * Fix typo: 'savedEmergencyAccesss' -> 'savedEmergencyAccesses' * Fix typo: 'owerOrgUser' -> 'ownerOrgUser' * Fix typo: 'Organiation' -> 'Organization' * Fix typo: 'extistingUser' -> 'existingUser' * Fix typo: 'availibleAccess' -> 'availableAccess' * Fix typo: 'HasEnouphStorage' -> 'HasEnoughStorage' * Fix typo: 'extistingOrg' -> 'existingOrg' * Fix typo: 'subcriber' -> 'subscriber' * Fix typo: 'availibleCollections' -> 'availableCollections' * Fix typo: 'Succes' -> 'Success' * Fix typo: 'CreateAsync_UpdateWithCollecitons_Works' -> 'CreateAsync_UpdateWithCollections_Works' * Fix typo: 'BadInsallationId' -> 'BadInstallationId' * Fix typo: 'OrgNotFamiles' -> 'OrgNotFamilies' * Revert "Fix typo: 'Organiation' -> 'Organization'" This reverts commit 8aadad1c25d853f26ec39029d157ef63e073d3d4. * Revert "Fix typos in error message" This reverts commit 81d201fc09ae4274b7fabe8c6fbcdbb91647bac8. --------- Co-authored-by: Daniel James Smith <djsmith@web.de>
2023-05-17 06:14:36 -04:00
private bool IsValidRequest(EmergencyAccess availableAccess, User requestingUser, EmergencyAccessType requestedAccessType)
2022-08-29 16:06:55 -04:00
{
[PM-1969] Spellcheck other (#2878) * Fix typo in error message: 'Unkown' -> 'Unknown' * Fix typos in error message * Fix typo in example text: 'licence' -> 'license' * Fix typo in validation: 'Ooganization' -> 'Organization' * Fix typo in text string: 'compatibilty' -> 'compatibility' * Fix typo: 'ProviderDisllowedOrganizationTypes' -> 'ProviderDisallowedOrganizationTypes' * Fix typo: 'NSubstitueVersion' -> 'NSubstituteVersion' * Fix typo: 'CreateIntialInvite' -> 'CreateInitialInvite' * Fix typo: '_queuryScheme' -> '_queryScheme' * Fix typo: 'GetApplicationCacheServiceBusSubcriptionName' -> 'GetApplicationCacheServiceBusSubscriptionName' * Fix typo: 'metaDataRespository' -> 'metaDataRepository' * Fix typo: 'cipherAttachements' -> 'cipherAttachments' * Fix typo: 'savedEmergencyAccesss' -> 'savedEmergencyAccesses' * Fix typo: 'owerOrgUser' -> 'ownerOrgUser' * Fix typo: 'Organiation' -> 'Organization' * Fix typo: 'extistingUser' -> 'existingUser' * Fix typo: 'availibleAccess' -> 'availableAccess' * Fix typo: 'HasEnouphStorage' -> 'HasEnoughStorage' * Fix typo: 'extistingOrg' -> 'existingOrg' * Fix typo: 'subcriber' -> 'subscriber' * Fix typo: 'availibleCollections' -> 'availableCollections' * Fix typo: 'Succes' -> 'Success' * Fix typo: 'CreateAsync_UpdateWithCollecitons_Works' -> 'CreateAsync_UpdateWithCollections_Works' * Fix typo: 'BadInsallationId' -> 'BadInstallationId' * Fix typo: 'OrgNotFamiles' -> 'OrgNotFamilies' * Revert "Fix typo: 'Organiation' -> 'Organization'" This reverts commit 8aadad1c25d853f26ec39029d157ef63e073d3d4. * Revert "Fix typos in error message" This reverts commit 81d201fc09ae4274b7fabe8c6fbcdbb91647bac8. --------- Co-authored-by: Daniel James Smith <djsmith@web.de>
2023-05-17 06:14:36 -04:00
return availableAccess != null &&
availableAccess.GranteeId == requestingUser.Id &&
availableAccess.Status == EmergencyAccessStatusType.RecoveryApproved &&
availableAccess.Type == requestedAccessType;
}
}