diff --git a/src/Core/Services/Implementations/PolicyService.cs b/src/Core/Services/Implementations/PolicyService.cs index c38ea9fde7..953317a0bf 100644 --- a/src/Core/Services/Implementations/PolicyService.cs +++ b/src/Core/Services/Implementations/PolicyService.cs @@ -54,37 +54,27 @@ namespace Bit.Core.Services case PolicyType.SingleOrg: if (!policy.Enabled) { - var requireSso = - await _policyRepository.GetByOrganizationIdTypeAsync(org.Id, PolicyType.RequireSso); - if (requireSso?.Enabled == true) - { - throw new BadRequestException("Single Sign-On Authentication policy is enabled."); - } - - var vaultTimeout = - await _policyRepository.GetByOrganizationIdTypeAsync(org.Id, PolicyType.MaximumVaultTimeout); - if (vaultTimeout?.Enabled == true) - { - throw new BadRequestException("Maximum Vault Timeout policy is enabled."); - } - - var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(org.Id); - if (ssoConfig?.GetData()?.UseKeyConnector == true) - { - throw new BadRequestException("KeyConnector is enabled."); - } + await RequiredBySsoAsync(org); + await RequiredByVaultTimeoutAsync(org); + await RequiredByKeyConnectorAsync(org); } break; case PolicyType.RequireSso: + if (policy.Enabled) + { + await DependsOnSingleOrgAsync(org); + } + else + { + await RequiredByKeyConnectorAsync(org); + } + break; + case PolicyType.MaximumVaultTimeout: if (policy.Enabled) { - var singleOrg = await _policyRepository.GetByOrganizationIdTypeAsync(org.Id, PolicyType.SingleOrg); - if (singleOrg?.Enabled != true) - { - throw new BadRequestException("Single Organization policy not enabled."); - } + await DependsOnSingleOrgAsync(org); } break; } @@ -144,5 +134,42 @@ namespace Bit.Core.Services await _policyRepository.UpsertAsync(policy); await _eventService.LogPolicyEventAsync(policy, Enums.EventType.Policy_Updated); } + + private async Task DependsOnSingleOrgAsync(Organization org) + { + var singleOrg = await _policyRepository.GetByOrganizationIdTypeAsync(org.Id, PolicyType.SingleOrg); + if (singleOrg?.Enabled != true) + { + throw new BadRequestException("Single Organization policy not enabled."); + } + } + + private async Task RequiredBySsoAsync(Organization org) + { + var requireSso = await _policyRepository.GetByOrganizationIdTypeAsync(org.Id, PolicyType.RequireSso); + if (requireSso?.Enabled == true) + { + throw new BadRequestException("Single Sign-On Authentication policy is enabled."); + } + } + + private async Task RequiredByKeyConnectorAsync(Organization org) + { + + var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(org.Id); + if (ssoConfig?.GetData()?.UseKeyConnector == true) + { + throw new BadRequestException("Key Connector is enabled."); + } + } + + private async Task RequiredByVaultTimeoutAsync(Organization org) + { + var vaultTimeout = await _policyRepository.GetByOrganizationIdTypeAsync(org.Id, PolicyType.MaximumVaultTimeout); + if (vaultTimeout?.Enabled == true) + { + throw new BadRequestException("Maximum Vault Timeout policy is enabled."); + } + } } } diff --git a/src/Core/Services/Implementations/SsoConfigService.cs b/src/Core/Services/Implementations/SsoConfigService.cs index 429c470756..b97c338699 100644 --- a/src/Core/Services/Implementations/SsoConfigService.cs +++ b/src/Core/Services/Implementations/SsoConfigService.cs @@ -65,10 +65,20 @@ namespace Bit.Core.Services private async Task VerifyDependenciesAsync(SsoConfig config) { - var policy = await _policyRepository.GetByOrganizationIdTypeAsync(config.OrganizationId, PolicyType.SingleOrg); - if (policy is not { Enabled: true }) + var singleOrgPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(config.OrganizationId, PolicyType.SingleOrg); + if (singleOrgPolicy is not { Enabled: true }) { - throw new BadRequestException("KeyConnector requires Single Organization to be enabled."); + throw new BadRequestException("Key Connector requires the Single Organization policy to be enabled."); + } + + var ssoPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(config.OrganizationId, PolicyType.RequireSso); + if (ssoPolicy is not { Enabled: true }) + { + throw new BadRequestException("Key Connector requires the Single Sign-On Authentication policy to be enabled."); + } + + if (!config.Enabled) { + throw new BadRequestException("You must enable SSO to use Key Connector."); } } diff --git a/test/Core.Test/Services/PolicyServiceTests.cs b/test/Core.Test/Services/PolicyServiceTests.cs index 18d68e82b0..dc7705dc1a 100644 --- a/test/Core.Test/Services/PolicyServiceTests.cs +++ b/test/Core.Test/Services/PolicyServiceTests.cs @@ -126,12 +126,16 @@ namespace Bit.Core.Test.Services .UpsertAsync(default); } - [Theory, CustomAutoData(typeof(SutProviderCustomization))] - public async Task SaveAsync_SingleOrg_KeyConnectorEnabled_ThrowsBadRequest( - [PolicyFixtures.Policy(Enums.PolicyType.SingleOrg)] Core.Models.Table.Policy policy, + [Theory] + [InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, Enums.PolicyType.SingleOrg)] + [InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, Enums.PolicyType.RequireSso)] + public async Task SaveAsync_PolicyRequiredByKeyConnector_DisablePolicy_ThrowsBadRequest( + Enums.PolicyType policyType, + Policy policy, SutProvider sutProvider) { policy.Enabled = false; + policy.Type = policyType; SetupOrg(sutProvider, policy.OrganizationId, new Organization { @@ -153,7 +157,7 @@ namespace Bit.Core.Test.Services Substitute.For(), Guid.NewGuid())); - Assert.Contains("KeyConnector is enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase); + Assert.Contains("Key Connector is enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase); await sutProvider.GetDependency() .DidNotReceiveWithAnyArgs() diff --git a/test/Core.Test/Services/SsoConfigServiceTests.cs b/test/Core.Test/Services/SsoConfigServiceTests.cs index 0db1864975..0205a1e760 100644 --- a/test/Core.Test/Services/SsoConfigServiceTests.cs +++ b/test/Core.Test/Services/SsoConfigServiceTests.cs @@ -145,7 +145,7 @@ namespace Bit.Core.Test.Services } [Theory, CustomAutoData(typeof(SutProviderCustomization))] - public async Task SaveAsync_KeyConnector_SingleOrgNotEnabled(SutProvider sutProvider) + public async Task SaveAsync_KeyConnector_SingleOrgNotEnabled_Throws(SutProvider sutProvider) { var utcNow = DateTime.UtcNow; @@ -162,7 +162,67 @@ namespace Bit.Core.Test.Services var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.SaveAsync(ssoConfig)); - Assert.Contains("KeyConnector requires Single Organization to be enabled.", exception.Message); + Assert.Contains("Key Connector requires the Single Organization policy to be enabled.", exception.Message); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .UpsertAsync(default); + } + + [Theory, CustomAutoData(typeof(SutProviderCustomization))] + public async Task SaveAsync_KeyConnector_SsoPolicyNotEnabled_Throws(SutProvider sutProvider) + { + var utcNow = DateTime.UtcNow; + + var ssoConfig = new SsoConfig + { + Id = default, + Data = "{\"useKeyConnector\": true}", + Enabled = true, + OrganizationId = Guid.NewGuid(), + CreationDate = utcNow.AddDays(-10), + RevisionDate = utcNow.AddDays(-10), + }; + + sutProvider.GetDependency().GetByOrganizationIdTypeAsync( + Arg.Any(), Enums.PolicyType.SingleOrg).Returns(new Policy + { + Enabled = true + }); + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.SaveAsync(ssoConfig)); + + Assert.Contains("Key Connector requires the Single Sign-On Authentication policy to be enabled.", exception.Message); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .UpsertAsync(default); + } + + [Theory, CustomAutoData(typeof(SutProviderCustomization))] + public async Task SaveAsync_KeyConnector_SsoConfigNotEnabled_Throws(SutProvider sutProvider) + { + var utcNow = DateTime.UtcNow; + + var ssoConfig = new SsoConfig + { + Id = default, + Data = "{\"useKeyConnector\": true}", + Enabled = false, + OrganizationId = Guid.NewGuid(), + CreationDate = utcNow.AddDays(-10), + RevisionDate = utcNow.AddDays(-10), + }; + + sutProvider.GetDependency().GetByOrganizationIdTypeAsync( + Arg.Any(), Arg.Any()).Returns(new Policy + { + Enabled = true + }); + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.SaveAsync(ssoConfig)); + + Assert.Contains("You must enable SSO to use Key Connector.", exception.Message); await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() .UpsertAsync(default);