mirror of
https://github.com/bitwarden/server.git
synced 2025-07-01 08:02:49 -05:00
Auth/PM-11945 - Registration with Email Verification - Fix Org Sponsored Free Family Plan not working (#4772)
* PM-11945 - Rename RegisterUserWithOptionalOrgInvite to RegisterUserViaOrgInvite as the org invite isn't optional in the function - just the overall process of registration. * PM-11945 - Yet another rename * PM-11945 - Wire up call to RegisterUserViaOrgSponsoredFreeFamilyPlanInviteToken and test. * PM-11945 - RegisterUserCommandTests - test new method * PM-11949 - Rename tests * PM-11945 - AccountsControllerTests.cs - add integration test for RegistrationWithEmailVerification_WithOrgSponsoredFreeFamilyPlanInviteToken_Succeeds * PM-11945 - Adjust naming per PR feedback to match docs. * PM-11945 - More renaming
This commit is contained in:
@ -31,6 +31,7 @@ public class RegisterFinishRequestModel : IValidatableObject
|
||||
public Guid? OrganizationUserId { get; set; }
|
||||
public string? OrgInviteToken { get; set; }
|
||||
|
||||
public string? OrgSponsoredFreeFamilyPlanToken { get; set; }
|
||||
|
||||
public User ToUser()
|
||||
{
|
||||
|
@ -24,7 +24,7 @@ public interface IRegisterUserCommand
|
||||
/// <param name="orgInviteToken">The org invite token sent to the user via email</param>
|
||||
/// <param name="orgUserId">The associated org user guid that was created at the time of invite</param>
|
||||
/// <returns><see cref="IdentityResult"/></returns>
|
||||
public Task<IdentityResult> RegisterUserWithOptionalOrgInvite(User user, string masterPasswordHash, string orgInviteToken, Guid? orgUserId);
|
||||
public Task<IdentityResult> RegisterUserViaOrganizationInviteToken(User user, string masterPasswordHash, string orgInviteToken, Guid? orgUserId);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new user with a given master password hash, sends a welcome email, and raises the signup reference event.
|
||||
@ -37,4 +37,6 @@ public interface IRegisterUserCommand
|
||||
/// <returns></returns>
|
||||
public Task<IdentityResult> RegisterUserViaEmailVerificationToken(User user, string masterPasswordHash, string emailVerificationToken);
|
||||
|
||||
public Task<IdentityResult> RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken(User user, string masterPasswordHash, string orgSponsoredFreeFamilyPlanInviteToken);
|
||||
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
@ -37,6 +38,8 @@ public class RegisterUserCommand : IRegisterUserCommand
|
||||
private readonly IUserService _userService;
|
||||
private readonly IMailService _mailService;
|
||||
|
||||
private readonly IValidateRedemptionTokenCommand _validateRedemptionTokenCommand;
|
||||
|
||||
private readonly string _disabledUserRegistrationExceptionMsg = "Open registration has been disabled by the system administrator.";
|
||||
|
||||
public RegisterUserCommand(
|
||||
@ -49,7 +52,8 @@ public class RegisterUserCommand : IRegisterUserCommand
|
||||
IDataProtectorTokenFactory<RegistrationEmailVerificationTokenable> registrationEmailVerificationTokenDataFactory,
|
||||
ICurrentContext currentContext,
|
||||
IUserService userService,
|
||||
IMailService mailService
|
||||
IMailService mailService,
|
||||
IValidateRedemptionTokenCommand validateRedemptionTokenCommand
|
||||
)
|
||||
{
|
||||
_globalSettings = globalSettings;
|
||||
@ -66,6 +70,7 @@ public class RegisterUserCommand : IRegisterUserCommand
|
||||
_userService = userService;
|
||||
_mailService = mailService;
|
||||
|
||||
_validateRedemptionTokenCommand = validateRedemptionTokenCommand;
|
||||
}
|
||||
|
||||
|
||||
@ -81,8 +86,7 @@ public class RegisterUserCommand : IRegisterUserCommand
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public async Task<IdentityResult> RegisterUserWithOptionalOrgInvite(User user, string masterPasswordHash,
|
||||
public async Task<IdentityResult> RegisterUserViaOrganizationInviteToken(User user, string masterPasswordHash,
|
||||
string orgInviteToken, Guid? orgUserId)
|
||||
{
|
||||
ValidateOrgInviteToken(orgInviteToken, orgUserId, user);
|
||||
@ -233,13 +237,8 @@ public class RegisterUserCommand : IRegisterUserCommand
|
||||
public async Task<IdentityResult> RegisterUserViaEmailVerificationToken(User user, string masterPasswordHash,
|
||||
string emailVerificationToken)
|
||||
{
|
||||
// We validate open registration on send of initial email and here b/c a user could technically start the
|
||||
// account creation process while open registration is enabled and then finish it after it has been
|
||||
// disabled by the self hosted admin.
|
||||
if (_globalSettings.DisableUserRegistration)
|
||||
{
|
||||
throw new BadRequestException(_disabledUserRegistrationExceptionMsg);
|
||||
}
|
||||
|
||||
ValidateOpenRegistrationAllowed();
|
||||
|
||||
var tokenable = ValidateRegistrationEmailVerificationTokenable(emailVerificationToken, user.Email);
|
||||
|
||||
@ -260,6 +259,48 @@ public class RegisterUserCommand : IRegisterUserCommand
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<IdentityResult> RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken(User user, string masterPasswordHash,
|
||||
string orgSponsoredFreeFamilyPlanInviteToken)
|
||||
{
|
||||
ValidateOpenRegistrationAllowed();
|
||||
await ValidateOrgSponsoredFreeFamilyPlanInviteToken(orgSponsoredFreeFamilyPlanInviteToken, user.Email);
|
||||
|
||||
user.EmailVerified = true;
|
||||
user.ApiKey = CoreHelpers.SecureRandomString(30); // API key can't be null.
|
||||
|
||||
var result = await _userService.CreateUserAsync(user, masterPasswordHash);
|
||||
if (result == IdentityResult.Success)
|
||||
{
|
||||
await _mailService.SendWelcomeEmailAsync(user);
|
||||
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.Signup, user, _currentContext));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void ValidateOpenRegistrationAllowed()
|
||||
{
|
||||
// We validate open registration on send of initial email and here b/c a user could technically start the
|
||||
// account creation process while open registration is enabled and then finish it after it has been
|
||||
// disabled by the self hosted admin.Ï
|
||||
if (_globalSettings.DisableUserRegistration)
|
||||
{
|
||||
throw new BadRequestException(_disabledUserRegistrationExceptionMsg);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ValidateOrgSponsoredFreeFamilyPlanInviteToken(string orgSponsoredFreeFamilyPlanInviteToken, string userEmail)
|
||||
{
|
||||
var (valid, sponsorship) = await _validateRedemptionTokenCommand.ValidateRedemptionTokenAsync(orgSponsoredFreeFamilyPlanInviteToken, userEmail);
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
throw new BadRequestException("Invalid org sponsored free family plan token.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private RegistrationEmailVerificationTokenable ValidateRegistrationEmailVerificationTokenable(string emailVerificationToken, string userEmail)
|
||||
{
|
||||
_registrationEmailVerificationTokenDataFactory.TryUnprotect(emailVerificationToken, out var tokenable);
|
||||
|
@ -75,7 +75,7 @@ public class AccountsController : Controller
|
||||
public async Task<RegisterResponseModel> PostRegister([FromBody] RegisterRequestModel model)
|
||||
{
|
||||
var user = model.ToUser();
|
||||
var identityResult = await _registerUserCommand.RegisterUserWithOptionalOrgInvite(user, model.MasterPasswordHash,
|
||||
var identityResult = await _registerUserCommand.RegisterUserViaOrganizationInviteToken(user, model.MasterPasswordHash,
|
||||
model.Token, model.OrganizationUserId);
|
||||
// delaysEnabled false is only for the new registration with email verification process
|
||||
return await ProcessRegistrationResult(identityResult, user, delaysEnabled: true);
|
||||
@ -151,12 +151,19 @@ public class AccountsController : Controller
|
||||
|
||||
if (!string.IsNullOrEmpty(model.OrgInviteToken) && model.OrganizationUserId.HasValue)
|
||||
{
|
||||
identityResult = await _registerUserCommand.RegisterUserWithOptionalOrgInvite(user, model.MasterPasswordHash,
|
||||
identityResult = await _registerUserCommand.RegisterUserViaOrganizationInviteToken(user, model.MasterPasswordHash,
|
||||
model.OrgInviteToken, model.OrganizationUserId);
|
||||
|
||||
return await ProcessRegistrationResult(identityResult, user, delaysEnabled);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(model.OrgSponsoredFreeFamilyPlanToken))
|
||||
{
|
||||
identityResult = await _registerUserCommand.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken(user, model.MasterPasswordHash, model.OrgSponsoredFreeFamilyPlanToken);
|
||||
|
||||
return await ProcessRegistrationResult(identityResult, user, delaysEnabled);
|
||||
}
|
||||
|
||||
identityResult = await _registerUserCommand.RegisterUserViaEmailVerificationToken(user, model.MasterPasswordHash, model.EmailVerificationToken);
|
||||
|
||||
return await ProcessRegistrationResult(identityResult, user, delaysEnabled);
|
||||
|
Reference in New Issue
Block a user