using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Models.Data; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains; using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Data.Organizations; using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using NSubstitute; using Xunit; namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationDomains; [SutProviderCustomize] public class VerifyOrganizationDomainCommandTests { [Theory, BitAutoData] public async Task UserVerifyOrganizationDomainAsync_ShouldThrowConflict_WhenDomainHasBeenClaimed(Guid id, SutProvider sutProvider) { var expected = new OrganizationDomain { Id = id, OrganizationId = Guid.NewGuid(), DomainName = "Test Domain", Txt = "btw+test18383838383" }; sutProvider.GetDependency() .UserId.Returns(Guid.NewGuid()); expected.SetVerifiedDate(); sutProvider.GetDependency() .GetByIdAsync(id) .Returns(expected); var requestAction = async () => await sutProvider.Sut.UserVerifyOrganizationDomainAsync(expected); var exception = await Assert.ThrowsAsync(requestAction); Assert.Contains("Domain has already been verified.", exception.Message); } [Theory, BitAutoData] public async Task UserVerifyOrganizationDomainAsync_ShouldThrowConflict_WhenDomainHasBeenClaimedByAnotherOrganization(Guid id, SutProvider sutProvider) { var expected = new OrganizationDomain { Id = id, OrganizationId = Guid.NewGuid(), DomainName = "Test Domain", Txt = "btw+test18383838383" }; sutProvider.GetDependency() .GetByIdAsync(id) .Returns(expected); sutProvider.GetDependency() .UserId.Returns(Guid.NewGuid()); sutProvider.GetDependency() .GetClaimedDomainsByDomainNameAsync(expected.DomainName) .Returns(new List { expected }); var requestAction = async () => await sutProvider.Sut.UserVerifyOrganizationDomainAsync(expected); var exception = await Assert.ThrowsAsync(requestAction); Assert.Contains("The domain is not available to be claimed.", exception.Message); } [Theory, BitAutoData] public async Task UserVerifyOrganizationDomainAsync_ShouldVerifyDomainUpdateAndLogEvent_WhenTxtRecordExists(Guid id, SutProvider sutProvider) { var expected = new OrganizationDomain { Id = id, OrganizationId = Guid.NewGuid(), DomainName = "Test Domain", Txt = "btw+test18383838383" }; sutProvider.GetDependency() .GetByIdAsync(id) .Returns(expected); sutProvider.GetDependency() .UserId.Returns(Guid.NewGuid()); sutProvider.GetDependency() .GetClaimedDomainsByDomainNameAsync(expected.DomainName) .Returns(new List()); sutProvider.GetDependency() .ResolveAsync(expected.DomainName, Arg.Any()) .Returns(true); var result = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(expected); Assert.NotNull(result.VerifiedDate); await sutProvider.GetDependency().Received(1) .ReplaceAsync(Arg.Any()); await sutProvider.GetDependency().Received(1) .LogOrganizationDomainEventAsync(Arg.Any(), EventType.OrganizationDomain_Verified); } [Theory, BitAutoData] public async Task UserVerifyOrganizationDomainAsync_ShouldNotSetVerifiedDate_WhenTxtRecordDoesNotExist(Guid id, SutProvider sutProvider) { var expected = new OrganizationDomain { Id = id, OrganizationId = Guid.NewGuid(), DomainName = "Test Domain", Txt = "btw+test18383838383" }; sutProvider.GetDependency() .GetByIdAsync(id) .Returns(expected); sutProvider.GetDependency() .UserId.Returns(Guid.NewGuid()); sutProvider.GetDependency() .GetClaimedDomainsByDomainNameAsync(expected.DomainName) .Returns(new List()); sutProvider.GetDependency() .ResolveAsync(expected.DomainName, Arg.Any()) .Returns(false); var result = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(expected); Assert.Null(result.VerifiedDate); await sutProvider.GetDependency().Received(1) .LogOrganizationDomainEventAsync(Arg.Any(), EventType.OrganizationDomain_NotVerified); } [Theory, BitAutoData] public async Task SystemVerifyOrganizationDomainAsync_CallsEventServiceWithUpdatedJobRunCount(SutProvider 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().ReceivedWithAnyArgs(1) .LogOrganizationDomainEventAsync(default, EventType.OrganizationDomain_NotVerified, EventSystemUser.DomainVerification); } [Theory, BitAutoData] public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningEnabled_WhenDomainIsVerified_ThenSingleOrgPolicyShouldBeEnabled( OrganizationDomain domain, Guid userId, SutProvider sutProvider) { sutProvider.GetDependency() .GetClaimedDomainsByDomainNameAsync(domain.DomainName) .Returns([]); sutProvider.GetDependency() .ResolveAsync(domain.DomainName, domain.Txt) .Returns(true); sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) .Returns(true); sutProvider.GetDependency() .UserId.Returns(userId); _ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain); await sutProvider.GetDependency() .Received(1) .SaveAsync(Arg.Is(x => x.Type == PolicyType.SingleOrg && x.OrganizationId == domain.OrganizationId && x.Enabled && x.PerformedBy is StandardUser && x.PerformedBy.UserId == userId)); } [Theory, BitAutoData] public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningDisabled_WhenDomainIsVerified_ThenSingleOrgPolicyShouldBeNotBeEnabled( OrganizationDomain domain, SutProvider sutProvider) { sutProvider.GetDependency() .GetClaimedDomainsByDomainNameAsync(domain.DomainName) .Returns([]); sutProvider.GetDependency() .ResolveAsync(domain.DomainName, domain.Txt) .Returns(true); sutProvider.GetDependency() .UserId.Returns(Guid.NewGuid()); sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) .Returns(false); _ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain); await sutProvider.GetDependency() .DidNotReceive() .SaveAsync(Arg.Any()); } [Theory, BitAutoData] public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningEnabled_WhenDomainIsNotVerified_ThenSingleOrgPolicyShouldNotBeEnabled( OrganizationDomain domain, SutProvider sutProvider) { sutProvider.GetDependency() .GetClaimedDomainsByDomainNameAsync(domain.DomainName) .Returns([]); sutProvider.GetDependency() .ResolveAsync(domain.DomainName, domain.Txt) .Returns(false); sutProvider.GetDependency() .UserId.Returns(Guid.NewGuid()); sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) .Returns(true); _ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain); await sutProvider.GetDependency() .DidNotReceive() .SaveAsync(Arg.Any()); } [Theory, BitAutoData] public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningDisabled_WhenDomainIsNotVerified_ThenSingleOrgPolicyShouldBeNotBeEnabled( OrganizationDomain domain, SutProvider sutProvider) { sutProvider.GetDependency() .GetClaimedDomainsByDomainNameAsync(domain.DomainName) .Returns([]); sutProvider.GetDependency() .ResolveAsync(domain.DomainName, domain.Txt) .Returns(false); sutProvider.GetDependency() .UserId.Returns(Guid.NewGuid()); sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) .Returns(true); _ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain); await sutProvider.GetDependency() .DidNotReceive() .SaveAsync(Arg.Any()); } [Theory, BitAutoData] public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningEnabled_WhenDomainIsVerified_ThenEmailShouldBeSentToUsersWhoBelongToTheDomain( ICollection organizationUsers, OrganizationDomain domain, Organization organization, SutProvider sutProvider) { foreach (var organizationUser in organizationUsers) { organizationUser.Email = $"{organizationUser.Name}@{domain.DomainName}"; } var mockedUsers = organizationUsers .Where(x => x.Status != OrganizationUserStatusType.Invited && x.Status != OrganizationUserStatusType.Revoked).ToList(); organization.Id = domain.OrganizationId; sutProvider.GetDependency() .GetClaimedDomainsByDomainNameAsync(domain.DomainName) .Returns([]); sutProvider.GetDependency() .GetByIdAsync(domain.OrganizationId) .Returns(organization); sutProvider.GetDependency() .ResolveAsync(domain.DomainName, domain.Txt) .Returns(true); sutProvider.GetDependency() .UserId.Returns(Guid.NewGuid()); sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.AccountDeprovisioning) .Returns(true); sutProvider.GetDependency() .GetManyDetailsByOrganizationAsync(domain.OrganizationId) .Returns(mockedUsers); _ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain); await sutProvider.GetDependency().Received().SendClaimedDomainUserEmailAsync( Arg.Is(x => x.EmailList.Count(e => e.EndsWith(domain.DomainName)) == mockedUsers.Count && x.Organization.Id == organization.Id)); } }