add basic auth for greenfield

This commit is contained in:
Kukks
2020-03-20 14:05:23 +01:00
parent 478b1463ff
commit 9d99c32305
10 changed files with 129 additions and 35 deletions

View File

@@ -6,14 +6,13 @@ using BTCPayServer.Data;
using BTCPayServer.Security;
using BTCPayServer.Security.APIKeys;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
namespace BTCPayServer.Controllers.RestApi
{
[ApiController]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.ApiKey)]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.ApiKeyOrBasic)]
public class ApiKeysController : ControllerBase
{
private readonly APIKeyRepository _apiKeyRepository;
@@ -34,7 +33,7 @@ namespace BTCPayServer.Controllers.RestApi
}
[HttpDelete("~/api/v1/api-keys/current")]
[Authorize(Policy = Policies.Unrestricted, AuthenticationSchemes = AuthenticationSchemes.ApiKey)]
[Authorize(Policy = Policies.Unrestricted, AuthenticationSchemes = AuthenticationSchemes.ApiKeyOrBasic)]
public async Task<ActionResult<ApiKeyData>> RevokeKey()
{
ControllerContext.HttpContext.GetAPIKey(out var apiKey);

View File

@@ -2,7 +2,6 @@ using System.Threading.Tasks;
using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Security;
using BTCPayServer.Security.APIKeys;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
@@ -15,7 +14,7 @@ namespace BTCPayServer.Controllers.RestApi
/// </summary>
[Route("api/test/apikey")]
[ApiController]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.ApiKey)]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.ApiKeyOrBasic)]
public class TestApiKeyController : ControllerBase
{
private readonly UserManager<ApplicationUser> _userManager;
@@ -28,28 +27,28 @@ namespace BTCPayServer.Controllers.RestApi
}
[HttpGet("me/id")]
[Authorize(Policy = Policies.CanViewProfile, AuthenticationSchemes = AuthenticationSchemes.ApiKey)]
[Authorize(Policy = Policies.CanViewProfile, AuthenticationSchemes = AuthenticationSchemes.ApiKeyOrBasic)]
public string GetCurrentUserId()
{
return _userManager.GetUserId(User);
}
[HttpGet("me")]
[Authorize(Policy = Policies.CanViewProfile, AuthenticationSchemes = AuthenticationSchemes.ApiKey)]
[Authorize(Policy = Policies.CanViewProfile, AuthenticationSchemes = AuthenticationSchemes.ApiKeyOrBasic)]
public async Task<ApplicationUser> GetCurrentUser()
{
return await _userManager.GetUserAsync(User);
}
[HttpGet("me/is-admin")]
[Authorize(Policy = Policies.CanModifyServerSettings, AuthenticationSchemes = AuthenticationSchemes.ApiKey)]
[Authorize(Policy = Policies.CanModifyServerSettings, AuthenticationSchemes = AuthenticationSchemes.ApiKeyOrBasic)]
public bool AmIAnAdmin()
{
return true;
}
[HttpGet("me/stores")]
[Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.ApiKey)]
[Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.ApiKeyOrBasic)]
public StoreData[] GetCurrentUserStores()
{
return this.HttpContext.GetStoresData();
@@ -57,7 +56,7 @@ namespace BTCPayServer.Controllers.RestApi
[HttpGet("me/stores/{storeId}/can-view")]
[Authorize(Policy = Policies.CanViewStoreSettings,
AuthenticationSchemes = AuthenticationSchemes.ApiKey)]
AuthenticationSchemes = AuthenticationSchemes.ApiKeyOrBasic)]
public bool CanViewStore(string storeId)
{
return true;
@@ -65,7 +64,7 @@ namespace BTCPayServer.Controllers.RestApi
[HttpGet("me/stores/{storeId}/can-edit")]
[Authorize(Policy = Policies.CanModifyStoreSettings,
AuthenticationSchemes = AuthenticationSchemes.ApiKey)]
AuthenticationSchemes = AuthenticationSchemes.ApiKeyOrBasic)]
public bool CanEditStore(string storeId)
{
return true;

View File

@@ -1,6 +1,4 @@
using System;
using Microsoft.Extensions.Logging;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -13,7 +11,6 @@ using BTCPayServer.Security;
using BTCPayServer.Security.APIKeys;
using BTCPayServer.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
@@ -23,7 +20,7 @@ using BTCPayServer.Client;
namespace BTCPayServer.Controllers.RestApi
{
[ApiController]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.ApiKey)]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.ApiKeyOrBasic)]
public class UsersController : ControllerBase
{
private readonly UserManager<ApplicationUser> _userManager;
@@ -40,8 +37,8 @@ namespace BTCPayServer.Controllers.RestApi
RoleManager<IdentityRole> roleManager, SettingsRepository settingsRepository,
EventAggregator eventAggregator,
IPasswordValidator<ApplicationUser> passwordValidator,
NicolasDorier.RateLimits.RateLimitService throttleService,
Configuration.BTCPayServerOptions options,
RateLimitService throttleService,
BTCPayServerOptions options,
IAuthorizationService authorizationService)
{
_userManager = userManager;
@@ -55,7 +52,7 @@ namespace BTCPayServer.Controllers.RestApi
_authorizationService = authorizationService;
}
[Authorize(Policy = Policies.CanViewProfile, AuthenticationSchemes = AuthenticationSchemes.ApiKey)]
[Authorize(Policy = Policies.CanViewProfile, AuthenticationSchemes = AuthenticationSchemes.ApiKeyOrBasic)]
[HttpGet("~/api/v1/users/me")]
public async Task<ActionResult<ApplicationUserData>> GetCurrentUser()
{
@@ -85,7 +82,7 @@ namespace BTCPayServer.Controllers.RestApi
// Even if subscription are unlocked, it is forbidden to create admin unauthenticated
if (anyAdmin && request.IsAdministrator is true && !isAuth)
return Forbid(AuthenticationSchemes.ApiKey);
return Forbid(AuthenticationSchemes.ApiKeyOrBasic);
// You are de-facto admin if there is no other admin, else you need to be auth and pass policy requirements
bool isAdmin = anyAdmin ? (await _authorizationService.AuthorizeAsync(User, null, new PolicyRequirement(Policies.CanModifyServerSettings))).Succeeded
&& (await _authorizationService.AuthorizeAsync(User, null, new PolicyRequirement(Policies.Unrestricted))).Succeeded
@@ -93,14 +90,14 @@ namespace BTCPayServer.Controllers.RestApi
: true;
// You need to be admin to create an admin
if (request.IsAdministrator is true && !isAdmin)
return Forbid(AuthenticationSchemes.ApiKey);
return Forbid(AuthenticationSchemes.ApiKeyOrBasic);
if (!isAdmin && policies.LockSubscription)
{
// If we are not admin and subscriptions are locked, we need to check the Policies.CanCreateUser.Key permission
var canCreateUser = (await _authorizationService.AuthorizeAsync(User, null, new PolicyRequirement(Policies.CanCreateUser))).Succeeded;
if (!isAuth || !canCreateUser)
return Forbid(AuthenticationSchemes.ApiKey);
return Forbid(AuthenticationSchemes.ApiKeyOrBasic);
}
var user = new ApplicationUser

View File

@@ -286,7 +286,8 @@ namespace BTCPayServer.Hosting
services.AddAuthentication()
.AddCookie()
.AddBitpayAuthentication()
.AddAPIKeyAuthentication();
.AddAPIKeyAuthentication()
.AddBasicAuthentication();
}
public static IApplicationBuilder UsePayServer(this IApplicationBuilder app)

