Files
server/test/Api.Test/Vault/Controllers/CiphersControllerTests.cs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1279 lines
62 KiB
C#
Raw Normal View History

using System.Security.Claims;
[PM-18088] Add unit test coverage for admin methods on CiphersController and CipherService (#5460) * Add comprehensive test coverage for CipherService restore, delete, and soft delete methods * Add comprehensive admin cipher management tests for CiphersController * Enhance CiphersController admin methods with comprehensive test coverage - Add tests for provider user scenarios in admin cipher management methods - Implement tests for custom user with edit any collection permissions - Add test coverage for RestrictProviderAccess feature flag - Improve test scenarios for delete, soft delete, and restore operations * Refactor CiphersControllerTests to simplify and optimize test methods * Optimize CiphersControllerTests with code cleanup and test method improvements * Extend CiphersControllerTests to support Admin and Owner roles * Add test cases for custom user cipher admin operations with EditAnyCollection permission checks - Extend CiphersControllerTests with scenarios for custom users without EditAnyCollection permission - Add test methods to verify NotFoundException is thrown when EditAnyCollection is false - Cover delete, soft delete, and restore operations for single and bulk cipher admin actions * Enhance CiphersControllerTests with granular access permission scenarios - Add test methods for admin and owner roles with specific cipher access scenarios - Implement tests for accessing specific and unassigned ciphers - Extend test coverage for delete, soft delete, and restore operations - Improve test method naming for clarity and precision * Add bulk admin cipher delete and soft delete tests for specific and unassigned ciphers - Implement test methods for DeleteManyAdmin and PutDeleteManyAdmin - Cover scenarios for owner and admin roles with access to specific and unassigned ciphers - Verify correct invocation of DeleteManyAsync and SoftDeleteManyAsync methods - Enhance test coverage for bulk cipher admin operations
2025-03-11 10:10:20 +00:00
using System.Text.Json;
using Bit.Api.Vault.Controllers;
using Bit.Api.Vault.Models.Request;
[PM-18088] Add unit test coverage for admin methods on CiphersController and CipherService (#5460) * Add comprehensive test coverage for CipherService restore, delete, and soft delete methods * Add comprehensive admin cipher management tests for CiphersController * Enhance CiphersController admin methods with comprehensive test coverage - Add tests for provider user scenarios in admin cipher management methods - Implement tests for custom user with edit any collection permissions - Add test coverage for RestrictProviderAccess feature flag - Improve test scenarios for delete, soft delete, and restore operations * Refactor CiphersControllerTests to simplify and optimize test methods * Optimize CiphersControllerTests with code cleanup and test method improvements * Extend CiphersControllerTests to support Admin and Owner roles * Add test cases for custom user cipher admin operations with EditAnyCollection permission checks - Extend CiphersControllerTests with scenarios for custom users without EditAnyCollection permission - Add test methods to verify NotFoundException is thrown when EditAnyCollection is false - Cover delete, soft delete, and restore operations for single and bulk cipher admin actions * Enhance CiphersControllerTests with granular access permission scenarios - Add test methods for admin and owner roles with specific cipher access scenarios - Implement tests for accessing specific and unassigned ciphers - Extend test coverage for delete, soft delete, and restore operations - Improve test method naming for clarity and precision * Add bulk admin cipher delete and soft delete tests for specific and unassigned ciphers - Implement test methods for DeleteManyAdmin and PutDeleteManyAdmin - Cover scenarios for owner and admin roles with access to specific and unassigned ciphers - Verify correct invocation of DeleteManyAsync and SoftDeleteManyAsync methods - Enhance test coverage for bulk cipher admin operations
2025-03-11 10:10:20 +00:00
using Bit.Api.Vault.Models.Response;
using Bit.Core;
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.Repositories;
using Bit.Core.Services;
using Bit.Core.Vault.Entities;
using Bit.Core.Vault.Models.Data;
using Bit.Core.Vault.Repositories;
using Bit.Core.Vault.Services;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using NSubstitute.ReturnsExtensions;
using Xunit;
using CipherType = Bit.Core.Vault.Enums.CipherType;
namespace Bit.Api.Test.Controllers;
[ControllerCustomize(typeof(CiphersController))]
[SutProviderCustomize]
public class CiphersControllerTests
{
[Theory, BitAutoData]
[PM-18087] Add cipher permissions to response models (#5418) * Add Manage permission to UserCipherDetails and CipherDetails_ReadByIdUserId * Add Manage property to CipherDetails and UserCipherDetailsQuery * Add integration test for CipherRepository Manage permission rules * Update CipherDetails_ReadWithoutOrganizationsByUserId to include Manage permission * Refactor UserCipherDetailsQuery to include detailed permission and organization properties * Refactor CipherRepositoryTests to improve test organization and readability - Split large test method into smaller, focused methods - Added helper methods for creating test data and performing assertions - Improved test coverage for cipher permissions in different scenarios - Maintained existing test logic while enhancing code structure * Refactor CipherRepositoryTests to consolidate cipher permission tests - Removed redundant helper methods for permission assertions - Simplified test methods for GetCipherPermissionsForOrganizationAsync, GetManyByUserIdAsync, and GetByIdAsync - Maintained existing test coverage for cipher manage permissions - Improved code readability and reduced code duplication * Add integration test for CipherRepository group collection manage permissions - Added new test method GetCipherPermissionsForOrganizationAsync_ManageProperty_RespectsCollectionGroupRules - Implemented helper method CreateCipherInOrganizationCollectionWithGroup to support group-based collection permission testing - Verified manage permissions are correctly applied based on group collection access settings * Add @Manage parameter to Cipher stored procedures - Updated CipherDetails_Create, CipherDetails_CreateWithCollections, and CipherDetails_Update stored procedures - Added @Manage parameter with comment "-- not used" - Included new stored procedure implementations in migration script - Consistent with previous work on adding Manage property to cipher details * Update UserCipherDetails functions to reorder Manage and ViewPassword columns * [PM-18086] Add CanRestore and CanDelete authorization methods. * [PM-18086] Address code review feedback. * [PM-18086] Add missing part. * [PM-18087] Add CipherPermissionsResponseModel for cipher permissions * Add GetManyOrganizationAbilityAsync method to application cache service * Add organization ability context to cipher response models This change introduces organization ability context to various cipher response models across multiple controllers. The modifications include: - Updating CipherResponseModel to include permissions based on user and organization ability - Modifying CiphersController methods to fetch and pass organization abilities - Updating SyncController to include organization abilities in sync response - Adding organization ability context to EmergencyAccessController response generation * Remove organization ability context from EmergencyAccessController This change simplifies the EmergencyAccessController by removing unnecessary organization ability fetching and passing. Since emergency access only retrieves personal ciphers, the organization ability context is no longer needed in the response generation. * Remove unused IApplicationCacheService from EmergencyAccessController * Refactor EmergencyAccessViewResponseModel constructor Remove unnecessary JsonConstructor attribute and simplify constructor initialization for EmergencyAccessViewResponseModel * Refactor organization ability retrieval in CiphersController Extract methods to simplify organization ability fetching for ciphers, reducing code duplication and improving readability. Added two private helper methods: - GetOrganizationAbilityAsync: Retrieves organization ability for a single cipher - GetManyOrganizationAbilitiesAsync: Retrieves organization abilities for multiple ciphers * Update CiphersControllerTests to use GetUserByPrincipalAsync Modify test methods to: - Replace GetProperUserId with GetUserByPrincipalAsync - Use User object instead of separate userId - Update mocking to return User object - Ensure user ID is correctly set in test scenarios * Refactor CipherPermissionsResponseModel to use constructor-based initialization * Refactor CipherPermissionsResponseModel to use record type and init-only properties * [PM-18086] Undo files * [PM-18086] Undo files * Refactor organization abilities retrieval in cipher-related controllers and models - Update CiphersController to use GetOrganizationAbilitiesAsync instead of individual methods - Modify CipherResponseModel and CipherDetailsResponseModel to accept organization abilities dictionary - Update CipherPermissionsResponseModel to handle organization abilities lookup - Remove deprecated organization ability retrieval methods - Simplify sync and emergency access response model handling of organization abilities * Remove GetManyOrganizationAbilityAsync method - Delete unused method from IApplicationCacheService interface - Remove corresponding implementation in InMemoryApplicationCacheService - Continues cleanup of organization ability retrieval methods * Update CiphersControllerTests to include organization abilities retrieval - Add organization abilities retrieval in test setup for PutCollections_vNext method - Ensure consistent mocking of IApplicationCacheService in test scenarios * Update error message for missing organization ability --------- Co-authored-by: Jimmy Vo <huynhmaivo82@gmail.com>
2025-03-10 15:27:30 +00:00
public async Task PutPartialShouldReturnCipherWithGivenFolderAndFavoriteValues(User user, Guid folderId, SutProvider<CiphersController> sutProvider)
{
var isFavorite = true;
var cipherId = Guid.NewGuid();
sutProvider.GetDependency<IUserService>()
[PM-18087] Add cipher permissions to response models (#5418) * Add Manage permission to UserCipherDetails and CipherDetails_ReadByIdUserId * Add Manage property to CipherDetails and UserCipherDetailsQuery * Add integration test for CipherRepository Manage permission rules * Update CipherDetails_ReadWithoutOrganizationsByUserId to include Manage permission * Refactor UserCipherDetailsQuery to include detailed permission and organization properties * Refactor CipherRepositoryTests to improve test organization and readability - Split large test method into smaller, focused methods - Added helper methods for creating test data and performing assertions - Improved test coverage for cipher permissions in different scenarios - Maintained existing test logic while enhancing code structure * Refactor CipherRepositoryTests to consolidate cipher permission tests - Removed redundant helper methods for permission assertions - Simplified test methods for GetCipherPermissionsForOrganizationAsync, GetManyByUserIdAsync, and GetByIdAsync - Maintained existing test coverage for cipher manage permissions - Improved code readability and reduced code duplication * Add integration test for CipherRepository group collection manage permissions - Added new test method GetCipherPermissionsForOrganizationAsync_ManageProperty_RespectsCollectionGroupRules - Implemented helper method CreateCipherInOrganizationCollectionWithGroup to support group-based collection permission testing - Verified manage permissions are correctly applied based on group collection access settings * Add @Manage parameter to Cipher stored procedures - Updated CipherDetails_Create, CipherDetails_CreateWithCollections, and CipherDetails_Update stored procedures - Added @Manage parameter with comment "-- not used" - Included new stored procedure implementations in migration script - Consistent with previous work on adding Manage property to cipher details * Update UserCipherDetails functions to reorder Manage and ViewPassword columns * [PM-18086] Add CanRestore and CanDelete authorization methods. * [PM-18086] Address code review feedback. * [PM-18086] Add missing part. * [PM-18087] Add CipherPermissionsResponseModel for cipher permissions * Add GetManyOrganizationAbilityAsync method to application cache service * Add organization ability context to cipher response models This change introduces organization ability context to various cipher response models across multiple controllers. The modifications include: - Updating CipherResponseModel to include permissions based on user and organization ability - Modifying CiphersController methods to fetch and pass organization abilities - Updating SyncController to include organization abilities in sync response - Adding organization ability context to EmergencyAccessController response generation * Remove organization ability context from EmergencyAccessController This change simplifies the EmergencyAccessController by removing unnecessary organization ability fetching and passing. Since emergency access only retrieves personal ciphers, the organization ability context is no longer needed in the response generation. * Remove unused IApplicationCacheService from EmergencyAccessController * Refactor EmergencyAccessViewResponseModel constructor Remove unnecessary JsonConstructor attribute and simplify constructor initialization for EmergencyAccessViewResponseModel * Refactor organization ability retrieval in CiphersController Extract methods to simplify organization ability fetching for ciphers, reducing code duplication and improving readability. Added two private helper methods: - GetOrganizationAbilityAsync: Retrieves organization ability for a single cipher - GetManyOrganizationAbilitiesAsync: Retrieves organization abilities for multiple ciphers * Update CiphersControllerTests to use GetUserByPrincipalAsync Modify test methods to: - Replace GetProperUserId with GetUserByPrincipalAsync - Use User object instead of separate userId - Update mocking to return User object - Ensure user ID is correctly set in test scenarios * Refactor CipherPermissionsResponseModel to use constructor-based initialization * Refactor CipherPermissionsResponseModel to use record type and init-only properties * [PM-18086] Undo files * [PM-18086] Undo files * Refactor organization abilities retrieval in cipher-related controllers and models - Update CiphersController to use GetOrganizationAbilitiesAsync instead of individual methods - Modify CipherResponseModel and CipherDetailsResponseModel to accept organization abilities dictionary - Update CipherPermissionsResponseModel to handle organization abilities lookup - Remove deprecated organization ability retrieval methods - Simplify sync and emergency access response model handling of organization abilities * Remove GetManyOrganizationAbilityAsync method - Delete unused method from IApplicationCacheService interface - Remove corresponding implementation in InMemoryApplicationCacheService - Continues cleanup of organization ability retrieval methods * Update CiphersControllerTests to include organization abilities retrieval - Add organization abilities retrieval in test setup for PutCollections_vNext method - Ensure consistent mocking of IApplicationCacheService in test scenarios * Update error message for missing organization ability --------- Co-authored-by: Jimmy Vo <huynhmaivo82@gmail.com>
2025-03-10 15:27:30 +00:00
.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>())
.Returns(user);
var cipherDetails = new CipherDetails
{
[PM-18087] Add cipher permissions to response models (#5418) * Add Manage permission to UserCipherDetails and CipherDetails_ReadByIdUserId * Add Manage property to CipherDetails and UserCipherDetailsQuery * Add integration test for CipherRepository Manage permission rules * Update CipherDetails_ReadWithoutOrganizationsByUserId to include Manage permission * Refactor UserCipherDetailsQuery to include detailed permission and organization properties * Refactor CipherRepositoryTests to improve test organization and readability - Split large test method into smaller, focused methods - Added helper methods for creating test data and performing assertions - Improved test coverage for cipher permissions in different scenarios - Maintained existing test logic while enhancing code structure * Refactor CipherRepositoryTests to consolidate cipher permission tests - Removed redundant helper methods for permission assertions - Simplified test methods for GetCipherPermissionsForOrganizationAsync, GetManyByUserIdAsync, and GetByIdAsync - Maintained existing test coverage for cipher manage permissions - Improved code readability and reduced code duplication * Add integration test for CipherRepository group collection manage permissions - Added new test method GetCipherPermissionsForOrganizationAsync_ManageProperty_RespectsCollectionGroupRules - Implemented helper method CreateCipherInOrganizationCollectionWithGroup to support group-based collection permission testing - Verified manage permissions are correctly applied based on group collection access settings * Add @Manage parameter to Cipher stored procedures - Updated CipherDetails_Create, CipherDetails_CreateWithCollections, and CipherDetails_Update stored procedures - Added @Manage parameter with comment "-- not used" - Included new stored procedure implementations in migration script - Consistent with previous work on adding Manage property to cipher details * Update UserCipherDetails functions to reorder Manage and ViewPassword columns * [PM-18086] Add CanRestore and CanDelete authorization methods. * [PM-18086] Address code review feedback. * [PM-18086] Add missing part. * [PM-18087] Add CipherPermissionsResponseModel for cipher permissions * Add GetManyOrganizationAbilityAsync method to application cache service * Add organization ability context to cipher response models This change introduces organization ability context to various cipher response models across multiple controllers. The modifications include: - Updating CipherResponseModel to include permissions based on user and organization ability - Modifying CiphersController methods to fetch and pass organization abilities - Updating SyncController to include organization abilities in sync response - Adding organization ability context to EmergencyAccessController response generation * Remove organization ability context from EmergencyAccessController This change simplifies the EmergencyAccessController by removing unnecessary organization ability fetching and passing. Since emergency access only retrieves personal ciphers, the organization ability context is no longer needed in the response generation. * Remove unused IApplicationCacheService from EmergencyAccessController * Refactor EmergencyAccessViewResponseModel constructor Remove unnecessary JsonConstructor attribute and simplify constructor initialization for EmergencyAccessViewResponseModel * Refactor organization ability retrieval in CiphersController Extract methods to simplify organization ability fetching for ciphers, reducing code duplication and improving readability. Added two private helper methods: - GetOrganizationAbilityAsync: Retrieves organization ability for a single cipher - GetManyOrganizationAbilitiesAsync: Retrieves organization abilities for multiple ciphers * Update CiphersControllerTests to use GetUserByPrincipalAsync Modify test methods to: - Replace GetProperUserId with GetUserByPrincipalAsync - Use User object instead of separate userId - Update mocking to return User object - Ensure user ID is correctly set in test scenarios * Refactor CipherPermissionsResponseModel to use constructor-based initialization * Refactor CipherPermissionsResponseModel to use record type and init-only properties * [PM-18086] Undo files * [PM-18086] Undo files * Refactor organization abilities retrieval in cipher-related controllers and models - Update CiphersController to use GetOrganizationAbilitiesAsync instead of individual methods - Modify CipherResponseModel and CipherDetailsResponseModel to accept organization abilities dictionary - Update CipherPermissionsResponseModel to handle organization abilities lookup - Remove deprecated organization ability retrieval methods - Simplify sync and emergency access response model handling of organization abilities * Remove GetManyOrganizationAbilityAsync method - Delete unused method from IApplicationCacheService interface - Remove corresponding implementation in InMemoryApplicationCacheService - Continues cleanup of organization ability retrieval methods * Update CiphersControllerTests to include organization abilities retrieval - Add organization abilities retrieval in test setup for PutCollections_vNext method - Ensure consistent mocking of IApplicationCacheService in test scenarios * Update error message for missing organization ability --------- Co-authored-by: Jimmy Vo <huynhmaivo82@gmail.com>
2025-03-10 15:27:30 +00:00
UserId = user.Id,
Favorite = isFavorite,
FolderId = folderId,
Type = Core.Vault.Enums.CipherType.SecureNote,
Data = "{}"
};
sutProvider.GetDependency<ICipherRepository>()
[PM-18087] Add cipher permissions to response models (#5418) * Add Manage permission to UserCipherDetails and CipherDetails_ReadByIdUserId * Add Manage property to CipherDetails and UserCipherDetailsQuery * Add integration test for CipherRepository Manage permission rules * Update CipherDetails_ReadWithoutOrganizationsByUserId to include Manage permission * Refactor UserCipherDetailsQuery to include detailed permission and organization properties * Refactor CipherRepositoryTests to improve test organization and readability - Split large test method into smaller, focused methods - Added helper methods for creating test data and performing assertions - Improved test coverage for cipher permissions in different scenarios - Maintained existing test logic while enhancing code structure * Refactor CipherRepositoryTests to consolidate cipher permission tests - Removed redundant helper methods for permission assertions - Simplified test methods for GetCipherPermissionsForOrganizationAsync, GetManyByUserIdAsync, and GetByIdAsync - Maintained existing test coverage for cipher manage permissions - Improved code readability and reduced code duplication * Add integration test for CipherRepository group collection manage permissions - Added new test method GetCipherPermissionsForOrganizationAsync_ManageProperty_RespectsCollectionGroupRules - Implemented helper method CreateCipherInOrganizationCollectionWithGroup to support group-based collection permission testing - Verified manage permissions are correctly applied based on group collection access settings * Add @Manage parameter to Cipher stored procedures - Updated CipherDetails_Create, CipherDetails_CreateWithCollections, and CipherDetails_Update stored procedures - Added @Manage parameter with comment "-- not used" - Included new stored procedure implementations in migration script - Consistent with previous work on adding Manage property to cipher details * Update UserCipherDetails functions to reorder Manage and ViewPassword columns * [PM-18086] Add CanRestore and CanDelete authorization methods. * [PM-18086] Address code review feedback. * [PM-18086] Add missing part. * [PM-18087] Add CipherPermissionsResponseModel for cipher permissions * Add GetManyOrganizationAbilityAsync method to application cache service * Add organization ability context to cipher response models This change introduces organization ability context to various cipher response models across multiple controllers. The modifications include: - Updating CipherResponseModel to include permissions based on user and organization ability - Modifying CiphersController methods to fetch and pass organization abilities - Updating SyncController to include organization abilities in sync response - Adding organization ability context to EmergencyAccessController response generation * Remove organization ability context from EmergencyAccessController This change simplifies the EmergencyAccessController by removing unnecessary organization ability fetching and passing. Since emergency access only retrieves personal ciphers, the organization ability context is no longer needed in the response generation. * Remove unused IApplicationCacheService from EmergencyAccessController * Refactor EmergencyAccessViewResponseModel constructor Remove unnecessary JsonConstructor attribute and simplify constructor initialization for EmergencyAccessViewResponseModel * Refactor organization ability retrieval in CiphersController Extract methods to simplify organization ability fetching for ciphers, reducing code duplication and improving readability. Added two private helper methods: - GetOrganizationAbilityAsync: Retrieves organization ability for a single cipher - GetManyOrganizationAbilitiesAsync: Retrieves organization abilities for multiple ciphers * Update CiphersControllerTests to use GetUserByPrincipalAsync Modify test methods to: - Replace GetProperUserId with GetUserByPrincipalAsync - Use User object instead of separate userId - Update mocking to return User object - Ensure user ID is correctly set in test scenarios * Refactor CipherPermissionsResponseModel to use constructor-based initialization * Refactor CipherPermissionsResponseModel to use record type and init-only properties * [PM-18086] Undo files * [PM-18086] Undo files * Refactor organization abilities retrieval in cipher-related controllers and models - Update CiphersController to use GetOrganizationAbilitiesAsync instead of individual methods - Modify CipherResponseModel and CipherDetailsResponseModel to accept organization abilities dictionary - Update CipherPermissionsResponseModel to handle organization abilities lookup - Remove deprecated organization ability retrieval methods - Simplify sync and emergency access response model handling of organization abilities * Remove GetManyOrganizationAbilityAsync method - Delete unused method from IApplicationCacheService interface - Remove corresponding implementation in InMemoryApplicationCacheService - Continues cleanup of organization ability retrieval methods * Update CiphersControllerTests to include organization abilities retrieval - Add organization abilities retrieval in test setup for PutCollections_vNext method - Ensure consistent mocking of IApplicationCacheService in test scenarios * Update error message for missing organization ability --------- Co-authored-by: Jimmy Vo <huynhmaivo82@gmail.com>
2025-03-10 15:27:30 +00:00
.GetByIdAsync(cipherId, user.Id)
.Returns(Task.FromResult(cipherDetails));
var result = await sutProvider.Sut.PutPartial(cipherId, new CipherPartialRequestModel { Favorite = isFavorite, FolderId = folderId.ToString() });
Assert.Equal(folderId, result.FolderId);
Assert.Equal(isFavorite, result.Favorite);
}
[Theory, BitAutoData]
[PM-18087] Add cipher permissions to response models (#5418) * Add Manage permission to UserCipherDetails and CipherDetails_ReadByIdUserId * Add Manage property to CipherDetails and UserCipherDetailsQuery * Add integration test for CipherRepository Manage permission rules * Update CipherDetails_ReadWithoutOrganizationsByUserId to include Manage permission * Refactor UserCipherDetailsQuery to include detailed permission and organization properties * Refactor CipherRepositoryTests to improve test organization and readability - Split large test method into smaller, focused methods - Added helper methods for creating test data and performing assertions - Improved test coverage for cipher permissions in different scenarios - Maintained existing test logic while enhancing code structure * Refactor CipherRepositoryTests to consolidate cipher permission tests - Removed redundant helper methods for permission assertions - Simplified test methods for GetCipherPermissionsForOrganizationAsync, GetManyByUserIdAsync, and GetByIdAsync - Maintained existing test coverage for cipher manage permissions - Improved code readability and reduced code duplication * Add integration test for CipherRepository group collection manage permissions - Added new test method GetCipherPermissionsForOrganizationAsync_ManageProperty_RespectsCollectionGroupRules - Implemented helper method CreateCipherInOrganizationCollectionWithGroup to support group-based collection permission testing - Verified manage permissions are correctly applied based on group collection access settings * Add @Manage parameter to Cipher stored procedures - Updated CipherDetails_Create, CipherDetails_CreateWithCollections, and CipherDetails_Update stored procedures - Added @Manage parameter with comment "-- not used" - Included new stored procedure implementations in migration script - Consistent with previous work on adding Manage property to cipher details * Update UserCipherDetails functions to reorder Manage and ViewPassword columns * [PM-18086] Add CanRestore and CanDelete authorization methods. * [PM-18086] Address code review feedback. * [PM-18086] Add missing part. * [PM-18087] Add CipherPermissionsResponseModel for cipher permissions * Add GetManyOrganizationAbilityAsync method to application cache service * Add organization ability context to cipher response models This change introduces organization ability context to various cipher response models across multiple controllers. The modifications include: - Updating CipherResponseModel to include permissions based on user and organization ability - Modifying CiphersController methods to fetch and pass organization abilities - Updating SyncController to include organization abilities in sync response - Adding organization ability context to EmergencyAccessController response generation * Remove organization ability context from EmergencyAccessController This change simplifies the EmergencyAccessController by removing unnecessary organization ability fetching and passing. Since emergency access only retrieves personal ciphers, the organization ability context is no longer needed in the response generation. * Remove unused IApplicationCacheService from EmergencyAccessController * Refactor EmergencyAccessViewResponseModel constructor Remove unnecessary JsonConstructor attribute and simplify constructor initialization for EmergencyAccessViewResponseModel * Refactor organization ability retrieval in CiphersController Extract methods to simplify organization ability fetching for ciphers, reducing code duplication and improving readability. Added two private helper methods: - GetOrganizationAbilityAsync: Retrieves organization ability for a single cipher - GetManyOrganizationAbilitiesAsync: Retrieves organization abilities for multiple ciphers * Update CiphersControllerTests to use GetUserByPrincipalAsync Modify test methods to: - Replace GetProperUserId with GetUserByPrincipalAsync - Use User object instead of separate userId - Update mocking to return User object - Ensure user ID is correctly set in test scenarios * Refactor CipherPermissionsResponseModel to use constructor-based initialization * Refactor CipherPermissionsResponseModel to use record type and init-only properties * [PM-18086] Undo files * [PM-18086] Undo files * Refactor organization abilities retrieval in cipher-related controllers and models - Update CiphersController to use GetOrganizationAbilitiesAsync instead of individual methods - Modify CipherResponseModel and CipherDetailsResponseModel to accept organization abilities dictionary - Update CipherPermissionsResponseModel to handle organization abilities lookup - Remove deprecated organization ability retrieval methods - Simplify sync and emergency access response model handling of organization abilities * Remove GetManyOrganizationAbilityAsync method - Delete unused method from IApplicationCacheService interface - Remove corresponding implementation in InMemoryApplicationCacheService - Continues cleanup of organization ability retrieval methods * Update CiphersControllerTests to include organization abilities retrieval - Add organization abilities retrieval in test setup for PutCollections_vNext method - Ensure consistent mocking of IApplicationCacheService in test scenarios * Update error message for missing organization ability --------- Co-authored-by: Jimmy Vo <huynhmaivo82@gmail.com>
2025-03-10 15:27:30 +00:00
public async Task PutCollections_vNextShouldThrowExceptionWhenCipherIsNullOrNoOrgValue(Guid id, CipherCollectionsRequestModel model, User user,
SutProvider<CiphersController> sutProvider)
{
[PM-18087] Add cipher permissions to response models (#5418) * Add Manage permission to UserCipherDetails and CipherDetails_ReadByIdUserId * Add Manage property to CipherDetails and UserCipherDetailsQuery * Add integration test for CipherRepository Manage permission rules * Update CipherDetails_ReadWithoutOrganizationsByUserId to include Manage permission * Refactor UserCipherDetailsQuery to include detailed permission and organization properties * Refactor CipherRepositoryTests to improve test organization and readability - Split large test method into smaller, focused methods - Added helper methods for creating test data and performing assertions - Improved test coverage for cipher permissions in different scenarios - Maintained existing test logic while enhancing code structure * Refactor CipherRepositoryTests to consolidate cipher permission tests - Removed redundant helper methods for permission assertions - Simplified test methods for GetCipherPermissionsForOrganizationAsync, GetManyByUserIdAsync, and GetByIdAsync - Maintained existing test coverage for cipher manage permissions - Improved code readability and reduced code duplication * Add integration test for CipherRepository group collection manage permissions - Added new test method GetCipherPermissionsForOrganizationAsync_ManageProperty_RespectsCollectionGroupRules - Implemented helper method CreateCipherInOrganizationCollectionWithGroup to support group-based collection permission testing - Verified manage permissions are correctly applied based on group collection access settings * Add @Manage parameter to Cipher stored procedures - Updated CipherDetails_Create, CipherDetails_CreateWithCollections, and CipherDetails_Update stored procedures - Added @Manage parameter with comment "-- not used" - Included new stored procedure implementations in migration script - Consistent with previous work on adding Manage property to cipher details * Update UserCipherDetails functions to reorder Manage and ViewPassword columns * [PM-18086] Add CanRestore and CanDelete authorization methods. * [PM-18086] Address code review feedback. * [PM-18086] Add missing part. * [PM-18087] Add CipherPermissionsResponseModel for cipher permissions * Add GetManyOrganizationAbilityAsync method to application cache service * Add organization ability context to cipher response models This change introduces organization ability context to various cipher response models across multiple controllers. The modifications include: - Updating CipherResponseModel to include permissions based on user and organization ability - Modifying CiphersController methods to fetch and pass organization abilities - Updating SyncController to include organization abilities in sync response - Adding organization ability context to EmergencyAccessController response generation * Remove organization ability context from EmergencyAccessController This change simplifies the EmergencyAccessController by removing unnecessary organization ability fetching and passing. Since emergency access only retrieves personal ciphers, the organization ability context is no longer needed in the response generation. * Remove unused IApplicationCacheService from EmergencyAccessController * Refactor EmergencyAccessViewResponseModel constructor Remove unnecessary JsonConstructor attribute and simplify constructor initialization for EmergencyAccessViewResponseModel * Refactor organization ability retrieval in CiphersController Extract methods to simplify organization ability fetching for ciphers, reducing code duplication and improving readability. Added two private helper methods: - GetOrganizationAbilityAsync: Retrieves organization ability for a single cipher - GetManyOrganizationAbilitiesAsync: Retrieves organization abilities for multiple ciphers * Update CiphersControllerTests to use GetUserByPrincipalAsync Modify test methods to: - Replace GetProperUserId with GetUserByPrincipalAsync - Use User object instead of separate userId - Update mocking to return User object - Ensure user ID is correctly set in test scenarios * Refactor CipherPermissionsResponseModel to use constructor-based initialization * Refactor CipherPermissionsResponseModel to use record type and init-only properties * [PM-18086] Undo files * [PM-18086] Undo files * Refactor organization abilities retrieval in cipher-related controllers and models - Update CiphersController to use GetOrganizationAbilitiesAsync instead of individual methods - Modify CipherResponseModel and CipherDetailsResponseModel to accept organization abilities dictionary - Update CipherPermissionsResponseModel to handle organization abilities lookup - Remove deprecated organization ability retrieval methods - Simplify sync and emergency access response model handling of organization abilities * Remove GetManyOrganizationAbilityAsync method - Delete unused method from IApplicationCacheService interface - Remove corresponding implementation in InMemoryApplicationCacheService - Continues cleanup of organization ability retrieval methods * Update CiphersControllerTests to include organization abilities retrieval - Add organization abilities retrieval in test setup for PutCollections_vNext method - Ensure consistent mocking of IApplicationCacheService in test scenarios * Update error message for missing organization ability --------- Co-authored-by: Jimmy Vo <huynhmaivo82@gmail.com>
2025-03-10 15:27:30 +00:00
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user);
sutProvider.GetDependency<ICurrentContext>().OrganizationUser(Guid.NewGuid()).Returns(false);
[PM-18087] Add cipher permissions to response models (#5418) * Add Manage permission to UserCipherDetails and CipherDetails_ReadByIdUserId * Add Manage property to CipherDetails and UserCipherDetailsQuery * Add integration test for CipherRepository Manage permission rules * Update CipherDetails_ReadWithoutOrganizationsByUserId to include Manage permission * Refactor UserCipherDetailsQuery to include detailed permission and organization properties * Refactor CipherRepositoryTests to improve test organization and readability - Split large test method into smaller, focused methods - Added helper methods for creating test data and performing assertions - Improved test coverage for cipher permissions in different scenarios - Maintained existing test logic while enhancing code structure * Refactor CipherRepositoryTests to consolidate cipher permission tests - Removed redundant helper methods for permission assertions - Simplified test methods for GetCipherPermissionsForOrganizationAsync, GetManyByUserIdAsync, and GetByIdAsync - Maintained existing test coverage for cipher manage permissions - Improved code readability and reduced code duplication * Add integration test for CipherRepository group collection manage permissions - Added new test method GetCipherPermissionsForOrganizationAsync_ManageProperty_RespectsCollectionGroupRules - Implemented helper method CreateCipherInOrganizationCollectionWithGroup to support group-based collection permission testing - Verified manage permissions are correctly applied based on group collection access settings * Add @Manage parameter to Cipher stored procedures - Updated CipherDetails_Create, CipherDetails_CreateWithCollections, and CipherDetails_Update stored procedures - Added @Manage parameter with comment "-- not used" - Included new stored procedure implementations in migration script - Consistent with previous work on adding Manage property to cipher details * Update UserCipherDetails functions to reorder Manage and ViewPassword columns * [PM-18086] Add CanRestore and CanDelete authorization methods. * [PM-18086] Address code review feedback. * [PM-18086] Add missing part. * [PM-18087] Add CipherPermissionsResponseModel for cipher permissions * Add GetManyOrganizationAbilityAsync method to application cache service * Add organization ability context to cipher response models This change introduces organization ability context to various cipher response models across multiple controllers. The modifications include: - Updating CipherResponseModel to include permissions based on user and organization ability - Modifying CiphersController methods to fetch and pass organization abilities - Updating SyncController to include organization abilities in sync response - Adding organization ability context to EmergencyAccessController response generation * Remove organization ability context from EmergencyAccessController This change simplifies the EmergencyAccessController by removing unnecessary organization ability fetching and passing. Since emergency access only retrieves personal ciphers, the organization ability context is no longer needed in the response generation. * Remove unused IApplicationCacheService from EmergencyAccessController * Refactor EmergencyAccessViewResponseModel constructor Remove unnecessary JsonConstructor attribute and simplify constructor initialization for EmergencyAccessViewResponseModel * Refactor organization ability retrieval in CiphersController Extract methods to simplify organization ability fetching for ciphers, reducing code duplication and improving readability. Added two private helper methods: - GetOrganizationAbilityAsync: Retrieves organization ability for a single cipher - GetManyOrganizationAbilitiesAsync: Retrieves organization abilities for multiple ciphers * Update CiphersControllerTests to use GetUserByPrincipalAsync Modify test methods to: - Replace GetProperUserId with GetUserByPrincipalAsync - Use User object instead of separate userId - Update mocking to return User object - Ensure user ID is correctly set in test scenarios * Refactor CipherPermissionsResponseModel to use constructor-based initialization * Refactor CipherPermissionsResponseModel to use record type and init-only properties * [PM-18086] Undo files * [PM-18086] Undo files * Refactor organization abilities retrieval in cipher-related controllers and models - Update CiphersController to use GetOrganizationAbilitiesAsync instead of individual methods - Modify CipherResponseModel and CipherDetailsResponseModel to accept organization abilities dictionary - Update CipherPermissionsResponseModel to handle organization abilities lookup - Remove deprecated organization ability retrieval methods - Simplify sync and emergency access response model handling of organization abilities * Remove GetManyOrganizationAbilityAsync method - Delete unused method from IApplicationCacheService interface - Remove corresponding implementation in InMemoryApplicationCacheService - Continues cleanup of organization ability retrieval methods * Update CiphersControllerTests to include organization abilities retrieval - Add organization abilities retrieval in test setup for PutCollections_vNext method - Ensure consistent mocking of IApplicationCacheService in test scenarios * Update error message for missing organization ability --------- Co-authored-by: Jimmy Vo <huynhmaivo82@gmail.com>
2025-03-10 15:27:30 +00:00
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(id, user.Id).ReturnsNull();
var requestAction = async () => await sutProvider.Sut.PutCollections_vNext(id, model);
await Assert.ThrowsAsync<NotFoundException>(requestAction);
}
[Theory, BitAutoData]
public async Task PutCollections_vNextShouldSaveUpdatedCipher(Guid id, CipherCollectionsRequestModel model, Guid userId, SutProvider<CiphersController> sutProvider)
{
SetupUserAndOrgMocks(id, userId, sutProvider);
var cipherDetails = CreateCipherDetailsMock(id, userId);
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(id, userId).ReturnsForAnyArgs(cipherDetails);
sutProvider.GetDependency<ICollectionCipherRepository>().GetManyByUserIdCipherIdAsync(userId, id).Returns((ICollection<CollectionCipher>)new List<CollectionCipher>());
[PM-18087] Add cipher permissions to response models (#5418) * Add Manage permission to UserCipherDetails and CipherDetails_ReadByIdUserId * Add Manage property to CipherDetails and UserCipherDetailsQuery * Add integration test for CipherRepository Manage permission rules * Update CipherDetails_ReadWithoutOrganizationsByUserId to include Manage permission * Refactor UserCipherDetailsQuery to include detailed permission and organization properties * Refactor CipherRepositoryTests to improve test organization and readability - Split large test method into smaller, focused methods - Added helper methods for creating test data and performing assertions - Improved test coverage for cipher permissions in different scenarios - Maintained existing test logic while enhancing code structure * Refactor CipherRepositoryTests to consolidate cipher permission tests - Removed redundant helper methods for permission assertions - Simplified test methods for GetCipherPermissionsForOrganizationAsync, GetManyByUserIdAsync, and GetByIdAsync - Maintained existing test coverage for cipher manage permissions - Improved code readability and reduced code duplication * Add integration test for CipherRepository group collection manage permissions - Added new test method GetCipherPermissionsForOrganizationAsync_ManageProperty_RespectsCollectionGroupRules - Implemented helper method CreateCipherInOrganizationCollectionWithGroup to support group-based collection permission testing - Verified manage permissions are correctly applied based on group collection access settings * Add @Manage parameter to Cipher stored procedures - Updated CipherDetails_Create, CipherDetails_CreateWithCollections, and CipherDetails_Update stored procedures - Added @Manage parameter with comment "-- not used" - Included new stored procedure implementations in migration script - Consistent with previous work on adding Manage property to cipher details * Update UserCipherDetails functions to reorder Manage and ViewPassword columns * [PM-18086] Add CanRestore and CanDelete authorization methods. * [PM-18086] Address code review feedback. * [PM-18086] Add missing part. * [PM-18087] Add CipherPermissionsResponseModel for cipher permissions * Add GetManyOrganizationAbilityAsync method to application cache service * Add organization ability context to cipher response models This change introduces organization ability context to various cipher response models across multiple controllers. The modifications include: - Updating CipherResponseModel to include permissions based on user and organization ability - Modifying CiphersController methods to fetch and pass organization abilities - Updating SyncController to include organization abilities in sync response - Adding organization ability context to EmergencyAccessController response generation * Remove organization ability context from EmergencyAccessController This change simplifies the EmergencyAccessController by removing unnecessary organization ability fetching and passing. Since emergency access only retrieves personal ciphers, the organization ability context is no longer needed in the response generation. * Remove unused IApplicationCacheService from EmergencyAccessController * Refactor EmergencyAccessViewResponseModel constructor Remove unnecessary JsonConstructor attribute and simplify constructor initialization for EmergencyAccessViewResponseModel * Refactor organization ability retrieval in CiphersController Extract methods to simplify organization ability fetching for ciphers, reducing code duplication and improving readability. Added two private helper methods: - GetOrganizationAbilityAsync: Retrieves organization ability for a single cipher - GetManyOrganizationAbilitiesAsync: Retrieves organization abilities for multiple ciphers * Update CiphersControllerTests to use GetUserByPrincipalAsync Modify test methods to: - Replace GetProperUserId with GetUserByPrincipalAsync - Use User object instead of separate userId - Update mocking to return User object - Ensure user ID is correctly set in test scenarios * Refactor CipherPermissionsResponseModel to use constructor-based initialization * Refactor CipherPermissionsResponseModel to use record type and init-only properties * [PM-18086] Undo files * [PM-18086] Undo files * Refactor organization abilities retrieval in cipher-related controllers and models - Update CiphersController to use GetOrganizationAbilitiesAsync instead of individual methods - Modify CipherResponseModel and CipherDetailsResponseModel to accept organization abilities dictionary - Update CipherPermissionsResponseModel to handle organization abilities lookup - Remove deprecated organization ability retrieval methods - Simplify sync and emergency access response model handling of organization abilities * Remove GetManyOrganizationAbilityAsync method - Delete unused method from IApplicationCacheService interface - Remove corresponding implementation in InMemoryApplicationCacheService - Continues cleanup of organization ability retrieval methods * Update CiphersControllerTests to include organization abilities retrieval - Add organization abilities retrieval in test setup for PutCollections_vNext method - Ensure consistent mocking of IApplicationCacheService in test scenarios * Update error message for missing organization ability --------- Co-authored-by: Jimmy Vo <huynhmaivo82@gmail.com>
2025-03-10 15:27:30 +00:00
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilitiesAsync().Returns(new Dictionary<Guid, OrganizationAbility> { { cipherDetails.OrganizationId.Value, new OrganizationAbility() } });
var cipherService = sutProvider.GetDependency<ICipherService>();
await sutProvider.Sut.PutCollections_vNext(id, model);
await cipherService.ReceivedWithAnyArgs().SaveCollectionsAsync(default, default, default, default);
}
[Theory, BitAutoData]
public async Task PutCollections_vNextReturnOptionalDetailsCipherUnavailableFalse(Guid id, CipherCollectionsRequestModel model, Guid userId, SutProvider<CiphersController> sutProvider)
{
SetupUserAndOrgMocks(id, userId, sutProvider);
var cipherDetails = CreateCipherDetailsMock(id, userId);
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(id, userId).ReturnsForAnyArgs(cipherDetails);
sutProvider.GetDependency<ICollectionCipherRepository>().GetManyByUserIdCipherIdAsync(userId, id).Returns((ICollection<CollectionCipher>)new List<CollectionCipher>());
[PM-18087] Add cipher permissions to response models (#5418) * Add Manage permission to UserCipherDetails and CipherDetails_ReadByIdUserId * Add Manage property to CipherDetails and UserCipherDetailsQuery * Add integration test for CipherRepository Manage permission rules * Update CipherDetails_ReadWithoutOrganizationsByUserId to include Manage permission * Refactor UserCipherDetailsQuery to include detailed permission and organization properties * Refactor CipherRepositoryTests to improve test organization and readability - Split large test method into smaller, focused methods - Added helper methods for creating test data and performing assertions - Improved test coverage for cipher permissions in different scenarios - Maintained existing test logic while enhancing code structure * Refactor CipherRepositoryTests to consolidate cipher permission tests - Removed redundant helper methods for permission assertions - Simplified test methods for GetCipherPermissionsForOrganizationAsync, GetManyByUserIdAsync, and GetByIdAsync - Maintained existing test coverage for cipher manage permissions - Improved code readability and reduced code duplication * Add integration test for CipherRepository group collection manage permissions - Added new test method GetCipherPermissionsForOrganizationAsync_ManageProperty_RespectsCollectionGroupRules - Implemented helper method CreateCipherInOrganizationCollectionWithGroup to support group-based collection permission testing - Verified manage permissions are correctly applied based on group collection access settings * Add @Manage parameter to Cipher stored procedures - Updated CipherDetails_Create, CipherDetails_CreateWithCollections, and CipherDetails_Update stored procedures - Added @Manage parameter with comment "-- not used" - Included new stored procedure implementations in migration script - Consistent with previous work on adding Manage property to cipher details * Update UserCipherDetails functions to reorder Manage and ViewPassword columns * [PM-18086] Add CanRestore and CanDelete authorization methods. * [PM-18086] Address code review feedback. * [PM-18086] Add missing part. * [PM-18087] Add CipherPermissionsResponseModel for cipher permissions * Add GetManyOrganizationAbilityAsync method to application cache service * Add organization ability context to cipher response models This change introduces organization ability context to various cipher response models across multiple controllers. The modifications include: - Updating CipherResponseModel to include permissions based on user and organization ability - Modifying CiphersController methods to fetch and pass organization abilities - Updating SyncController to include organization abilities in sync response - Adding organization ability context to EmergencyAccessController response generation * Remove organization ability context from EmergencyAccessController This change simplifies the EmergencyAccessController by removing unnecessary organization ability fetching and passing. Since emergency access only retrieves personal ciphers, the organization ability context is no longer needed in the response generation. * Remove unused IApplicationCacheService from EmergencyAccessController * Refactor EmergencyAccessViewResponseModel constructor Remove unnecessary JsonConstructor attribute and simplify constructor initialization for EmergencyAccessViewResponseModel * Refactor organization ability retrieval in CiphersController Extract methods to simplify organization ability fetching for ciphers, reducing code duplication and improving readability. Added two private helper methods: - GetOrganizationAbilityAsync: Retrieves organization ability for a single cipher - GetManyOrganizationAbilitiesAsync: Retrieves organization abilities for multiple ciphers * Update CiphersControllerTests to use GetUserByPrincipalAsync Modify test methods to: - Replace GetProperUserId with GetUserByPrincipalAsync - Use User object instead of separate userId - Update mocking to return User object - Ensure user ID is correctly set in test scenarios * Refactor CipherPermissionsResponseModel to use constructor-based initialization * Refactor CipherPermissionsResponseModel to use record type and init-only properties * [PM-18086] Undo files * [PM-18086] Undo files * Refactor organization abilities retrieval in cipher-related controllers and models - Update CiphersController to use GetOrganizationAbilitiesAsync instead of individual methods - Modify CipherResponseModel and CipherDetailsResponseModel to accept organization abilities dictionary - Update CipherPermissionsResponseModel to handle organization abilities lookup - Remove deprecated organization ability retrieval methods - Simplify sync and emergency access response model handling of organization abilities * Remove GetManyOrganizationAbilityAsync method - Delete unused method from IApplicationCacheService interface - Remove corresponding implementation in InMemoryApplicationCacheService - Continues cleanup of organization ability retrieval methods * Update CiphersControllerTests to include organization abilities retrieval - Add organization abilities retrieval in test setup for PutCollections_vNext method - Ensure consistent mocking of IApplicationCacheService in test scenarios * Update error message for missing organization ability --------- Co-authored-by: Jimmy Vo <huynhmaivo82@gmail.com>
2025-03-10 15:27:30 +00:00
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilitiesAsync().Returns(new Dictionary<Guid, OrganizationAbility> { { cipherDetails.OrganizationId.Value, new OrganizationAbility() } });
var result = await sutProvider.Sut.PutCollections_vNext(id, model);
Assert.IsType<OptionalCipherDetailsResponseModel>(result);
Assert.False(result.Unavailable);
}
[Theory, BitAutoData]
public async Task PutCollections_vNextReturnOptionalDetailsCipherUnavailableTrue(Guid id, CipherCollectionsRequestModel model, Guid userId, SutProvider<CiphersController> sutProvider)
{
SetupUserAndOrgMocks(id, userId, sutProvider);
var cipherDetails = CreateCipherDetailsMock(id, userId);
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(id, userId).ReturnsForAnyArgs(cipherDetails, [(CipherDetails)null]);
sutProvider.GetDependency<ICollectionCipherRepository>().GetManyByUserIdCipherIdAsync(userId, id).Returns((ICollection<CollectionCipher>)new List<CollectionCipher>());
var result = await sutProvider.Sut.PutCollections_vNext(id, model);
Assert.IsType<OptionalCipherDetailsResponseModel>(result);
Assert.True(result.Unavailable);
}
private void SetupUserAndOrgMocks(Guid id, Guid userId, SutProvider<CiphersController> sutProvider)
{
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
[PM-18087] Add cipher permissions to response models (#5418) * Add Manage permission to UserCipherDetails and CipherDetails_ReadByIdUserId * Add Manage property to CipherDetails and UserCipherDetailsQuery * Add integration test for CipherRepository Manage permission rules * Update CipherDetails_ReadWithoutOrganizationsByUserId to include Manage permission * Refactor UserCipherDetailsQuery to include detailed permission and organization properties * Refactor CipherRepositoryTests to improve test organization and readability - Split large test method into smaller, focused methods - Added helper methods for creating test data and performing assertions - Improved test coverage for cipher permissions in different scenarios - Maintained existing test logic while enhancing code structure * Refactor CipherRepositoryTests to consolidate cipher permission tests - Removed redundant helper methods for permission assertions - Simplified test methods for GetCipherPermissionsForOrganizationAsync, GetManyByUserIdAsync, and GetByIdAsync - Maintained existing test coverage for cipher manage permissions - Improved code readability and reduced code duplication * Add integration test for CipherRepository group collection manage permissions - Added new test method GetCipherPermissionsForOrganizationAsync_ManageProperty_RespectsCollectionGroupRules - Implemented helper method CreateCipherInOrganizationCollectionWithGroup to support group-based collection permission testing - Verified manage permissions are correctly applied based on group collection access settings * Add @Manage parameter to Cipher stored procedures - Updated CipherDetails_Create, CipherDetails_CreateWithCollections, and CipherDetails_Update stored procedures - Added @Manage parameter with comment "-- not used" - Included new stored procedure implementations in migration script - Consistent with previous work on adding Manage property to cipher details * Update UserCipherDetails functions to reorder Manage and ViewPassword columns * [PM-18086] Add CanRestore and CanDelete authorization methods. * [PM-18086] Address code review feedback. * [PM-18086] Add missing part. * [PM-18087] Add CipherPermissionsResponseModel for cipher permissions * Add GetManyOrganizationAbilityAsync method to application cache service * Add organization ability context to cipher response models This change introduces organization ability context to various cipher response models across multiple controllers. The modifications include: - Updating CipherResponseModel to include permissions based on user and organization ability - Modifying CiphersController methods to fetch and pass organization abilities - Updating SyncController to include organization abilities in sync response - Adding organization ability context to EmergencyAccessController response generation * Remove organization ability context from EmergencyAccessController This change simplifies the EmergencyAccessController by removing unnecessary organization ability fetching and passing. Since emergency access only retrieves personal ciphers, the organization ability context is no longer needed in the response generation. * Remove unused IApplicationCacheService from EmergencyAccessController * Refactor EmergencyAccessViewResponseModel constructor Remove unnecessary JsonConstructor attribute and simplify constructor initialization for EmergencyAccessViewResponseModel * Refactor organization ability retrieval in CiphersController Extract methods to simplify organization ability fetching for ciphers, reducing code duplication and improving readability. Added two private helper methods: - GetOrganizationAbilityAsync: Retrieves organization ability for a single cipher - GetManyOrganizationAbilitiesAsync: Retrieves organization abilities for multiple ciphers * Update CiphersControllerTests to use GetUserByPrincipalAsync Modify test methods to: - Replace GetProperUserId with GetUserByPrincipalAsync - Use User object instead of separate userId - Update mocking to return User object - Ensure user ID is correctly set in test scenarios * Refactor CipherPermissionsResponseModel to use constructor-based initialization * Refactor CipherPermissionsResponseModel to use record type and init-only properties * [PM-18086] Undo files * [PM-18086] Undo files * Refactor organization abilities retrieval in cipher-related controllers and models - Update CiphersController to use GetOrganizationAbilitiesAsync instead of individual methods - Modify CipherResponseModel and CipherDetailsResponseModel to accept organization abilities dictionary - Update CipherPermissionsResponseModel to handle organization abilities lookup - Remove deprecated organization ability retrieval methods - Simplify sync and emergency access response model handling of organization abilities * Remove GetManyOrganizationAbilityAsync method - Delete unused method from IApplicationCacheService interface - Remove corresponding implementation in InMemoryApplicationCacheService - Continues cleanup of organization ability retrieval methods * Update CiphersControllerTests to include organization abilities retrieval - Add organization abilities retrieval in test setup for PutCollections_vNext method - Ensure consistent mocking of IApplicationCacheService in test scenarios * Update error message for missing organization ability --------- Co-authored-by: Jimmy Vo <huynhmaivo82@gmail.com>
2025-03-10 15:27:30 +00:00
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(new User { Id = userId });
sutProvider.GetDependency<ICurrentContext>().OrganizationUser(default).ReturnsForAnyArgs(true);
sutProvider.GetDependency<ICollectionCipherRepository>().GetManyByUserIdCipherIdAsync(userId, id).Returns(new List<CollectionCipher>());
}
private CipherDetails CreateCipherDetailsMock(Guid id, Guid userId)
{
return new CipherDetails
{
Id = id,
UserId = userId,
OrganizationId = Guid.NewGuid(),
Type = CipherType.Login,
ViewPassword = true,
Data = @"
{
""Uris"": [
{
""Uri"": ""https://bitwarden.com""
}
],
""Username"": ""testuser"",
""Password"": ""securepassword123""
}"
};
}
[Theory]
[BitAutoData(OrganizationUserType.Admin, true, true)]
[BitAutoData(OrganizationUserType.Owner, true, true)]
[BitAutoData(OrganizationUserType.Custom, false, true)]
[BitAutoData(OrganizationUserType.Custom, true, true)]
[BitAutoData(OrganizationUserType.Admin, false, false)]
[BitAutoData(OrganizationUserType.Owner, false, false)]
[BitAutoData(OrganizationUserType.Custom, false, false)]
public async Task CanEditCiphersAsAdminAsync_FlexibleCollections_Success(
OrganizationUserType userType, bool allowAdminsAccessToAllItems, bool shouldSucceed,
CurrentContextOrganization organization, Guid userId, Cipher cipher, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = organization.Id;
organization.Type = userType;
if (userType == OrganizationUserType.Custom)
{
// Assume custom users have EditAnyCollections for success case
organization.Permissions.EditAnyCollection = shouldSucceed;
}
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipher });
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility
{
Id = organization.Id,
AllowAdminAccessToAllCollectionItems = allowAdminsAccessToAllItems
});
if (shouldSucceed)
{
await sutProvider.Sut.DeleteAdmin(cipher.Id.ToString());
await sutProvider.GetDependency<ICipherService>().ReceivedWithAnyArgs()
.DeleteAsync(default, default);
}
else
{
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteAdmin(cipher.Id.ToString()));
await sutProvider.GetDependency<ICipherService>().DidNotReceiveWithAnyArgs()
.DeleteAsync(default, default);
}
}
[Theory]
[BitAutoData(false)]
[BitAutoData(false)]
[BitAutoData(true)]
public async Task CanEditCiphersAsAdminAsync_Providers(
bool restrictProviders, Cipher cipher, CurrentContextOrganization organization, Guid userId, SutProvider<CiphersController> sutProvider
)
{
cipher.OrganizationId = organization.Id;
// Simulate that the user is a provider for the organization
sutProvider.GetDependency<ICurrentContext>().EditAnyCollection(organization.Id).Returns(true);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organization.Id).Returns(true);
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipher });
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility
{
Id = organization.Id,
AllowAdminAccessToAllCollectionItems = false
});
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(restrictProviders);
// Non restricted providers should succeed
if (!restrictProviders)
{
await sutProvider.Sut.DeleteAdmin(cipher.Id.ToString());
await sutProvider.GetDependency<ICipherService>().ReceivedWithAnyArgs()
.DeleteAsync(default, default);
}
else // Otherwise, they should fail
{
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteAdmin(cipher.Id.ToString()));
await sutProvider.GetDependency<ICipherService>().DidNotReceiveWithAnyArgs()
.DeleteAsync(default, default);
}
await sutProvider.GetDependency<ICurrentContext>().Received().ProviderUserForOrgAsync(organization.Id);
}
[PM-18088] Add unit test coverage for admin methods on CiphersController and CipherService (#5460) * Add comprehensive test coverage for CipherService restore, delete, and soft delete methods * Add comprehensive admin cipher management tests for CiphersController * Enhance CiphersController admin methods with comprehensive test coverage - Add tests for provider user scenarios in admin cipher management methods - Implement tests for custom user with edit any collection permissions - Add test coverage for RestrictProviderAccess feature flag - Improve test scenarios for delete, soft delete, and restore operations * Refactor CiphersControllerTests to simplify and optimize test methods * Optimize CiphersControllerTests with code cleanup and test method improvements * Extend CiphersControllerTests to support Admin and Owner roles * Add test cases for custom user cipher admin operations with EditAnyCollection permission checks - Extend CiphersControllerTests with scenarios for custom users without EditAnyCollection permission - Add test methods to verify NotFoundException is thrown when EditAnyCollection is false - Cover delete, soft delete, and restore operations for single and bulk cipher admin actions * Enhance CiphersControllerTests with granular access permission scenarios - Add test methods for admin and owner roles with specific cipher access scenarios - Implement tests for accessing specific and unassigned ciphers - Extend test coverage for delete, soft delete, and restore operations - Improve test method naming for clarity and precision * Add bulk admin cipher delete and soft delete tests for specific and unassigned ciphers - Implement test methods for DeleteManyAdmin and PutDeleteManyAdmin - Cover scenarios for owner and admin roles with access to specific and unassigned ciphers - Verify correct invocation of DeleteManyAsync and SoftDeleteManyAsync methods - Enhance test coverage for bulk cipher admin operations
2025-03-11 10:10:20 +00:00
[Theory]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.Admin)]
public async Task DeleteAdmin_WithOwnerOrAdmin_WithAccessToSpecificCipher_DeletesCipher(
OrganizationUserType organizationUserType, Cipher cipher, Guid userId,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = organization.Id;
organization.Type = organizationUserType;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<ICipherRepository>()
.GetManyByUserIdAsync(userId)
.Returns(new List<CipherDetails>
{
new() { Id = cipher.Id, OrganizationId = cipher.OrganizationId, Edit = true }
});
await sutProvider.Sut.DeleteAdmin(cipher.Id.ToString());
await sutProvider.GetDependency<ICipherService>().Received(1).DeleteAsync(cipher, userId, true);
}
[Theory]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.Admin)]
public async Task DeleteAdmin_WithOwnerOrAdmin_WithAccessToUnassignedCipher_DeletesCipher(
OrganizationUserType organizationUserType, Cipher cipher, Guid userId,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = organization.Id;
organization.Type = organizationUserType;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<ICipherRepository>()
.GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organization.Id)
.Returns(new List<CipherOrganizationDetails> { new() { Id = cipher.Id } });
await sutProvider.Sut.DeleteAdmin(cipher.Id.ToString());
await sutProvider.GetDependency<ICipherService>().Received(1).DeleteAsync(cipher, userId, true);
}
[Theory]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.Admin)]
public async Task DeleteAdmin_WithAdminOrOwnerAndAccessToAllCollectionItems_DeletesCipher(
OrganizationUserType organizationUserType, Cipher cipher, Guid userId,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = organization.Id;
organization.Type = organizationUserType;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipher });
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility
{
Id = organization.Id,
AllowAdminAccessToAllCollectionItems = true
});
await sutProvider.Sut.DeleteAdmin(cipher.Id.ToString());
await sutProvider.GetDependency<ICipherService>().Received(1).DeleteAsync(cipher, userId, true);
}
[Theory]
[BitAutoData]
public async Task DeleteAdmin_WithCustomUser_WithEditAnyCollectionTrue_DeletesCipher(
Cipher cipher, Guid userId,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = organization.Id;
organization.Type = OrganizationUserType.Custom;
organization.Permissions.EditAnyCollection = true;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipher });
await sutProvider.Sut.DeleteAdmin(cipher.Id.ToString());
await sutProvider.GetDependency<ICipherService>().Received(1).DeleteAsync(cipher, userId, true);
}
[Theory]
[BitAutoData]
public async Task DeleteAdmin_WithCustomUser_WithEditAnyCollectionFalse_ThrowsNotFoundException(
Cipher cipher, Guid userId,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = organization.Id;
organization.Type = OrganizationUserType.Custom;
organization.Permissions.EditAnyCollection = false;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher);
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteAdmin(cipher.Id.ToString()));
}
[Theory]
[BitAutoData]
public async Task DeleteAdmin_WithProviderUser_DeletesCipher(
Cipher cipher, Guid userId, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = Guid.NewGuid();
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(cipher.OrganizationId.Value).Returns(true);
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(cipher.OrganizationId.Value).Returns(new List<Cipher> { cipher });
await sutProvider.Sut.DeleteAdmin(cipher.Id.ToString());
await sutProvider.GetDependency<ICipherService>().Received(1).DeleteAsync(cipher, userId, true);
}
[Theory]
[BitAutoData]
public async Task DeleteAdmin_WithProviderUser_WithRestrictProviderAccessTrue_ThrowsNotFoundException(
Cipher cipher, Guid userId, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = Guid.NewGuid();
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(cipher.OrganizationId.Value).Returns(true);
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(true);
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteAdmin(cipher.Id.ToString()));
}
[Theory]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.Admin)]
public async Task DeleteManyAdmin_WithOwnerOrAdmin_WithAccessToSpecificCiphers_DeletesCiphers(
OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
model.OrganizationId = organization.Id.ToString();
model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
organization.Type = organizationUserType;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>()
.GetManyByUserIdAsync(userId)
.Returns(ciphers.Select(c => new CipherDetails
{
Id = c.Id,
OrganizationId = organization.Id,
Edit = true
}).ToList());
await sutProvider.Sut.DeleteManyAdmin(model);
await sutProvider.GetDependency<ICipherService>()
.Received(1)
.DeleteManyAsync(
Arg.Is<IEnumerable<Guid>>(ids =>
ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()),
userId, organization.Id, true);
}
[Theory]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.Admin)]
public async Task DeleteManyAdmin_WithOwnerOrAdmin_WithAccessToUnassignedCiphers_DeletesCiphers(
OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
model.OrganizationId = organization.Id.ToString();
model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
organization.Type = organizationUserType;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>()
.GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organization.Id)
.Returns(ciphers.Select(c => new CipherOrganizationDetails { Id = c.Id }).ToList());
await sutProvider.Sut.DeleteManyAdmin(model);
await sutProvider.GetDependency<ICipherService>()
.Received(1)
.DeleteManyAsync(
Arg.Is<IEnumerable<Guid>>(ids =>
ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()),
userId, organization.Id, true);
}
[Theory]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.Admin)]
public async Task DeleteManyAdmin_WithOwnerOrAdmin_WithAccessToAllCollectionItems_DeletesCiphers(
OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
model.OrganizationId = organization.Id.ToString();
model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
organization.Type = organizationUserType;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(ciphers);
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility
{
Id = organization.Id,
AllowAdminAccessToAllCollectionItems = true
});
await sutProvider.Sut.DeleteManyAdmin(model);
await sutProvider.GetDependency<ICipherService>()
.Received(1)
.DeleteManyAsync(
Arg.Is<IEnumerable<Guid>>(ids =>
ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()),
userId, organization.Id, true);
}
[Theory]
[BitAutoData]
public async Task DeleteManyAdmin_WithCustomUser_WithEditAnyCollectionTrue_DeletesCiphers(
CipherBulkDeleteRequestModel model,
Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization,
SutProvider<CiphersController> sutProvider)
{
model.OrganizationId = organization.Id.ToString();
model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
organization.Type = OrganizationUserType.Custom;
organization.Permissions.EditAnyCollection = true;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(ciphers);
await sutProvider.Sut.DeleteManyAdmin(model);
await sutProvider.GetDependency<ICipherService>()
.Received(1)
.DeleteManyAsync(
Arg.Is<IEnumerable<Guid>>(ids =>
ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()),
userId, organization.Id, true);
}
[Theory]
[BitAutoData]
public async Task DeleteManyAdmin_WithCustomUser_WithEditAnyCollectionFalse_ThrowsNotFoundException(
CipherBulkDeleteRequestModel model,
Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization,
SutProvider<CiphersController> sutProvider)
{
model.OrganizationId = organization.Id.ToString();
model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
organization.Type = OrganizationUserType.Custom;
organization.Permissions.EditAnyCollection = false;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteManyAdmin(model));
}
[Theory]
[BitAutoData]
public async Task DeleteManyAdmin_WithProviderUser_DeletesCiphers(
CipherBulkDeleteRequestModel model, Guid userId,
List<Cipher> ciphers, SutProvider<CiphersController> sutProvider)
{
var organizationId = Guid.NewGuid();
model.OrganizationId = organizationId.ToString();
model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
foreach (var cipher in ciphers)
{
cipher.OrganizationId = organizationId;
}
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organizationId).Returns(true);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organizationId).Returns(ciphers);
await sutProvider.Sut.DeleteManyAdmin(model);
await sutProvider.GetDependency<ICipherService>()
.Received(1)
.DeleteManyAsync(
Arg.Is<IEnumerable<Guid>>(ids =>
ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()),
userId, organizationId, true);
}
[Theory]
[BitAutoData]
public async Task DeleteManyAdmin_WithProviderUser_WithRestrictProviderAccessTrue_ThrowsNotFoundException(
CipherBulkDeleteRequestModel model, SutProvider<CiphersController> sutProvider)
{
var organizationId = Guid.NewGuid();
model.OrganizationId = organizationId.ToString();
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organizationId).Returns(true);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(true);
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteManyAdmin(model));
}
[Theory]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.Admin)]
public async Task PutDeleteAdmin_WithOwnerOrAdmin_WithAccessToSpecificCipher_SoftDeletesCipher(
OrganizationUserType organizationUserType, Cipher cipher, Guid userId,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = organization.Id;
organization.Type = organizationUserType;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>()
.GetManyByUserIdAsync(userId)
.Returns(new List<CipherDetails>
{
new() { Id = cipher.Id, OrganizationId = cipher.OrganizationId, Edit = true }
});
await sutProvider.Sut.PutDeleteAdmin(cipher.Id.ToString());
await sutProvider.GetDependency<ICipherService>().Received(1).SoftDeleteAsync(cipher, userId, true);
}
[Theory]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.Admin)]
public async Task PutDeleteAdmin_WithOwnerOrAdmin_WithAccessToUnassignedCipher_SoftDeletesCipher(
OrganizationUserType organizationUserType, Cipher cipher, Guid userId,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = organization.Id;
organization.Type = organizationUserType;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>()
.GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organization.Id)
.Returns(new List<CipherOrganizationDetails> { new() { Id = cipher.Id } });
await sutProvider.Sut.PutDeleteAdmin(cipher.Id.ToString());
await sutProvider.GetDependency<ICipherService>().Received(1).SoftDeleteAsync(cipher, userId, true);
}
[Theory]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.Admin)]
public async Task PutDeleteAdmin_WithOwnerOrAdmin_WithAccessToAllCollectionItems_SoftDeletesCipher(
OrganizationUserType organizationUserType, Cipher cipher, Guid userId,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = organization.Id;
organization.Type = organizationUserType;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipher });
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility
{
Id = organization.Id,
AllowAdminAccessToAllCollectionItems = true
});
await sutProvider.Sut.PutDeleteAdmin(cipher.Id.ToString());
await sutProvider.GetDependency<ICipherService>().Received(1).SoftDeleteAsync(cipher, userId, true);
}
[Theory]
[BitAutoData]
public async Task PutDeleteAdmin_WithCustomUser_WithEditAnyCollectionTrue_SoftDeletesCipher(
Cipher cipher, Guid userId,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = organization.Id;
organization.Type = OrganizationUserType.Custom;
organization.Permissions.EditAnyCollection = true;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipher });
await sutProvider.Sut.PutDeleteAdmin(cipher.Id.ToString());
await sutProvider.GetDependency<ICipherService>().Received(1).SoftDeleteAsync(cipher, userId, true);
}
[Theory]
[BitAutoData]
public async Task PutDeleteAdmin_WithCustomUser_WithEditAnyCollectionFalse_ThrowsNotFoundException(
Cipher cipher, Guid userId,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = organization.Id;
organization.Type = OrganizationUserType.Custom;
organization.Permissions.EditAnyCollection = false;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipher });
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutDeleteAdmin(cipher.Id.ToString()));
}
[Theory]
[BitAutoData]
public async Task PutDeleteAdmin_WithProviderUser_SoftDeletesCipher(
Cipher cipher, Guid userId, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = Guid.NewGuid();
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(cipher.OrganizationId.Value).Returns(true);
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(cipher.OrganizationId.Value).Returns(new List<Cipher> { cipher });
await sutProvider.Sut.PutDeleteAdmin(cipher.Id.ToString());
await sutProvider.GetDependency<ICipherService>().Received(1).SoftDeleteAsync(cipher, userId, true);
}
[Theory]
[BitAutoData]
public async Task PutDeleteAdmin_WithProviderUser_WithRestrictProviderAccessTrue_ThrowsNotFoundException(
Cipher cipher, Guid userId, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = Guid.NewGuid();
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(cipher.OrganizationId.Value).Returns(true);
sutProvider.GetDependency<ICipherRepository>().GetByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(true);
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutDeleteAdmin(cipher.Id.ToString()));
}
[Theory]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.Admin)]
public async Task PutDeleteManyAdmin_WithOwnerOrAdmin_WithAccessToSpecificCiphers_SoftDeletesCiphers(
OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
model.OrganizationId = organization.Id.ToString();
model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
organization.Type = organizationUserType;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>()
.GetManyByUserIdAsync(userId)
.Returns(ciphers.Select(c => new CipherDetails
{
Id = c.Id,
OrganizationId = organization.Id,
Edit = true
}).ToList());
await sutProvider.Sut.PutDeleteManyAdmin(model);
await sutProvider.GetDependency<ICipherService>()
.Received(1)
.SoftDeleteManyAsync(
Arg.Is<IEnumerable<Guid>>(ids =>
ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()),
userId, organization.Id, true);
}
[Theory]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.Admin)]
public async Task PutDeleteManyAdmin_WithOwnerOrAdmin_WithAccessToUnassignedCiphers_SoftDeletesCiphers(
OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
model.OrganizationId = organization.Id.ToString();
model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
organization.Type = organizationUserType;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>()
.GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organization.Id)
.Returns(ciphers.Select(c => new CipherOrganizationDetails { Id = c.Id }).ToList());
await sutProvider.Sut.PutDeleteManyAdmin(model);
await sutProvider.GetDependency<ICipherService>()
.Received(1)
.SoftDeleteManyAsync(
Arg.Is<IEnumerable<Guid>>(ids =>
ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()),
userId, organization.Id, true);
}
[Theory]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.Admin)]
public async Task PutDeleteManyAdmin_WithOwnerOrAdmin_WithAccessToAllCollectionItems_SoftDeletesCiphers(
OrganizationUserType organizationUserType, CipherBulkDeleteRequestModel model, Guid userId, List<Cipher> ciphers,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
model.OrganizationId = organization.Id.ToString();
model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
organization.Type = organizationUserType;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(ciphers);
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility
{
Id = organization.Id,
AllowAdminAccessToAllCollectionItems = true
});
await sutProvider.Sut.PutDeleteManyAdmin(model);
await sutProvider.GetDependency<ICipherService>()
.Received(1)
.SoftDeleteManyAsync(
Arg.Is<IEnumerable<Guid>>(ids =>
ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()),
userId, organization.Id, true);
}
[Theory]
[BitAutoData]
public async Task PutDeleteManyAdmin_WithCustomUser_WithEditAnyCollectionTrue_SoftDeletesCiphers(
CipherBulkDeleteRequestModel model,
Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization,
SutProvider<CiphersController> sutProvider)
{
model.OrganizationId = organization.Id.ToString();
model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
organization.Type = OrganizationUserType.Custom;
organization.Permissions.EditAnyCollection = true;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(ciphers);
await sutProvider.Sut.PutDeleteManyAdmin(model);
await sutProvider.GetDependency<ICipherService>()
.Received(1)
.SoftDeleteManyAsync(
Arg.Is<IEnumerable<Guid>>(ids =>
ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()),
userId, organization.Id, true);
}
[Theory]
[BitAutoData]
public async Task PutDeleteManyAdmin_WithCustomUser_WithEditAnyCollectionFalse_ThrowsNotFoundException(
CipherBulkDeleteRequestModel model,
Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization,
SutProvider<CiphersController> sutProvider)
{
model.OrganizationId = organization.Id.ToString();
model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
organization.Type = OrganizationUserType.Custom;
organization.Permissions.EditAnyCollection = false;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutDeleteManyAdmin(model));
}
[Theory]
[BitAutoData]
public async Task PutDeleteManyAdmin_WithProviderUser_SoftDeletesCiphers(
CipherBulkDeleteRequestModel model, Guid userId,
List<Cipher> ciphers, SutProvider<CiphersController> sutProvider)
{
var organizationId = Guid.NewGuid();
model.OrganizationId = organizationId.ToString();
model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
foreach (var cipher in ciphers)
{
cipher.OrganizationId = organizationId;
}
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organizationId).Returns(true);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organizationId).Returns(ciphers);
await sutProvider.Sut.PutDeleteManyAdmin(model);
await sutProvider.GetDependency<ICipherService>()
.Received(1)
.SoftDeleteManyAsync(
Arg.Is<IEnumerable<Guid>>(ids =>
ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()),
userId, organizationId, true);
}
[Theory]
[BitAutoData]
public async Task PutDeleteManyAdmin_WithProviderUser_WithRestrictProviderAccessTrue_ThrowsNotFoundException(
CipherBulkDeleteRequestModel model, SutProvider<CiphersController> sutProvider)
{
var organizationId = Guid.NewGuid();
model.OrganizationId = organizationId.ToString();
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organizationId).Returns(true);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(true);
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutDeleteManyAdmin(model));
}
[Theory]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.Admin)]
public async Task PutRestoreAdmin_WithOwnerOrAdmin_WithAccessToSpecificCipher_RestoresCipher(
OrganizationUserType organizationUserType, CipherDetails cipher, Guid userId,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = organization.Id;
cipher.Type = CipherType.Login;
cipher.Data = JsonSerializer.Serialize(new CipherLoginData());
organization.Type = organizationUserType;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICipherRepository>().GetOrganizationDetailsByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>()
.GetManyByUserIdAsync(userId)
.Returns(new List<CipherDetails>
{
new() { Id = cipher.Id, OrganizationId = cipher.OrganizationId, Edit = true }
});
var result = await sutProvider.Sut.PutRestoreAdmin(cipher.Id.ToString());
Assert.NotNull(result);
Assert.IsType<CipherMiniResponseModel>(result);
await sutProvider.GetDependency<ICipherService>().Received(1).RestoreAsync(cipher, userId, true);
}
[Theory]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.Admin)]
public async Task PutRestoreAdmin_WithOwnerOrAdmin_WithAccessToUnassignedCipher_RestoresCipher(
OrganizationUserType organizationUserType, CipherDetails cipher, Guid userId,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = organization.Id;
cipher.Type = CipherType.Login;
cipher.Data = JsonSerializer.Serialize(new CipherLoginData());
organization.Type = organizationUserType;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICipherRepository>().GetOrganizationDetailsByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>()
.GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organization.Id)
.Returns(new List<CipherOrganizationDetails> { new() { Id = cipher.Id } });
var result = await sutProvider.Sut.PutRestoreAdmin(cipher.Id.ToString());
Assert.NotNull(result);
Assert.IsType<CipherMiniResponseModel>(result);
await sutProvider.GetDependency<ICipherService>().Received(1).RestoreAsync(cipher, userId, true);
}
[Theory]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.Admin)]
public async Task PutRestoreAdmin_WithOwnerOrAdmin_WithAccessToAllCollectionItems_RestoresCipher(
OrganizationUserType organizationUserType, CipherDetails cipher, Guid userId,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = organization.Id;
cipher.Type = CipherType.Login;
cipher.Data = JsonSerializer.Serialize(new CipherLoginData());
organization.Type = organizationUserType;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICipherRepository>().GetOrganizationDetailsByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipher });
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility
{
Id = organization.Id,
AllowAdminAccessToAllCollectionItems = true
});
var result = await sutProvider.Sut.PutRestoreAdmin(cipher.Id.ToString());
Assert.NotNull(result);
await sutProvider.GetDependency<ICipherService>().Received(1).RestoreAsync(cipher, userId, true);
}
[Theory]
[BitAutoData]
public async Task PutRestoreAdmin_WithCustomUser_WithEditAnyCollectionTrue_RestoresCipher(
CipherDetails cipher, Guid userId,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = organization.Id;
cipher.Type = CipherType.Login;
cipher.Data = JsonSerializer.Serialize(new CipherLoginData());
organization.Type = OrganizationUserType.Custom;
organization.Permissions.EditAnyCollection = true;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICipherRepository>().GetOrganizationDetailsByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipher });
var result = await sutProvider.Sut.PutRestoreAdmin(cipher.Id.ToString());
Assert.NotNull(result);
Assert.IsType<CipherMiniResponseModel>(result);
await sutProvider.GetDependency<ICipherService>().Received(1).RestoreAsync(cipher, userId, true);
}
[Theory]
[BitAutoData]
public async Task PutRestoreAdmin_WithCustomUser_WithEditAnyCollectionFalse_ThrowsNotFoundException(
CipherDetails cipher, Guid userId,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = organization.Id;
cipher.Type = CipherType.Login;
cipher.Data = JsonSerializer.Serialize(new CipherLoginData());
organization.Type = OrganizationUserType.Custom;
organization.Permissions.EditAnyCollection = false;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICipherRepository>().GetOrganizationDetailsByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(new List<Cipher> { cipher });
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutRestoreAdmin(cipher.Id.ToString()));
}
[Theory]
[BitAutoData]
public async Task PutRestoreAdmin_WithProviderUser_RestoresCipher(
CipherDetails cipher, Guid userId, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = Guid.NewGuid();
cipher.Type = CipherType.Login;
cipher.Data = JsonSerializer.Serialize(new CipherLoginData());
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(cipher.OrganizationId.Value).Returns(true);
sutProvider.GetDependency<ICipherRepository>().GetOrganizationDetailsByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(cipher.OrganizationId.Value).Returns(new List<Cipher> { cipher });
var result = await sutProvider.Sut.PutRestoreAdmin(cipher.Id.ToString());
Assert.NotNull(result);
Assert.IsType<CipherMiniResponseModel>(result);
await sutProvider.GetDependency<ICipherService>().Received(1).RestoreAsync(cipher, userId, true);
}
[Theory]
[BitAutoData]
public async Task PutRestoreAdmin_WithProviderUser_WithRestrictProviderAccessTrue_ThrowsNotFoundException(
CipherDetails cipher, Guid userId, SutProvider<CiphersController> sutProvider)
{
cipher.OrganizationId = Guid.NewGuid();
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(cipher.OrganizationId.Value).Returns(true);
sutProvider.GetDependency<ICipherRepository>().GetOrganizationDetailsByIdAsync(cipher.Id).Returns(cipher);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(true);
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutRestoreAdmin(cipher.Id.ToString()));
}
[Theory]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.Admin)]
public async Task PutRestoreManyAdmin_WithOwnerOrAdmin_WithAccessToSpecificCiphers_RestoresCiphers(
OrganizationUserType organizationUserType, CipherBulkRestoreRequestModel model, Guid userId, List<Cipher> ciphers,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
model.OrganizationId = organization.Id;
model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
organization.Type = organizationUserType;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(ciphers);
sutProvider.GetDependency<ICipherRepository>()
.GetManyByUserIdAsync(userId)
.Returns(ciphers.Select(c => new CipherDetails
{
Id = c.Id,
OrganizationId = organization.Id,
Edit = true
}).ToList());
var cipherOrgDetails = ciphers.Select(c => new CipherOrganizationDetails
{
Id = c.Id,
OrganizationId = organization.Id
}).ToList();
sutProvider.GetDependency<ICipherService>()
.RestoreManyAsync(Arg.Is<HashSet<Guid>>(ids =>
ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count == model.Ids.Count()),
userId, organization.Id, true)
.Returns(cipherOrgDetails);
var result = await sutProvider.Sut.PutRestoreManyAdmin(model);
Assert.NotNull(result);
await sutProvider.GetDependency<ICipherService>().Received(1)
.RestoreManyAsync(
Arg.Is<HashSet<Guid>>(ids =>
ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count == model.Ids.Count()),
userId, organization.Id, true);
}
[Theory]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.Admin)]
public async Task PutRestoreManyAdmin_WithOwnerOrAdmin_WithAccessToUnassignedCiphers_RestoresCiphers(
OrganizationUserType organizationUserType, CipherBulkRestoreRequestModel model, Guid userId,
List<Cipher> ciphers, CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
model.OrganizationId = organization.Id;
model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
organization.Type = organizationUserType;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
var cipherOrgDetails = ciphers.Select(c => new CipherOrganizationDetails
{
Id = c.Id,
OrganizationId = organization.Id,
Type = CipherType.Login,
Data = JsonSerializer.Serialize(new CipherLoginData())
}).ToList();
sutProvider.GetDependency<ICipherRepository>()
.GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organization.Id)
.Returns(cipherOrgDetails);
sutProvider.GetDependency<ICipherService>()
.RestoreManyAsync(Arg.Is<HashSet<Guid>>(ids =>
ids.All(id => model.Ids.Contains(id.ToString()) && ids.Count == model.Ids.Count())),
userId, organization.Id, true)
.Returns(cipherOrgDetails);
var result = await sutProvider.Sut.PutRestoreManyAdmin(model);
Assert.NotNull(result);
Assert.Equal(model.Ids.Count(), result.Data.Count());
await sutProvider.GetDependency<ICipherService>()
.Received(1)
.RestoreManyAsync(
Arg.Is<HashSet<Guid>>(ids =>
ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count == model.Ids.Count()),
userId, organization.Id, true);
}
[Theory]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.Admin)]
public async Task PutRestoreManyAdmin_WithOwnerOrAdmin_WithAccessToAllCollectionItems_RestoresCiphers(
OrganizationUserType organizationUserType, CipherBulkRestoreRequestModel model, Guid userId, List<Cipher> ciphers,
CurrentContextOrganization organization, SutProvider<CiphersController> sutProvider)
{
model.OrganizationId = organization.Id;
model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
organization.Type = organizationUserType;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(ciphers);
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility
{
Id = organization.Id,
AllowAdminAccessToAllCollectionItems = true
});
var cipherOrgDetails = ciphers.Select(c => new CipherOrganizationDetails
{
Id = c.Id,
OrganizationId = organization.Id,
Type = CipherType.Login,
Data = JsonSerializer.Serialize(new CipherLoginData())
}).ToList();
sutProvider.GetDependency<ICipherService>()
.RestoreManyAsync(Arg.Any<HashSet<Guid>>(), userId, organization.Id, true)
.Returns(cipherOrgDetails);
var result = await sutProvider.Sut.PutRestoreManyAdmin(model);
Assert.NotNull(result);
Assert.Equal(ciphers.Count, result.Data.Count());
await sutProvider.GetDependency<ICipherService>().Received(1)
.RestoreManyAsync(
Arg.Is<HashSet<Guid>>(ids =>
ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count == model.Ids.Count()),
userId, organization.Id, true);
}
[Theory]
[BitAutoData]
public async Task PutRestoreManyAdmin_WithCustomUser_WithEditAnyCollectionTrue_RestoresCiphers(
CipherBulkRestoreRequestModel model,
Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization,
SutProvider<CiphersController> sutProvider)
{
model.OrganizationId = organization.Id;
model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
organization.Type = OrganizationUserType.Custom;
organization.Permissions.EditAnyCollection = true;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(ciphers);
var cipherOrgDetails = ciphers.Select(c => new CipherOrganizationDetails
{
Id = c.Id,
OrganizationId = organization.Id,
Type = CipherType.Login,
Data = JsonSerializer.Serialize(new CipherLoginData())
}).ToList();
sutProvider.GetDependency<ICipherService>()
.RestoreManyAsync(
Arg.Is<HashSet<Guid>>(ids =>
ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count == model.Ids.Count()),
userId, organization.Id, true)
.Returns(cipherOrgDetails);
var result = await sutProvider.Sut.PutRestoreManyAdmin(model);
Assert.NotNull(result);
Assert.Equal(ciphers.Count, result.Data.Count());
await sutProvider.GetDependency<ICipherService>()
.Received(1)
.RestoreManyAsync(
Arg.Is<HashSet<Guid>>(ids =>
ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count == model.Ids.Count()),
userId, organization.Id, true);
}
[Theory]
[BitAutoData]
public async Task PutRestoreManyAdmin_WithCustomUser_WithEditAnyCollectionFalse_ThrowsNotFoundException(
CipherBulkRestoreRequestModel model,
Guid userId, List<Cipher> ciphers, CurrentContextOrganization organization,
SutProvider<CiphersController> sutProvider)
{
model.OrganizationId = organization.Id;
model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
organization.Type = OrganizationUserType.Custom;
organization.Permissions.EditAnyCollection = false;
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(organization.Id).Returns(ciphers);
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutRestoreManyAdmin(model));
}
[Theory]
[BitAutoData]
public async Task PutRestoreManyAdmin_WithProviderUser_RestoresCiphers(
CipherBulkRestoreRequestModel model, Guid userId,
List<Cipher> ciphers, SutProvider<CiphersController> sutProvider)
{
model.OrganizationId = Guid.NewGuid();
model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(model.OrganizationId).Returns(true);
sutProvider.GetDependency<ICipherRepository>().GetManyByOrganizationIdAsync(model.OrganizationId).Returns(ciphers);
var cipherOrgDetails = ciphers.Select(c => new CipherOrganizationDetails
{
Id = c.Id,
OrganizationId = model.OrganizationId
}).ToList();
sutProvider.GetDependency<ICipherService>()
.RestoreManyAsync(
Arg.Any<HashSet<Guid>>(),
userId, model.OrganizationId, true)
.Returns(cipherOrgDetails);
var result = await sutProvider.Sut.PutRestoreManyAdmin(model);
Assert.NotNull(result);
await sutProvider.GetDependency<ICipherService>()
.Received(1)
.RestoreManyAsync(
Arg.Is<HashSet<Guid>>(ids =>
ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count == model.Ids.Count()),
userId, model.OrganizationId, true);
}
[Theory]
[BitAutoData]
public async Task PutRestoreManyAdmin_WithProviderUser_WithRestrictProviderAccessTrue_ThrowsNotFoundException(
CipherBulkRestoreRequestModel model, SutProvider<CiphersController> sutProvider)
{
model.OrganizationId = Guid.NewGuid();
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(model.OrganizationId).Returns(true);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(true);
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutRestoreManyAdmin(model));
}
}