1
0
mirror of https://github.com/bitwarden/server.git synced 2025-04-21 13:05:11 -05:00

feat: generate txt record server-side and remove initial domain verification, refs AC-2350 (#3940)

This commit is contained in:
Vincent Salucci 2024-04-08 14:32:20 -05:00 committed by GitHub
parent 9a2d383417
commit de8b7b14b8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 9 additions and 74 deletions

View File

@ -80,7 +80,6 @@ public class OrganizationDomainController : Controller
var organizationDomain = new OrganizationDomain var organizationDomain = new OrganizationDomain
{ {
OrganizationId = orgId, OrganizationId = orgId,
Txt = model.Txt,
DomainName = model.DomainName.ToLower() DomainName = model.DomainName.ToLower()
}; };

View File

@ -4,9 +4,6 @@ namespace Bit.Api.AdminConsole.Models.Request;
public class OrganizationDomainRequestModel public class OrganizationDomainRequestModel
{ {
[Required]
public string Txt { get; set; }
[Required] [Required]
public string DomainName { get; set; } public string DomainName { get; set; }
} }

View File

@ -5,6 +5,7 @@ using Bit.Core.Exceptions;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Settings; using Bit.Core.Settings;
using Bit.Core.Utilities;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains; namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains;
@ -50,26 +51,16 @@ public class CreateOrganizationDomainCommand : ICreateOrganizationDomainCommand
throw new ConflictException("A domain already exists for this organization."); throw new ConflictException("A domain already exists for this organization.");
} }
try // Generate and set DNS TXT Record
{ // DNS-Based Service Discovery RFC: https://www.ietf.org/rfc/rfc6763.txt; see section 6.1
if (await _dnsResolverService.ResolveAsync(organizationDomain.DomainName, organizationDomain.Txt)) // Google uses 43 chars for their TXT record value: https://support.google.com/a/answer/2716802
{ // A random 44 character string was used here to keep parity with prior client-side generation of 47 characters
organizationDomain.SetVerifiedDate(); organizationDomain.Txt = string.Join("=", "bw", CoreHelpers.RandomString(44));
}
}
catch (Exception e)
{
_logger.LogError(e, "Error verifying Organization domain.");
}
organizationDomain.SetNextRunDate(_globalSettings.DomainVerification.VerificationInterval); organizationDomain.SetNextRunDate(_globalSettings.DomainVerification.VerificationInterval);
organizationDomain.SetLastCheckedDate();
var orgDomain = await _organizationDomainRepository.CreateAsync(organizationDomain); var orgDomain = await _organizationDomainRepository.CreateAsync(organizationDomain);
await _eventService.LogOrganizationDomainEventAsync(orgDomain, EventType.OrganizationDomain_Added); await _eventService.LogOrganizationDomainEventAsync(orgDomain, EventType.OrganizationDomain_Added);
await _eventService.LogOrganizationDomainEventAsync(orgDomain,
orgDomain.VerifiedDate != null ? EventType.OrganizationDomain_Verified : EventType.OrganizationDomain_NotVerified);
return orgDomain; return orgDomain;
} }

View File