View File

@@ -1,15 +1,10 @@
using System;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Security.Bitpay;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Primitives;

View File

@@ -5,5 +5,7 @@
public const string Cookie = "Identity.Application";
public const string Bitpay = "Bitpay";
public const string ApiKey = "GreenfieldApiKey";
public const string Basic = "Basic";
public const string ApiKeyOrBasic = "Basic,GreenfieldApiKey";
}
}

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Security.APIKeys;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace BTCPayServer.Security.Basic
{
public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions>
{
private readonly IOptionsMonitor<IdentityOptions> _identityOptions;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly UserManager<ApplicationUser> _userManager;
public BasicAuthenticationHandler(
IOptionsMonitor<IdentityOptions> identityOptions,
SignInManager<ApplicationUser> signInManager,
UserManager<ApplicationUser> userManager,
IOptionsMonitor<BasicAuthenticationOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock) : base(options, logger, encoder, clock)
{
_identityOptions = identityOptions;
_signInManager = signInManager;
_userManager = userManager;
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
string authHeader = Context.Request.Headers["Authorization"];
if (authHeader == null || !authHeader.StartsWith("Basic ")) return AuthenticateResult.NoResult();
var encodedUsernamePassword = authHeader.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries)[1]?.Trim();
var decodedUsernamePassword = Encoding.UTF8.GetString(Convert.FromBase64String(encodedUsernamePassword)).Split(':');
var username = decodedUsernamePassword[0];
var password = decodedUsernamePassword[1];
var result = await _signInManager.PasswordSignInAsync(username, password, true, true);
if (!result.Succeeded) return AuthenticateResult.Fail(result.ToString());
var user = await _userManager.FindByNameAsync(username);
var claims = new List<Claim>()
{
new Claim(_identityOptions.CurrentValue.ClaimsIdentity.UserIdClaimType, user.Id),
new Claim(APIKeyConstants.ClaimTypes.Permission, Permission.Create(Policies.Unrestricted).ToString())
};
return AuthenticateResult.Success(new AuthenticationTicket(
new ClaimsPrincipal(new ClaimsIdentity(claims, APIKeyConstants.AuthenticationType)), APIKeyConstants.AuthenticationType));
}
}
}

View File

@@ -0,0 +1,8 @@
using Microsoft.AspNetCore.Authentication;
namespace BTCPayServer.Security.Basic
{
public class BasicAuthenticationOptions : AuthenticationSchemeOptions
{
}
}

