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

abstract class implements handle

This commit is contained in:
Thomas Rittson 2025-03-21 13:59:58 +10:00
parent dad05fca77
commit b5d1c3d2b4
No known key found for this signature in database
GPG Key ID: CDDDA03861C35E27
5 changed files with 87 additions and 68 deletions

View File

@ -146,7 +146,7 @@ public class OrganizationUsersController : Controller
}
[HttpGet("")]
[ManageUsersRequirement]
[OrganizationAuthorize(typeof(ManageUsersRequirement))]
public async Task<ListResponseModel<OrganizationUserUserDetailsResponseModel>> Get(Guid orgId, bool includeGroups = false, bool includeCollections = false)
{
var organizationUsers = await _organizationUserUserDetailsQuery.GetOrganizationUserUserDetails(

View File

@ -0,0 +1,32 @@
#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 AdminConsoleRequirementsHandler(ICurrentContext currentContext, IHttpContextAccessor httpContextAccessor)
: OrganizationRequirementHandler(currentContext, httpContextAccessor)
{
protected override async Task<bool> HandleOrganizationRequirementAsync(IOrganizationRequirement requirement,
Guid organizationId, CurrentContextOrganization? organization)
{
var authorized = requirement switch
{
ManageUsersRequirement => await ManageUsersAsync(organizationId, organization),
_ => false
};
return authorized;
}
private async Task<bool> ManageUsersAsync(Guid organizationId, CurrentContextOrganization? organization)
=> organization is
{ Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or
{ Permissions.ManageUsers: true }
|| await IsProviderForOrganizationAsync(organizationId);
}

View File

@ -1,67 +0,0 @@
#nullable enable
using Bit.Core.Context;
using Bit.Core.Enums;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
namespace Bit.Core.AdminConsole.OrganizationFeatures;
public abstract class OrganizationRequirementAttribute
: AuthorizeAttribute, IAuthorizationRequirement, IAuthorizationRequirementData
{
public IEnumerable<IAuthorizationRequirement> GetRequirements() => [this];
}
public abstract class OrganizationRequirementHandler : AuthorizationHandler<OrganizationRequirementAttribute>
{
protected Guid? OrganizationId { get; set; }
protected CurrentContextOrganization? Organization { get; set; }
protected OrganizationRequirementHandler(ICurrentContext currentContext, IHttpContextAccessor httpContextAccessor)
{
if (httpContextAccessor.HttpContext is null)
{
return;
}
httpContextAccessor.HttpContext.GetRouteData().Values.TryGetValue("orgId", out var orgIdParam);
if (!Guid.TryParse(orgIdParam?.ToString(), out var orgId))
{
// No orgId supplied, unable to authorize
return;
}
OrganizationId = orgId;
if (OrganizationId.HasValue)
{
Organization = currentContext.GetOrganization(OrganizationId.Value);
}
}
}
public class ManageUsersRequirementAttribute : OrganizationRequirementAttribute;
public class AdminConsoleRequirementsHandler(ICurrentContext currentContext, IHttpContextAccessor httpContextAccessor)
: OrganizationRequirementHandler(currentContext, httpContextAccessor)
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
OrganizationRequirementAttribute requirement)
{
var authorized = requirement switch
{
ManageUsersRequirementAttribute => Organization is
{ Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or
{ Permissions.ManageUsers: true },
_ => false
};
if (authorized)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}

View File

@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Authorization;
namespace Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization;
public class OrganizationAuthorizeAttribute(Type requirementType)
: AuthorizeAttribute, IAuthorizationRequirementData
{
public IEnumerable<IAuthorizationRequirement> GetRequirements()
{
var requirement = (IOrganizationRequirement)Activator.CreateInstance(requirementType)!;
yield return requirement;
}
}

View File

@ -0,0 +1,41 @@
#nullable enable
using Bit.Core.Context;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
namespace Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization;
public interface IOrganizationRequirement : IAuthorizationRequirement;
public abstract class OrganizationRequirementHandler(ICurrentContext currentContext, IHttpContextAccessor httpContextAccessor) : AuthorizationHandler<IOrganizationRequirement>
{
protected abstract Task<bool> HandleOrganizationRequirementAsync(IOrganizationRequirement requirement, Guid organizationId, CurrentContextOrganization? organization);
protected async Task<bool> IsProviderForOrganizationAsync(Guid organizationId) =>
await currentContext.ProviderUserForOrgAsync(organizationId);
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, IOrganizationRequirement requirement)
{
if (httpContextAccessor.HttpContext is null)
{
return;
}
httpContextAccessor.HttpContext.GetRouteData().Values.TryGetValue("orgId", out var orgIdParam);
if (!Guid.TryParse(orgIdParam?.ToString(), out var orgId))
{
// No orgId supplied, unable to authorize
return;
}
var organization = currentContext.GetOrganization(orgId);
var authorized = await HandleOrganizationRequirementAsync(requirement, orgId, organization);
if (authorized)
{
context.Succeed(requirement);
}
}
}