1
0
mirror of https://github.com/bitwarden/server synced 2025-12-14 15:23:42 +00:00

Added auto confirm to single org check locations. Removed additional call within policy service.

This commit is contained in:
Jared McCannon
2025-12-04 19:35:18 -06:00
parent 962dfa7ac2
commit 25363ae59b
6 changed files with 84 additions and 27 deletions

View File

@@ -202,13 +202,13 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand
userOrgs, userOrgs,
user))) user)))
.Match( .Match(
error => error.Message, error => new BadRequestException(error.Message),
_ => string.Empty _ => null
); );
if (!string.IsNullOrEmpty(error)) if (error is not null)
{ {
throw new BadRequestException(error); throw error;
} }
} }

View File

@@ -4,6 +4,7 @@
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Enforcement.AutoConfirm;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
using Bit.Core.AdminConsole.Services; using Bit.Core.AdminConsole.Services;
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
@@ -29,7 +30,8 @@ public class RestoreOrganizationUserCommand(
IUserRepository userRepository, IUserRepository userRepository,
IOrganizationService organizationService, IOrganizationService organizationService,
IFeatureService featureService, IFeatureService featureService,
IPolicyRequirementQuery policyRequirementQuery) : IRestoreOrganizationUserCommand IPolicyRequirementQuery policyRequirementQuery,
IAutomaticUserConfirmationPolicyEnforcementValidator automaticUserConfirmationPolicyEnforcementValidator) : IRestoreOrganizationUserCommand
{ {
public async Task RestoreUserAsync(OrganizationUser organizationUser, Guid? restoringUserId) public async Task RestoreUserAsync(OrganizationUser organizationUser, Guid? restoringUserId)
{ {
@@ -300,6 +302,25 @@ public class RestoreOrganizationUserCommand(
{ {
throw new BadRequestException(user.Email + " is not compliant with the two-step login policy"); throw new BadRequestException(user.Email + " is not compliant with the two-step login policy");
} }
if (featureService.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers))
{
var validationResult = await automaticUserConfirmationPolicyEnforcementValidator.IsCompliantAsync(
new AutomaticUserConfirmationPolicyEnforcementRequest(orgUser.OrganizationId,
allOrgUsers,
user!));
var badRequestException = validationResult.Match(
error => new BadRequestException(user.Email +
" is not compliant with the automatic user confirmation policy: " +
error.Message),
_ => null);
if (badRequestException is not null)
{
throw badRequestException;
}
}
} }
private async Task<bool> IsTwoFactorRequiredForOrganizationAsync(Guid userId, Guid organizationId) private async Task<bool> IsTwoFactorRequiredForOrganizationAsync(Guid userId, Guid organizationId)

View File

@@ -3,6 +3,8 @@
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
using Bit.Core.AdminConsole.Services; using Bit.Core.AdminConsole.Services;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Organizations.Models; using Bit.Core.Billing.Organizations.Models;
@@ -42,7 +44,9 @@ public class CloudOrganizationSignUpCommand(
IPushNotificationService pushNotificationService, IPushNotificationService pushNotificationService,
ICollectionRepository collectionRepository, ICollectionRepository collectionRepository,
IDeviceRepository deviceRepository, IDeviceRepository deviceRepository,
IPricingClient pricingClient) : ICloudOrganizationSignUpCommand IPricingClient pricingClient,
IPolicyRequirementQuery policyRequirementQuery,
IFeatureService featureService) : ICloudOrganizationSignUpCommand
{ {
public async Task<SignUpOrganizationResponse> SignUpOrganizationAsync(OrganizationSignup signup) public async Task<SignUpOrganizationResponse> SignUpOrganizationAsync(OrganizationSignup signup)
{ {
@@ -236,6 +240,17 @@ public class CloudOrganizationSignUpCommand(
private async Task ValidateSignUpPoliciesAsync(Guid ownerId) private async Task ValidateSignUpPoliciesAsync(Guid ownerId)
{ {
if (featureService.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers))
{
var requirement = await policyRequirementQuery.GetAsync<AutomaticUserConfirmationPolicyRequirement>(ownerId);
if (requirement.UserBelongsToOrganizationWithAutomaticUserConfirmationEnabled())
{
throw new BadRequestException("You may not create an organization. You belong to an organization " +
"which has a policy that prohibits you from being a member of any other organization.");
}
}
var anySingleOrgPolicies = await policyService.AnyPoliciesApplicableToUserAsync(ownerId, PolicyType.SingleOrg); var anySingleOrgPolicies = await policyService.AnyPoliciesApplicableToUserAsync(ownerId, PolicyType.SingleOrg);
if (anySingleOrgPolicies) if (anySingleOrgPolicies)
{ {

View File

@@ -2,6 +2,8 @@
#nullable disable #nullable disable
using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
using Bit.Core.AdminConsole.Services; using Bit.Core.AdminConsole.Services;
using Bit.Core.Auth.Models.Business.Tokenables; using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Entities; using Bit.Core.Entities;
@@ -28,6 +30,8 @@ public class InitPendingOrganizationCommand : IInitPendingOrganizationCommand
private readonly IGlobalSettings _globalSettings; private readonly IGlobalSettings _globalSettings;
private readonly IPolicyService _policyService; private readonly IPolicyService _policyService;
private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IFeatureService _featureService;
private readonly IPolicyRequirementQuery _policyRequirementQuery;
public InitPendingOrganizationCommand( public InitPendingOrganizationCommand(
IOrganizationService organizationService, IOrganizationService organizationService,
@@ -37,7 +41,9 @@ public class InitPendingOrganizationCommand : IInitPendingOrganizationCommand
IDataProtectionProvider dataProtectionProvider, IDataProtectionProvider dataProtectionProvider,
IGlobalSettings globalSettings, IGlobalSettings globalSettings,
IPolicyService policyService, IPolicyService policyService,
IOrganizationUserRepository organizationUserRepository IOrganizationUserRepository organizationUserRepository,
IFeatureService featureService,
IPolicyRequirementQuery policyRequirementQuery
) )
{ {
_organizationService = organizationService; _organizationService = organizationService;
@@ -48,6 +54,8 @@ public class InitPendingOrganizationCommand : IInitPendingOrganizationCommand
_globalSettings = globalSettings; _globalSettings = globalSettings;
_policyService = policyService; _policyService = policyService;
_organizationUserRepository = organizationUserRepository; _organizationUserRepository = organizationUserRepository;
_featureService = featureService;
_policyRequirementQuery = policyRequirementQuery;
} }
public async Task InitPendingOrganizationAsync(User user, Guid organizationId, Guid organizationUserId, string publicKey, string privateKey, string collectionName, string emailToken) public async Task InitPendingOrganizationAsync(User user, Guid organizationId, Guid organizationUserId, string publicKey, string privateKey, string collectionName, string emailToken)
@@ -113,6 +121,17 @@ public class InitPendingOrganizationCommand : IInitPendingOrganizationCommand
private async Task ValidateSignUpPoliciesAsync(Guid ownerId) private async Task ValidateSignUpPoliciesAsync(Guid ownerId)
{ {
if (_featureService.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers))
{
var requirement = await _policyRequirementQuery.GetAsync<AutomaticUserConfirmationPolicyRequirement>(ownerId);
if (requirement.UserBelongsToOrganizationWithAutomaticUserConfirmationEnabled())
{
throw new BadRequestException("You may not create an organization. You belong to an organization " +
"which has a policy that prohibits you from being a member of any other organization.");
}
}
var anySingleOrgPolicies = await _policyService.AnyPoliciesApplicableToUserAsync(ownerId, PolicyType.SingleOrg); var anySingleOrgPolicies = await _policyService.AnyPoliciesApplicableToUserAsync(ownerId, PolicyType.SingleOrg);
if (anySingleOrgPolicies) if (anySingleOrgPolicies)
{ {

View File

@@ -2,6 +2,8 @@
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Interfaces;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
using Bit.Core.AdminConsole.Services; using Bit.Core.AdminConsole.Services;
using Bit.Core.Billing.Organizations.Models; using Bit.Core.Billing.Organizations.Models;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Services;
@@ -31,6 +33,8 @@ public class SelfHostedOrganizationSignUpCommand : ISelfHostedOrganizationSignUp
private readonly IPolicyService _policyService; private readonly IPolicyService _policyService;
private readonly IGlobalSettings _globalSettings; private readonly IGlobalSettings _globalSettings;
private readonly IPaymentService _paymentService; private readonly IPaymentService _paymentService;
private readonly IFeatureService _featureService;
private readonly IPolicyRequirementQuery _policyRequirementQuery;
public SelfHostedOrganizationSignUpCommand( public SelfHostedOrganizationSignUpCommand(
IOrganizationRepository organizationRepository, IOrganizationRepository organizationRepository,
@@ -44,7 +48,9 @@ public class SelfHostedOrganizationSignUpCommand : ISelfHostedOrganizationSignUp
ILicensingService licensingService, ILicensingService licensingService,
IPolicyService policyService, IPolicyService policyService,
IGlobalSettings globalSettings, IGlobalSettings globalSettings,
IPaymentService paymentService) IPaymentService paymentService,
IFeatureService featureService,
IPolicyRequirementQuery policyRequirementQuery)
{ {
_organizationRepository = organizationRepository; _organizationRepository = organizationRepository;
_organizationUserRepository = organizationUserRepository; _organizationUserRepository = organizationUserRepository;
@@ -58,6 +64,8 @@ public class SelfHostedOrganizationSignUpCommand : ISelfHostedOrganizationSignUp
_policyService = policyService; _policyService = policyService;
_globalSettings = globalSettings; _globalSettings = globalSettings;
_paymentService = paymentService; _paymentService = paymentService;
_featureService = featureService;
_policyRequirementQuery = policyRequirementQuery;
} }
public async Task<(Organization organization, OrganizationUser? organizationUser)> SignUpAsync( public async Task<(Organization organization, OrganizationUser? organizationUser)> SignUpAsync(
@@ -103,6 +111,17 @@ public class SelfHostedOrganizationSignUpCommand : ISelfHostedOrganizationSignUp
private async Task ValidateSignUpPoliciesAsync(Guid ownerId) private async Task ValidateSignUpPoliciesAsync(Guid ownerId)
{ {
if (_featureService.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers))
{
var requirement = await _policyRequirementQuery.GetAsync<AutomaticUserConfirmationPolicyRequirement>(ownerId);
if (requirement.UserBelongsToOrganizationWithAutomaticUserConfirmationEnabled())
{
throw new BadRequestException("You may not create an organization. You belong to an organization " +
"which has a policy that prohibits you from being a member of any other organization.");
}
}
var anySingleOrgPolicies = await _policyService.AnyPoliciesApplicableToUserAsync(ownerId, PolicyType.SingleOrg); var anySingleOrgPolicies = await _policyService.AnyPoliciesApplicableToUserAsync(ownerId, PolicyType.SingleOrg);
if (anySingleOrgPolicies) if (anySingleOrgPolicies)
{ {

View File

@@ -89,31 +89,14 @@ public class PolicyService : IPolicyService
private async Task<IEnumerable<OrganizationUserPolicyDetails>> QueryOrganizationUserPolicyDetailsAsync(Guid userId, PolicyType policyType, OrganizationUserStatusType minStatus = OrganizationUserStatusType.Accepted) private async Task<IEnumerable<OrganizationUserPolicyDetails>> QueryOrganizationUserPolicyDetailsAsync(Guid userId, PolicyType policyType, OrganizationUserStatusType minStatus = OrganizationUserStatusType.Accepted)
{ {
var organizationUserPolicyDetails = await _organizationUserRepository.GetByUserIdWithPolicyDetailsAsync(userId, policyType); var organizationUserPolicyDetails = await _organizationUserRepository.GetByUserIdWithPolicyDetailsAsync(userId, policyType);
var excludedUserTypes = GetUserTypesExcludedFromPolicy(policyType);
OrganizationUserType[] excludedUserTypes;
var appliesToProviders = false;
if (policyType == PolicyType.SingleOrg
&& _featureService.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers)
&& (await _organizationUserRepository.GetByUserIdWithPolicyDetailsAsync(userId, PolicyType.AutomaticUserConfirmation)).Any())
{
minStatus = OrganizationUserStatusType.Revoked;
excludedUserTypes = [];
appliesToProviders = true;
}
else
{
excludedUserTypes = GetUserTypesExcludedFromPolicy(policyType);
}
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync(); var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
return organizationUserPolicyDetails.Where(o => return organizationUserPolicyDetails.Where(o =>
(!orgAbilities.TryGetValue(o.OrganizationId, out var orgAbility) || orgAbility.UsePolicies) && (!orgAbilities.TryGetValue(o.OrganizationId, out var orgAbility) || orgAbility.UsePolicies) &&
o.PolicyEnabled && o.PolicyEnabled &&
!excludedUserTypes.Contains(o.OrganizationUserType) && !excludedUserTypes.Contains(o.OrganizationUserType) &&
o.OrganizationUserStatus >= minStatus && o.OrganizationUserStatus >= minStatus &&
(o.IsProvider && appliesToProviders || !o.IsProvider)); // the user is a provider and the policy applies to providers, or they are not a provider !o.IsProvider);
} }
private OrganizationUserType[] GetUserTypesExcludedFromPolicy(PolicyType policyType) private OrganizationUserType[] GetUserTypesExcludedFromPolicy(PolicyType policyType)