1
0
mirror of https://github.com/bitwarden/server.git synced 2025-04-05 13:08:17 -05:00

One handler per requirement

This commit is contained in:
Thomas Rittson 2025-03-21 14:48:49 +10:00
parent 366aac238f
commit 4dfc99dd85
No known key found for this signature in database
GPG Key ID: CDDDA03861C35E27
6 changed files with 71 additions and 53 deletions

View File

@ -7,7 +7,6 @@ using Bit.Core;
using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures; using Bit.Core.AdminConsole.OrganizationFeatures;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
@ -130,16 +129,10 @@ public class OrganizationUsersController : Controller
return response; return response;
} }
[OrganizationAuthorize<OrganizationMemberRequirement>]
[HttpGet("mini-details")] [HttpGet("mini-details")]
public async Task<ListResponseModel<OrganizationUserUserMiniDetailsResponseModel>> GetMiniDetails(Guid orgId) public async Task<ListResponseModel<OrganizationUserUserMiniDetailsResponseModel>> 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); var organizationUserUserDetails = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(orgId);
return new ListResponseModel<OrganizationUserUserMiniDetailsResponseModel>( return new ListResponseModel<OrganizationUserUserMiniDetailsResponseModel>(
organizationUserUserDetails.Select(ou => new OrganizationUserUserMiniDetailsResponseModel(ou))); organizationUserUserDetails.Select(ou => new OrganizationUserUserMiniDetailsResponseModel(ou)));

View File

@ -107,6 +107,6 @@ public static class ServiceCollectionExtensions
services.AddScoped<IAuthorizationHandler, SecurityTaskAuthorizationHandler>(); services.AddScoped<IAuthorizationHandler, SecurityTaskAuthorizationHandler>();
services.AddScoped<IAuthorizationHandler, SecurityTaskOrganizationAuthorizationHandler>(); services.AddScoped<IAuthorizationHandler, SecurityTaskOrganizationAuthorizationHandler>();
services.AddScoped<IAuthorizationHandler, AdminConsoleRequirementsHandler>(); services.AddScoped<IAuthorizationHandler, ManageUserRequirementHandler>();
} }
} }

View File

@ -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<IOrganizationRequirement>
{
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<bool> ManageUsersAsync(Guid organizationId, CurrentContextOrganization? organization)
=> organization is
{ Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or
{ Permissions.ManageUsers: true }
|| await currentContext.ProviderUserForOrgAsync(organizationId);
}

View File

@ -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<ManageUsersRequirement>(currentContext, httpContextAccessor)
{
private readonly ICurrentContext _currentContext = currentContext;
protected override async Task<bool> Authorize(Guid organizationId, CurrentContextOrganization? organizationClaims)
=> organizationClaims is { Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or
{ Permissions.ManageUsers: true }
|| await _currentContext.ProviderUserForOrgAsync(organizationId);
}

View File

@ -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<OrganizationMemberRequirement>(currentContext, httpContextAccessor)
{
protected override Task<bool> Authorize(Guid organizationId, CurrentContextOrganization? organizationClaims)
=> Task.FromResult(organizationClaims is not null);
}

View File

@ -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<T>(ICurrentContext currentContext, IHttpContextAccessor httpContextAccessor)
: AuthorizationHandler<T>
where T : IAuthorizationRequirement
{
protected abstract Task<bool> 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);
}
}
}