1
0
mirror of https://github.com/bitwarden/server synced 2025-12-10 13:23:27 +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,
user)))
.Match(
error => error.Message,
_ => string.Empty
error => new BadRequestException(error.Message),
_ => 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.Enums;
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.Services;
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
@@ -29,7 +30,8 @@ public class RestoreOrganizationUserCommand(
IUserRepository userRepository,
IOrganizationService organizationService,
IFeatureService featureService,
IPolicyRequirementQuery policyRequirementQuery) : IRestoreOrganizationUserCommand
IPolicyRequirementQuery policyRequirementQuery,
IAutomaticUserConfirmationPolicyEnforcementValidator automaticUserConfirmationPolicyEnforcementValidator) : IRestoreOrganizationUserCommand
{
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");
}
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)

View File

@@ -3,6 +3,8 @@
using Bit.Core.AdminConsole.Entities;
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.Billing.Enums;
using Bit.Core.Billing.Organizations.Models;
@@ -42,7 +44,9 @@ public class CloudOrganizationSignUpCommand(
IPushNotificationService pushNotificationService,
ICollectionRepository collectionRepository,
IDeviceRepository deviceRepository,
IPricingClient pricingClient) : ICloudOrganizationSignUpCommand
IPricingClient pricingClient,
IPolicyRequirementQuery policyRequirementQuery,
IFeatureService featureService) : ICloudOrganizationSignUpCommand
{
public async Task<SignUpOrganizationResponse> SignUpOrganizationAsync(OrganizationSignup signup)
{
@@ -236,6 +240,17 @@ public class CloudOrganizationSignUpCommand(
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);
if (anySingleOrgPolicies)
{

View File

@@ -2,6 +2,8 @@
#nullable disable
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.Auth.Models.Business.Tokenables;
using Bit.Core.Entities;
@@ -28,6 +30,8 @@ public class InitPendingOrganizationCommand : IInitPendingOrganizationCommand
private readonly IGlobalSettings _globalSettings;
private readonly IPolicyService _policyService;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IFeatureService _featureService;
private readonly IPolicyRequirementQuery _policyRequirementQuery;
public InitPendingOrganizationCommand(
IOrganizationService organizationService,
@@ -37,7 +41,9 @@ public class InitPendingOrganizationCommand : IInitPendingOrganizationCommand
IDataProtectionProvider dataProtectionProvider,
IGlobalSettings globalSettings,
IPolicyService policyService,
IOrganizationUserRepository organizationUserRepository
IOrganizationUserRepository organizationUserRepository,
IFeatureService featureService,
IPolicyRequirementQuery policyRequirementQuery
)
{
_organizationService = organizationService;
@@ -48,6 +54,8 @@ public class InitPendingOrganizationCommand : IInitPendingOrganizationCommand
_globalSettings = globalSettings;
_policyService = policyService;
_organizationUserRepository = organizationUserRepository;
_featureService = featureService;
_policyRequirementQuery = policyRequirementQuery;
}
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)
{
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);
if (anySingleOrgPolicies)
{

View File

@@ -2,6 +2,8 @@
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
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.Billing.Organizations.Models;
using Bit.Core.Billing.Services;
@@ -31,6 +33,8 @@ public class SelfHostedOrganizationSignUpCommand : ISelfHostedOrganizationSignUp
private readonly IPolicyService _policyService;
private readonly IGlobalSettings _globalSettings;
private readonly IPaymentService _paymentService;
private readonly IFeatureService _featureService;
private readonly IPolicyRequirementQuery _policyRequirementQuery;
public SelfHostedOrganizationSignUpCommand(
IOrganizationRepository organizationRepository,
@@ -44,7 +48,9 @@ public class SelfHostedOrganizationSignUpCommand : ISelfHostedOrganizationSignUp
ILicensingService licensingService,
IPolicyService policyService,
IGlobalSettings globalSettings,
IPaymentService paymentService)
IPaymentService paymentService,
IFeatureService featureService,
IPolicyRequirementQuery policyRequirementQuery)
{
_organizationRepository = organizationRepository;
_organizationUserRepository = organizationUserRepository;
@@ -58,6 +64,8 @@ public class SelfHostedOrganizationSignUpCommand : ISelfHostedOrganizationSignUp
_policyService = policyService;
_globalSettings = globalSettings;
_paymentService = paymentService;
_featureService = featureService;
_policyRequirementQuery = policyRequirementQuery;
}
public async Task<(Organization organization, OrganizationUser? organizationUser)> SignUpAsync(
@@ -103,6 +111,17 @@ public class SelfHostedOrganizationSignUpCommand : ISelfHostedOrganizationSignUp
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);
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)
{
var organizationUserPolicyDetails = await _organizationUserRepository.GetByUserIdWithPolicyDetailsAsync(userId, 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 excludedUserTypes = GetUserTypesExcludedFromPolicy(policyType);
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
return organizationUserPolicyDetails.Where(o =>
(!orgAbilities.TryGetValue(o.OrganizationId, out var orgAbility) || orgAbility.UsePolicies) &&
o.PolicyEnabled &&
!excludedUserTypes.Contains(o.OrganizationUserType) &&
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)