From f8d6b205baf067ce1480172c3392b910b4882c7e Mon Sep 17 00:00:00 2001 From: Sven Date: Thu, 12 Mar 2026 20:03:31 -0500 Subject: [PATCH] Finalize org data ownership policy requirement --- .../ImportFeatures/ImportCiphersCommand.cs | 18 +---- .../Services/Implementations/CipherService.cs | 15 +--- .../ImportCiphersAsyncCommandTests.cs | 77 ++----------------- .../Vault/Services/CipherServiceTests.cs | 53 +------------ 4 files changed, 15 insertions(+), 148 deletions(-) diff --git a/src/Core/Tools/ImportFeatures/ImportCiphersCommand.cs b/src/Core/Tools/ImportFeatures/ImportCiphersCommand.cs index 3f856e96fc17..5be4acf31f43 100644 --- a/src/Core/Tools/ImportFeatures/ImportCiphersCommand.cs +++ b/src/Core/Tools/ImportFeatures/ImportCiphersCommand.cs @@ -1,15 +1,12 @@ // FIXME: Update this file to be null safe and then delete the line below #nullable disable -using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements; -using Bit.Core.AdminConsole.Services; using Bit.Core.Entities; using Bit.Core.Exceptions; using Bit.Core.Platform.Push; using Bit.Core.Repositories; -using Bit.Core.Services; using Bit.Core.Tools.ImportFeatures.Interfaces; using Bit.Core.Vault.Entities; using Bit.Core.Vault.Models.Data; @@ -22,12 +19,10 @@ public class ImportCiphersCommand : IImportCiphersCommand private readonly ICipherRepository _cipherRepository; private readonly IFolderRepository _folderRepository; private readonly IPushNotificationService _pushService; - private readonly IPolicyService _policyService; private readonly IOrganizationRepository _organizationRepository; private readonly IOrganizationUserRepository _organizationUserRepository; private readonly ICollectionRepository _collectionRepository; private readonly IPolicyRequirementQuery _policyRequirementQuery; - private readonly IFeatureService _featureService; public ImportCiphersCommand( ICipherRepository cipherRepository, @@ -36,9 +31,7 @@ public ImportCiphersCommand( IOrganizationRepository organizationRepository, IOrganizationUserRepository organizationUserRepository, IPushNotificationService pushService, - IPolicyService policyService, - IPolicyRequirementQuery policyRequirementQuery, - IFeatureService featureService) + IPolicyRequirementQuery policyRequirementQuery) { _cipherRepository = cipherRepository; _folderRepository = folderRepository; @@ -46,9 +39,7 @@ public ImportCiphersCommand( _organizationUserRepository = organizationUserRepository; _collectionRepository = collectionRepository; _pushService = pushService; - _policyService = policyService; _policyRequirementQuery = policyRequirementQuery; - _featureService = featureService; } public async Task ImportIntoIndividualVaultAsync( @@ -58,11 +49,10 @@ public async Task ImportIntoIndividualVaultAsync( Guid importingUserId) { // Make sure the user can save new ciphers to their personal vault - var organizationDataOwnershipEnabled = _featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements) - ? (await _policyRequirementQuery.GetAsync(importingUserId)).State == OrganizationDataOwnershipState.Enabled - : await _policyService.AnyPoliciesApplicableToUserAsync(importingUserId, PolicyType.OrganizationDataOwnership); + var organizationDataOwnershipPolicyRequirement = + await _policyRequirementQuery.GetAsync(importingUserId); - if (organizationDataOwnershipEnabled) + if (organizationDataOwnershipPolicyRequirement.State == OrganizationDataOwnershipState.Enabled) { throw new BadRequestException("You cannot import items into your personal vault because you are " + "a member of an organization which forbids it."); diff --git a/src/Core/Vault/Services/Implementations/CipherService.cs b/src/Core/Vault/Services/Implementations/CipherService.cs index 5f235879c676..d47659017679 100644 --- a/src/Core/Vault/Services/Implementations/CipherService.cs +++ b/src/Core/Vault/Services/Implementations/CipherService.cs @@ -3,10 +3,8 @@ using System.Text.Json; using Bit.Core.AdminConsole.Entities; -using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements; -using Bit.Core.AdminConsole.Services; using Bit.Core.Billing.Pricing; using Bit.Core.Enums; using Bit.Core.Exceptions; @@ -32,14 +30,12 @@ public class CipherService : ICipherService private readonly ICollectionRepository _collectionRepository; private readonly IUserRepository _userRepository; private readonly IOrganizationRepository _organizationRepository; - private readonly IOrganizationUserRepository _organizationUserRepository; private readonly ICollectionCipherRepository _collectionCipherRepository; private readonly ISecurityTaskRepository _securityTaskRepository; private readonly IPushNotificationService _pushService; private readonly IAttachmentStorageService _attachmentStorageService; private readonly IEventService _eventService; private readonly IUserService _userService; - private readonly IPolicyService _policyService; private readonly GlobalSettings _globalSettings; private const long _fileSizeLeeway = 1024L * 1024L; // 1MB private readonly IGetCipherPermissionsForUserQuery _getCipherPermissionsForUserQuery; @@ -54,14 +50,12 @@ public CipherService( ICollectionRepository collectionRepository, IUserRepository userRepository, IOrganizationRepository organizationRepository, - IOrganizationUserRepository organizationUserRepository, ICollectionCipherRepository collectionCipherRepository, ISecurityTaskRepository securityTaskRepository, IPushNotificationService pushService, IAttachmentStorageService attachmentStorageService, IEventService eventService, IUserService userService, - IPolicyService policyService, GlobalSettings globalSettings, IGetCipherPermissionsForUserQuery getCipherPermissionsForUserQuery, IPolicyRequirementQuery policyRequirementQuery, @@ -74,14 +68,12 @@ public CipherService( _collectionRepository = collectionRepository; _userRepository = userRepository; _organizationRepository = organizationRepository; - _organizationUserRepository = organizationUserRepository; _collectionCipherRepository = collectionCipherRepository; _securityTaskRepository = securityTaskRepository; _pushService = pushService; _attachmentStorageService = attachmentStorageService; _eventService = eventService; _userService = userService; - _policyService = policyService; _globalSettings = globalSettings; _getCipherPermissionsForUserQuery = getCipherPermissionsForUserQuery; _policyRequirementQuery = policyRequirementQuery; @@ -152,11 +144,10 @@ public async Task SaveDetailsAsync(CipherDetails cipher, Guid savingUserId, Date } else { - var organizationDataOwnershipEnabled = _featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements) - ? (await _policyRequirementQuery.GetAsync(savingUserId)).State == OrganizationDataOwnershipState.Enabled - : await _policyService.AnyPoliciesApplicableToUserAsync(savingUserId, PolicyType.OrganizationDataOwnership); + var organizationDataOwnershipPolicyRequirement = + await _policyRequirementQuery.GetAsync(savingUserId); - if (organizationDataOwnershipEnabled) + if (organizationDataOwnershipPolicyRequirement.State == OrganizationDataOwnershipState.Enabled) { throw new BadRequestException("Due to an Enterprise Policy, you are restricted from saving items to your personal vault."); } diff --git a/test/Core.Test/Tools/ImportFeatures/ImportCiphersAsyncCommandTests.cs b/test/Core.Test/Tools/ImportFeatures/ImportCiphersAsyncCommandTests.cs index f6b1bd200ae9..ccf4e85c0489 100644 --- a/test/Core.Test/Tools/ImportFeatures/ImportCiphersAsyncCommandTests.cs +++ b/test/Core.Test/Tools/ImportFeatures/ImportCiphersAsyncCommandTests.cs @@ -1,14 +1,11 @@ using Bit.Core.AdminConsole.Entities; -using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements; -using Bit.Core.AdminConsole.Services; using Bit.Core.Entities; using Bit.Core.Exceptions; using Bit.Core.Platform.Push; using Bit.Core.Repositories; -using Bit.Core.Services; using Bit.Core.Test.AutoFixture.CipherFixtures; using Bit.Core.Tools.ImportFeatures; using Bit.Core.Vault.Entities; @@ -26,43 +23,11 @@ namespace Bit.Core.Test.Tools.ImportFeatures; public class ImportCiphersAsyncCommandTests { [Theory, BitAutoData] - public async Task ImportIntoIndividualVaultAsync_Success( + public async Task ImportIntoIndividualVaultAsync_WithOrganizationDataOwnershipPolicyDisabled_Success( Guid importingUserId, List ciphers, SutProvider sutProvider) { - sutProvider.GetDependency() - .AnyPoliciesApplicableToUserAsync(importingUserId, PolicyType.OrganizationDataOwnership) - .Returns(false); - - sutProvider.GetDependency() - .GetManyByUserIdAsync(importingUserId) - .Returns(new List()); - - var folders = new List { new Folder { UserId = importingUserId } }; - - var folderRelationships = new List>(); - - // Act - await sutProvider.Sut.ImportIntoIndividualVaultAsync(folders, ciphers, folderRelationships, importingUserId); - - // Assert - await sutProvider.GetDependency() - .Received(1) - .CreateAsync(importingUserId, ciphers, Arg.Any>()); - await sutProvider.GetDependency().Received(1).PushSyncVaultAsync(importingUserId); - } - - [Theory, BitAutoData] - public async Task ImportIntoIndividualVaultAsync_WithPolicyRequirementsEnabled_WithOrganizationDataOwnershipPolicyDisabled_Success( - Guid importingUserId, - List ciphers, - SutProvider sutProvider) - { - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.PolicyRequirements) - .Returns(true); - sutProvider.GetDependency() .GetAsync(importingUserId) .Returns(new OrganizationDataOwnershipPolicyRequirement( @@ -86,29 +51,7 @@ await sutProvider.GetDependency() } [Theory, BitAutoData] - public async Task ImportIntoIndividualVaultAsync_ThrowsBadRequestException( - List folders, - List ciphers, - SutProvider sutProvider) - { - var userId = Guid.NewGuid(); - folders.ForEach(f => f.UserId = userId); - ciphers.ForEach(c => c.UserId = userId); - - sutProvider.GetDependency() - .AnyPoliciesApplicableToUserAsync(userId, PolicyType.OrganizationDataOwnership) - .Returns(true); - - var folderRelationships = new List>(); - - var exception = await Assert.ThrowsAsync(() => - sutProvider.Sut.ImportIntoIndividualVaultAsync(folders, ciphers, folderRelationships, userId)); - - Assert.Equal("You cannot import items into your personal vault because you are a member of an organization which forbids it.", exception.Message); - } - - [Theory, BitAutoData] - public async Task ImportIntoIndividualVaultAsync_WithPolicyRequirementsEnabled_WithOrganizationDataOwnershipPolicyEnabled_ThrowsBadRequestException( + public async Task ImportIntoIndividualVaultAsync_WithOrganizationDataOwnershipPolicyEnabled_ThrowsBadRequestException( List folders, List ciphers, SutProvider sutProvider) @@ -117,10 +60,6 @@ public async Task ImportIntoIndividualVaultAsync_WithPolicyRequirementsEnabled_W folders.ForEach(f => f.UserId = userId); ciphers.ForEach(c => c.UserId = userId); - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.PolicyRequirements) - .Returns(true); - sutProvider.GetDependency() .GetAsync(userId) .Returns(new OrganizationDataOwnershipPolicyRequirement( @@ -142,10 +81,6 @@ public async Task ImportIntoIndividualVaultAsync_FavoriteCiphers_PersistsFavorit SutProvider sutProvider ) { - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.PolicyRequirements) - .Returns(true); - sutProvider.GetDependency() .GetAsync(importingUserId) .Returns(new OrganizationDataOwnershipPolicyRequirement( @@ -337,9 +272,11 @@ public async Task ImportIntoIndividualVaultAsync_WithArchivedCiphers_PreservesAr ciphers[0].UserId = importingUserId; ciphers[0].ArchivedDate = archivedDate; - sutProvider.GetDependency() - .AnyPoliciesApplicableToUserAsync(importingUserId, PolicyType.OrganizationDataOwnership) - .Returns(false); + sutProvider.GetDependency() + .GetAsync(importingUserId) + .Returns(new OrganizationDataOwnershipPolicyRequirement( + OrganizationDataOwnershipState.Disabled, + [])); sutProvider.GetDependency() .GetManyByUserIdAsync(importingUserId) diff --git a/test/Core.Test/Vault/Services/CipherServiceTests.cs b/test/Core.Test/Vault/Services/CipherServiceTests.cs index 56430d7d7320..4abc8c803a95 100644 --- a/test/Core.Test/Vault/Services/CipherServiceTests.cs +++ b/test/Core.Test/Vault/Services/CipherServiceTests.cs @@ -4,7 +4,6 @@ using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements; -using Bit.Core.AdminConsole.Services; using Bit.Core.Billing.Enums; using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing.Premium; @@ -238,52 +237,6 @@ public async Task SaveDetailsAsync_PersonalVault_WithOrganizationDataOwnershipPo cipher.UserId = savingUserId; cipher.OrganizationId = null; - sutProvider.GetDependency() - .AnyPoliciesApplicableToUserAsync(savingUserId, PolicyType.OrganizationDataOwnership) - .Returns(true); - - var exception = await Assert.ThrowsAsync( - () => sutProvider.Sut.SaveDetailsAsync(cipher, savingUserId, null)); - Assert.Contains("restricted from saving items to your personal vault", exception.Message); - } - - [Theory] - [BitAutoData] - public async Task SaveDetailsAsync_PersonalVault_WithOrganizationDataOwnershipPolicyDisabled_Succeeds( - SutProvider sutProvider, - CipherDetails cipher, - Guid savingUserId) - { - cipher.Id = default; - cipher.UserId = savingUserId; - cipher.OrganizationId = null; - - sutProvider.GetDependency() - .AnyPoliciesApplicableToUserAsync(savingUserId, PolicyType.OrganizationDataOwnership) - .Returns(false); - - await sutProvider.Sut.SaveDetailsAsync(cipher, savingUserId, null); - - await sutProvider.GetDependency() - .Received(1) - .CreateAsync(cipher); - } - - [Theory] - [BitAutoData] - public async Task SaveDetailsAsync_PersonalVault_WithPolicyRequirementsEnabled_WithOrganizationDataOwnershipPolicyEnabled_Throws( - SutProvider sutProvider, - CipherDetails cipher, - Guid savingUserId) - { - cipher.Id = default; - cipher.UserId = savingUserId; - cipher.OrganizationId = null; - - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.PolicyRequirements) - .Returns(true); - sutProvider.GetDependency() .GetAsync(savingUserId) .Returns(new OrganizationDataOwnershipPolicyRequirement( @@ -297,7 +250,7 @@ public async Task SaveDetailsAsync_PersonalVault_WithPolicyRequirementsEnabled_W [Theory] [BitAutoData] - public async Task SaveDetailsAsync_PersonalVault_WithPolicyRequirementsEnabled_WithOrganizationDataOwnershipPolicyDisabled_Succeeds( + public async Task SaveDetailsAsync_PersonalVault_WithOrganizationDataOwnershipPolicyDisabled_Succeeds( SutProvider sutProvider, CipherDetails cipher, Guid savingUserId) @@ -306,10 +259,6 @@ public async Task SaveDetailsAsync_PersonalVault_WithPolicyRequirementsEnabled_W cipher.UserId = savingUserId; cipher.OrganizationId = null; - sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.PolicyRequirements) - .Returns(true); - sutProvider.GetDependency() .GetAsync(savingUserId) .Returns(new OrganizationDataOwnershipPolicyRequirement(