From fd8f36ad73b5e72926f78fe4ca173e7e64484b38 Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Wed, 26 Mar 2025 13:33:44 +1000 Subject: [PATCH] Cleanup and xmldocs --- .../OrganizationUsersController.cs | 4 ++-- .../Authorization/AuthorizeAttribute.cs | 21 +++++++++++++++++++ ...tribute.cs => IOrganizationRequirement.cs} | 17 ++++++--------- .../OrganizationRequirementHandler.cs | 9 +++++++- 4 files changed, 37 insertions(+), 14 deletions(-) create mode 100644 src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/AuthorizeAttribute.cs rename src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/{OrganizationAuthorizeAttribute.cs => IOrganizationRequirement.cs} (56%) diff --git a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs index 9809e09141..2ccc1b97db 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs @@ -137,7 +137,7 @@ public class OrganizationUsersController : Controller return response; } - [OrganizationAuthorize] + [Authorize] [HttpGet("mini-details")] public async Task> GetMiniDetails(Guid orgId) { @@ -147,7 +147,7 @@ public class OrganizationUsersController : Controller } [HttpGet("")] - [OrganizationAuthorize] + [Authorize] public async Task> Get(Guid orgId, bool includeGroups = false, bool includeCollections = false) { var organizationUsers = await _organizationUserUserDetailsQuery.GetOrganizationUserUserDetails( diff --git a/src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/AuthorizeAttribute.cs b/src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/AuthorizeAttribute.cs new file mode 100644 index 0000000000..93705e7fc1 --- /dev/null +++ b/src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/AuthorizeAttribute.cs @@ -0,0 +1,21 @@ +#nullable enable + +using Microsoft.AspNetCore.Authorization; + +namespace Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; + +/// +/// An attribute which requires authorization using the specified requirement. +/// This uses the standard ASP.NET authorization middleware. +/// +/// The IAuthorizationRequirement that will be used to authorize the user. +public class AuthorizeAttribute + : AuthorizeAttribute, IAuthorizationRequirementData + where T : IAuthorizationRequirement, new() +{ + public IEnumerable GetRequirements() + { + var requirement = new T(); + yield return requirement; + } +} diff --git a/src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/OrganizationAuthorizeAttribute.cs b/src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/IOrganizationRequirement.cs similarity index 56% rename from src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/OrganizationAuthorizeAttribute.cs rename to src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/IOrganizationRequirement.cs index 6ad4764a98..a2f2ff74e7 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/OrganizationAuthorizeAttribute.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/IOrganizationRequirement.cs @@ -5,19 +5,14 @@ using Microsoft.AspNetCore.Authorization; namespace Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; +/// +/// A requirement that implements this interface will be handled by , +/// which calls AuthorizeAsync with the organization details from the route. +/// This is used for simple role-based checks. +/// This may only be used on endpoints with {orgId} in their path. +/// public interface IOrganizationRequirement : IAuthorizationRequirement { // TODO: avoid injecting all of ICurrentContext? public Task AuthorizeAsync(Guid organizationId, CurrentContextOrganization? organizationClaims, ICurrentContext currentContext); } - -public class OrganizationAuthorizeAttribute - : AuthorizeAttribute, IAuthorizationRequirementData - where T : IOrganizationRequirement, new() -{ - public IEnumerable GetRequirements() - { - var requirement = new T(); - yield return requirement; - } -} diff --git a/src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/OrganizationRequirementHandler.cs b/src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/OrganizationRequirementHandler.cs index 85303c67a2..5a2402d9e7 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/OrganizationRequirementHandler.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/OrganizationRequirementHandler.cs @@ -6,6 +6,13 @@ using Microsoft.AspNetCore.Http; namespace Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; +/// +/// Handles any requirement that implements . +/// Retrieves the Organization ID from the route and then passes it to the requirement's AuthorizeAsync callback to +/// determine whether the action is authorized. +/// +/// +/// public class OrganizationRequirementHandler(ICurrentContext currentContext, IHttpContextAccessor httpContextAccessor) : AuthorizationHandler { @@ -14,7 +21,7 @@ public class OrganizationRequirementHandler(ICurrentContext currentContext, IHtt var organizationId = httpContextAccessor.GetOrganizationId(); if (organizationId is null) { - return; + throw new Exception("No organizationId found in route. IOrganizationRequirement cannot be used on this endpoint."); } var organization = currentContext.GetOrganization(organizationId.Value);