From 4dfc99dd85cf6cb923142ddd9c77daf7fbabbdfa Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Fri, 21 Mar 2025 14:48:49 +1000 Subject: [PATCH] One handler per requirement --- .../OrganizationUsersController.cs | 9 +--- .../Utilities/ServiceCollectionExtensions.cs | 2 +- .../AdminConsoleRequirementsHandler.cs | 44 ------------------- .../ManageUserRequirementHandler.cs | 21 +++++++++ .../OrganizationUserRequirementHandler.cs | 16 +++++++ .../OrganizationRequirementHandler.cs | 32 ++++++++++++++ 6 files changed, 71 insertions(+), 53 deletions(-) delete mode 100644 src/Core/AdminConsole/OrganizationFeatures/AdminConsoleRequirementsHandler.cs create mode 100644 src/Core/AdminConsole/OrganizationFeatures/ManageUserRequirementHandler.cs create mode 100644 src/Core/AdminConsole/OrganizationFeatures/OrganizationUserRequirementHandler.cs create mode 100644 src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/OrganizationRequirementHandler.cs diff --git a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs index 788efca03b..8a5d4f9009 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs @@ -7,7 +7,6 @@ using Bit.Core; using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; using Bit.Core.AdminConsole.OrganizationFeatures; -using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; using Bit.Core.AdminConsole.Repositories; @@ -130,16 +129,10 @@ public class OrganizationUsersController : Controller return response; } + [OrganizationAuthorize] [HttpGet("mini-details")] public async Task> GetMiniDetails(Guid orgId) { - var authorizationResult = await _authorizationService.AuthorizeAsync(User, new OrganizationScope(orgId), - OrganizationUserUserMiniDetailsOperations.ReadAll); - if (!authorizationResult.Succeeded) - { - throw new NotFoundException(); - } - var organizationUserUserDetails = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(orgId); return new ListResponseModel( organizationUserUserDetails.Select(ou => new OrganizationUserUserMiniDetailsResponseModel(ou))); diff --git a/src/Api/Utilities/ServiceCollectionExtensions.cs b/src/Api/Utilities/ServiceCollectionExtensions.cs index 16ae5ba829..41c086e81f 100644 --- a/src/Api/Utilities/ServiceCollectionExtensions.cs +++ b/src/Api/Utilities/ServiceCollectionExtensions.cs @@ -107,6 +107,6 @@ public static class ServiceCollectionExtensions services.AddScoped(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); } } diff --git a/src/Core/AdminConsole/OrganizationFeatures/AdminConsoleRequirementsHandler.cs b/src/Core/AdminConsole/OrganizationFeatures/AdminConsoleRequirementsHandler.cs deleted file mode 100644 index 4be6b567ac..0000000000 --- a/src/Core/AdminConsole/OrganizationFeatures/AdminConsoleRequirementsHandler.cs +++ /dev/null @@ -1,44 +0,0 @@ -#nullable enable - -using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; -using Bit.Core.Context; -using Bit.Core.Enums; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; - -namespace Bit.Core.AdminConsole.OrganizationFeatures; - -public class ManageUsersRequirement : IOrganizationRequirement; - -public class AdminConsoleRequirementsHandler(ICurrentContext currentContext, IHttpContextAccessor httpContextAccessor) - : AuthorizationHandler -{ - protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, - IOrganizationRequirement requirement) - { - var organizationId = httpContextAccessor.GetOrganizationId(); - if (organizationId is null) - { - return; - } - - var organization = currentContext.GetOrganization(organizationId.Value); - - var authorized = requirement switch - { - ManageUsersRequirement => await ManageUsersAsync(organizationId.Value, organization), - _ => false - }; - - if (authorized) - { - context.Succeed(requirement); - } - } - - private async Task ManageUsersAsync(Guid organizationId, CurrentContextOrganization? organization) - => organization is - { Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or - { Permissions.ManageUsers: true } - || await currentContext.ProviderUserForOrgAsync(organizationId); -} diff --git a/src/Core/AdminConsole/OrganizationFeatures/ManageUserRequirementHandler.cs b/src/Core/AdminConsole/OrganizationFeatures/ManageUserRequirementHandler.cs new file mode 100644 index 0000000000..1ade49f7de --- /dev/null +++ b/src/Core/AdminConsole/OrganizationFeatures/ManageUserRequirementHandler.cs @@ -0,0 +1,21 @@ +#nullable enable + +using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; +using Bit.Core.Context; +using Bit.Core.Enums; +using Microsoft.AspNetCore.Http; + +namespace Bit.Core.AdminConsole.OrganizationFeatures; + +public class ManageUsersRequirement : IOrganizationRequirement; + +public class ManageUserRequirementHandler(ICurrentContext currentContext, IHttpContextAccessor httpContextAccessor) + : OrganizationRequirementHandler(currentContext, httpContextAccessor) +{ + private readonly ICurrentContext _currentContext = currentContext; + + protected override async Task Authorize(Guid organizationId, CurrentContextOrganization? organizationClaims) + => organizationClaims is { Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or + { Permissions.ManageUsers: true } + || await _currentContext.ProviderUserForOrgAsync(organizationId); +} diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUserRequirementHandler.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUserRequirementHandler.cs new file mode 100644 index 0000000000..3437321963 --- /dev/null +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUserRequirementHandler.cs @@ -0,0 +1,16 @@ +#nullable enable + +using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; +using Bit.Core.Context; +using Microsoft.AspNetCore.Http; + +namespace Bit.Core.AdminConsole.OrganizationFeatures; + +public class OrganizationMemberRequirement : IOrganizationRequirement; + +public class OrganizationMemberRequirementHandler(ICurrentContext currentContext, IHttpContextAccessor httpContextAccessor) + : OrganizationRequirementHandler(currentContext, httpContextAccessor) +{ + protected override Task Authorize(Guid organizationId, CurrentContextOrganization? organizationClaims) + => Task.FromResult(organizationClaims is not null); +} diff --git a/src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/OrganizationRequirementHandler.cs b/src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/OrganizationRequirementHandler.cs new file mode 100644 index 0000000000..5b0c82832f --- /dev/null +++ b/src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/OrganizationRequirementHandler.cs @@ -0,0 +1,32 @@ +#nullable enable + +using Bit.Core.Context; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; + +namespace Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; + +public abstract class OrganizationRequirementHandler(ICurrentContext currentContext, IHttpContextAccessor httpContextAccessor) + : AuthorizationHandler + where T : IAuthorizationRequirement +{ + protected abstract Task Authorize(Guid organizationId, CurrentContextOrganization? organizationClaims); + + protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, T requirement) + { + var organizationId = httpContextAccessor.GetOrganizationId(); + if (organizationId is null) + { + return; + } + + var organization = currentContext.GetOrganization(organizationId.Value); + + var authorized = await Authorize(organizationId.Value, organization); + + if (authorized) + { + context.Succeed(requirement); + } + } +}