Move Bearer to Greenfield

This commit is contained in:
Dennis Reimann 2024-05-21 13:24:38 +02:00
parent f2b62bce8e
commit aafedd7ccb
No known key found for this signature in database
GPG key ID: 5009E1797F03F8D0
5 changed files with 77 additions and 20 deletions

View file

@ -3,10 +3,10 @@ namespace BTCPayServer.Abstractions.Constants
public class AuthenticationSchemes
{
public const string Cookie = "Identity.Application";
public const string Bearer = "Identity.Bearer";
public const string Bitpay = "Bitpay";
public const string Greenfield = "Greenfield.APIKeys,Greenfield.Basic";
public const string Greenfield = "Greenfield.APIKeys,Greenfield.Basic,Greenfield.Bearer";
public const string GreenfieldAPIKeys = "Greenfield.APIKeys";
public const string GreenfieldBasic = "Greenfield.Basic";
public const string GreenfieldBearer = "Greenfield.Bearer";
}
}

View file

@ -83,7 +83,7 @@ public class BlockHeaders : IEnumerable<RPCBlockHeader>
}
}
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Bearer)]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.GreenfieldBearer)]
public class BTCPayAppHub : Hub<IBTCPayAppHubClient>, IBTCPayAppHubServer
{
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;

View file

@ -6,14 +6,9 @@ using BTCPayApp.CommonServer;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Client;
using BTCPayServer.Common;
using BTCPayServer.Controllers;
using BTCPayServer.Data;
using BTCPayServer.Events;
using BTCPayServer.Security.Greenfield;
using BTCPayServer.Services;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Authentication.BearerToken;
using Microsoft.AspNetCore.Authorization;
@ -23,27 +18,19 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.Data;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using NBitcoin;
using NBitcoin.DataEncoders;
using NBXplorer;
using NicolasDorier.RateLimits;
namespace BTCPayServer.App;
[ApiController]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Bearer)]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.GreenfieldBearer)]
[Route("btcpayapp")]
public class BtcPayAppController(
APIKeyRepository apiKeyRepository,
StoreRepository storeRepository,
BTCPayNetworkProvider btcPayNetworkProvider,
IExplorerClientProvider explorerClientProvider,
EventAggregator eventAggregator,
SignInManager<ApplicationUser> signInManager,
UserManager<ApplicationUser> userManager,
TimeProvider timeProvider,
PaymentMethodHandlerDictionary handlers,
IFileService fileService,
ISettingsRepository settingsRepository,
UriResolver uriResolver,
IOptionsMonitor<BearerTokenOptions> bearerTokenOptions)
@ -130,7 +117,7 @@ public class BtcPayAppController(
return TypedResults.Problem(message, statusCode: 401);
}
signInManager.AuthenticationScheme = AuthenticationSchemes.Bearer;
signInManager.AuthenticationScheme = AuthenticationSchemes.GreenfieldBearer;
var signInResult = await signInManager.PasswordSignInAsync(login.Email, login.Password, true, true);
if (signInResult.RequiresTwoFactor)
{
@ -154,7 +141,7 @@ public class BtcPayAppController(
[RateLimitsFilter(ZoneLimits.Login, Scope = RateLimitsScope.RemoteAddress)]
public async Task<Results<Ok<AccessTokenResponse>, UnauthorizedHttpResult, SignInHttpResult, ChallengeHttpResult>> Refresh(RefreshRequest refresh)
{
const string scheme = AuthenticationSchemes.Bearer;
const string scheme = AuthenticationSchemes.GreenfieldBearer;
var authenticationTicket = bearerTokenOptions.Get(scheme).RefreshTokenProtector.Unprotect(refresh.RefreshToken);
var expiresUtc = authenticationTicket?.Properties.ExpiresUtc;

View file

@ -16,7 +16,7 @@ namespace BTCPayServer.Security
public static AuthenticationBuilder AddBearerAuthentication(this AuthenticationBuilder builder)
{
builder.AddBearerToken(AuthenticationSchemes.Bearer, options =>
builder.AddBearerToken(AuthenticationSchemes.GreenfieldBearer, options =>
{
options.BearerTokenExpiration = TimeSpan.FromMinutes(30.0);
options.RefreshTokenExpiration = TimeSpan.FromDays(3.0);

View file

@ -0,0 +1,70 @@
#nullable enable
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Client;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using AuthenticationSchemes = BTCPayServer.Abstractions.Constants.AuthenticationSchemes;
using StoreData = BTCPayServer.Client.Models.StoreData;
namespace BTCPayServer.Security.GreenField;
public class BearerAuthorizationHandler(IOptionsMonitor<IdentityOptions> identityOptions)
: AuthorizationHandler<PolicyRequirement>
{
//TODO: In the future, we will add these store permissions to actual aspnet roles, and remove this class.
private static readonly PermissionSet _serverAdminRolePermissions = new([Permission.Create(Policies.CanViewStoreSettings)]);
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PolicyRequirement requirement)
{
if (context.User.Identity?.AuthenticationType != AuthenticationSchemes.GreenfieldBearer)
return;
var userId = context.User.Claims.FirstOrDefault(c => c.Type == identityOptions.CurrentValue.ClaimsIdentity.UserIdClaimType)?.Value;
if (string.IsNullOrEmpty(userId))
return;
StoreData? store = null;
var success = false;
var isAdmin = context.User.IsInRole(Roles.ServerAdmin);
var storeId = context.Resource as string;
var policy = requirement.Policy;
var requiredUnscoped = false;
if (policy.EndsWith(':'))
{
policy = policy[..^1];
requiredUnscoped = true;
}
if (Policies.IsServerPolicy(policy) && isAdmin)
{
success = true;
}
else if (Policies.IsUserPolicy(policy) && userId is not null)
{
success = true;
}
else if (Policies.IsStorePolicy(policy))
{
if (isAdmin && storeId is not null)
{
success = _serverAdminRolePermissions.HasPermission(policy, storeId);
}
/*if (!success && store?.HasPermission(userId, policy) is true)
{
success = true;
}*/
if (!success && store is null && requiredUnscoped)
{
success = true;
}
}
if (success)
{
context.Succeed(requirement);
}
}
}