View File

@@ -0,0 +1,17 @@
using BTCPayServer.Security.Basic;
using Microsoft.AspNetCore.Authentication;
namespace BTCPayServer.Security.APIKeys
{
public static class BasicExtensions
{
public static AuthenticationBuilder AddBasicAuthentication(this AuthenticationBuilder builder)
{
builder.AddScheme<BasicAuthenticationOptions, BasicAuthenticationHandler>(AuthenticationSchemes.Basic,
o => { });
return builder;
}
}
}

View File

@@ -5,8 +5,7 @@
"description": "A full API to use your BTCPay Server",
"contact": {
"name": "BTCPay Server",
"url": "https://btcpayserver.org",
"email": "nicolas.dorier@gmail.com"
"url": "https://btcpayserver.org"
},
"version": "v1"
},
@@ -105,7 +104,8 @@
{
"GreenField Authentication": [
"btcpay.user.canviewprofile"
]
],
"Basic": []
}
]
}
@@ -182,7 +182,8 @@
{
"GreenField Authentication": [
"btcpay.server.cancreateuser"
]
],
"Basic": []
}
]
}
@@ -208,7 +209,8 @@
},
"security": [
{
"GreenField Authentication": []
"GreenField Authentication": [],
"Basic": []
}
]
},
@@ -232,7 +234,8 @@
},
"security": [
{
"GreenField Authentication": [ "unrestricted" ]
"GreenField Authentication": [ "unrestricted" ],
"Basic": []
}
]
}
@@ -347,9 +350,17 @@
"securitySchemes": {
"GreenField Authentication": {
"type": "apiKey",
"description": "BTCPay Server supports authenticating and authorizing users through an API Key that is generated by them. Send the API Key as a header value to Authorization with the format: token {token}. For a smoother experience, you can generate a url that redirects users to an API key creation screen.\n\n The following permissions applies to the context of the user creating the API Key:\n * `unrestricted`: Allow unrestricted access to your account.\n * `btcpay.server.canmodifyserversettings`: Allow total control on the server settings. (only if user is administrator)\n * `btcpay.server.cancreateuser`: Allow the creation new users on this server. (only if user is administrator)\n * `btcpay.user.canviewprofile`: Allow view access to your user profile.\n * `btcpay.user.canmodifyprofile`: Allow view and modification access to your user profile.\n\nThe following permissions applies to all stores of the user, you can limit to a specific store with the following format: `btcpay.store.cancreateinvoice:6HSHAEU4iYWtjxtyRs9KyPjM9GAQp8kw2T9VWbGG1FnZ`:\n * `btcpay.store.canviewstoresettings`: Allow view access to the stores settings. \n * `btcpay.store.canmodifystoresettings`: Allow view and modification access to the stores settings.\n * `btcpay.store.cancreateinvoice`: Allow invoice creation of the store.\n\nNote that API Keys only limits permission of a user and can never expand it. If an API Key has the permission `btcpay.server.canmodifyserversettings` but that the user account creating this API Key is not administrator, the API Key will not be able to modify the server settings.\n",
"description": "BTCPay Server supports authenticating and authorizing users through an API Key that is generated by them. Send the API Key as a header value to Authorization with the format: `token {token}`. For a smoother experience, you can generate a url that redirects users to an API key creation screen.\n\n The following permissions applies to the context of the user creating the API Key:\n * `unrestricted`: Allow unrestricted access to your account.\n * `btcpay.server.canmodifyserversettings`: Allow total control on the server settings. (only if user is administrator)\n * `btcpay.server.cancreateuser`: Allow the creation of new users on this server. (only if user is an administrator)\n * `btcpay.user.canviewprofile`: Allow view access to your user profile.\n * `btcpay.user.canmodifyprofile`: Allow view and modification access to your user profile.\n\nThe following permissions applies to all stores of the user, you can limit to a specific store with the following format: `btcpay.store.cancreateinvoice:6HSHAEU4iYWtjxtyRs9KyPjM9GAQp8kw2T9VWbGG1FnZ`:\n * `btcpay.store.canviewstoresettings`: Allow view access to the stores settings. \n * `btcpay.store.canmodifystoresettings`: Allow view and modification access to the stores settings.\n * `btcpay.store.cancreateinvoice`: Allow invoice creation of the store.\n\nNote that API Keys only limits permission of a user and can never expand it. If an API Key has the permission `btcpay.server.canmodifyserversettings` but that the user account creating this API Key is not administrator, the API Key will not be able to modify the server settings.\n",
"name": "Authorization",
"in": "header"
"in": "header",
"scheme": "token"
},
"Basic": {
"type": "http",
"description": "BTCPay Server supports authenticating and authorizing users through the Basic HTTP authentication scheme. Send the user and password encoded in base64 with the format `Basic {base64(username:password)}`. Using this authentication method implicitly provides you with the `unrestricted` permission",
"name": "Authorization",
"in": "header",
"scheme": "Basic"
}
}
},