1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-21 12:05:42 +01:00

[PM-13722] Refactor ValidateOrganizationsDomainAsync (#4905)

Refactored ValidateOrganizationsDomainAsync to use VerifyOrganizationDomainAsync
This commit is contained in:
Jared McCannon 2024-10-18 07:45:34 -05:00 committed by GitHub
parent 1d3188d3f5
commit 4fec7cadb7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 109 additions and 75 deletions

View File

@ -101,7 +101,7 @@ public class OrganizationDomainController : Controller
throw new NotFoundException();
}
organizationDomain = await _verifyOrganizationDomainCommand.VerifyOrganizationDomainAsync(organizationDomain);
organizationDomain = await _verifyOrganizationDomainCommand.UserVerifyOrganizationDomainAsync(organizationDomain);
return new OrganizationDomainResponseModel(organizationDomain);
}

View File

@ -6,7 +6,6 @@ using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Microsoft.Extensions.Logging;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains;
@ -14,21 +13,15 @@ public class CreateOrganizationDomainCommand : ICreateOrganizationDomainCommand
{
private readonly IOrganizationDomainRepository _organizationDomainRepository;
private readonly IEventService _eventService;
private readonly IDnsResolverService _dnsResolverService;
private readonly ILogger<VerifyOrganizationDomainCommand> _logger;
private readonly IGlobalSettings _globalSettings;
public CreateOrganizationDomainCommand(
IOrganizationDomainRepository organizationDomainRepository,
IEventService eventService,
IDnsResolverService dnsResolverService,
ILogger<VerifyOrganizationDomainCommand> logger,
IGlobalSettings globalSettings)
{
_organizationDomainRepository = organizationDomainRepository;
_eventService = eventService;
_dnsResolverService = dnsResolverService;
_logger = logger;
_globalSettings = globalSettings;
}

View File

@ -4,5 +4,6 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfa
public interface IVerifyOrganizationDomainCommand
{
Task<OrganizationDomain> VerifyOrganizationDomainAsync(OrganizationDomain organizationDomain);
Task<OrganizationDomain> UserVerifyOrganizationDomainAsync(OrganizationDomain organizationDomain);
Task<OrganizationDomain> SystemVerifyOrganizationDomainAsync(OrganizationDomain organizationDomain);
}

View File

@ -4,6 +4,7 @@ using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Microsoft.Extensions.Logging;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains;
@ -13,34 +14,85 @@ public class VerifyOrganizationDomainCommand : IVerifyOrganizationDomainCommand
private readonly IOrganizationDomainRepository _organizationDomainRepository;
private readonly IDnsResolverService _dnsResolverService;
private readonly IEventService _eventService;
private readonly IGlobalSettings _globalSettings;
private readonly ILogger<VerifyOrganizationDomainCommand> _logger;
public VerifyOrganizationDomainCommand(
IOrganizationDomainRepository organizationDomainRepository,
IDnsResolverService dnsResolverService,
IEventService eventService,
IGlobalSettings globalSettings,
ILogger<VerifyOrganizationDomainCommand> logger)
{
_organizationDomainRepository = organizationDomainRepository;
_dnsResolverService = dnsResolverService;
_eventService = eventService;
_globalSettings = globalSettings;
_logger = logger;
}
public async Task<OrganizationDomain> VerifyOrganizationDomainAsync(OrganizationDomain domain)
public async Task<OrganizationDomain> UserVerifyOrganizationDomainAsync(OrganizationDomain organizationDomain)
{
var domainVerificationResult = await VerifyOrganizationDomainAsync(organizationDomain);
await _eventService.LogOrganizationDomainEventAsync(domainVerificationResult,
domainVerificationResult.VerifiedDate != null
? EventType.OrganizationDomain_Verified
: EventType.OrganizationDomain_NotVerified);
await _organizationDomainRepository.ReplaceAsync(domainVerificationResult);
return domainVerificationResult;
}
public async Task<OrganizationDomain> SystemVerifyOrganizationDomainAsync(OrganizationDomain organizationDomain)
{
organizationDomain.SetJobRunCount();
var domainVerificationResult = await VerifyOrganizationDomainAsync(organizationDomain);
if (domainVerificationResult.VerifiedDate is not null)
{
_logger.LogInformation(Constants.BypassFiltersEventId, "Successfully validated domain");
await _eventService.LogOrganizationDomainEventAsync(domainVerificationResult,
EventType.OrganizationDomain_Verified,
EventSystemUser.DomainVerification);
}
else
{
domainVerificationResult.SetNextRunDate(_globalSettings.DomainVerification.VerificationInterval);
await _eventService.LogOrganizationDomainEventAsync(domainVerificationResult,
EventType.OrganizationDomain_NotVerified,
EventSystemUser.DomainVerification);
_logger.LogInformation(Constants.BypassFiltersEventId,
"Verification for organization {OrgId} with domain {Domain} failed",
domainVerificationResult.OrganizationId, domainVerificationResult.DomainName);
}
await _organizationDomainRepository.ReplaceAsync(domainVerificationResult);
return domainVerificationResult;
}
private async Task<OrganizationDomain> VerifyOrganizationDomainAsync(OrganizationDomain domain)
{
domain.SetLastCheckedDate();
if (domain.VerifiedDate is not null)
{
domain.SetLastCheckedDate();
await _organizationDomainRepository.ReplaceAsync(domain);
throw new ConflictException("Domain has already been verified.");
}
var claimedDomain =
await _organizationDomainRepository.GetClaimedDomainsByDomainNameAsync(domain.DomainName);
if (claimedDomain.Any())
if (claimedDomain.Count > 0)
{
domain.SetLastCheckedDate();
await _organizationDomainRepository.ReplaceAsync(domain);
throw new ConflictException("The domain is not available to be claimed.");
}
@ -58,11 +110,6 @@ public class VerifyOrganizationDomainCommand : IVerifyOrganizationDomainCommand
domain.DomainName, e.Message);
}
domain.SetLastCheckedDate();
await _organizationDomainRepository.ReplaceAsync(domain);
await _eventService.LogOrganizationDomainEventAsync(domain,
domain.VerifiedDate != null ? EventType.OrganizationDomain_Verified : EventType.OrganizationDomain_NotVerified);
return domain;
}
}

