1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-01 16:12: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:
Jared Snider
2024-09-12 15:24:47 -04:00
committed by GitHub
parent 95ba256511
commit 7d8df767cd
7 changed files with 231 additions and 29 deletions

View File

@ -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()
{

View File

@ -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);
}

View File

@ -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);