From 71def39015e5614bb2698c813f60dde654b65489 Mon Sep 17 00:00:00 2001 From: Vincent Salucci <26154748+vincentsalucci@users.noreply.github.com> Date: Wed, 27 Dec 2023 18:07:06 -0600 Subject: [PATCH] [AC-1809] Update OrganizationAbility with Collection Management Settings (#3571) * feat: Update OrganizationAbility with LimitCollectionCreationDeletion, refs AC-1809 * feat: Update OrganizationAbility constructor usage to pass feature flag state, refs AC-1809 * feat: Update EF retrieval of org abilities to include new property from database, refs AC-1809 * feat: Update sproc to include LimitCollectionCreationDeletion property and create migration, refs AC-1809 * feat: Inject ApplicationCache into handler accessing LimitCollectionCreationDeletion, refs AC-1809 * feat: remove collection management settings from CurrentContextOrganization and update tests, refs AC-1809 * feat: add AllowAdminAccessToAllCollectionItems to OrganizationAbility pipeline, refs AC-1809 --------- Co-authored-by: Thomas Rittson Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> --- .../BulkCollectionAuthorizationHandler.cs | 39 ++++++++-- .../Groups/GroupAuthorizationHandler.cs | 33 +++++++- .../OrganizationUserAuthorizationHandler.cs | 30 +++++++- .../Context/CurrentContextOrganization.cs | 4 - .../Data/Organizations/OrganizationAbility.cs | 4 + src/Core/Context/CurrentContext.cs | 9 +-- .../Repositories/OrganizationRepository.cs | 4 +- .../Organization_ReadAbilities.sql | 4 +- ...BulkCollectionAuthorizationHandlerTests.cs | 76 +++++++++++++++---- .../GroupAuthorizationHandlerTests.cs | 37 ++++++++- ...ganizationUserAuthorizationHandlerTests.cs | 37 ++++++++- ...litiesWithCollectionManagementSettings.sql | 30 ++++++++ 12 files changed, 257 insertions(+), 50 deletions(-) create mode 100644 util/Migrator/DbScripts/2023-12-21_00_OrganizationAbilitiesWithCollectionManagementSettings.sql diff --git a/src/Api/Vault/AuthorizationHandlers/Collections/BulkCollectionAuthorizationHandler.cs b/src/Api/Vault/AuthorizationHandlers/Collections/BulkCollectionAuthorizationHandler.cs index 76544e3b8..45e8bc945 100644 --- a/src/Api/Vault/AuthorizationHandlers/Collections/BulkCollectionAuthorizationHandler.cs +++ b/src/Api/Vault/AuthorizationHandlers/Collections/BulkCollectionAuthorizationHandler.cs @@ -4,6 +4,7 @@ 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.Utilities; @@ -20,6 +21,7 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext); @@ -27,11 +29,13 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler /// Ensures the acting user is allowed to update the target collections or manage access permissions for them. /// - private async Task CanUpdateCollection(AuthorizationHandlerContext context, + private async Task CanUpdateCollectionAsync(AuthorizationHandlerContext context, IAuthorizationRequirement requirement, ICollection resources, CurrentContextOrganization? org) { @@ -226,9 +235,10 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler assignedCollectionIds.Contains(tc.Id)); } + + private async Task GetOrganizationAbilityAsync(CurrentContextOrganization? organization) + { + // If the CurrentContextOrganization is null, then the user isn't a member of the org so the setting is + // irrelevant + if (organization == null) + { + return null; + } + + (await _applicationCacheService.GetOrganizationAbilitiesAsync()) + .TryGetValue(organization.Id, out var organizationAbility); + + return organizationAbility; + } } diff --git a/src/Api/Vault/AuthorizationHandlers/Groups/GroupAuthorizationHandler.cs b/src/Api/Vault/AuthorizationHandlers/Groups/GroupAuthorizationHandler.cs index 0081fdfd7..2d3bf053a 100644 --- a/src/Api/Vault/AuthorizationHandlers/Groups/GroupAuthorizationHandler.cs +++ b/src/Api/Vault/AuthorizationHandlers/Groups/GroupAuthorizationHandler.cs @@ -3,6 +3,7 @@ using Bit.Core; using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Exceptions; +using Bit.Core.Models.Data.Organizations; using Bit.Core.Services; using Microsoft.AspNetCore.Authorization; @@ -16,15 +17,18 @@ public class GroupAuthorizationHandler : AuthorizationHandler _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext); public GroupAuthorizationHandler( ICurrentContext currentContext, - IFeatureService featureService) + IFeatureService featureService, + IApplicationCacheService applicationCacheService) { _currentContext = currentContext; _featureService = featureService; + _applicationCacheService = applicationCacheService; } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, @@ -62,10 +66,8 @@ public class GroupAuthorizationHandler : AuthorizationHandler GetOrganizationAbilityAsync(CurrentContextOrganization? organization) + { + // If the CurrentContextOrganization is null, then the user isn't a member of the org so the setting is + // irrelevant + if (organization == null) + { + return null; + } + + (await _applicationCacheService.GetOrganizationAbilitiesAsync()) + .TryGetValue(organization.Id, out var organizationAbility); + + return organizationAbility; + } } diff --git a/src/Api/Vault/AuthorizationHandlers/OrganizationUsers/OrganizationUserAuthorizationHandler.cs b/src/Api/Vault/AuthorizationHandlers/OrganizationUsers/OrganizationUserAuthorizationHandler.cs index b78b65df3..965165613 100644 --- a/src/Api/Vault/AuthorizationHandlers/OrganizationUsers/OrganizationUserAuthorizationHandler.cs +++ b/src/Api/Vault/AuthorizationHandlers/OrganizationUsers/OrganizationUserAuthorizationHandler.cs @@ -3,6 +3,7 @@ using Bit.Core; using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Exceptions; +using Bit.Core.Models.Data.Organizations; using Bit.Core.Services; using Microsoft.AspNetCore.Authorization; @@ -16,15 +17,18 @@ public class OrganizationUserAuthorizationHandler : AuthorizationHandler _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext); public OrganizationUserAuthorizationHandler( ICurrentContext currentContext, - IFeatureService featureService) + IFeatureService featureService, + IApplicationCacheService applicationCacheService) { _currentContext = currentContext; _featureService = featureService; + _applicationCacheService = applicationCacheService; } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, @@ -64,7 +68,6 @@ public class OrganizationUserAuthorizationHandler : AuthorizationHandler GetOrganizationAbilityAsync(CurrentContextOrganization? organization) + { + // If the CurrentContextOrganization is null, then the user isn't a member of the org so the setting is + // irrelevant + if (organization == null) + { + return null; + } + + (await _applicationCacheService.GetOrganizationAbilitiesAsync()) + .TryGetValue(organization.Id, out var organizationAbility); + + return organizationAbility; + } } diff --git a/src/Core/AdminConsole/Context/CurrentContextOrganization.cs b/src/Core/AdminConsole/Context/CurrentContextOrganization.cs index a1806137e..3c9dc10cc 100644 --- a/src/Core/AdminConsole/Context/CurrentContextOrganization.cs +++ b/src/Core/AdminConsole/Context/CurrentContextOrganization.cs @@ -15,14 +15,10 @@ public class CurrentContextOrganization Type = orgUser.Type; Permissions = CoreHelpers.LoadClassFromJsonData(orgUser.Permissions); AccessSecretsManager = orgUser.AccessSecretsManager && orgUser.UseSecretsManager && orgUser.Enabled; - LimitCollectionCreationDeletion = orgUser.LimitCollectionCreationDeletion; - AllowAdminAccessToAllCollectionItems = orgUser.AllowAdminAccessToAllCollectionItems; } public Guid Id { get; set; } public OrganizationUserType Type { get; set; } public Permissions Permissions { get; set; } = new(); public bool AccessSecretsManager { get; set; } - public bool LimitCollectionCreationDeletion { get; set; } - public bool AllowAdminAccessToAllCollectionItems { get; set; } } diff --git a/src/Core/AdminConsole/Models/Data/Organizations/OrganizationAbility.cs b/src/Core/AdminConsole/Models/Data/Organizations/OrganizationAbility.cs index e4506a82d..07db80d43 100644 --- a/src/Core/AdminConsole/Models/Data/Organizations/OrganizationAbility.cs +++ b/src/Core/AdminConsole/Models/Data/Organizations/OrganizationAbility.cs @@ -21,6 +21,8 @@ public class OrganizationAbility UseResetPassword = organization.UseResetPassword; UseCustomPermissions = organization.UseCustomPermissions; UsePolicies = organization.UsePolicies; + LimitCollectionCreationDeletion = organization.LimitCollectionCreationDeletion; + AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems; } public Guid Id { get; set; } @@ -35,4 +37,6 @@ public class OrganizationAbility public bool UseResetPassword { get; set; } public bool UseCustomPermissions { get; set; } public bool UsePolicies { get; set; } + public bool LimitCollectionCreationDeletion { get; set; } + public bool AllowAdminAccessToAllCollectionItems { get; set; } } diff --git a/src/Core/Context/CurrentContext.cs b/src/Core/Context/CurrentContext.cs index 478edda0d..b346c20f3 100644 --- a/src/Core/Context/CurrentContext.cs +++ b/src/Core/Context/CurrentContext.cs @@ -54,7 +54,7 @@ public class CurrentContext : ICurrentContext { _providerOrganizationRepository = providerOrganizationRepository; _providerUserRepository = providerUserRepository; - _featureService = featureService; + _featureService = featureService; ; } public async virtual Task BuildAsync(HttpContext httpContext, GlobalSettings globalSettings) @@ -383,15 +383,10 @@ public class CurrentContext : ICurrentContext throw new FeatureUnavailableException("Flexible Collections is ON when it should be OFF."); } - var canCreateNewCollections = false; var org = GetOrganization(orgId); - if (org != null) - { - canCreateNewCollections = !org.LimitCollectionCreationDeletion || org.Permissions.CreateNewCollections; - } return await EditAssignedCollections(orgId) || await DeleteAssignedCollections(orgId) - || canCreateNewCollections; + || (org != null && org.Permissions.CreateNewCollections); } public async Task ManageGroups(Guid orgId) diff --git a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs index 8cdfe3a42..50d0d0e2d 100644 --- a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs @@ -88,7 +88,9 @@ public class OrganizationRepository : Repository().UserId.Returns(userId); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().GetOrganizationAbilitiesAsync().Returns(organizationAbilities); await sutProvider.Sut.HandleAsync(context); @@ -56,7 +60,8 @@ public class BulkCollectionAuthorizationHandlerTests var actingUserId = Guid.NewGuid(); organization.Type = OrganizationUserType.User; - organization.LimitCollectionCreationDeletion = false; + + var organizationAbilities = ArrangeOrganizationAbilitiesDictionary(organization.Id, false); var context = new AuthorizationHandlerContext( new[] { BulkCollectionOperations.Create }, @@ -65,6 +70,7 @@ public class BulkCollectionAuthorizationHandlerTests sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().GetOrganizationAbilitiesAsync().Returns(organizationAbilities); await sutProvider.Sut.HandleAsync(context); @@ -83,7 +89,6 @@ public class BulkCollectionAuthorizationHandlerTests var actingUserId = Guid.NewGuid(); organization.Type = userType; - organization.LimitCollectionCreationDeletion = true; organization.Permissions = new Permissions { EditAnyCollection = false, @@ -92,6 +97,8 @@ public class BulkCollectionAuthorizationHandlerTests ManageUsers = false }; + var organizationAbilities = ArrangeOrganizationAbilitiesDictionary(organization.Id, true); + var context = new AuthorizationHandlerContext( new[] { BulkCollectionOperations.Create }, new ClaimsPrincipal(), @@ -99,6 +106,8 @@ public class BulkCollectionAuthorizationHandlerTests sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().GetOrganizationAbilitiesAsync().Returns(organizationAbilities); + sutProvider.GetDependency().ProviderUserForOrgAsync(Arg.Any()).Returns(false); await sutProvider.Sut.HandleAsync(context); @@ -111,6 +120,8 @@ public class BulkCollectionAuthorizationHandlerTests ICollection collections, SutProvider sutProvider) { + var organizationAbilities = ArrangeOrganizationAbilitiesDictionary(collections.First().OrganizationId, true); + var context = new AuthorizationHandlerContext( new[] { BulkCollectionOperations.Create }, new ClaimsPrincipal(), @@ -119,6 +130,8 @@ public class BulkCollectionAuthorizationHandlerTests sutProvider.GetDependency().UserId.Returns(userId); sutProvider.GetDependency().GetOrganization(Arg.Any()).Returns((CurrentContextOrganization)null); + sutProvider.GetDependency().GetOrganizationAbilitiesAsync().Returns(organizationAbilities); + sutProvider.GetDependency().ProviderUserForOrgAsync(Arg.Any()).Returns(false); await sutProvider.Sut.HandleAsync(context); Assert.False(context.HasSucceeded); @@ -134,7 +147,6 @@ public class BulkCollectionAuthorizationHandlerTests CurrentContextOrganization organization) { organization.Type = userType; - organization.LimitCollectionCreationDeletion = true; organization.Permissions = new Permissions(); var operationsToTest = new[] @@ -217,7 +229,6 @@ public class BulkCollectionAuthorizationHandlerTests } organization.Type = OrganizationUserType.User; - organization.LimitCollectionCreationDeletion = false; organization.Permissions = new Permissions(); var operationsToTest = new[] @@ -254,7 +265,6 @@ public class BulkCollectionAuthorizationHandlerTests var actingUserId = Guid.NewGuid(); organization.Type = OrganizationUserType.User; - organization.LimitCollectionCreationDeletion = false; organization.Permissions = new Permissions(); var operationsToTest = new[] @@ -293,7 +303,6 @@ public class BulkCollectionAuthorizationHandlerTests var actingUserId = Guid.NewGuid(); organization.Type = userType; - organization.LimitCollectionCreationDeletion = true; organization.Permissions = new Permissions { EditAnyCollection = false, @@ -367,7 +376,6 @@ public class BulkCollectionAuthorizationHandlerTests CurrentContextOrganization organization) { organization.Type = userType; - organization.LimitCollectionCreationDeletion = true; organization.Permissions = new Permissions(); sutProvider.GetDependency().UserId.Returns(userId); @@ -431,7 +439,6 @@ public class BulkCollectionAuthorizationHandlerTests } organization.Type = OrganizationUserType.User; - organization.LimitCollectionCreationDeletion = false; organization.Permissions = new Permissions(); sutProvider.GetDependency().UserId.Returns(actingUserId); @@ -462,7 +469,6 @@ public class BulkCollectionAuthorizationHandlerTests } organization.Type = OrganizationUserType.User; - organization.LimitCollectionCreationDeletion = false; organization.Permissions = new Permissions(); sutProvider.GetDependency().UserId.Returns(actingUserId); @@ -491,7 +497,6 @@ public class BulkCollectionAuthorizationHandlerTests var actingUserId = Guid.NewGuid(); organization.Type = userType; - organization.LimitCollectionCreationDeletion = true; organization.Permissions = new Permissions { EditAnyCollection = false, @@ -543,7 +548,6 @@ public class BulkCollectionAuthorizationHandlerTests CurrentContextOrganization organization) { organization.Type = userType; - organization.LimitCollectionCreationDeletion = true; organization.Permissions = new Permissions(); var operationsToTest = new[] @@ -661,7 +665,6 @@ public class BulkCollectionAuthorizationHandlerTests var actingUserId = Guid.NewGuid(); organization.Type = userType; - organization.LimitCollectionCreationDeletion = false; organization.Permissions = new Permissions { EditAnyCollection = false, @@ -742,9 +745,10 @@ public class BulkCollectionAuthorizationHandlerTests CurrentContextOrganization organization) { organization.Type = userType; - organization.LimitCollectionCreationDeletion = true; organization.Permissions = new Permissions(); + var organizationAbilities = ArrangeOrganizationAbilitiesDictionary(organization.Id, true); + var context = new AuthorizationHandlerContext( new[] { BulkCollectionOperations.Delete }, new ClaimsPrincipal(), @@ -752,6 +756,8 @@ public class BulkCollectionAuthorizationHandlerTests sutProvider.GetDependency().UserId.Returns(userId); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().GetOrganizationAbilitiesAsync() + .Returns(organizationAbilities); await sutProvider.Sut.HandleAsync(context); @@ -767,12 +773,13 @@ public class BulkCollectionAuthorizationHandlerTests var actingUserId = Guid.NewGuid(); organization.Type = OrganizationUserType.Custom; - organization.LimitCollectionCreationDeletion = false; organization.Permissions = new Permissions { DeleteAnyCollection = true }; + var organizationAbilities = ArrangeOrganizationAbilitiesDictionary(organization.Id, true); + var context = new AuthorizationHandlerContext( new[] { BulkCollectionOperations.Delete }, new ClaimsPrincipal(), @@ -780,6 +787,8 @@ public class BulkCollectionAuthorizationHandlerTests sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().GetOrganizationAbilitiesAsync() + .Returns(organizationAbilities); await sutProvider.Sut.HandleAsync(context); @@ -797,9 +806,13 @@ public class BulkCollectionAuthorizationHandlerTests organization.Type = OrganizationUserType.User; organization.Permissions = new Permissions(); + var organizationAbilities = ArrangeOrganizationAbilitiesDictionary(organization.Id, false); + sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); sutProvider.GetDependency().GetManyByUserIdAsync(actingUserId, Arg.Any()).Returns(collections); + sutProvider.GetDependency().GetOrganizationAbilitiesAsync() + .Returns(organizationAbilities); foreach (var c in collections) { @@ -828,7 +841,6 @@ public class BulkCollectionAuthorizationHandlerTests var actingUserId = Guid.NewGuid(); organization.Type = userType; - organization.LimitCollectionCreationDeletion = true; organization.Permissions = new Permissions { EditAnyCollection = false, @@ -837,6 +849,8 @@ public class BulkCollectionAuthorizationHandlerTests ManageUsers = false }; + var organizationAbilities = ArrangeOrganizationAbilitiesDictionary(organization.Id, true); + var context = new AuthorizationHandlerContext( new[] { BulkCollectionOperations.Delete }, new ClaimsPrincipal(), @@ -844,6 +858,9 @@ public class BulkCollectionAuthorizationHandlerTests sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().GetOrganizationAbilitiesAsync() + .Returns(organizationAbilities); + sutProvider.GetDependency().ProviderUserForOrgAsync(Arg.Any()).Returns(false); await sutProvider.Sut.HandleAsync(context); @@ -864,6 +881,7 @@ public class BulkCollectionAuthorizationHandlerTests sutProvider.GetDependency().UserId.Returns(userId); sutProvider.GetDependency().GetOrganization(Arg.Any()).Returns((CurrentContextOrganization)null); + sutProvider.GetDependency().ProviderUserForOrgAsync(Arg.Any()).Returns(false); await sutProvider.Sut.HandleAsync(context); Assert.False(context.HasSucceeded); @@ -919,6 +937,16 @@ public class BulkCollectionAuthorizationHandlerTests var actingUserId = Guid.NewGuid(); var orgId = collections.First().OrganizationId; + var organizationAbilities = new Dictionary + { + { collections.First().OrganizationId, + new OrganizationAbility + { + LimitCollectionCreationDeletion = true + } + } + }; + var operationsToTest = new[] { BulkCollectionOperations.Create, @@ -933,6 +961,8 @@ public class BulkCollectionAuthorizationHandlerTests { sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().GetOrganization(orgId).Returns((CurrentContextOrganization)null); + sutProvider.GetDependency().GetOrganizationAbilitiesAsync() + .Returns(organizationAbilities); sutProvider.GetDependency().ProviderUserForOrgAsync(Arg.Any()).Returns(true); var context = new AuthorizationHandlerContext( @@ -950,4 +980,18 @@ public class BulkCollectionAuthorizationHandlerTests sutProvider.Recreate(); } } + + private static Dictionary ArrangeOrganizationAbilitiesDictionary(Guid orgId, + bool limitCollectionCreationDeletion) + { + return new Dictionary + { + { orgId, + new OrganizationAbility + { + LimitCollectionCreationDeletion = limitCollectionCreationDeletion + } + } + }; + } } diff --git a/test/Api.Test/Vault/AuthorizationHandlers/GroupAuthorizationHandlerTests.cs b/test/Api.Test/Vault/AuthorizationHandlers/GroupAuthorizationHandlerTests.cs index 74711c80b..79d1ebada 100644 --- a/test/Api.Test/Vault/AuthorizationHandlers/GroupAuthorizationHandlerTests.cs +++ b/test/Api.Test/Vault/AuthorizationHandlers/GroupAuthorizationHandlerTests.cs @@ -4,6 +4,8 @@ using Bit.Core; using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Models.Data; +using Bit.Core.Models.Data.Organizations; +using Bit.Core.Services; using Bit.Core.Test.AutoFixture; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; @@ -26,9 +28,10 @@ public class GroupAuthorizationHandlerTests CurrentContextOrganization organization) { organization.Type = userType; - organization.LimitCollectionCreationDeletion = true; organization.Permissions = new Permissions(); + var organizationAbilities = ArrangeOrganizationAbilitiesDictionary(organization.Id, true); + var context = new AuthorizationHandlerContext( new[] { GroupOperations.ReadAll(organization.Id) }, new ClaimsPrincipal(), @@ -36,6 +39,7 @@ public class GroupAuthorizationHandlerTests sutProvider.GetDependency().UserId.Returns(userId); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().GetOrganizationAbilitiesAsync().Returns(organizationAbilities); await sutProvider.Sut.HandleAsync(context); @@ -48,9 +52,10 @@ public class GroupAuthorizationHandlerTests SutProvider sutProvider, CurrentContextOrganization organization) { organization.Type = OrganizationUserType.User; - organization.LimitCollectionCreationDeletion = true; organization.Permissions = new Permissions(); + var organizationAbilities = ArrangeOrganizationAbilitiesDictionary(organization.Id, true); + var context = new AuthorizationHandlerContext( new[] { GroupOperations.ReadAll(organization.Id) }, new ClaimsPrincipal(), @@ -59,6 +64,7 @@ public class GroupAuthorizationHandlerTests sutProvider.GetDependency() .UserId .Returns(userId); + sutProvider.GetDependency().GetOrganizationAbilitiesAsync().Returns(organizationAbilities); sutProvider.GetDependency() .ProviderUserForOrgAsync(organization.Id) .Returns(true); @@ -83,7 +89,6 @@ public class GroupAuthorizationHandlerTests var actingUserId = Guid.NewGuid(); organization.Type = OrganizationUserType.Custom; - organization.LimitCollectionCreationDeletion = limitCollectionCreationDeletion; organization.Permissions = new Permissions { EditAnyCollection = editAnyCollection, @@ -92,6 +97,8 @@ public class GroupAuthorizationHandlerTests ManageUsers = manageUsers }; + var organizationAbilities = ArrangeOrganizationAbilitiesDictionary(organization.Id, limitCollectionCreationDeletion); + var context = new AuthorizationHandlerContext( new[] { GroupOperations.ReadAll(organization.Id) }, new ClaimsPrincipal(), @@ -99,6 +106,7 @@ public class GroupAuthorizationHandlerTests sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().GetOrganizationAbilitiesAsync().Returns(organizationAbilities); await sutProvider.Sut.HandleAsync(context); @@ -116,7 +124,6 @@ public class GroupAuthorizationHandlerTests var actingUserId = Guid.NewGuid(); organization.Type = userType; - organization.LimitCollectionCreationDeletion = true; organization.Permissions = new Permissions { EditAnyCollection = false, @@ -126,6 +133,8 @@ public class GroupAuthorizationHandlerTests AccessImportExport = false }; + var organizationAbilities = ArrangeOrganizationAbilitiesDictionary(organization.Id, true); + var context = new AuthorizationHandlerContext( new[] { GroupOperations.ReadAll(organization.Id) }, new ClaimsPrincipal(), @@ -133,6 +142,8 @@ public class GroupAuthorizationHandlerTests sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().GetOrganizationAbilitiesAsync().Returns(organizationAbilities); + sutProvider.GetDependency().ProviderUserForOrgAsync(Arg.Any()).Returns(false); await sutProvider.Sut.HandleAsync(context); @@ -145,6 +156,8 @@ public class GroupAuthorizationHandlerTests Guid organizationId, SutProvider sutProvider) { + var organizationAbilities = ArrangeOrganizationAbilitiesDictionary(organizationId, true); + var context = new AuthorizationHandlerContext( new[] { GroupOperations.ReadAll(organizationId) }, new ClaimsPrincipal(), @@ -153,6 +166,8 @@ public class GroupAuthorizationHandlerTests sutProvider.GetDependency().UserId.Returns(userId); sutProvider.GetDependency().GetOrganization(Arg.Any()).Returns((CurrentContextOrganization)null); + sutProvider.GetDependency().GetOrganizationAbilitiesAsync().Returns(organizationAbilities); + sutProvider.GetDependency().ProviderUserForOrgAsync(Arg.Any()).Returns(false); await sutProvider.Sut.HandleAsync(context); Assert.False(context.HasSucceeded); @@ -194,4 +209,18 @@ public class GroupAuthorizationHandlerTests Assert.False(context.HasSucceeded); Assert.True(context.HasFailed); } + + private static Dictionary ArrangeOrganizationAbilitiesDictionary(Guid orgId, + bool limitCollectionCreationDeletion) + { + return new Dictionary + { + { orgId, + new OrganizationAbility + { + LimitCollectionCreationDeletion = limitCollectionCreationDeletion + } + } + }; + } } diff --git a/test/Api.Test/Vault/AuthorizationHandlers/OrganizationUserAuthorizationHandlerTests.cs b/test/Api.Test/Vault/AuthorizationHandlers/OrganizationUserAuthorizationHandlerTests.cs index 1f6916faf..c93d8a0f6 100644 --- a/test/Api.Test/Vault/AuthorizationHandlers/OrganizationUserAuthorizationHandlerTests.cs +++ b/test/Api.Test/Vault/AuthorizationHandlers/OrganizationUserAuthorizationHandlerTests.cs @@ -4,6 +4,8 @@ using Bit.Core; using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Models.Data; +using Bit.Core.Models.Data.Organizations; +using Bit.Core.Services; using Bit.Core.Test.AutoFixture; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; @@ -26,9 +28,10 @@ public class OrganizationUserAuthorizationHandlerTests CurrentContextOrganization organization) { organization.Type = userType; - organization.LimitCollectionCreationDeletion = true; organization.Permissions = new Permissions(); + var organizationAbilities = ArrangeOrganizationAbilitiesDictionary(organization.Id, true); + var context = new AuthorizationHandlerContext( new[] { OrganizationUserOperations.ReadAll(organization.Id) }, new ClaimsPrincipal(), @@ -36,6 +39,7 @@ public class OrganizationUserAuthorizationHandlerTests sutProvider.GetDependency().UserId.Returns(userId); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().GetOrganizationAbilitiesAsync().Returns(organizationAbilities); await sutProvider.Sut.HandleAsync(context); @@ -48,9 +52,10 @@ public class OrganizationUserAuthorizationHandlerTests SutProvider sutProvider, CurrentContextOrganization organization) { organization.Type = OrganizationUserType.User; - organization.LimitCollectionCreationDeletion = true; organization.Permissions = new Permissions(); + var organizationAbilities = ArrangeOrganizationAbilitiesDictionary(organization.Id, true); + var context = new AuthorizationHandlerContext( new[] { OrganizationUserOperations.ReadAll(organization.Id) }, new ClaimsPrincipal(), @@ -59,6 +64,7 @@ public class OrganizationUserAuthorizationHandlerTests sutProvider.GetDependency() .UserId .Returns(userId); + sutProvider.GetDependency().GetOrganizationAbilitiesAsync().Returns(organizationAbilities); sutProvider.GetDependency() .ProviderUserForOrgAsync(organization.Id) .Returns(true); @@ -83,7 +89,6 @@ public class OrganizationUserAuthorizationHandlerTests var actingUserId = Guid.NewGuid(); organization.Type = OrganizationUserType.Custom; - organization.LimitCollectionCreationDeletion = limitCollectionCreationDeletion; organization.Permissions = new Permissions { EditAnyCollection = editAnyCollection, @@ -92,6 +97,8 @@ public class OrganizationUserAuthorizationHandlerTests ManageUsers = manageUsers }; + var organizationAbilities = ArrangeOrganizationAbilitiesDictionary(organization.Id, limitCollectionCreationDeletion); + var context = new AuthorizationHandlerContext( new[] { OrganizationUserOperations.ReadAll(organization.Id) }, new ClaimsPrincipal(), @@ -99,6 +106,7 @@ public class OrganizationUserAuthorizationHandlerTests sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().GetOrganizationAbilitiesAsync().Returns(organizationAbilities); await sutProvider.Sut.HandleAsync(context); @@ -116,7 +124,6 @@ public class OrganizationUserAuthorizationHandlerTests var actingUserId = Guid.NewGuid(); organization.Type = userType; - organization.LimitCollectionCreationDeletion = true; organization.Permissions = new Permissions { EditAnyCollection = false, @@ -125,6 +132,8 @@ public class OrganizationUserAuthorizationHandlerTests ManageUsers = false }; + var organizationAbilities = ArrangeOrganizationAbilitiesDictionary(organization.Id, true); + var context = new AuthorizationHandlerContext( new[] { OrganizationUserOperations.ReadAll(organization.Id) }, new ClaimsPrincipal(), @@ -132,6 +141,8 @@ public class OrganizationUserAuthorizationHandlerTests sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); + sutProvider.GetDependency().GetOrganizationAbilitiesAsync().Returns(organizationAbilities); + sutProvider.GetDependency().ProviderUserForOrgAsync(Arg.Any()).Returns(false); await sutProvider.Sut.HandleAsync(context); @@ -144,6 +155,8 @@ public class OrganizationUserAuthorizationHandlerTests Guid organizationId, SutProvider sutProvider) { + var organizationAbilities = ArrangeOrganizationAbilitiesDictionary(organizationId, true); + var context = new AuthorizationHandlerContext( new[] { OrganizationUserOperations.ReadAll(organizationId) }, new ClaimsPrincipal(), @@ -152,6 +165,8 @@ public class OrganizationUserAuthorizationHandlerTests sutProvider.GetDependency().UserId.Returns(userId); sutProvider.GetDependency().GetOrganization(Arg.Any()).Returns((CurrentContextOrganization)null); + sutProvider.GetDependency().GetOrganizationAbilitiesAsync().Returns(organizationAbilities); + sutProvider.GetDependency().ProviderUserForOrgAsync(Arg.Any()).Returns(false); await sutProvider.Sut.HandleAsync(context); Assert.False(context.HasSucceeded); @@ -191,4 +206,18 @@ public class OrganizationUserAuthorizationHandlerTests Assert.True(context.HasFailed); } + + private static Dictionary ArrangeOrganizationAbilitiesDictionary(Guid orgId, + bool limitCollectionCreationDeletion) + { + return new Dictionary + { + { orgId, + new OrganizationAbility + { + LimitCollectionCreationDeletion = limitCollectionCreationDeletion + } + } + }; + } } diff --git a/util/Migrator/DbScripts/2023-12-21_00_OrganizationAbilitiesWithCollectionManagementSettings.sql b/util/Migrator/DbScripts/2023-12-21_00_OrganizationAbilitiesWithCollectionManagementSettings.sql new file mode 100644 index 000000000..c06bfe8d2 --- /dev/null +++ b/util/Migrator/DbScripts/2023-12-21_00_OrganizationAbilitiesWithCollectionManagementSettings.sql @@ -0,0 +1,30 @@ +--Update stored procedure to include LimitCollectionCreationDeletion property +CREATE OR ALTER PROCEDURE [dbo].[Organization_ReadAbilities] +AS +BEGIN + SET NOCOUNT ON + + SELECT + [Id], + [UseEvents], + [Use2fa], + CASE + WHEN [Use2fa] = 1 AND [TwoFactorProviders] IS NOT NULL AND [TwoFactorProviders] != '{}' THEN + 1 + ELSE + 0 + END AS [Using2fa], + [UsersGetPremium], + [UseCustomPermissions], + [UseSso], + [UseKeyConnector], + [UseScim], + [UseResetPassword], + [UsePolicies], + [Enabled], + [LimitCollectionCreationDeletion], + [AllowAdminAccessToAllCollectionItems] + FROM + [dbo].[Organization] +END +GO