1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 07:36:14 -05:00

[PM-17449] Add logic to handle email updates for managed users. (#5422)

This commit is contained in:
Jimmy Vo
2025-02-20 15:38:59 -05:00
committed by GitHub
parent 2f4d5283d3
commit 06c96a96c5
5 changed files with 38 additions and 100 deletions

View File

@ -149,12 +149,6 @@ public class AccountsController : Controller
throw new BadRequestException("MasterPasswordHash", "Invalid password.");
}
// If Account Deprovisioning is enabled, we need to check if the user is managed by any organization.
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
&& await _userService.IsManagedByAnyOrganizationAsync(user.Id))
{
throw new BadRequestException("Cannot change emails for accounts owned by an organization. Contact your organization administrator for additional details.");
}
await _userService.InitiateEmailChangeAsync(user, model.NewEmail);
}
@ -173,12 +167,6 @@ public class AccountsController : Controller
throw new BadRequestException("You cannot change your email when using Key Connector.");
}
// If Account Deprovisioning is enabled, we need to check if the user is managed by any organization.
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
&& await _userService.IsManagedByAnyOrganizationAsync(user.Id))
{
throw new BadRequestException("Cannot change emails for accounts owned by an organization. Contact your organization administrator for additional details.");
}
var result = await _userService.ChangeEmailAsync(user, model.MasterPasswordHash, model.NewEmail,
model.NewMasterPasswordHash, model.Token, model.Key);

View File

@ -49,6 +49,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
private readonly ICipherRepository _cipherRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationDomainRepository _organizationDomainRepository;
private readonly IMailService _mailService;
private readonly IPushNotificationService _pushService;
private readonly IdentityErrorDescriber _identityErrorDescriber;
@ -81,6 +82,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
ICipherRepository cipherRepository,
IOrganizationUserRepository organizationUserRepository,
IOrganizationRepository organizationRepository,
IOrganizationDomainRepository organizationDomainRepository,
IMailService mailService,
IPushNotificationService pushService,
IUserStore<User> store,
@ -127,6 +129,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
_cipherRepository = cipherRepository;
_organizationUserRepository = organizationUserRepository;
_organizationRepository = organizationRepository;
_organizationDomainRepository = organizationDomainRepository;
_mailService = mailService;
_pushService = pushService;
_identityOptions = optionsAccessor?.Value ?? new IdentityOptions();
@ -521,6 +524,13 @@ public class UserService : UserManager<User>, IUserService, IDisposable
return IdentityResult.Failed(_identityErrorDescriber.PasswordMismatch());
}
var managedUserValidationResult = await ValidateManagedUserAsync(user, newEmail);
if (!managedUserValidationResult.Succeeded)
{
return managedUserValidationResult;
}
if (!await base.VerifyUserTokenAsync(user, _identityOptions.Tokens.ChangeEmailTokenProvider,
GetChangeEmailTokenPurpose(newEmail), token))
{
@ -586,6 +596,31 @@ public class UserService : UserManager<User>, IUserService, IDisposable
return IdentityResult.Success;
}
private async Task<IdentityResult> ValidateManagedUserAsync(User user, string newEmail)
{
var managingOrganizations = await GetOrganizationsManagingUserAsync(user.Id);
if (!managingOrganizations.Any())
{
return IdentityResult.Success;
}
var newDomain = CoreHelpers.GetEmailDomain(newEmail);
var verifiedDomains = await _organizationDomainRepository.GetVerifiedDomainsByOrganizationIdsAsync(managingOrganizations.Select(org => org.Id));
if (verifiedDomains.Any(verifiedDomain => verifiedDomain.DomainName == newDomain))
{
return IdentityResult.Success;
}
return IdentityResult.Failed(new IdentityError
{
Code = "EmailDomainMismatch",
Description = "Your new email must match your organization domain."
});
}
public async Task<IdentityResult> ChangePasswordAsync(User user, string masterPassword, string newMasterPassword, string passwordHint,
string key)
{