1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-01 08:02:49 -05:00

[PM-10314] Auto-enable Single Org when a Domain is Verified (#4897)

Updated domain verification to auto-enable single org policy.
This commit is contained in:
Jared McCannon
2024-10-24 10:13:45 -05:00
committed by GitHub
parent a128cf1506
commit 0c346d6070
11 changed files with 244 additions and 62 deletions

View File

@ -0,0 +1,6 @@
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
public interface IOrganizationHasVerifiedDomainsQuery
{
Task<bool> HasVerifiedDomainsAsync(Guid orgId);
}

View File

@ -0,0 +1,10 @@
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
using Bit.Core.Repositories;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains;
public class OrganizationHasVerifiedDomainsQuery(IOrganizationDomainRepository domainRepository) : IOrganizationHasVerifiedDomainsQuery
{
public async Task<bool> HasVerifiedDomainsAsync(Guid orgId) =>
(await domainRepository.GetDomainsByOrganizationIdAsync(orgId)).Any(od => od.VerifiedDate is not null);
}

View File

@ -1,4 +1,7 @@
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
using Bit.Core.AdminConsole.Services;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
@ -15,6 +18,9 @@ public class VerifyOrganizationDomainCommand : IVerifyOrganizationDomainCommand
private readonly IDnsResolverService _dnsResolverService;
private readonly IEventService _eventService;
private readonly IGlobalSettings _globalSettings;
private readonly IPolicyService _policyService;
private readonly IFeatureService _featureService;
private readonly IOrganizationService _organizationService;
private readonly ILogger<VerifyOrganizationDomainCommand> _logger;
public VerifyOrganizationDomainCommand(
@ -22,12 +28,18 @@ public class VerifyOrganizationDomainCommand : IVerifyOrganizationDomainCommand
IDnsResolverService dnsResolverService,
IEventService eventService,
IGlobalSettings globalSettings,
IPolicyService policyService,
IFeatureService featureService,
IOrganizationService organizationService,
ILogger<VerifyOrganizationDomainCommand> logger)
{
_organizationDomainRepository = organizationDomainRepository;
_dnsResolverService = dnsResolverService;
_eventService = eventService;
_globalSettings = globalSettings;
_policyService = policyService;
_featureService = featureService;
_organizationService = organizationService;
_logger = logger;
}
@ -102,6 +114,8 @@ public class VerifyOrganizationDomainCommand : IVerifyOrganizationDomainCommand
if (await _dnsResolverService.ResolveAsync(domain.DomainName, domain.Txt))
{
domain.SetVerifiedDate();
await EnableSingleOrganizationPolicyAsync(domain.OrganizationId);
}
}
catch (Exception e)
@ -112,4 +126,13 @@ public class VerifyOrganizationDomainCommand : IVerifyOrganizationDomainCommand
return domain;
}
private async Task EnableSingleOrganizationPolicyAsync(Guid organizationId)
{
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning))
{
await _policyService.SaveAsync(
new Policy { OrganizationId = organizationId, Type = PolicyType.SingleOrg, Enabled = true }, null);
}
}
}

View File

@ -4,8 +4,4 @@ public interface IOrganizationDomainService
{
Task ValidateOrganizationsDomainAsync();
Task OrganizationDomainMaintenanceAsync();
/// <summary>
/// Indicates if the organization has any verified domains.
/// </summary>
Task<bool> HasVerifiedDomainsAsync(Guid orgId);
}

View File

@ -106,12 +106,6 @@ public class OrganizationDomainService : IOrganizationDomainService
}
}
public async Task<bool> HasVerifiedDomainsAsync(Guid orgId)
{
var orgDomains = await _domainRepository.GetDomainsByOrganizationIdAsync(orgId);
return orgDomains.Any(od => od.VerifiedDate != null);
}
private async Task<List<string>> GetAdminEmailsAsync(Guid organizationId)
{
var orgUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(organizationId);

View File

@ -1,6 +1,7 @@
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
@ -32,6 +33,7 @@ public class PolicyService : IPolicyService
private readonly IFeatureService _featureService;
private readonly ISavePolicyCommand _savePolicyCommand;
private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
private readonly IOrganizationHasVerifiedDomainsQuery _organizationHasVerifiedDomainsQuery;
public PolicyService(
IApplicationCacheService applicationCacheService,
@ -45,7 +47,8 @@ public class PolicyService : IPolicyService
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
IFeatureService featureService,
ISavePolicyCommand savePolicyCommand,
IRemoveOrganizationUserCommand removeOrganizationUserCommand)
IRemoveOrganizationUserCommand removeOrganizationUserCommand,
IOrganizationHasVerifiedDomainsQuery organizationHasVerifiedDomainsQuery)
{
_applicationCacheService = applicationCacheService;
_eventService = eventService;
@ -59,6 +62,7 @@ public class PolicyService : IPolicyService
_featureService = featureService;
_savePolicyCommand = savePolicyCommand;
_removeOrganizationUserCommand = removeOrganizationUserCommand;
_organizationHasVerifiedDomainsQuery = organizationHasVerifiedDomainsQuery;
}
public async Task SaveAsync(Policy policy, Guid? savingUserId)
@ -239,6 +243,7 @@ public class PolicyService : IPolicyService
case PolicyType.SingleOrg:
if (!policy.Enabled)
{
await HasVerifiedDomainsAsync(org);
await RequiredBySsoAsync(org);
await RequiredByVaultTimeoutAsync(org);
await RequiredByKeyConnectorAsync(org);
@ -279,6 +284,15 @@ public class PolicyService : IPolicyService
}
}
private async Task HasVerifiedDomainsAsync(Organization org)
{
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
&& await _organizationHasVerifiedDomainsQuery.HasVerifiedDomainsAsync(org.Id))
{
throw new BadRequestException("Organization has verified domains.");
}
}
private async Task SetPolicyConfiguration(Policy policy)
{
await _policyRepository.UpsertAsync(policy);

View File

@ -130,6 +130,7 @@ public static class OrganizationServiceCollectionExtensions
services.AddScoped<IGetOrganizationDomainByIdOrganizationIdQuery, GetOrganizationDomainByIdOrganizationIdQuery>();
services.AddScoped<IGetOrganizationDomainByOrganizationIdQuery, GetOrganizationDomainByOrganizationIdQuery>();
services.AddScoped<IDeleteOrganizationDomainCommand, DeleteOrganizationDomainCommand>();
services.AddScoped<IOrganizationHasVerifiedDomainsQuery, OrganizationHasVerifiedDomainsQuery>();
}
private static void AddOrganizationAuthCommands(this IServiceCollection services)