@ -7,7 +7,6 @@ using Bit.Core.Services;
using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes; using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute; using NSubstitute;
using NSubstitute.ExceptionExtensions;
using NSubstitute.ReturnsExtensions; using NSubstitute.ReturnsExtensions;
using Xunit; using Xunit;
@ -25,9 +24,6 @@ public class CreateOrganizationDomainCommandTests
sutProvider.GetDependency<IOrganizationDomainRepository>() sutProvider.GetDependency<IOrganizationDomainRepository>()
.GetDomainByOrgIdAndDomainNameAsync(orgDomain.OrganizationId, orgDomain.DomainName) .GetDomainByOrgIdAndDomainNameAsync(orgDomain.OrganizationId, orgDomain.DomainName)
.ReturnsNull(); .ReturnsNull();
sutProvider.GetDependency<IDnsResolverService>()
.ResolveAsync(orgDomain.DomainName, orgDomain.Txt)
.Returns(false);
orgDomain.SetNextRunDate(12); orgDomain.SetNextRunDate(12);
sutProvider.GetDependency<IOrganizationDomainRepository>() sutProvider.GetDependency<IOrganizationDomainRepository>()
.CreateAsync(orgDomain) .CreateAsync(orgDomain)
@ -38,12 +34,12 @@ public class CreateOrganizationDomainCommandTests
Assert.Equal(orgDomain.Id, result.Id); Assert.Equal(orgDomain.Id, result.Id);
Assert.Equal(orgDomain.OrganizationId, result.OrganizationId); Assert.Equal(orgDomain.OrganizationId, result.OrganizationId);
Assert.NotNull(result.LastCheckedDate); Assert.Null(result.LastCheckedDate);
Assert.Equal(orgDomain.Txt, result.Txt);
Assert.Equal(orgDomain.Txt.Length == 47, result.Txt.Length == 47);
Assert.Equal(orgDomain.NextRunDate, result.NextRunDate); Assert.Equal(orgDomain.NextRunDate, result.NextRunDate);
await sutProvider.GetDependency<IEventService>().Received(1) await sutProvider.GetDependency<IEventService>().Received(1)
.LogOrganizationDomainEventAsync(Arg.Any<OrganizationDomain>(), EventType.OrganizationDomain_Added); .LogOrganizationDomainEventAsync(Arg.Any<OrganizationDomain>(), EventType.OrganizationDomain_Added);
await sutProvider.GetDependency<IEventService>().Received(1)
.LogOrganizationDomainEventAsync(Arg.Any<OrganizationDomain>(), Arg.Is<EventType>(x => x == EventType.OrganizationDomain_NotVerified));
} }
[Theory, BitAutoData] [Theory, BitAutoData]
@ -79,52 +75,4 @@ public class CreateOrganizationDomainCommandTests
var exception = await Assert.ThrowsAsync<ConflictException>(requestAction); var exception = await Assert.ThrowsAsync<ConflictException>(requestAction);
Assert.Contains("A domain already exists for this organization.", exception.Message); Assert.Contains("A domain already exists for this organization.", exception.Message);
} }
[Theory, BitAutoData]
public async Task CreateAsync_ShouldNotSetVerifiedDate_WhenDomainCannotBeResolved(OrganizationDomain orgDomain,
SutProvider<CreateOrganizationDomainCommand> sutProvider)
{
sutProvider.GetDependency<IOrganizationDomainRepository>()
.GetClaimedDomainsByDomainNameAsync(orgDomain.DomainName)
.Returns(new List<OrganizationDomain>());
sutProvider.GetDependency<IOrganizationDomainRepository>()
.GetDomainByOrgIdAndDomainNameAsync(orgDomain.OrganizationId, orgDomain.DomainName)
.ReturnsNull();
sutProvider.GetDependency<IDnsResolverService>()
.ResolveAsync(orgDomain.DomainName, orgDomain.Txt)
.Throws(new DnsQueryException(""));
sutProvider.GetDependency<IOrganizationDomainRepository>()
.CreateAsync(orgDomain)
.Returns(orgDomain);
await sutProvider.Sut.CreateAsync(orgDomain);
Assert.Null(orgDomain.VerifiedDate);
}
[Theory, BitAutoData]
public async Task CreateAsync_ShouldSetVerifiedDateAndLogEvent_WhenDomainIsResolved(OrganizationDomain orgDomain,
SutProvider<CreateOrganizationDomainCommand> sutProvider)
{
sutProvider.GetDependency<IOrganizationDomainRepository>()
.GetClaimedDomainsByDomainNameAsync(orgDomain.DomainName)
.Returns(new List<OrganizationDomain>());
sutProvider.GetDependency<IOrganizationDomainRepository>()
.GetDomainByOrgIdAndDomainNameAsync(orgDomain.OrganizationId, orgDomain.DomainName)
.ReturnsNull();
sutProvider.GetDependency<IDnsResolverService>()
.ResolveAsync(orgDomain.DomainName, orgDomain.Txt)
.Returns(true);
sutProvider.GetDependency<IOrganizationDomainRepository>()
.CreateAsync(orgDomain)
.Returns(orgDomain);
var result = await sutProvider.Sut.CreateAsync(orgDomain);
Assert.NotNull(result.VerifiedDate);
await sutProvider.GetDependency<IEventService>().Received(1)
.LogOrganizationDomainEventAsync(Arg.Any<OrganizationDomain>(), EventType.OrganizationDomain_Added);
await sutProvider.GetDependency<IEventService>().Received(1)
.LogOrganizationDomainEventAsync(Arg.Any<OrganizationDomain>(), Arg.Is<EventType>(x => x == EventType.OrganizationDomain_Verified));
}
} }