mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2026-02-23 15:14:49 +01:00
Plugins: Add authorization hook (#3977)
* Plugins: Add authorization hook Makes the `PolicyRequirement` available to plugins. Adds a filter hook to the authorization handlers, so that plugins can extend and leverage the existing authorization policies and permissions. * Update to pass back and forth handle class
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace BTCPayServer.Security;
|
||||
|
||||
public class AuthorizationFilterHandle
|
||||
{
|
||||
public AuthorizationHandlerContext Context { get; }
|
||||
public PolicyRequirement Requirement { get; }
|
||||
public HttpContext HttpContext { get; }
|
||||
public bool Success { get; }
|
||||
|
||||
public AuthorizationFilterHandle(
|
||||
AuthorizationHandlerContext context,
|
||||
PolicyRequirement requirement,
|
||||
HttpContext httpContext)
|
||||
{
|
||||
Context = context;
|
||||
Requirement = requirement;
|
||||
HttpContext = httpContext;
|
||||
}
|
||||
}
|
||||
@@ -84,6 +84,10 @@ namespace BTCPayServer.Client
|
||||
{
|
||||
return policy.StartsWith("btcpay.server", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
public static bool IsPluginPolicy(string policy)
|
||||
{
|
||||
return policy.StartsWith("btcpay.plugin", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
public class Permission
|
||||
{
|
||||
|
||||
@@ -132,13 +132,13 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private T GetController<T>() where T : ControllerBase
|
||||
{
|
||||
var authoverride = new AuthorizationService(new GreenfieldAuthorizationHandler(_httpContextAccessor,
|
||||
_serviceProvider.GetService<UserManager<ApplicationUser>>(),
|
||||
_serviceProvider.GetService<StoreRepository>()));
|
||||
_serviceProvider.GetService<StoreRepository>(),
|
||||
_serviceProvider.GetService<IPluginHookService>()));
|
||||
|
||||
var controller = _serviceProvider.GetService<T>();
|
||||
controller.ControllerContext.HttpContext = _httpContextAccessor.HttpContext;
|
||||
@@ -172,7 +172,6 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
_greenfieldAuthorizationHandler = greenfieldAuthorizationHandler;
|
||||
}
|
||||
|
||||
|
||||
public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource,
|
||||
IEnumerable<IAuthorizationRequirement> requirements)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Services.Apps;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
@@ -23,19 +23,22 @@ namespace BTCPayServer.Security
|
||||
private readonly AppService _appService;
|
||||
private readonly PaymentRequestRepository _paymentRequestRepository;
|
||||
private readonly InvoiceRepository _invoiceRepository;
|
||||
private readonly IPluginHookService _pluginHookService;
|
||||
|
||||
public CookieAuthorizationHandler(IHttpContextAccessor httpContextAccessor,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
StoreRepository storeRepository,
|
||||
AppService appService,
|
||||
InvoiceRepository invoiceRepository,
|
||||
PaymentRequestRepository paymentRequestRepository)
|
||||
PaymentRequestRepository paymentRequestRepository,
|
||||
IPluginHookService pluginHookService)
|
||||
{
|
||||
_httpContext = httpContextAccessor.HttpContext;
|
||||
_userManager = userManager;
|
||||
_appService = appService;
|
||||
_storeRepository = storeRepository;
|
||||
_invoiceRepository = invoiceRepository;
|
||||
_pluginHookService = pluginHookService;
|
||||
_paymentRequestRepository = paymentRequestRepository;
|
||||
}
|
||||
|
||||
@@ -144,6 +147,14 @@ namespace BTCPayServer.Security
|
||||
if (context.User != null)
|
||||
success = true;
|
||||
break;
|
||||
default:
|
||||
if (Policies.IsPluginPolicy(requirement.Policy))
|
||||
{
|
||||
var handle = (AuthorizationFilterHandle)await _pluginHookService.ApplyFilter("handle-authorization-requirement",
|
||||
new AuthorizationFilterHandle(context, requirement, _httpContext));
|
||||
success = handle.Success;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (success)
|
||||
@@ -151,7 +162,6 @@ namespace BTCPayServer.Security
|
||||
context.Succeed(requirement);
|
||||
if (!explicitResource)
|
||||
{
|
||||
|
||||
if (store != null)
|
||||
{
|
||||
_httpContext.SetStoreData(store);
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Newtonsoft.Json;
|
||||
using StoreData = BTCPayServer.Data.StoreData;
|
||||
|
||||
namespace BTCPayServer.Security.Greenfield
|
||||
@@ -21,14 +17,17 @@ namespace BTCPayServer.Security.Greenfield
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly IPluginHookService _pluginHookService;
|
||||
|
||||
public LocalGreenfieldAuthorizationHandler(IHttpContextAccessor httpContextAccessor,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
StoreRepository storeRepository)
|
||||
StoreRepository storeRepository,
|
||||
IPluginHookService pluginHookService)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_userManager = userManager;
|
||||
_storeRepository = storeRepository;
|
||||
_pluginHookService = pluginHookService;
|
||||
}
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PolicyRequirement requirement)
|
||||
{
|
||||
@@ -38,7 +37,8 @@ namespace BTCPayServer.Security.Greenfield
|
||||
var newUser = new ClaimsPrincipal(new ClaimsIdentity(context.User.Claims,
|
||||
$"{GreenfieldConstants.AuthenticationType}"));
|
||||
var newContext = new AuthorizationHandlerContext(context.Requirements, newUser, null);
|
||||
return new GreenfieldAuthorizationHandler(_httpContextAccessor, _userManager, _storeRepository).HandleAsync(newContext);
|
||||
return new GreenfieldAuthorizationHandler(
|
||||
_httpContextAccessor, _userManager, _storeRepository, _pluginHookService).HandleAsync(newContext);
|
||||
}
|
||||
|
||||
var succeed = context.User.Identity.AuthenticationType == $"Local{GreenfieldConstants.AuthenticationType}";
|
||||
@@ -50,21 +50,23 @@ namespace BTCPayServer.Security.Greenfield
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class GreenfieldAuthorizationHandler : AuthorizationHandler<PolicyRequirement>
|
||||
|
||||
{
|
||||
private readonly HttpContext _HttpContext;
|
||||
private readonly HttpContext _httpContext;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly IPluginHookService _pluginHookService;
|
||||
|
||||
public GreenfieldAuthorizationHandler(IHttpContextAccessor httpContextAccessor,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
StoreRepository storeRepository)
|
||||
StoreRepository storeRepository,
|
||||
IPluginHookService pluginHookService)
|
||||
{
|
||||
_HttpContext = httpContextAccessor.HttpContext;
|
||||
_httpContext = httpContextAccessor.HttpContext;
|
||||
_userManager = userManager;
|
||||
_storeRepository = storeRepository;
|
||||
_pluginHookService = pluginHookService;
|
||||
}
|
||||
|
||||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
|
||||
@@ -85,7 +87,7 @@ namespace BTCPayServer.Security.Greenfield
|
||||
switch (policy)
|
||||
{
|
||||
case { } when Policies.IsStorePolicy(policy):
|
||||
var storeId = _HttpContext.GetImplicitStoreId();
|
||||
var storeId = _httpContext.GetImplicitStoreId();
|
||||
// Specific store action
|
||||
if (storeId != null)
|
||||
{
|
||||
@@ -102,7 +104,7 @@ namespace BTCPayServer.Security.Greenfield
|
||||
break;
|
||||
}
|
||||
success = true;
|
||||
_HttpContext.SetStoreData(store);
|
||||
_httpContext.SetStoreData(store);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -118,7 +120,7 @@ namespace BTCPayServer.Security.Greenfield
|
||||
}
|
||||
if (!requiredUnscoped && permissionedStores.Count is 0)
|
||||
break;
|
||||
_HttpContext.SetStoresData(permissionedStores.ToArray());
|
||||
_httpContext.SetStoresData(permissionedStores.ToArray());
|
||||
success = true;
|
||||
}
|
||||
break;
|
||||
@@ -133,6 +135,11 @@ namespace BTCPayServer.Security.Greenfield
|
||||
success = true;
|
||||
}
|
||||
break;
|
||||
case { } when Policies.IsPluginPolicy(requirement.Policy):
|
||||
var handle = (AuthorizationFilterHandle)await _pluginHookService.ApplyFilter("handle-authorization-requirement",
|
||||
new AuthorizationFilterHandle(context, requirement, _httpContext));
|
||||
success = handle.Success;
|
||||
break;
|
||||
case Policies.CanManageNotificationsForUser:
|
||||
case Policies.CanViewNotificationsForUser:
|
||||
case Policies.CanModifyProfile:
|
||||
@@ -147,7 +154,7 @@ namespace BTCPayServer.Security.Greenfield
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
_HttpContext.Items[RequestedPermissionKey] = policy;
|
||||
_httpContext.Items[RequestedPermissionKey] = policy;
|
||||
}
|
||||
public const string RequestedPermissionKey = nameof(RequestedPermissionKey);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user