View File

@ -1,4 +1,5 @@
using Bit.Core.Enums;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
using Bit.Core.Enums;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
@ -10,26 +11,29 @@ public class OrganizationDomainService : IOrganizationDomainService
{
private readonly IOrganizationDomainRepository _domainRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IDnsResolverService _dnsResolverService;
private readonly IEventService _eventService;
private readonly IMailService _mailService;
private readonly IVerifyOrganizationDomainCommand _verifyOrganizationDomainCommand;
private readonly TimeProvider _timeProvider;
private readonly ILogger<OrganizationDomainService> _logger;
private readonly IGlobalSettings _globalSettings;
public OrganizationDomainService(
IOrganizationDomainRepository domainRepository,
IOrganizationUserRepository organizationUserRepository,
IDnsResolverService dnsResolverService,
IEventService eventService,
IMailService mailService,
IVerifyOrganizationDomainCommand verifyOrganizationDomainCommand,
TimeProvider timeProvider,
ILogger<OrganizationDomainService> logger,
IGlobalSettings globalSettings)
{
_domainRepository = domainRepository;
_organizationUserRepository = organizationUserRepository;
_dnsResolverService = dnsResolverService;
_eventService = eventService;
_mailService = mailService;
_verifyOrganizationDomainCommand = verifyOrganizationDomainCommand;
_timeProvider = timeProvider;
_logger = logger;
_globalSettings = globalSettings;
}
@ -37,7 +41,7 @@ public class OrganizationDomainService : IOrganizationDomainService
public async Task ValidateOrganizationsDomainAsync()
{
//Date should be set 1 hour behind to ensure it selects all domains that should be verified
var runDate = DateTime.UtcNow.AddHours(-1);
var runDate = _timeProvider.GetUtcNow().UtcDateTime.AddHours(-1);
var verifiableDomains = await _domainRepository.GetManyByNextRunDateAsync(runDate);
@ -45,43 +49,17 @@ public class OrganizationDomainService : IOrganizationDomainService
foreach (var domain in verifiableDomains)
{
_logger.LogInformation(Constants.BypassFiltersEventId,
"Attempting verification for organization {OrgId} with domain {Domain}",
domain.OrganizationId,
domain.DomainName);
try
{
_logger.LogInformation(Constants.BypassFiltersEventId, "Attempting verification for organization {OrgId} with domain {Domain}", domain.OrganizationId, domain.DomainName);
var status = await _dnsResolverService.ResolveAsync(domain.DomainName, domain.Txt);
if (status)
{
_logger.LogInformation(Constants.BypassFiltersEventId, "Successfully validated domain");
// Update entry on OrganizationDomain table
domain.SetLastCheckedDate();
domain.SetVerifiedDate();
domain.SetJobRunCount();
await _domainRepository.ReplaceAsync(domain);
await _eventService.LogOrganizationDomainEventAsync(domain, EventType.OrganizationDomain_Verified,
EventSystemUser.DomainVerification);
}
else
{
// Update entry on OrganizationDomain table
domain.SetLastCheckedDate();
domain.SetJobRunCount();
domain.SetNextRunDate(_globalSettings.DomainVerification.VerificationInterval);
await _domainRepository.ReplaceAsync(domain);
await _eventService.LogOrganizationDomainEventAsync(domain, EventType.OrganizationDomain_NotVerified,
EventSystemUser.DomainVerification);
_logger.LogInformation(Constants.BypassFiltersEventId, "Verification for organization {OrgId} with domain {Domain} failed",
domain.OrganizationId, domain.DomainName);
}
_ = await _verifyOrganizationDomainCommand.SystemVerifyOrganizationDomainAsync(domain);
}
catch (Exception ex)
{
// Update entry on OrganizationDomain table
domain.SetLastCheckedDate();
domain.SetJobRunCount();
domain.SetNextRunDate(_globalSettings.DomainVerification.VerificationInterval);
await _domainRepository.ReplaceAsync(domain);

View File

@ -229,13 +229,13 @@ public class OrganizationDomainControllerTests
sutProvider.GetDependency<IOrganizationDomainRepository>()
.GetDomainByIdOrganizationIdAsync(organizationDomain.Id, organizationDomain.OrganizationId)
.Returns(organizationDomain);
sutProvider.GetDependency<IVerifyOrganizationDomainCommand>().VerifyOrganizationDomainAsync(organizationDomain)
sutProvider.GetDependency<IVerifyOrganizationDomainCommand>().UserVerifyOrganizationDomainAsync(organizationDomain)
.Returns(new OrganizationDomain());
var result = await sutProvider.Sut.Verify(organizationDomain.OrganizationId, organizationDomain.Id);
await sutProvider.GetDependency<IVerifyOrganizationDomainCommand>().Received(1)
.VerifyOrganizationDomainAsync(organizationDomain);
.UserVerifyOrganizationDomainAsync(organizationDomain);
Assert.IsType<OrganizationDomainResponseModel>(result);
}

View File

@ -15,7 +15,7 @@ namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationDomains;
public class VerifyOrganizationDomainCommandTests
{
[Theory, BitAutoData]
public async Task VerifyOrganizationDomain_ShouldThrowConflict_WhenDomainHasBeenClaimed(Guid id,
public async Task UserVerifyOrganizationDomain_ShouldThrowConflict_WhenDomainHasBeenClaimed(Guid id,
SutProvider<VerifyOrganizationDomainCommand> sutProvider)
{
var expected = new OrganizationDomain
@ -30,14 +30,14 @@ public class VerifyOrganizationDomainCommandTests
.GetByIdAsync(id)
.Returns(expected);
var requestAction = async () => await sutProvider.Sut.VerifyOrganizationDomainAsync(expected);
var requestAction = async () => await sutProvider.Sut.UserVerifyOrganizationDomainAsync(expected);
var exception = await Assert.ThrowsAsync<ConflictException>(requestAction);
Assert.Contains("Domain has already been verified.", exception.Message);
}
[Theory, BitAutoData]
public async Task VerifyOrganizationDomain_ShouldThrowConflict_WhenDomainHasBeenClaimedByAnotherOrganization(Guid id,
public async Task UserVerifyOrganizationDomain_ShouldThrowConflict_WhenDomainHasBeenClaimedByAnotherOrganization(Guid id,
SutProvider<VerifyOrganizationDomainCommand> sutProvider)
{
var expected = new OrganizationDomain
@ -54,14 +54,14 @@ public class VerifyOrganizationDomainCommandTests
.GetClaimedDomainsByDomainNameAsync(expected.DomainName)
.Returns(new List<OrganizationDomain> { expected });
var requestAction = async () => await sutProvider.Sut.VerifyOrganizationDomainAsync(expected);
var requestAction = async () => await sutProvider.Sut.UserVerifyOrganizationDomainAsync(expected);
var exception = await Assert.ThrowsAsync<ConflictException>(requestAction);
Assert.Contains("The domain is not available to be claimed.", exception.Message);
}
[Theory, BitAutoData]
public async Task VerifyOrganizationDomain_ShouldVerifyDomainUpdateAndLogEvent_WhenTxtRecordExists(Guid id,
public async Task UserVerifyOrganizationDomain_ShouldVerifyDomainUpdateAndLogEvent_WhenTxtRecordExists(Guid id,
SutProvider<VerifyOrganizationDomainCommand> sutProvider)
{
var expected = new OrganizationDomain
@ -81,7 +81,7 @@ public class VerifyOrganizationDomainCommandTests
.ResolveAsync(expected.DomainName, Arg.Any<string>())
.Returns(true);
var result = await sutProvider.Sut.VerifyOrganizationDomainAsync(expected);
var result = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(expected);
Assert.NotNull(result.VerifiedDate);
await sutProvider.GetDependency<IOrganizationDomainRepository>().Received(1)
@ -91,7 +91,7 @@ public class VerifyOrganizationDomainCommandTests
}
[Theory, BitAutoData]
public async Task VerifyOrganizationDomain_ShouldNotSetVerifiedDate_WhenTxtRecordDoesNotExist(Guid id,
public async Task UserVerifyOrganizationDomain_ShouldNotSetVerifiedDate_WhenTxtRecordDoesNotExist(Guid id,
SutProvider<VerifyOrganizationDomainCommand> sutProvider)
{
var expected = new OrganizationDomain
@ -111,10 +111,30 @@ public class VerifyOrganizationDomainCommandTests
.ResolveAsync(expected.DomainName, Arg.Any<string>())
.Returns(false);
var result = await sutProvider.Sut.VerifyOrganizationDomainAsync(expected);
var result = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(expected);
Assert.Null(result.VerifiedDate);
await sutProvider.GetDependency<IEventService>().Received(1)
.LogOrganizationDomainEventAsync(Arg.Any<OrganizationDomain>(), EventType.OrganizationDomain_NotVerified);
}
[Theory, BitAutoData]
public async Task SystemVerifyOrganizationDomain_CallsEventServiceWithUpdatedJobRunCount(SutProvider<VerifyOrganizationDomainCommand> sutProvider)
{
var domain = new OrganizationDomain()
{
Id = Guid.NewGuid(),
OrganizationId = Guid.NewGuid(),
CreationDate = DateTime.UtcNow,
DomainName = "test.com",
Txt = "btw+12345",
};
_ = await sutProvider.Sut.SystemVerifyOrganizationDomainAsync(domain);
await sutProvider.GetDependency<IEventService>().ReceivedWithAnyArgs(1)
.LogOrganizationDomainEventAsync(default, EventType.OrganizationDomain_NotVerified,
EventSystemUser.DomainVerification);
}
}

View File

@ -1,8 +1,7 @@
using Bit.Core.AdminConsole.Services.Implementations;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
using Bit.Core.AdminConsole.Services.Implementations;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
@ -36,18 +35,14 @@ public class OrganizationDomainServiceTests
Txt = "btw+6789"
}
};
sutProvider.GetDependency<IOrganizationDomainRepository>().GetManyByNextRunDateAsync(default)
.ReturnsForAnyArgs(domains);
await sutProvider.Sut.ValidateOrganizationsDomainAsync();
await sutProvider.GetDependency<IDnsResolverService>().ReceivedWithAnyArgs(2)
.ResolveAsync(default, default);
await sutProvider.GetDependency<IOrganizationDomainRepository>().ReceivedWithAnyArgs(2)
.ReplaceAsync(default);
await sutProvider.GetDependency<IEventService>().ReceivedWithAnyArgs(2)
.LogOrganizationDomainEventAsync(default, EventType.OrganizationDomain_NotVerified,
EventSystemUser.DomainVerification);
await sutProvider.GetDependency<IVerifyOrganizationDomainCommand>().ReceivedWithAnyArgs(2)
.SystemVerifyOrganizationDomainAsync(default);
}
[Theory, BitAutoData]