diff --git a/bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs b/bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs index 61d507fc21..566c9892e8 100644 --- a/bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs +++ b/bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs @@ -418,12 +418,15 @@ public class ProviderService : IProviderService "The organization is subscribed to Secrets Manager. Please contact Customer Support to manage the subscription."); } - var organizationAutoConfirmPolicyRequirement = await _policyRequirementQuery - .GetManyByOrganizationIdAsync(organizationId); - - if (organizationAutoConfirmPolicyRequirement.Any()) + if (_featureService.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers)) { - throw new BadRequestException(new AutoConfirmDoesNotAllowProviderUsers().Message); + var organizationAutoConfirmPolicyRequirement = await _policyRequirementQuery + .GetManyByOrganizationIdAsync(organizationId); + + if (organizationAutoConfirmPolicyRequirement.Any()) + { + throw new BadRequestException(new AutoConfirmDoesNotAllowProviderUsers().Message); + } } var providerOrganization = new ProviderOrganization diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/AutoConfirmUser/AutomaticallyConfirmOrganizationUsersValidator.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/AutoConfirmUser/AutomaticallyConfirmOrganizationUsersValidator.cs index 11b89de680..ba0c89aa6a 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/AutoConfirmUser/AutomaticallyConfirmOrganizationUsersValidator.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/AutoConfirmUser/AutomaticallyConfirmOrganizationUsersValidator.cs @@ -1,6 +1,7 @@ using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimedAccount; 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.Repositories; using Bit.Core.AdminConsole.Utilities.v2; @@ -8,6 +9,7 @@ using Bit.Core.AdminConsole.Utilities.v2.Validation; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; using Bit.Core.Enums; using Bit.Core.Repositories; +using Bit.Core.Services; using static Bit.Core.AdminConsole.Utilities.v2.Validation.ValidationResultHelpers; namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.AutoConfirmUser; @@ -16,6 +18,8 @@ public class AutomaticallyConfirmOrganizationUsersValidator( IOrganizationUserRepository organizationUserRepository, ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery, IPolicyRequirementQuery policyRequirementQuery, + IAutomaticUserConfirmationPolicyEnforcementQuery automaticUserConfirmationPolicyEnforcementQuery, + IUserService userService, IPolicyRepository policyRepository) : IAutomaticallyConfirmOrganizationUsersValidator { public async Task> ValidateAsync( @@ -61,7 +65,7 @@ public class AutomaticallyConfirmOrganizationUsersValidator( return Invalid(request, new UserDoesNotHaveTwoFactorEnabled()); } - if (await OrganizationUserConformsToSingleOrgPolicyAsync(request) is { } error) + if (await OrganizationUserConformsToAutomaticUserConfirmationPolicyAsync(request) is { } error) { return Invalid(request, error); } @@ -87,30 +91,27 @@ public class AutomaticallyConfirmOrganizationUsersValidator( .IsTwoFactorRequiredForOrganization(request.Organization!.Id); } - private async Task OrganizationUserConformsToSingleOrgPolicyAsync( + private async Task OrganizationUserConformsToAutomaticUserConfirmationPolicyAsync( AutomaticallyConfirmOrganizationUserValidationRequest request) { var allOrganizationUsersForUser = await organizationUserRepository .GetManyByUserAsync(request.OrganizationUser!.UserId!.Value); + var user = await userService.GetUserByIdAsync(request.OrganizationUser!.UserId!.Value); + if (allOrganizationUsersForUser.Count == 1) { return null; } - var policyRequirement = await policyRequirementQuery - .GetAsync(request.OrganizationUser!.UserId!.Value); - - if (policyRequirement.IsSingleOrgEnabledForThisOrganization(request.Organization!.Id)) - { - return new OrganizationEnforcesSingleOrgPolicy(); - } - - if (policyRequirement.IsSingleOrgEnabledForOrganizationsOtherThan(request.Organization.Id)) - { - return new OtherOrganizationEnforcesSingleOrgPolicy(); - } - - return null; + return (await automaticUserConfirmationPolicyEnforcementQuery.IsCompliantAsync( + new AutomaticUserConfirmationPolicyEnforcementRequest( + request.OrganizationUser, + allOrganizationUsersForUser.Where(x => x.OrganizationId != request.OrganizationId), + user))) + .Match( + error => error, + _ => null + ); } } diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommand.cs index 7cc108b5c1..82291d51ff 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/ConfirmOrganizationUserCommand.cs @@ -4,6 +4,7 @@ using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; 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; @@ -33,6 +34,7 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand private readonly IPolicyRequirementQuery _policyRequirementQuery; private readonly IFeatureService _featureService; private readonly ICollectionRepository _collectionRepository; + private readonly IAutomaticUserConfirmationPolicyEnforcementQuery _automaticUserConfirmationPolicyEnforcementQuery; public ConfirmOrganizationUserCommand( IOrganizationRepository organizationRepository, @@ -47,7 +49,8 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand IDeviceRepository deviceRepository, IPolicyRequirementQuery policyRequirementQuery, IFeatureService featureService, - ICollectionRepository collectionRepository) + ICollectionRepository collectionRepository, + IAutomaticUserConfirmationPolicyEnforcementQuery automaticUserConfirmationPolicyEnforcementQuery) { _organizationRepository = organizationRepository; _organizationUserRepository = organizationUserRepository; @@ -62,6 +65,7 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand _policyRequirementQuery = policyRequirementQuery; _featureService = featureService; _collectionRepository = collectionRepository; + _automaticUserConfirmationPolicyEnforcementQuery = automaticUserConfirmationPolicyEnforcementQuery; } public async Task ConfirmUserAsync(Guid organizationId, Guid organizationUserId, string key, @@ -127,6 +131,7 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand var organization = await _organizationRepository.GetByIdAsync(organizationId); var allUsersOrgs = await _organizationUserRepository.GetManyByManyUsersAsync(validSelectedUserIds); + var users = await _userRepository.GetManyAsync(validSelectedUserIds); var usersTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(validSelectedUserIds); @@ -188,6 +193,25 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand await ValidateTwoFactorAuthenticationPolicyAsync(user, organizationId, userTwoFactorEnabled); var hasOtherOrgs = userOrgs.Any(ou => ou.OrganizationId != organizationId); + + if (_featureService.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers)) + { + var error = (await _automaticUserConfirmationPolicyEnforcementQuery.IsCompliantAsync( + new AutomaticUserConfirmationPolicyEnforcementRequest( + userOrgs.First(x => x.OrganizationId == organizationId), + userOrgs.Where(x => x.OrganizationId != organizationId), + user))) + .Match( + error => error.Message, + _ => string.Empty + ); + + if (!string.IsNullOrEmpty(error)) + { + throw new BadRequestException(error); + } + } + var singleOrgPolicies = await _policyService.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.SingleOrg); var otherSingleOrgPolicies = singleOrgPolicies.Where(p => p.OrganizationId != organizationId);