diff --git a/src/Core/Services/Implementations/SsoConfigService.cs b/src/Core/Services/Implementations/SsoConfigService.cs index c219d954c..429c47075 100644 --- a/src/Core/Services/Implementations/SsoConfigService.cs +++ b/src/Core/Services/Implementations/SsoConfigService.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Threading.Tasks; using Bit.Core.Enums; using Bit.Core.Exceptions; @@ -12,17 +13,20 @@ namespace Bit.Core.Services private readonly ISsoConfigRepository _ssoConfigRepository; private readonly IPolicyRepository _policyRepository; private readonly IOrganizationRepository _organizationRepository; + private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IEventService _eventService; public SsoConfigService( ISsoConfigRepository ssoConfigRepository, IPolicyRepository policyRepository, IOrganizationRepository organizationRepository, + IOrganizationUserRepository organizationUserRepository, IEventService eventService) { _ssoConfigRepository = ssoConfigRepository; _policyRepository = policyRepository; _organizationRepository = organizationRepository; + _organizationUserRepository = organizationUserRepository; _eventService = eventService; } @@ -42,15 +46,23 @@ namespace Bit.Core.Services } var oldConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(config.OrganizationId); - if (oldConfig?.GetData()?.UseKeyConnector == true && !useKeyConnector) + var disabledKeyConnector = oldConfig?.GetData()?.UseKeyConnector == true && !useKeyConnector; + if (disabledKeyConnector && await AnyOrgUserHasKeyConnectorEnabledAsync(config.OrganizationId)) { - throw new BadRequestException("KeyConnector cannot be disabled at this moment."); + throw new BadRequestException("Key Connector cannot be disabled at this moment."); } await LogEventsAsync(config, oldConfig); await _ssoConfigRepository.UpsertAsync(config); } + private async Task AnyOrgUserHasKeyConnectorEnabledAsync(Guid organizationId) + { + var userDetails = + await _organizationUserRepository.GetManyDetailsByOrganizationAsync(organizationId); + return userDetails.Any(u => u.UsesKeyConnector); + } + private async Task VerifyDependenciesAsync(SsoConfig config) { var policy = await _policyRepository.GetByOrganizationIdTypeAsync(config.OrganizationId, PolicyType.SingleOrg); diff --git a/test/Core.Test/Services/SsoConfigServiceTests.cs b/test/Core.Test/Services/SsoConfigServiceTests.cs index f148b37a0..0db186497 100644 --- a/test/Core.Test/Services/SsoConfigServiceTests.cs +++ b/test/Core.Test/Services/SsoConfigServiceTests.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using Bit.Core.Exceptions; +using Bit.Core.Models.Data; using Bit.Core.Models.Table; using Bit.Core.Repositories; using Bit.Core.Services; @@ -96,16 +97,53 @@ namespace Bit.Core.Test.Services var ssoConfigRepository = sutProvider.GetDependency(); ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(oldSsoConfig); ssoConfigRepository.UpsertAsync(newSsoConfig).Returns(Task.CompletedTask); + sutProvider.GetDependency().GetManyDetailsByOrganizationAsync(orgId) + .Returns(new[] { new OrganizationUserUserDetails { UsesKeyConnector = true } }); var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.SaveAsync(newSsoConfig)); - Assert.Contains("KeyConnector cannot be disabled at this moment.", exception.Message); + Assert.Contains("Key Connector cannot be disabled at this moment.", exception.Message); await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() .UpsertAsync(default); } + [Theory, CustomAutoData(typeof(SutProviderCustomization))] + public async Task SaveAsync_AllowDisablingKeyConnectorWhenNoUserIsUsingIt( + SutProvider sutProvider, Guid orgId) + { + var utcNow = DateTime.UtcNow; + + var oldSsoConfig = new SsoConfig + { + Id = 1, + Data = "{\"useKeyConnector\": true}", + Enabled = true, + OrganizationId = orgId, + CreationDate = utcNow.AddDays(-10), + RevisionDate = utcNow.AddDays(-10), + }; + + var newSsoConfig = new SsoConfig + { + Id = 1, + Data = "{}", + Enabled = true, + OrganizationId = orgId, + CreationDate = utcNow.AddDays(-10), + RevisionDate = utcNow, + }; + + var ssoConfigRepository = sutProvider.GetDependency(); + ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(oldSsoConfig); + ssoConfigRepository.UpsertAsync(newSsoConfig).Returns(Task.CompletedTask); + sutProvider.GetDependency().GetManyDetailsByOrganizationAsync(orgId) + .Returns(new[] { new OrganizationUserUserDetails { UsesKeyConnector = false } }); + + await sutProvider.Sut.SaveAsync(newSsoConfig); + } + [Theory, CustomAutoData(typeof(SutProviderCustomization))] public async Task SaveAsync_KeyConnector_SingleOrgNotEnabled(SutProvider sutProvider) {