From 79a4b75b524e8024aec47c695e681b1fbd1ea60f Mon Sep 17 00:00:00 2001 From: Rui Tome Date: Wed, 20 May 2026 15:47:24 +0100 Subject: [PATCH 1/5] Implement UpdateCollectionManagementSettingsCommand and associated interface for managing organization collection settings * Add UpdateCollectionManagementSettingsCommand to handle updates to organization collection management settings. * Create IUpdateCollectionManagementSettingsCommand interface to define the update method. * Implement unit tests for UpdateCollectionManagementSettingsCommand to verify event logging and exception handling. --- ...dateCollectionManagementSettingsCommand.cs | 15 +++ ...dateCollectionManagementSettingsCommand.cs | 99 +++++++++++++++ ...ollectionManagementSettingsCommandTests.cs | 120 ++++++++++++++++++ 3 files changed, 234 insertions(+) create mode 100644 src/Core/AdminConsole/OrganizationFeatures/Organizations/Interfaces/IUpdateCollectionManagementSettingsCommand.cs create mode 100644 src/Core/AdminConsole/OrganizationFeatures/Organizations/UpdateCollectionManagementSettingsCommand.cs create mode 100644 test/Core.Test/AdminConsole/OrganizationFeatures/Organizations/UpdateCollectionManagementSettingsCommandTests.cs diff --git a/src/Core/AdminConsole/OrganizationFeatures/Organizations/Interfaces/IUpdateCollectionManagementSettingsCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/Organizations/Interfaces/IUpdateCollectionManagementSettingsCommand.cs new file mode 100644 index 000000000000..0d38c1a3c358 --- /dev/null +++ b/src/Core/AdminConsole/OrganizationFeatures/Organizations/Interfaces/IUpdateCollectionManagementSettingsCommand.cs @@ -0,0 +1,15 @@ +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Models.Business; + +namespace Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Interfaces; + +public interface IUpdateCollectionManagementSettingsCommand +{ + /// + /// Updates an organization's collection management settings. + /// + /// The unique identifier of the organization to update. + /// The collection management settings to apply. + /// The updated organization. + Task UpdateCollectionManagementSettingsAsync(Guid organizationId, OrganizationCollectionManagementSettings settings); +} diff --git a/src/Core/AdminConsole/OrganizationFeatures/Organizations/UpdateCollectionManagementSettingsCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/Organizations/UpdateCollectionManagementSettingsCommand.cs new file mode 100644 index 000000000000..43e7b73c6776 --- /dev/null +++ b/src/Core/AdminConsole/OrganizationFeatures/Organizations/UpdateCollectionManagementSettingsCommand.cs @@ -0,0 +1,99 @@ +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Models.Business; +using Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Interfaces; +using Bit.Core.Enums; +using Bit.Core.Exceptions; +using Bit.Core.Platform.Push; +using Bit.Core.Repositories; +using Bit.Core.Services; + +namespace Bit.Core.AdminConsole.OrganizationFeatures.Organizations; + +public class UpdateCollectionManagementSettingsCommand : IUpdateCollectionManagementSettingsCommand +{ + private readonly IOrganizationRepository _organizationRepository; + private readonly IApplicationCacheService _applicationCacheService; + private readonly IEventService _eventService; + private readonly IPushNotificationService _pushNotificationService; + + public UpdateCollectionManagementSettingsCommand( + IOrganizationRepository organizationRepository, + IApplicationCacheService applicationCacheService, + IEventService eventService, + IPushNotificationService pushNotificationService) + { + _organizationRepository = organizationRepository; + _applicationCacheService = applicationCacheService; + _eventService = eventService; + _pushNotificationService = pushNotificationService; + } + + public async Task UpdateCollectionManagementSettingsAsync(Guid organizationId, OrganizationCollectionManagementSettings settings) + { + var existingOrganization = await _organizationRepository.GetByIdAsync(organizationId); + if (existingOrganization == null) + { + throw new NotFoundException(); + } + + var loggingActions = CreateCollectionManagementLoggingActions(existingOrganization, settings); + + existingOrganization.LimitCollectionCreation = settings.LimitCollectionCreation; + existingOrganization.LimitCollectionDeletion = settings.LimitCollectionDeletion; + existingOrganization.LimitItemDeletion = settings.LimitItemDeletion; + existingOrganization.AllowAdminAccessToAllCollectionItems = settings.AllowAdminAccessToAllCollectionItems; + existingOrganization.RevisionDate = DateTime.UtcNow; + + await _organizationRepository.ReplaceAsync(existingOrganization); + await _applicationCacheService.UpsertOrganizationAbilityAsync(existingOrganization); + + if (loggingActions.Any()) + { + await Task.WhenAll(loggingActions.Select(action => action())); + } + + await _pushNotificationService.PushSyncOrganizationCollectionManagementSettingsAsync(existingOrganization); + + return existingOrganization; + } + + private List> CreateCollectionManagementLoggingActions( + Organization existingOrganization, OrganizationCollectionManagementSettings settings) + { + var loggingActions = new List>(); + + if (existingOrganization.LimitCollectionCreation != settings.LimitCollectionCreation) + { + var eventType = settings.LimitCollectionCreation + ? EventType.Organization_CollectionManagement_LimitCollectionCreationEnabled + : EventType.Organization_CollectionManagement_LimitCollectionCreationDisabled; + loggingActions.Add(() => _eventService.LogOrganizationEventAsync(existingOrganization, eventType)); + } + + if (existingOrganization.LimitCollectionDeletion != settings.LimitCollectionDeletion) + { + var eventType = settings.LimitCollectionDeletion + ? EventType.Organization_CollectionManagement_LimitCollectionDeletionEnabled + : EventType.Organization_CollectionManagement_LimitCollectionDeletionDisabled; + loggingActions.Add(() => _eventService.LogOrganizationEventAsync(existingOrganization, eventType)); + } + + if (existingOrganization.LimitItemDeletion != settings.LimitItemDeletion) + { + var eventType = settings.LimitItemDeletion + ? EventType.Organization_CollectionManagement_LimitItemDeletionEnabled + : EventType.Organization_CollectionManagement_LimitItemDeletionDisabled; + loggingActions.Add(() => _eventService.LogOrganizationEventAsync(existingOrganization, eventType)); + } + + if (existingOrganization.AllowAdminAccessToAllCollectionItems != settings.AllowAdminAccessToAllCollectionItems) + { + var eventType = settings.AllowAdminAccessToAllCollectionItems + ? EventType.Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsEnabled + : EventType.Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsDisabled; + loggingActions.Add(() => _eventService.LogOrganizationEventAsync(existingOrganization, eventType)); + } + + return loggingActions; + } +} diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/Organizations/UpdateCollectionManagementSettingsCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/Organizations/UpdateCollectionManagementSettingsCommandTests.cs new file mode 100644 index 000000000000..f66a8715c3ab --- /dev/null +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/Organizations/UpdateCollectionManagementSettingsCommandTests.cs @@ -0,0 +1,120 @@ +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Models.Business; +using Bit.Core.AdminConsole.OrganizationFeatures.Organizations; +using Bit.Core.Enums; +using Bit.Core.Exceptions; +using Bit.Core.Repositories; +using Bit.Core.Services; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using Xunit; + +namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Organizations; + +[SutProviderCustomize] +public class UpdateCollectionManagementSettingsCommandTests +{ + [Theory] + [BitAutoData(false, true, false, true)] + [BitAutoData(true, false, true, false)] + public async Task UpdateCollectionManagementSettingsAsync_WhenSettingsChanged_LogsSpecificEvents( + bool newLimitCollectionCreation, + bool newLimitCollectionDeletion, + bool newLimitItemDeletion, + bool newAllowAdminAccessToAllCollectionItems, + Organization existingOrganization, SutProvider sutProvider) + { + // Arrange + existingOrganization.LimitCollectionCreation = false; + existingOrganization.LimitCollectionDeletion = false; + existingOrganization.LimitItemDeletion = false; + existingOrganization.AllowAdminAccessToAllCollectionItems = false; + + sutProvider.GetDependency() + .GetByIdAsync(existingOrganization.Id) + .Returns(existingOrganization); + + var settings = new OrganizationCollectionManagementSettings + { + LimitCollectionCreation = newLimitCollectionCreation, + LimitCollectionDeletion = newLimitCollectionDeletion, + LimitItemDeletion = newLimitItemDeletion, + AllowAdminAccessToAllCollectionItems = newAllowAdminAccessToAllCollectionItems + }; + + // Act + await sutProvider.Sut.UpdateCollectionManagementSettingsAsync(existingOrganization.Id, settings); + + // Assert + var eventService = sutProvider.GetDependency(); + if (newLimitCollectionCreation) + { + await eventService.Received(1).LogOrganizationEventAsync( + Arg.Is(org => org.Id == existingOrganization.Id), + Arg.Is(e => e == EventType.Organization_CollectionManagement_LimitCollectionCreationEnabled)); + } + else + { + await eventService.DidNotReceive().LogOrganizationEventAsync( + Arg.Is(org => org.Id == existingOrganization.Id), + Arg.Is(e => e == EventType.Organization_CollectionManagement_LimitCollectionCreationEnabled)); + } + + if (newLimitCollectionDeletion) + { + await eventService.Received(1).LogOrganizationEventAsync( + Arg.Is(org => org.Id == existingOrganization.Id), + Arg.Is(e => e == EventType.Organization_CollectionManagement_LimitCollectionDeletionEnabled)); + } + else + { + await eventService.DidNotReceive().LogOrganizationEventAsync( + Arg.Is(org => org.Id == existingOrganization.Id), + Arg.Is(e => e == EventType.Organization_CollectionManagement_LimitCollectionDeletionEnabled)); + } + + if (newLimitItemDeletion) + { + await eventService.Received(1).LogOrganizationEventAsync( + Arg.Is(org => org.Id == existingOrganization.Id), + Arg.Is(e => e == EventType.Organization_CollectionManagement_LimitItemDeletionEnabled)); + } + else + { + await eventService.DidNotReceive().LogOrganizationEventAsync( + Arg.Is(org => org.Id == existingOrganization.Id), + Arg.Is(e => e == EventType.Organization_CollectionManagement_LimitItemDeletionEnabled)); + } + + if (newAllowAdminAccessToAllCollectionItems) + { + await eventService.Received(1).LogOrganizationEventAsync( + Arg.Is(org => org.Id == existingOrganization.Id), + Arg.Is(e => e == EventType.Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsEnabled)); + } + else + { + await eventService.DidNotReceive().LogOrganizationEventAsync( + Arg.Is(org => org.Id == existingOrganization.Id), + Arg.Is(e => e == EventType.Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsEnabled)); + } + } + + [Theory, BitAutoData] + public async Task UpdateCollectionManagementSettingsAsync_WhenOrganizationNotFound_ThrowsNotFoundException( + Guid organizationId, OrganizationCollectionManagementSettings settings, SutProvider sutProvider) + { + // Arrange + sutProvider.GetDependency() + .GetByIdAsync(organizationId) + .Returns((Organization)null); + + // Act/Assert + await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateCollectionManagementSettingsAsync(organizationId, settings)); + + await sutProvider.GetDependency() + .Received(1) + .GetByIdAsync(organizationId); + } +} From 55742a5eb26b0cbacf59b356b1448d18c04d632d Mon Sep 17 00:00:00 2001 From: Rui Tome Date: Wed, 20 May 2026 15:48:31 +0100 Subject: [PATCH 2/5] Add IUpdateCollectionManagementSettingsCommand to service collection * Register IUpdateCollectionManagementSettingsCommand and its implementation, UpdateCollectionManagementSettingsCommand, in the service collection for managing organization collection settings. --- .../OrganizationServiceCollectionExtensions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs index b2ccbadb1236..4000d6067f76 100644 --- a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs +++ b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs @@ -105,6 +105,7 @@ private static void AddOrganizationUpdateCommands(this IServiceCollection servic { services.AddScoped(); services.AddScoped(); + services.AddScoped(); } private static void AddOrganizationEnableCommands(this IServiceCollection services) => From db99685a1b95b234e79184ad5a21443806f9dcc5 Mon Sep 17 00:00:00 2001 From: Rui Tome Date: Wed, 20 May 2026 15:55:29 +0100 Subject: [PATCH 3/5] Rename command --- ...ganizationUpdateCollectionManagementCommand.cs} | 4 ++-- ...ganizationUpdateCollectionManagementCommand.cs} | 6 +++--- .../OrganizationServiceCollectionExtensions.cs | 2 +- ...ationUpdateCollectionManagementCommandTests.cs} | 14 +++++++------- 4 files changed, 13 insertions(+), 13 deletions(-) rename src/Core/AdminConsole/OrganizationFeatures/Organizations/Interfaces/{IUpdateCollectionManagementSettingsCommand.cs => IOrganizationUpdateCollectionManagementCommand.cs} (72%) rename src/Core/AdminConsole/OrganizationFeatures/Organizations/{UpdateCollectionManagementSettingsCommand.cs => OrganizationUpdateCollectionManagementCommand.cs} (93%) rename test/Core.Test/AdminConsole/OrganizationFeatures/Organizations/{UpdateCollectionManagementSettingsCommandTests.cs => OrganizationUpdateCollectionManagementCommandTests.cs} (87%) diff --git a/src/Core/AdminConsole/OrganizationFeatures/Organizations/Interfaces/IUpdateCollectionManagementSettingsCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/Organizations/Interfaces/IOrganizationUpdateCollectionManagementCommand.cs similarity index 72% rename from src/Core/AdminConsole/OrganizationFeatures/Organizations/Interfaces/IUpdateCollectionManagementSettingsCommand.cs rename to src/Core/AdminConsole/OrganizationFeatures/Organizations/Interfaces/IOrganizationUpdateCollectionManagementCommand.cs index 0d38c1a3c358..7dd3dba83e19 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/Organizations/Interfaces/IUpdateCollectionManagementSettingsCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/Organizations/Interfaces/IOrganizationUpdateCollectionManagementCommand.cs @@ -3,7 +3,7 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Interfaces; -public interface IUpdateCollectionManagementSettingsCommand +public interface IOrganizationUpdateCollectionManagementCommand { /// /// Updates an organization's collection management settings. @@ -11,5 +11,5 @@ public interface IUpdateCollectionManagementSettingsCommand /// The unique identifier of the organization to update. /// The collection management settings to apply. /// The updated organization. - Task UpdateCollectionManagementSettingsAsync(Guid organizationId, OrganizationCollectionManagementSettings settings); + Task UpdateAsync(Guid organizationId, OrganizationCollectionManagementSettings settings); } diff --git a/src/Core/AdminConsole/OrganizationFeatures/Organizations/UpdateCollectionManagementSettingsCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/Organizations/OrganizationUpdateCollectionManagementCommand.cs similarity index 93% rename from src/Core/AdminConsole/OrganizationFeatures/Organizations/UpdateCollectionManagementSettingsCommand.cs rename to src/Core/AdminConsole/OrganizationFeatures/Organizations/OrganizationUpdateCollectionManagementCommand.cs index 43e7b73c6776..58724ea1b399 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/Organizations/UpdateCollectionManagementSettingsCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/Organizations/OrganizationUpdateCollectionManagementCommand.cs @@ -9,14 +9,14 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.Organizations; -public class UpdateCollectionManagementSettingsCommand : IUpdateCollectionManagementSettingsCommand +public class OrganizationUpdateCollectionManagementCommand : IOrganizationUpdateCollectionManagementCommand { private readonly IOrganizationRepository _organizationRepository; private readonly IApplicationCacheService _applicationCacheService; private readonly IEventService _eventService; private readonly IPushNotificationService _pushNotificationService; - public UpdateCollectionManagementSettingsCommand( + public OrganizationUpdateCollectionManagementCommand( IOrganizationRepository organizationRepository, IApplicationCacheService applicationCacheService, IEventService eventService, @@ -28,7 +28,7 @@ public UpdateCollectionManagementSettingsCommand( _pushNotificationService = pushNotificationService; } - public async Task UpdateCollectionManagementSettingsAsync(Guid organizationId, OrganizationCollectionManagementSettings settings) + public async Task UpdateAsync(Guid organizationId, OrganizationCollectionManagementSettings settings) { var existingOrganization = await _organizationRepository.GetByIdAsync(organizationId); if (existingOrganization == null) diff --git a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs index 4000d6067f76..c9a9a96ec459 100644 --- a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs +++ b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs @@ -105,7 +105,7 @@ private static void AddOrganizationUpdateCommands(this IServiceCollection servic { services.AddScoped(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); } private static void AddOrganizationEnableCommands(this IServiceCollection services) => diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/Organizations/UpdateCollectionManagementSettingsCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/Organizations/OrganizationUpdateCollectionManagementCommandTests.cs similarity index 87% rename from test/Core.Test/AdminConsole/OrganizationFeatures/Organizations/UpdateCollectionManagementSettingsCommandTests.cs rename to test/Core.Test/AdminConsole/OrganizationFeatures/Organizations/OrganizationUpdateCollectionManagementCommandTests.cs index f66a8715c3ab..d246ef37a06b 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/Organizations/UpdateCollectionManagementSettingsCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/Organizations/OrganizationUpdateCollectionManagementCommandTests.cs @@ -13,17 +13,17 @@ namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Organizations; [SutProviderCustomize] -public class UpdateCollectionManagementSettingsCommandTests +public class OrganizationUpdateCollectionManagementCommandTests { [Theory] [BitAutoData(false, true, false, true)] [BitAutoData(true, false, true, false)] - public async Task UpdateCollectionManagementSettingsAsync_WhenSettingsChanged_LogsSpecificEvents( + public async Task UpdateAsync_WhenSettingsChanged_LogsSpecificEvents( bool newLimitCollectionCreation, bool newLimitCollectionDeletion, bool newLimitItemDeletion, bool newAllowAdminAccessToAllCollectionItems, - Organization existingOrganization, SutProvider sutProvider) + Organization existingOrganization, SutProvider sutProvider) { // Arrange existingOrganization.LimitCollectionCreation = false; @@ -44,7 +44,7 @@ public async Task UpdateCollectionManagementSettingsAsync_WhenSettingsChanged_Lo }; // Act - await sutProvider.Sut.UpdateCollectionManagementSettingsAsync(existingOrganization.Id, settings); + await sutProvider.Sut.UpdateAsync(existingOrganization.Id, settings); // Assert var eventService = sutProvider.GetDependency(); @@ -102,8 +102,8 @@ await eventService.DidNotReceive().LogOrganizationEventAsync( } [Theory, BitAutoData] - public async Task UpdateCollectionManagementSettingsAsync_WhenOrganizationNotFound_ThrowsNotFoundException( - Guid organizationId, OrganizationCollectionManagementSettings settings, SutProvider sutProvider) + public async Task UpdateAsync_WhenOrganizationNotFound_ThrowsNotFoundException( + Guid organizationId, OrganizationCollectionManagementSettings settings, SutProvider sutProvider) { // Arrange sutProvider.GetDependency() @@ -111,7 +111,7 @@ public async Task UpdateCollectionManagementSettingsAsync_WhenOrganizationNotFou .Returns((Organization)null); // Act/Assert - await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateCollectionManagementSettingsAsync(organizationId, settings)); + await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateAsync(organizationId, settings)); await sutProvider.GetDependency() .Received(1) From b1b85afb4d4bf8123b6ccf27a72fe091ef29e0b7 Mon Sep 17 00:00:00 2001 From: Rui Tome Date: Wed, 20 May 2026 15:56:41 +0100 Subject: [PATCH 4/5] Update OrganizationsController to use IOrganizationUpdateCollectionManagementCommand * Added IOrganizationUpdateCollectionManagementCommand to the OrganizationsController for managing collection settings updates. * Updated the constructor to inject the new command and modified the PutCollectionManagement method to utilize it. * Adjusted unit tests to reflect the changes in the command used for updating collection management settings. --- .../AdminConsole/Controllers/OrganizationsController.cs | 7 +++++-- .../Controllers/OrganizationsControllerTests.cs | 8 ++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Api/AdminConsole/Controllers/OrganizationsController.cs b/src/Api/AdminConsole/Controllers/OrganizationsController.cs index f802624b725f..96bce7cea609 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationsController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationsController.cs @@ -65,6 +65,7 @@ public class OrganizationsController : Controller private readonly IPricingClient _pricingClient; private readonly IOrganizationUpdateKeysCommand _organizationUpdateKeysCommand; private readonly IOrganizationUpdateCommand _organizationUpdateCommand; + private readonly IOrganizationUpdateCollectionManagementCommand _organizationUpdateCollectionManagementCommand; public OrganizationsController( IOrganizationRepository organizationRepository, @@ -88,7 +89,8 @@ public OrganizationsController( IOrganizationDeleteCommand organizationDeleteCommand, IPricingClient pricingClient, IOrganizationUpdateKeysCommand organizationUpdateKeysCommand, - IOrganizationUpdateCommand organizationUpdateCommand) + IOrganizationUpdateCommand organizationUpdateCommand, + IOrganizationUpdateCollectionManagementCommand organizationUpdateCollectionManagementCommand) { _organizationRepository = organizationRepository; _organizationUserRepository = organizationUserRepository; @@ -112,6 +114,7 @@ public OrganizationsController( _pricingClient = pricingClient; _organizationUpdateKeysCommand = organizationUpdateKeysCommand; _organizationUpdateCommand = organizationUpdateCommand; + _organizationUpdateCollectionManagementCommand = organizationUpdateCollectionManagementCommand; } [HttpGet("{id}")] @@ -547,7 +550,7 @@ public async Task PutCollectionManagement(Guid id, [F throw new NotFoundException(); } - var organization = await _organizationService.UpdateCollectionManagementSettingsAsync(id, model.ToSettings()); + var organization = await _organizationUpdateCollectionManagementCommand.UpdateAsync(id, model.ToSettings()); var plan = await _pricingClient.GetPlan(organization.PlanType); return new OrganizationResponseModel(organization, plan); } diff --git a/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs b/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs index a8acf8fe02ed..63583ac20ba5 100644 --- a/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs +++ b/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs @@ -205,8 +205,8 @@ public async Task PutCollectionManagement_ValidRequest_Success( var plan = MockPlans.Get(PlanType.EnterpriseAnnually); sutProvider.GetDependency().GetPlan(Arg.Any()).Returns(plan); - sutProvider.GetDependency() - .UpdateCollectionManagementSettingsAsync( + sutProvider.GetDependency() + .UpdateAsync( organization.Id, Arg.Is(s => s.LimitCollectionCreation == model.LimitCollectionCreation && @@ -219,9 +219,9 @@ public async Task PutCollectionManagement_ValidRequest_Success( await sutProvider.Sut.PutCollectionManagement(organization.Id, model); // Assert - await sutProvider.GetDependency() + await sutProvider.GetDependency() .Received(1) - .UpdateCollectionManagementSettingsAsync( + .UpdateAsync( organization.Id, Arg.Is(s => s.LimitCollectionCreation == model.LimitCollectionCreation && From d60e8f0062e95856672fb228cd142f1376d2280c Mon Sep 17 00:00:00 2001 From: Rui Tome Date: Wed, 20 May 2026 15:57:46 +0100 Subject: [PATCH 5/5] Refactor IOrganizationService and OrganizationService to remove UpdateCollectionManagementSettingsAsync method * Removed the UpdateCollectionManagementSettingsAsync method from IOrganizationService and its implementation in OrganizationService. * Cleaned up unused usings related to collection management settings in both service files. * Updated unit tests to reflect the removal of the collection management settings update functionality. --- .../Services/IOrganizationService.cs | 2 - .../Implementations/OrganizationService.cs | 69 ------------ .../Services/OrganizationServiceTests.cs | 104 ------------------ 3 files changed, 175 deletions(-) diff --git a/src/Core/AdminConsole/Services/IOrganizationService.cs b/src/Core/AdminConsole/Services/IOrganizationService.cs index f55899b97141..e96224ff02e4 100644 --- a/src/Core/AdminConsole/Services/IOrganizationService.cs +++ b/src/Core/AdminConsole/Services/IOrganizationService.cs @@ -2,7 +2,6 @@ #nullable disable using Bit.Core.AdminConsole.Entities; -using Bit.Core.AdminConsole.Models.Business; using Bit.Core.Auth.Enums; using Bit.Core.Entities; using Bit.Core.Enums; @@ -20,7 +19,6 @@ public interface IOrganizationService Task AdjustSeatsAsync(Guid organizationId, int seatAdjustment); Task UpdateExpirationDateAsync(Guid organizationId, DateTime? expirationDate); Task UpdateAsync(Organization organization, bool updateBilling = false); - Task UpdateCollectionManagementSettingsAsync(Guid organizationId, OrganizationCollectionManagementSettings settings); Task UpdateTwoFactorProviderAsync(Organization organization, TwoFactorProviderType type); Task DisableTwoFactorProviderAsync(Organization organization, TwoFactorProviderType type); Task InviteUserAsync(Guid organizationId, Guid? invitingUserId, EventSystemUser? systemUser, diff --git a/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs b/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs index 4381b9fe8d70..b47d32cb5d3f 100644 --- a/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs +++ b/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs @@ -3,7 +3,6 @@ using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Enums.Provider; -using Bit.Core.AdminConsole.Models.Business; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models; @@ -389,35 +388,6 @@ await _stripeAdapter.UpdateCustomerAsync(organization.GatewayCustomerId, } } - public async Task UpdateCollectionManagementSettingsAsync(Guid organizationId, OrganizationCollectionManagementSettings settings) - { - var existingOrganization = await _organizationRepository.GetByIdAsync(organizationId); - if (existingOrganization == null) - { - throw new NotFoundException(); - } - - // Create logging actions based on what will change - var loggingActions = CreateCollectionManagementLoggingActions(existingOrganization, settings); - - existingOrganization.LimitCollectionCreation = settings.LimitCollectionCreation; - existingOrganization.LimitCollectionDeletion = settings.LimitCollectionDeletion; - existingOrganization.LimitItemDeletion = settings.LimitItemDeletion; - existingOrganization.AllowAdminAccessToAllCollectionItems = settings.AllowAdminAccessToAllCollectionItems; - existingOrganization.RevisionDate = DateTime.UtcNow; - - await ReplaceAndUpdateCacheAsync(existingOrganization); - - if (loggingActions.Any()) - { - await Task.WhenAll(loggingActions.Select(action => action())); - } - - await _pushNotificationService.PushSyncOrganizationCollectionManagementSettingsAsync(existingOrganization); - - return existingOrganization; - } - public async Task UpdateTwoFactorProviderAsync(Organization organization, TwoFactorProviderType type) { if (!type.ToString().Contains("Organization")) @@ -1122,43 +1092,4 @@ public static OrganizationUserStatusType GetPriorActiveOrganizationUserStatusTyp return status; } - private List> CreateCollectionManagementLoggingActions( - Organization existingOrganization, OrganizationCollectionManagementSettings settings) - { - var loggingActions = new List>(); - - if (existingOrganization.LimitCollectionCreation != settings.LimitCollectionCreation) - { - var eventType = settings.LimitCollectionCreation - ? EventType.Organization_CollectionManagement_LimitCollectionCreationEnabled - : EventType.Organization_CollectionManagement_LimitCollectionCreationDisabled; - loggingActions.Add(() => _eventService.LogOrganizationEventAsync(existingOrganization, eventType)); - } - - if (existingOrganization.LimitCollectionDeletion != settings.LimitCollectionDeletion) - { - var eventType = settings.LimitCollectionDeletion - ? EventType.Organization_CollectionManagement_LimitCollectionDeletionEnabled - : EventType.Organization_CollectionManagement_LimitCollectionDeletionDisabled; - loggingActions.Add(() => _eventService.LogOrganizationEventAsync(existingOrganization, eventType)); - } - - if (existingOrganization.LimitItemDeletion != settings.LimitItemDeletion) - { - var eventType = settings.LimitItemDeletion - ? EventType.Organization_CollectionManagement_LimitItemDeletionEnabled - : EventType.Organization_CollectionManagement_LimitItemDeletionDisabled; - loggingActions.Add(() => _eventService.LogOrganizationEventAsync(existingOrganization, eventType)); - } - - if (existingOrganization.AllowAdminAccessToAllCollectionItems != settings.AllowAdminAccessToAllCollectionItems) - { - var eventType = settings.AllowAdminAccessToAllCollectionItems - ? EventType.Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsEnabled - : EventType.Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsDisabled; - loggingActions.Add(() => _eventService.LogOrganizationEventAsync(existingOrganization, eventType)); - } - - return loggingActions; - } } diff --git a/test/Core.Test/AdminConsole/Services/OrganizationServiceTests.cs b/test/Core.Test/AdminConsole/Services/OrganizationServiceTests.cs index 269ce3008d8b..4ba8ca9ef2c2 100644 --- a/test/Core.Test/AdminConsole/Services/OrganizationServiceTests.cs +++ b/test/Core.Test/AdminConsole/Services/OrganizationServiceTests.cs @@ -1,7 +1,6 @@ using System.Text.Json; using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Enums.Provider; -using Bit.Core.AdminConsole.Models.Business; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models; @@ -1231,109 +1230,6 @@ await organizationRepository .GetByIdentifierAsync(Arg.Is(id => id == organization.Identifier)); } - [Theory] - [BitAutoData(false, true, false, true)] - [BitAutoData(true, false, true, false)] - public async Task UpdateCollectionManagementSettingsAsync_WhenSettingsChanged_LogsSpecificEvents( - bool newLimitCollectionCreation, - bool newLimitCollectionDeletion, - bool newLimitItemDeletion, - bool newAllowAdminAccessToAllCollectionItems, - Organization existingOrganization, SutProvider sutProvider) - { - // Arrange - existingOrganization.LimitCollectionCreation = false; - existingOrganization.LimitCollectionDeletion = false; - existingOrganization.LimitItemDeletion = false; - existingOrganization.AllowAdminAccessToAllCollectionItems = false; - - sutProvider.GetDependency() - .GetByIdAsync(existingOrganization.Id) - .Returns(existingOrganization); - - var settings = new OrganizationCollectionManagementSettings - { - LimitCollectionCreation = newLimitCollectionCreation, - LimitCollectionDeletion = newLimitCollectionDeletion, - LimitItemDeletion = newLimitItemDeletion, - AllowAdminAccessToAllCollectionItems = newAllowAdminAccessToAllCollectionItems - }; - - // Act - await sutProvider.Sut.UpdateCollectionManagementSettingsAsync(existingOrganization.Id, settings); - - // Assert - var eventService = sutProvider.GetDependency(); - if (newLimitCollectionCreation) - { - await eventService.Received(1).LogOrganizationEventAsync( - Arg.Is(org => org.Id == existingOrganization.Id), - Arg.Is(e => e == EventType.Organization_CollectionManagement_LimitCollectionCreationEnabled)); - } - else - { - await eventService.DidNotReceive().LogOrganizationEventAsync( - Arg.Is(org => org.Id == existingOrganization.Id), - Arg.Is(e => e == EventType.Organization_CollectionManagement_LimitCollectionCreationEnabled)); - } - - if (newLimitCollectionDeletion) - { - await eventService.Received(1).LogOrganizationEventAsync( - Arg.Is(org => org.Id == existingOrganization.Id), - Arg.Is(e => e == EventType.Organization_CollectionManagement_LimitCollectionDeletionEnabled)); - } - else - { - await eventService.DidNotReceive().LogOrganizationEventAsync( - Arg.Is(org => org.Id == existingOrganization.Id), - Arg.Is(e => e == EventType.Organization_CollectionManagement_LimitCollectionDeletionEnabled)); - } - - if (newLimitItemDeletion) - { - await eventService.Received(1).LogOrganizationEventAsync( - Arg.Is(org => org.Id == existingOrganization.Id), - Arg.Is(e => e == EventType.Organization_CollectionManagement_LimitItemDeletionEnabled)); - } - else - { - await eventService.DidNotReceive().LogOrganizationEventAsync( - Arg.Is(org => org.Id == existingOrganization.Id), - Arg.Is(e => e == EventType.Organization_CollectionManagement_LimitItemDeletionEnabled)); - } - - if (newAllowAdminAccessToAllCollectionItems) - { - await eventService.Received(1).LogOrganizationEventAsync( - Arg.Is(org => org.Id == existingOrganization.Id), - Arg.Is(e => e == EventType.Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsEnabled)); - } - else - { - await eventService.DidNotReceive().LogOrganizationEventAsync( - Arg.Is(org => org.Id == existingOrganization.Id), - Arg.Is(e => e == EventType.Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsEnabled)); - } - } - - [Theory, BitAutoData] - public async Task UpdateCollectionManagementSettingsAsync_WhenOrganizationNotFound_ThrowsNotFoundException( - Guid organizationId, OrganizationCollectionManagementSettings settings, SutProvider sutProvider) - { - // Arrange - sutProvider.GetDependency() - .GetByIdAsync(organizationId) - .Returns((Organization)null); - - // Act/Assert - await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateCollectionManagementSettingsAsync(organizationId, settings)); - - await sutProvider.GetDependency() - .Received(1) - .GetByIdAsync(organizationId); - } - [Theory, PaidOrganizationCustomize(CheckedPlanType = PlanType.EnterpriseAnnually), BitAutoData] public async Task AdjustSeatsAsync_WithFeatureFlag_UsesUpdateOrganizationSubscriptionCommand( Organization organization, SutProvider sutProvider)