mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 22:14:26 +01:00
Feature: Plugin can extend whether an account can login or not
This commit is contained in:
@@ -5,6 +5,7 @@ using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Services;
|
||||
@@ -12,28 +13,19 @@ using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace BTCPayServer.Security.Greenfield
|
||||
{
|
||||
public class APIKeysAuthenticationHandler : AuthenticationHandler<GreenfieldAuthenticationOptions>
|
||||
public class APIKeysAuthenticationHandler(
|
||||
APIKeyRepository apiKeyRepository,
|
||||
IOptionsMonitor<IdentityOptions> identityOptions,
|
||||
IOptionsMonitor<GreenfieldAuthenticationOptions> options,
|
||||
ILoggerFactory logger,
|
||||
UrlEncoder encoder,
|
||||
UserService userService,
|
||||
UserManager<ApplicationUser> userManager)
|
||||
: AuthenticationHandler<GreenfieldAuthenticationOptions>(options, logger, encoder)
|
||||
{
|
||||
private readonly APIKeyRepository _apiKeyRepository;
|
||||
private readonly IOptionsMonitor<IdentityOptions> _identityOptions;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
|
||||
public APIKeysAuthenticationHandler(
|
||||
APIKeyRepository apiKeyRepository,
|
||||
IOptionsMonitor<IdentityOptions> identityOptions,
|
||||
IOptionsMonitor<GreenfieldAuthenticationOptions> options,
|
||||
ILoggerFactory logger,
|
||||
UrlEncoder encoder,
|
||||
UserManager<ApplicationUser> userManager) : base(options, logger, encoder)
|
||||
{
|
||||
_apiKeyRepository = apiKeyRepository;
|
||||
_identityOptions = identityOptions;
|
||||
_userManager = userManager;
|
||||
}
|
||||
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
|
||||
{
|
||||
// This one deserve some explanation...
|
||||
@@ -45,27 +37,25 @@ namespace BTCPayServer.Security.Greenfield
|
||||
// the login page.
|
||||
// But this isn't what we want when we call the API programmatically, instead we want an error 401 with a json error message.
|
||||
// This hack modify a request's header to trick the CookieAuthenticationHandler to not do a redirection.
|
||||
if (!Request.Headers.Accept.Any(s => s is string && s.StartsWith("text/html", StringComparison.OrdinalIgnoreCase)))
|
||||
if (!Request.Headers.Accept.Any(s => s != null && s.StartsWith("text/html", StringComparison.OrdinalIgnoreCase)))
|
||||
Request.Headers.XRequestedWith = new Microsoft.Extensions.Primitives.StringValues("XMLHttpRequest");
|
||||
return base.HandleChallengeAsync(properties);
|
||||
}
|
||||
private bool IsJson(string contentType)
|
||||
{
|
||||
return contentType?.StartsWith("application/json", StringComparison.OrdinalIgnoreCase) is true;
|
||||
}
|
||||
|
||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
if (!Context.Request.HttpContext.GetAPIKey(out var apiKey) || string.IsNullOrEmpty(apiKey))
|
||||
return AuthenticateResult.NoResult();
|
||||
|
||||
var key = await _apiKeyRepository.GetKey(apiKey, true);
|
||||
if (!UserService.TryCanLogin(key?.User, out var error))
|
||||
var key = await apiKeyRepository.GetKey(apiKey, true);
|
||||
var loggingContext = new UserService.CanLoginContext(key?.User, baseUrl: Request.GetRequestBaseUrl());
|
||||
if (!await userService.CanLogin(loggingContext) || key is null)
|
||||
{
|
||||
return AuthenticateResult.Fail($"ApiKey authentication failed: {error}");
|
||||
return AuthenticateResult.Fail($"ApiKey authentication failed: {loggingContext.Failures[0].Text.Value}");
|
||||
}
|
||||
|
||||
var claims = new List<Claim> { new (_identityOptions.CurrentValue.ClaimsIdentity.UserIdClaimType, key.UserId) };
|
||||
claims.AddRange((await _userManager.GetRolesAsync(key.User)).Select(s => new Claim(_identityOptions.CurrentValue.ClaimsIdentity.RoleClaimType, s)));
|
||||
var claims = new List<Claim> { new (identityOptions.CurrentValue.ClaimsIdentity.UserIdClaimType, key.UserId) };
|
||||
claims.AddRange((await userManager.GetRolesAsync(key.User)).Select(s => new Claim(identityOptions.CurrentValue.ClaimsIdentity.RoleClaimType, s)));
|
||||
claims.AddRange(Permission.ToPermissions(key.GetBlob()?.Permissions ?? Array.Empty<string>()).Select(permission =>
|
||||
new Claim(GreenfieldConstants.ClaimTypes.Permission, permission.ToString())));
|
||||
return AuthenticateResult.Success(new AuthenticationTicket(
|
||||
|
||||
Reference in New Issue
Block a user