mirror of
https://github.com/bitwarden/server
synced 2025-12-29 06:33:43 +00:00
Merge branch 'refs/heads/main' into km/pm-10600
This commit is contained in:
@@ -4,8 +4,10 @@ namespace Bit.Core.AdminConsole.Enums.Provider;
|
||||
|
||||
public enum ProviderType : byte
|
||||
{
|
||||
[Display(ShortName = "MSP", Name = "Managed Service Provider", Description = "Access to clients organization")]
|
||||
[Display(ShortName = "MSP", Name = "Managed Service Provider", Description = "Access to clients organization", Order = 0)]
|
||||
Msp = 0,
|
||||
[Display(ShortName = "Reseller", Name = "Reseller", Description = "Access to clients billing")]
|
||||
[Display(ShortName = "Reseller", Name = "Reseller", Description = "Access to clients billing", Order = 1000)]
|
||||
Reseller = 1,
|
||||
[Display(ShortName = "MOE", Name = "Multi-organization Enterprise", Description = "Access to multiple organizations", Order = 1)]
|
||||
MultiOrganizationEnterprise = 2,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Bit.Core.AdminConsole.Entities.Provider;
|
||||
using Bit.Core.Billing.Enums;
|
||||
|
||||
namespace Bit.Core.AdminConsole.Providers.Interfaces;
|
||||
|
||||
@@ -6,4 +7,5 @@ public interface ICreateProviderCommand
|
||||
{
|
||||
Task CreateMspAsync(Provider provider, string ownerEmail, int teamsMinimumSeats, int enterpriseMinimumSeats);
|
||||
Task CreateResellerAsync(Provider provider);
|
||||
Task CreateMultiOrganizationEnterpriseAsync(Provider provider, string ownerEmail, PlanType plan, int minimumSeats);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,14 @@ using Bit.Core.Utilities;
|
||||
namespace Bit.Core.Auth.Models.Api.Request.Accounts;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
public enum RegisterFinishTokenType : byte
|
||||
{
|
||||
EmailVerification = 1,
|
||||
OrganizationInvite = 2,
|
||||
OrgSponsoredFreeFamilyPlan = 3,
|
||||
EmergencyAccessInvite = 4,
|
||||
ProviderInvite = 5,
|
||||
}
|
||||
|
||||
public class RegisterFinishRequestModel : IValidatableObject
|
||||
{
|
||||
@@ -36,6 +44,10 @@ public class RegisterFinishRequestModel : IValidatableObject
|
||||
public string? AcceptEmergencyAccessInviteToken { get; set; }
|
||||
public Guid? AcceptEmergencyAccessId { get; set; }
|
||||
|
||||
public string? ProviderInviteToken { get; set; }
|
||||
|
||||
public Guid? ProviderUserId { get; set; }
|
||||
|
||||
public User ToUser()
|
||||
{
|
||||
var user = new User
|
||||
@@ -54,6 +66,32 @@ public class RegisterFinishRequestModel : IValidatableObject
|
||||
return user;
|
||||
}
|
||||
|
||||
public RegisterFinishTokenType GetTokenType()
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(EmailVerificationToken))
|
||||
{
|
||||
return RegisterFinishTokenType.EmailVerification;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(OrgInviteToken) && OrganizationUserId.HasValue)
|
||||
{
|
||||
return RegisterFinishTokenType.OrganizationInvite;
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(OrgSponsoredFreeFamilyPlanToken))
|
||||
{
|
||||
return RegisterFinishTokenType.OrgSponsoredFreeFamilyPlan;
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(AcceptEmergencyAccessInviteToken) && AcceptEmergencyAccessId.HasValue)
|
||||
{
|
||||
return RegisterFinishTokenType.EmergencyAccessInvite;
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(ProviderInviteToken) && ProviderUserId.HasValue)
|
||||
{
|
||||
return RegisterFinishTokenType.ProviderInvite;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Invalid token type.");
|
||||
}
|
||||
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
|
||||
@@ -61,4 +61,16 @@ public interface IRegisterUserCommand
|
||||
public Task<IdentityResult> RegisterUserViaAcceptEmergencyAccessInviteToken(User user, string masterPasswordHash,
|
||||
string acceptEmergencyAccessInviteToken, Guid acceptEmergencyAccessId);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new user with a given master password hash, sends a welcome email, and raises the signup reference event.
|
||||
/// If a valid token is provided, the user will be created with their email verified.
|
||||
/// If the token is invalid or expired, an error will be thrown.
|
||||
/// </summary>
|
||||
/// <param name="user">The <see cref="User"/> to create</param>
|
||||
/// <param name="masterPasswordHash">The hashed master password the user entered</param>
|
||||
/// <param name="providerInviteToken">The provider invite token sent to the user via email</param>
|
||||
/// <param name="providerUserId">The provider user id which is used to validate the invite token</param>
|
||||
/// <returns><see cref="IdentityResult"/></returns>
|
||||
public Task<IdentityResult> RegisterUserViaProviderInviteToken(User user, string masterPasswordHash, string providerInviteToken, Guid providerUserId);
|
||||
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ public class RegisterUserCommand : IRegisterUserCommand
|
||||
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory;
|
||||
private readonly IDataProtectorTokenFactory<RegistrationEmailVerificationTokenable> _registrationEmailVerificationTokenDataFactory;
|
||||
private readonly IDataProtector _organizationServiceDataProtector;
|
||||
private readonly IDataProtector _providerServiceDataProtector;
|
||||
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
@@ -75,6 +76,8 @@ public class RegisterUserCommand : IRegisterUserCommand
|
||||
|
||||
_validateRedemptionTokenCommand = validateRedemptionTokenCommand;
|
||||
_emergencyAccessInviteTokenDataFactory = emergencyAccessInviteTokenDataFactory;
|
||||
|
||||
_providerServiceDataProtector = dataProtectionProvider.CreateProtector("ProviderServiceDataProtector");
|
||||
}
|
||||
|
||||
|
||||
@@ -303,6 +306,25 @@ public class RegisterUserCommand : IRegisterUserCommand
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<IdentityResult> RegisterUserViaProviderInviteToken(User user, string masterPasswordHash,
|
||||
string providerInviteToken, Guid providerUserId)
|
||||
{
|
||||
ValidateOpenRegistrationAllowed();
|
||||
ValidateProviderInviteToken(providerInviteToken, providerUserId, 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
|
||||
@@ -333,6 +355,15 @@ public class RegisterUserCommand : IRegisterUserCommand
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateProviderInviteToken(string providerInviteToken, Guid providerUserId, string userEmail)
|
||||
{
|
||||
if (!CoreHelpers.TokenIsValid("ProviderUserInvite", _providerServiceDataProtector, providerInviteToken, userEmail, providerUserId,
|
||||
_globalSettings.OrganizationInviteExpirationHours))
|
||||
{
|
||||
throw new BadRequestException("Invalid provider invite token.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private RegistrationEmailVerificationTokenable ValidateRegistrationEmailVerificationTokenable(string emailVerificationToken, string userEmail)
|
||||
{
|
||||
|
||||
@@ -117,7 +117,6 @@ public static class FeatureFlagKeys
|
||||
public const string RestrictProviderAccess = "restrict-provider-access";
|
||||
public const string PM4154BulkEncryptionService = "PM-4154-bulk-encryption-service";
|
||||
public const string VaultBulkManagementAction = "vault-bulk-management-action";
|
||||
public const string BulkDeviceApproval = "bulk-device-approval";
|
||||
public const string MemberAccessReport = "ac-2059-member-access-report";
|
||||
public const string BlockLegacyUsers = "block-legacy-users";
|
||||
public const string InlineMenuFieldQualification = "inline-menu-field-qualification";
|
||||
@@ -146,8 +145,10 @@ public static class FeatureFlagKeys
|
||||
public const string RemoveServerVersionHeader = "remove-server-version-header";
|
||||
public const string AccessIntelligence = "pm-13227-access-intelligence";
|
||||
public const string VerifiedSsoDomainEndpoint = "pm-12337-refactor-sso-details-endpoint";
|
||||
public const string PM12275_MultiOrganizationEnterprises = "pm-12275-multi-organization-enterprises";
|
||||
public const string Pm13322AddPolicyDefinitions = "pm-13322-add-policy-definitions";
|
||||
public const string LimitCollectionCreationDeletionSplit = "pm-10863-limit-collection-creation-deletion-split";
|
||||
public const string GeneratorToolsModernization = "generator-tools-modernization";
|
||||
|
||||
public static List<string> GetAllKeys()
|
||||
{
|
||||
@@ -163,7 +164,6 @@ public static class FeatureFlagKeys
|
||||
return new Dictionary<string, string>()
|
||||
{
|
||||
{ DuoRedirect, "true" },
|
||||
{ BulkDeviceApproval, "true" },
|
||||
{ CipherKeyEncryption, "true" },
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user