diff --git a/src/Core/AdminConsole/Enums/EventSystemUser.cs b/src/Core/AdminConsole/Enums/EventSystemUser.cs index df9be9a350b7..c3e13705dd9f 100644 --- a/src/Core/AdminConsole/Enums/EventSystemUser.cs +++ b/src/Core/AdminConsole/Enums/EventSystemUser.cs @@ -2,6 +2,7 @@ public enum EventSystemUser : byte { + Unknown = 0, SCIM = 1, DomainVerification = 2, PublicApi = 3, diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommand.cs index 870fa72aa709..f44d1251cb6b 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommand.cs @@ -1,7 +1,9 @@ using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.Models.Data; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces; using Bit.Core.AdminConsole.Services; +using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; @@ -12,124 +14,114 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains; -public class VerifyOrganizationDomainCommand : IVerifyOrganizationDomainCommand +public class VerifyOrganizationDomainCommand( + IOrganizationDomainRepository organizationDomainRepository, + IDnsResolverService dnsResolverService, + IEventService eventService, + IGlobalSettings globalSettings, + IPolicyService policyService, + IFeatureService featureService, + ICurrentContext currentContext, + ILogger logger) + : IVerifyOrganizationDomainCommand { - private readonly IOrganizationDomainRepository _organizationDomainRepository; - private readonly IDnsResolverService _dnsResolverService; - private readonly IEventService _eventService; - private readonly IGlobalSettings _globalSettings; - private readonly IPolicyService _policyService; - private readonly IFeatureService _featureService; - private readonly ILogger _logger; - - public VerifyOrganizationDomainCommand( - IOrganizationDomainRepository organizationDomainRepository, - IDnsResolverService dnsResolverService, - IEventService eventService, - IGlobalSettings globalSettings, - IPolicyService policyService, - IFeatureService featureService, - ILogger logger) - { - _organizationDomainRepository = organizationDomainRepository; - _dnsResolverService = dnsResolverService; - _eventService = eventService; - _globalSettings = globalSettings; - _policyService = policyService; - _featureService = featureService; - _logger = logger; - } public async Task UserVerifyOrganizationDomainAsync(OrganizationDomain organizationDomain) { - var domainVerificationResult = await VerifyOrganizationDomainAsync(organizationDomain); + var actingUser = new StandardUser(currentContext.UserId ?? Guid.Empty, await currentContext.OrganizationOwner(organizationDomain.OrganizationId)); - await _eventService.LogOrganizationDomainEventAsync(domainVerificationResult, + var domainVerificationResult = await VerifyOrganizationDomainAsync(organizationDomain, actingUser); + + await eventService.LogOrganizationDomainEventAsync(domainVerificationResult, domainVerificationResult.VerifiedDate != null ? EventType.OrganizationDomain_Verified : EventType.OrganizationDomain_NotVerified); - await _organizationDomainRepository.ReplaceAsync(domainVerificationResult); + await organizationDomainRepository.ReplaceAsync(domainVerificationResult); return domainVerificationResult; } public async Task SystemVerifyOrganizationDomainAsync(OrganizationDomain organizationDomain) { + var actingUser = new SystemUser(EventSystemUser.DomainVerification); + organizationDomain.SetJobRunCount(); - var domainVerificationResult = await VerifyOrganizationDomainAsync(organizationDomain); + var domainVerificationResult = await VerifyOrganizationDomainAsync(organizationDomain, actingUser); if (domainVerificationResult.VerifiedDate is not null) { - _logger.LogInformation(Constants.BypassFiltersEventId, "Successfully validated domain"); + logger.LogInformation(Constants.BypassFiltersEventId, "Successfully validated domain"); - await _eventService.LogOrganizationDomainEventAsync(domainVerificationResult, + await eventService.LogOrganizationDomainEventAsync(domainVerificationResult, EventType.OrganizationDomain_Verified, EventSystemUser.DomainVerification); } else { - domainVerificationResult.SetNextRunDate(_globalSettings.DomainVerification.VerificationInterval); + domainVerificationResult.SetNextRunDate(globalSettings.DomainVerification.VerificationInterval); - await _eventService.LogOrganizationDomainEventAsync(domainVerificationResult, + await eventService.LogOrganizationDomainEventAsync(domainVerificationResult, EventType.OrganizationDomain_NotVerified, EventSystemUser.DomainVerification); - _logger.LogInformation(Constants.BypassFiltersEventId, + logger.LogInformation(Constants.BypassFiltersEventId, "Verification for organization {OrgId} with domain {Domain} failed", domainVerificationResult.OrganizationId, domainVerificationResult.DomainName); } - await _organizationDomainRepository.ReplaceAsync(domainVerificationResult); + await organizationDomainRepository.ReplaceAsync(domainVerificationResult); return domainVerificationResult; } - private async Task VerifyOrganizationDomainAsync(OrganizationDomain domain) + private async Task VerifyOrganizationDomainAsync(OrganizationDomain domain, IActingUser actingUser) { domain.SetLastCheckedDate(); if (domain.VerifiedDate is not null) { - await _organizationDomainRepository.ReplaceAsync(domain); + await organizationDomainRepository.ReplaceAsync(domain); throw new ConflictException("Domain has already been verified."); } var claimedDomain = - await _organizationDomainRepository.GetClaimedDomainsByDomainNameAsync(domain.DomainName); + await organizationDomainRepository.GetClaimedDomainsByDomainNameAsync(domain.DomainName); if (claimedDomain.Count > 0) { - await _organizationDomainRepository.ReplaceAsync(domain); + await organizationDomainRepository.ReplaceAsync(domain); throw new ConflictException("The domain is not available to be claimed."); } try { - if (await _dnsResolverService.ResolveAsync(domain.DomainName, domain.Txt)) + if (await dnsResolverService.ResolveAsync(domain.DomainName, domain.Txt)) { domain.SetVerifiedDate(); - await EnableSingleOrganizationPolicyAsync(domain.OrganizationId); + await EnableSingleOrganizationPolicyAsync(domain.OrganizationId, actingUser); } } catch (Exception e) { - _logger.LogError("Error verifying Organization domain: {domain}. {errorMessage}", + logger.LogError("Error verifying Organization domain: {domain}. {errorMessage}", domain.DomainName, e.Message); } return domain; } - private async Task EnableSingleOrganizationPolicyAsync(Guid organizationId) + private async Task EnableSingleOrganizationPolicyAsync(Guid organizationId, IActingUser actingUser) { - if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)) + if (featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)) { - await _policyService.SaveAsync( - new Policy { OrganizationId = organizationId, Type = PolicyType.SingleOrg, Enabled = true }, null); + await policyService.SaveAsync( + new Policy { OrganizationId = organizationId, Type = PolicyType.SingleOrg, Enabled = true }, + savingUserId: actingUser is StandardUser standardUser ? standardUser.UserId : null, + eventSystemUser: actingUser is SystemUser systemUser ? systemUser.SystemUserType : null); } } } diff --git a/src/Core/AdminConsole/Services/IPolicyService.cs b/src/Core/AdminConsole/Services/IPolicyService.cs index 16ff2f4fa11c..715c3a34d934 100644 --- a/src/Core/AdminConsole/Services/IPolicyService.cs +++ b/src/Core/AdminConsole/Services/IPolicyService.cs @@ -9,7 +9,7 @@ namespace Bit.Core.AdminConsole.Services; public interface IPolicyService { - Task SaveAsync(Policy policy, Guid? savingUserId); + Task SaveAsync(Policy policy, Guid? savingUserId, EventSystemUser? eventSystemUser = null); /// /// Get the combined master password policy options for the specified user. diff --git a/src/Core/AdminConsole/Services/Implementations/PolicyService.cs b/src/Core/AdminConsole/Services/Implementations/PolicyService.cs index 29d78910c8d2..69ec27fd8a2a 100644 --- a/src/Core/AdminConsole/Services/Implementations/PolicyService.cs +++ b/src/Core/AdminConsole/Services/Implementations/PolicyService.cs @@ -70,7 +70,7 @@ public PolicyService( _currentContext = currentContext; } - public async Task SaveAsync(Policy policy, Guid? savingUserId) + public async Task SaveAsync(Policy policy, Guid? savingUserId, EventSystemUser? eventSystemUser = null) { if (_featureService.IsEnabled(FeatureFlagKeys.Pm13322AddPolicyDefinitions)) { @@ -84,7 +84,7 @@ public async Task SaveAsync(Policy policy, Guid? savingUserId) Data = policy.Data, PerformedBy = savingUserId.HasValue ? new StandardUser(savingUserId.Value, await _currentContext.OrganizationOwner(policy.OrganizationId)) - : null + : new SystemUser(eventSystemUser ?? EventSystemUser.Unknown) }; await _savePolicyCommand.SaveAsync(policyUpdate); diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommandTests.cs index 2fcaf8134c80..e2a12c675e81 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommandTests.cs @@ -2,6 +2,7 @@ using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains; using Bit.Core.AdminConsole.Services; +using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; @@ -143,7 +144,7 @@ await sutProvider.GetDependency().ReceivedWithAnyArgs(1) [Theory, BitAutoData] public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningEnabled_WhenDomainIsVerified_ThenSingleOrgPolicyShouldBeEnabled( - OrganizationDomain domain, SutProvider sutProvider) + OrganizationDomain domain, Guid userId, SutProvider sutProvider) { sutProvider.GetDependency() .GetClaimedDomainsByDomainNameAsync(domain.DomainName) @@ -157,11 +158,14 @@ public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithA .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), null); + .SaveAsync(Arg.Is(x => x.Type == PolicyType.SingleOrg && x.OrganizationId == domain.OrganizationId && x.Enabled), userId); } [Theory, BitAutoData] diff --git a/test/Core.Test/AdminConsole/Services/EventServiceTests.cs b/test/Core.Test/AdminConsole/Services/EventServiceTests.cs index 18f5371b4943..d064fce2ecd8 100644 --- a/test/Core.Test/AdminConsole/Services/EventServiceTests.cs +++ b/test/Core.Test/AdminConsole/Services/EventServiceTests.cs @@ -169,7 +169,6 @@ public async Task LogOrganizationUserEvent_WithEventSystemUser_LogsRequiredInfo( new EventMessage() { IpAddress = ipAddress, - DeviceType = DeviceType.Server, OrganizationId = orgUser.OrganizationId, UserId = orgUser.UserId, OrganizationUserId = orgUser.Id,