From 76818fa3856aee2a9058069099da35ea499e76f5 Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Fri, 27 Mar 2020 12:44:21 +0900 Subject: [PATCH 1/5] Rename API Keys folder to GreenField --- .../APIKeys/APIKeyAuthenticationHandler.cs | 100 ------------------ .../APIKeys/APIKeyAuthenticationOptions.cs | 8 -- .../APIKeys/APIKeyAuthorizationHandler.cs | 96 ----------------- .../Security/APIKeys/APIKeyConstants.cs | 15 --- .../Security/APIKeys/APIKeyExtensions.cs | 64 ----------- .../Security/APIKeys/APIKeyRepository.cs | 72 ------------- 6 files changed, 355 deletions(-) delete mode 100644 BTCPayServer/Security/APIKeys/APIKeyAuthenticationHandler.cs delete mode 100644 BTCPayServer/Security/APIKeys/APIKeyAuthenticationOptions.cs delete mode 100644 BTCPayServer/Security/APIKeys/APIKeyAuthorizationHandler.cs delete mode 100644 BTCPayServer/Security/APIKeys/APIKeyConstants.cs delete mode 100644 BTCPayServer/Security/APIKeys/APIKeyExtensions.cs delete mode 100644 BTCPayServer/Security/APIKeys/APIKeyRepository.cs diff --git a/BTCPayServer/Security/APIKeys/APIKeyAuthenticationHandler.cs b/BTCPayServer/Security/APIKeys/APIKeyAuthenticationHandler.cs deleted file mode 100644 index bfb9b4c7e..000000000 --- a/BTCPayServer/Security/APIKeys/APIKeyAuthenticationHandler.cs +++ /dev/null @@ -1,100 +0,0 @@ -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.Bitpay; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace BTCPayServer.Security.APIKeys -{ - public class APIKeyAuthenticationHandler : AuthenticationHandler - { - private readonly APIKeyRepository _apiKeyRepository; - private readonly IOptionsMonitor _identityOptions; - private readonly SignInManager _signInManager; - private readonly UserManager _userManager; - - public APIKeyAuthenticationHandler( - APIKeyRepository apiKeyRepository, - IOptionsMonitor identityOptions, - IOptionsMonitor options, - ILoggerFactory logger, - UrlEncoder encoder, - ISystemClock clock, - SignInManager signInManager, - UserManager userManager) : base(options, logger, encoder, clock) - { - _apiKeyRepository = apiKeyRepository; - _identityOptions = identityOptions; - _signInManager = signInManager; - _userManager = userManager; - } - - protected override async Task HandleAuthenticateAsync() - { - var res = await HandleApiKeyAuthenticateResult(); - if (res.None) - { - return await HandleBasicAuthenticateAsync(); - } - - return res; - } - - private async Task HandleApiKeyAuthenticateResult() - { - if (!Context.Request.HttpContext.GetAPIKey(out var apiKey) || string.IsNullOrEmpty(apiKey)) - return AuthenticateResult.NoResult(); - - var key = await _apiKeyRepository.GetKey(apiKey); - - if (key == null) - { - return AuthenticateResult.Fail("ApiKey authentication failed"); - } - - List claims = new List(); - claims.Add(new Claim(_identityOptions.CurrentValue.ClaimsIdentity.UserIdClaimType, key.UserId)); - claims.AddRange(Permission.ToPermissions(key.Permissions).Select(permission => - new Claim(APIKeyConstants.ClaimTypes.Permission, permission.ToString()))); - return AuthenticateResult.Success(new AuthenticationTicket( - new ClaimsPrincipal(new ClaimsIdentity(claims, APIKeyConstants.AuthenticationType)), - APIKeyConstants.AuthenticationType)); - } - - private async Task HandleBasicAuthenticateAsync() - { - string authHeader = Context.Request.Headers["Authorization"]; - - if (authHeader == null || !authHeader.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase)) 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() - { - 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)); - } - } -} diff --git a/BTCPayServer/Security/APIKeys/APIKeyAuthenticationOptions.cs b/BTCPayServer/Security/APIKeys/APIKeyAuthenticationOptions.cs deleted file mode 100644 index d03e796cd..000000000 --- a/BTCPayServer/Security/APIKeys/APIKeyAuthenticationOptions.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Microsoft.AspNetCore.Authentication; - -namespace BTCPayServer.Security.Bitpay -{ - public class APIKeyAuthenticationOptions : AuthenticationSchemeOptions - { - } -} diff --git a/BTCPayServer/Security/APIKeys/APIKeyAuthorizationHandler.cs b/BTCPayServer/Security/APIKeys/APIKeyAuthorizationHandler.cs deleted file mode 100644 index 2b5b147e0..000000000 --- a/BTCPayServer/Security/APIKeys/APIKeyAuthorizationHandler.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; -using BTCPayServer.Client; -using BTCPayServer.Data; -using BTCPayServer.Services.Stores; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; - -namespace BTCPayServer.Security.APIKeys -{ - public class APIKeyAuthorizationHandler : AuthorizationHandler - - { - private readonly HttpContext _HttpContext; - private readonly UserManager _userManager; - private readonly StoreRepository _storeRepository; - - public APIKeyAuthorizationHandler(IHttpContextAccessor httpContextAccessor, - UserManager userManager, - StoreRepository storeRepository) - { - _HttpContext = httpContextAccessor.HttpContext; - _userManager = userManager; - _storeRepository = storeRepository; - } - - protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, - PolicyRequirement requirement) - { - if (context.User.Identity.AuthenticationType != APIKeyConstants.AuthenticationType) - return; - - bool success = false; - switch (requirement.Policy) - { - case Policies.CanModifyProfile: - case Policies.CanViewProfile: - case Policies.Unrestricted: - success = context.HasPermission(Permission.Create(requirement.Policy)); - break; - - case Policies.CanViewStoreSettings: - case Policies.CanModifyStoreSettings: - var storeId = _HttpContext.GetImplicitStoreId(); - var userid = _userManager.GetUserId(context.User); - // Specific store action - if (storeId != null) - { - if (context.HasPermission(Permission.Create(requirement.Policy, storeId))) - { - if (string.IsNullOrEmpty(userid)) - break; - var store = await _storeRepository.FindStore((string)storeId, userid); - if (store == null) - break; - success = true; - _HttpContext.SetStoreData(store); - } - } - else - { - var stores = await _storeRepository.GetStoresByUserId(userid); - List permissionedStores = new List(); - foreach (var store in stores) - { - if (context.HasPermission(Permission.Create(requirement.Policy, store.Id))) - permissionedStores.Add(store); - } - _HttpContext.SetStoresData(stores.ToArray()); - success = true; - } - break; - case Policies.CanCreateUser: - case Policies.CanModifyServerSettings: - if (context.HasPermission(Permission.Create(requirement.Policy))) - { - var user = await _userManager.GetUserAsync(context.User); - if (user == null) - break; - if (!await _userManager.IsInRoleAsync(user, Roles.ServerAdmin)) - break; - success = true; - } - break; - } - - if (success) - { - context.Succeed(requirement); - } - } - } -} diff --git a/BTCPayServer/Security/APIKeys/APIKeyConstants.cs b/BTCPayServer/Security/APIKeys/APIKeyConstants.cs deleted file mode 100644 index 3ec317a53..000000000 --- a/BTCPayServer/Security/APIKeys/APIKeyConstants.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; -using BTCPayServer.Client; - -namespace BTCPayServer.Security.APIKeys -{ - public static class APIKeyConstants - { - public const string AuthenticationType = "APIKey"; - - public static class ClaimTypes - { - public const string Permission = "APIKey.Permission"; - } - } -} diff --git a/BTCPayServer/Security/APIKeys/APIKeyExtensions.cs b/BTCPayServer/Security/APIKeys/APIKeyExtensions.cs deleted file mode 100644 index 8671b6992..000000000 --- a/BTCPayServer/Security/APIKeys/APIKeyExtensions.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Linq; -using BTCPayServer.Client; -using BTCPayServer.Security.Bitpay; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Primitives; - -namespace BTCPayServer.Security.APIKeys -{ - public static class APIKeyExtensions - { - public static bool GetAPIKey(this HttpContext httpContext, out StringValues apiKey) - { - if (httpContext.Request.Headers.TryGetValue("Authorization", out var value) && - value.ToString().StartsWith("token ", StringComparison.InvariantCultureIgnoreCase)) - { - apiKey = value.ToString().Substring("token ".Length); - return true; - } - - return false; - } - - public static AuthenticationBuilder AddAPIKeyAuthentication(this AuthenticationBuilder builder) - { - builder.AddScheme(AuthenticationSchemes.Greenfield, - o => { }); - return builder; - } - - public static IServiceCollection AddAPIKeyAuthentication(this IServiceCollection serviceCollection) - { - serviceCollection.AddSingleton(); - serviceCollection.AddScoped(); - return serviceCollection; - } - - public static string[] GetPermissions(this AuthorizationHandlerContext context) - { - return context.User.Claims.Where(c => - c.Type.Equals(APIKeyConstants.ClaimTypes.Permission, StringComparison.InvariantCultureIgnoreCase)) - .Select(claim => claim.Value).ToArray(); - } - - public static bool HasPermission(this AuthorizationHandlerContext context, Permission permission) - { - foreach (var claim in context.User.Claims.Where(c => - c.Type.Equals(APIKeyConstants.ClaimTypes.Permission, StringComparison.InvariantCultureIgnoreCase))) - { - if (Permission.TryParse(claim.Value, out var claimPermission)) - { - if (claimPermission.Contains(permission)) - { - return true; - } - } - } - return false; - } - } -} diff --git a/BTCPayServer/Security/APIKeys/APIKeyRepository.cs b/BTCPayServer/Security/APIKeys/APIKeyRepository.cs deleted file mode 100644 index c30fd7a8c..000000000 --- a/BTCPayServer/Security/APIKeys/APIKeyRepository.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using BTCPayServer.Data; -using Microsoft.EntityFrameworkCore; - -namespace BTCPayServer.Security.APIKeys -{ - public class APIKeyRepository - { - private readonly ApplicationDbContextFactory _applicationDbContextFactory; - - public APIKeyRepository(ApplicationDbContextFactory applicationDbContextFactory) - { - _applicationDbContextFactory = applicationDbContextFactory; - } - - public async Task GetKey(string apiKey) - { - using (var context = _applicationDbContextFactory.CreateContext()) - { - return await EntityFrameworkQueryableExtensions.SingleOrDefaultAsync(context.ApiKeys, - data => data.Id == apiKey && data.Type != APIKeyType.Legacy); - } - } - - public async Task> GetKeys(APIKeyQuery query) - { - using (var context = _applicationDbContextFactory.CreateContext()) - { - var queryable = context.ApiKeys.AsQueryable(); - if (query?.UserId != null && query.UserId.Any()) - { - queryable = queryable.Where(data => query.UserId.Contains(data.UserId)); - } - - return await queryable.ToListAsync(); - } - } - - public async Task CreateKey(APIKeyData key) - { - if (key.Type == APIKeyType.Legacy || !string.IsNullOrEmpty(key.StoreId) || string.IsNullOrEmpty(key.UserId)) - { - throw new InvalidOperationException("cannot save a bitpay legacy api key with this repository"); - } - - using (var context = _applicationDbContextFactory.CreateContext()) - { - await context.ApiKeys.AddAsync(key); - await context.SaveChangesAsync(); - } - } - - public async Task Remove(string id, string getUserId) - { - using (var context = _applicationDbContextFactory.CreateContext()) - { - var key = await EntityFrameworkQueryableExtensions.SingleOrDefaultAsync(context.ApiKeys, - data => data.Id == id && data.UserId == getUserId); - context.ApiKeys.Remove(key); - await context.SaveChangesAsync(); - } - } - - public class APIKeyQuery - { - public string[] UserId { get; set; } - } - } -} From ac14f199e4bec6a0300b77757e8959eb449715e5 Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Fri, 27 Mar 2020 12:55:21 +0900 Subject: [PATCH 2/5] Renaming GreenField classes --- BTCPayServer.Tests/ApiKeysTests.cs | 2 +- BTCPayServer/Controllers/ManageController.APIKeys.cs | 2 +- BTCPayServer/Controllers/ManageController.cs | 3 +-- BTCPayServer/Controllers/RestApi/ApiKeysController.cs | 2 +- BTCPayServer/Controllers/RestApi/UsersController.cs | 4 ++-- BTCPayServer/Hosting/BTCPayServerServices.cs | 2 +- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/BTCPayServer.Tests/ApiKeysTests.cs b/BTCPayServer.Tests/ApiKeysTests.cs index fde2e5433..28c226279 100644 --- a/BTCPayServer.Tests/ApiKeysTests.cs +++ b/BTCPayServer.Tests/ApiKeysTests.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using BTCPayServer.Client; using BTCPayServer.Client.Models; using BTCPayServer.Data; -using BTCPayServer.Security.APIKeys; +using BTCPayServer.Security.GreenField; using BTCPayServer.Tests.Logging; using BTCPayServer.Views.Manage; using Newtonsoft.Json; diff --git a/BTCPayServer/Controllers/ManageController.APIKeys.cs b/BTCPayServer/Controllers/ManageController.APIKeys.cs index 429741e2c..c0f6def40 100644 --- a/BTCPayServer/Controllers/ManageController.APIKeys.cs +++ b/BTCPayServer/Controllers/ManageController.APIKeys.cs @@ -7,7 +7,7 @@ using BTCPayServer.Client; using BTCPayServer.Data; using BTCPayServer.Models; using BTCPayServer.Security; -using BTCPayServer.Security.APIKeys; +using BTCPayServer.Security.GreenField; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using NBitcoin; diff --git a/BTCPayServer/Controllers/ManageController.cs b/BTCPayServer/Controllers/ManageController.cs index 5fdaae147..8c309e6a4 100644 --- a/BTCPayServer/Controllers/ManageController.cs +++ b/BTCPayServer/Controllers/ManageController.cs @@ -19,9 +19,8 @@ using System.Globalization; using BTCPayServer.Security; using BTCPayServer.U2F; using BTCPayServer.Data; -using BTCPayServer.Security.APIKeys; using Microsoft.AspNetCore.Routing; - +using BTCPayServer.Security.GreenField; namespace BTCPayServer.Controllers { diff --git a/BTCPayServer/Controllers/RestApi/ApiKeysController.cs b/BTCPayServer/Controllers/RestApi/ApiKeysController.cs index c0b2ab010..b596294ed 100644 --- a/BTCPayServer/Controllers/RestApi/ApiKeysController.cs +++ b/BTCPayServer/Controllers/RestApi/ApiKeysController.cs @@ -4,10 +4,10 @@ using BTCPayServer.Client; using BTCPayServer.Client.Models; using BTCPayServer.Data; using BTCPayServer.Security; -using BTCPayServer.Security.APIKeys; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; +using BTCPayServer.Security.GreenField; namespace BTCPayServer.Controllers.RestApi { diff --git a/BTCPayServer/Controllers/RestApi/UsersController.cs b/BTCPayServer/Controllers/RestApi/UsersController.cs index cd2140963..dcfb6d3a3 100644 --- a/BTCPayServer/Controllers/RestApi/UsersController.cs +++ b/BTCPayServer/Controllers/RestApi/UsersController.cs @@ -8,7 +8,7 @@ using BTCPayServer.Data; using BTCPayServer.Events; using BTCPayServer.Logging; using BTCPayServer.Security; -using BTCPayServer.Security.APIKeys; +using BTCPayServer.Security.GreenField; using BTCPayServer.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; @@ -74,7 +74,7 @@ namespace BTCPayServer.Controllers.RestApi return BadRequest(CreateValidationProblem(nameof(request.Password), "Password is missing")); var anyAdmin = (await _userManager.GetUsersInRoleAsync(Roles.ServerAdmin)).Any(); var policies = await _settingsRepository.GetSettingAsync() ?? new PoliciesSettings(); - var isAuth = User.Identity.AuthenticationType == APIKeyConstants.AuthenticationType; + var isAuth = User.Identity.AuthenticationType == GreenFieldConstants.AuthenticationType; // If registration are locked and that an admin exists, don't accept unauthenticated connection if (anyAdmin && policies.LockSubscription && !isAuth) diff --git a/BTCPayServer/Hosting/BTCPayServerServices.cs b/BTCPayServer/Hosting/BTCPayServerServices.cs index 07d1282e7..74f967690 100644 --- a/BTCPayServer/Hosting/BTCPayServerServices.cs +++ b/BTCPayServer/Hosting/BTCPayServerServices.cs @@ -32,7 +32,6 @@ using BTCPayServer.Payments.Bitcoin; using BTCPayServer.Payments.Changelly; using BTCPayServer.Payments.Lightning; using BTCPayServer.Security; -using BTCPayServer.Security.APIKeys; using BTCPayServer.Services.PaymentRequests; using Microsoft.AspNetCore.Mvc.ModelBinding; using NBXplorer.DerivationStrategy; @@ -44,6 +43,7 @@ using BundlerMinifier.TagHelpers; using Microsoft.AspNetCore.Authorization; using BTCPayServer.Security.Bitpay; using Serilog; +using BTCPayServer.Security.GreenField; namespace BTCPayServer.Hosting { From afdee9d8a271ec22ff7b617a8ea875e8b753f77f Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Fri, 27 Mar 2020 12:58:45 +0900 Subject: [PATCH 3/5] Move directories, rename controllers --- .../ApiKeysController.cs | 2 +- .../TestApiKeyController.cs | 2 +- .../UsersController.cs | 2 +- .../Security/GreenField/APIKeyExtensions.cs | 64 +++++++++++ .../Security/GreenField/APIKeyRepository.cs | 72 +++++++++++++ .../GreenFieldAuthenticationHandler.cs | 100 ++++++++++++++++++ .../GreenFieldAuthenticationOptions.cs | 8 ++ .../GreenFieldAuthorizationHandler.cs | 96 +++++++++++++++++ .../GreenField/GreenFieldConstants.cs | 15 +++ 9 files changed, 358 insertions(+), 3 deletions(-) rename BTCPayServer/Controllers/{RestApi => GreenField}/ApiKeysController.cs (97%) rename BTCPayServer/Controllers/{RestApi => GreenField}/TestApiKeyController.cs (98%) rename BTCPayServer/Controllers/{RestApi => GreenField}/UsersController.cs (99%) create mode 100644 BTCPayServer/Security/GreenField/APIKeyExtensions.cs create mode 100644 BTCPayServer/Security/GreenField/APIKeyRepository.cs create mode 100644 BTCPayServer/Security/GreenField/GreenFieldAuthenticationHandler.cs create mode 100644 BTCPayServer/Security/GreenField/GreenFieldAuthenticationOptions.cs create mode 100644 BTCPayServer/Security/GreenField/GreenFieldAuthorizationHandler.cs create mode 100644 BTCPayServer/Security/GreenField/GreenFieldConstants.cs diff --git a/BTCPayServer/Controllers/RestApi/ApiKeysController.cs b/BTCPayServer/Controllers/GreenField/ApiKeysController.cs similarity index 97% rename from BTCPayServer/Controllers/RestApi/ApiKeysController.cs rename to BTCPayServer/Controllers/GreenField/ApiKeysController.cs index b596294ed..acb99d415 100644 --- a/BTCPayServer/Controllers/RestApi/ApiKeysController.cs +++ b/BTCPayServer/Controllers/GreenField/ApiKeysController.cs @@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using BTCPayServer.Security.GreenField; -namespace BTCPayServer.Controllers.RestApi +namespace BTCPayServer.Controllers.GreenField { [ApiController] [Authorize(AuthenticationSchemes = AuthenticationSchemes.Greenfield)] diff --git a/BTCPayServer/Controllers/RestApi/TestApiKeyController.cs b/BTCPayServer/Controllers/GreenField/TestApiKeyController.cs similarity index 98% rename from BTCPayServer/Controllers/RestApi/TestApiKeyController.cs rename to BTCPayServer/Controllers/GreenField/TestApiKeyController.cs index a3c89c67c..804558fe9 100644 --- a/BTCPayServer/Controllers/RestApi/TestApiKeyController.cs +++ b/BTCPayServer/Controllers/GreenField/TestApiKeyController.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; -namespace BTCPayServer.Controllers.RestApi +namespace BTCPayServer.Controllers.GreenField { /// /// this controller serves as a testing endpoint for our api key unit tests diff --git a/BTCPayServer/Controllers/RestApi/UsersController.cs b/BTCPayServer/Controllers/GreenField/UsersController.cs similarity index 99% rename from BTCPayServer/Controllers/RestApi/UsersController.cs rename to BTCPayServer/Controllers/GreenField/UsersController.cs index dcfb6d3a3..873a2deb0 100644 --- a/BTCPayServer/Controllers/RestApi/UsersController.cs +++ b/BTCPayServer/Controllers/GreenField/UsersController.cs @@ -17,7 +17,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using NicolasDorier.RateLimits; using BTCPayServer.Client; -namespace BTCPayServer.Controllers.RestApi +namespace BTCPayServer.Controllers.GreenField { [ApiController] [Authorize(AuthenticationSchemes = AuthenticationSchemes.Greenfield)] diff --git a/BTCPayServer/Security/GreenField/APIKeyExtensions.cs b/BTCPayServer/Security/GreenField/APIKeyExtensions.cs new file mode 100644 index 000000000..7b12c0bc5 --- /dev/null +++ b/BTCPayServer/Security/GreenField/APIKeyExtensions.cs @@ -0,0 +1,64 @@ +using System; +using System.Linq; +using BTCPayServer.Client; +using BTCPayServer.Security.Bitpay; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Primitives; + +namespace BTCPayServer.Security.GreenField +{ + public static class APIKeyExtensions + { + public static bool GetAPIKey(this HttpContext httpContext, out StringValues apiKey) + { + if (httpContext.Request.Headers.TryGetValue("Authorization", out var value) && + value.ToString().StartsWith("token ", StringComparison.InvariantCultureIgnoreCase)) + { + apiKey = value.ToString().Substring("token ".Length); + return true; + } + + return false; + } + + public static AuthenticationBuilder AddAPIKeyAuthentication(this AuthenticationBuilder builder) + { + builder.AddScheme(AuthenticationSchemes.Greenfield, + o => { }); + return builder; + } + + public static IServiceCollection AddAPIKeyAuthentication(this IServiceCollection serviceCollection) + { + serviceCollection.AddSingleton(); + serviceCollection.AddScoped(); + return serviceCollection; + } + + public static string[] GetPermissions(this AuthorizationHandlerContext context) + { + return context.User.Claims.Where(c => + c.Type.Equals(GreenFieldConstants.ClaimTypes.Permission, StringComparison.InvariantCultureIgnoreCase)) + .Select(claim => claim.Value).ToArray(); + } + + public static bool HasPermission(this AuthorizationHandlerContext context, Permission permission) + { + foreach (var claim in context.User.Claims.Where(c => + c.Type.Equals(GreenFieldConstants.ClaimTypes.Permission, StringComparison.InvariantCultureIgnoreCase))) + { + if (Permission.TryParse(claim.Value, out var claimPermission)) + { + if (claimPermission.Contains(permission)) + { + return true; + } + } + } + return false; + } + } +} diff --git a/BTCPayServer/Security/GreenField/APIKeyRepository.cs b/BTCPayServer/Security/GreenField/APIKeyRepository.cs new file mode 100644 index 000000000..75058f71d --- /dev/null +++ b/BTCPayServer/Security/GreenField/APIKeyRepository.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using BTCPayServer.Data; +using Microsoft.EntityFrameworkCore; + +namespace BTCPayServer.Security.GreenField +{ + public class APIKeyRepository + { + private readonly ApplicationDbContextFactory _applicationDbContextFactory; + + public APIKeyRepository(ApplicationDbContextFactory applicationDbContextFactory) + { + _applicationDbContextFactory = applicationDbContextFactory; + } + + public async Task GetKey(string apiKey) + { + using (var context = _applicationDbContextFactory.CreateContext()) + { + return await EntityFrameworkQueryableExtensions.SingleOrDefaultAsync(context.ApiKeys, + data => data.Id == apiKey && data.Type != APIKeyType.Legacy); + } + } + + public async Task> GetKeys(APIKeyQuery query) + { + using (var context = _applicationDbContextFactory.CreateContext()) + { + var queryable = context.ApiKeys.AsQueryable(); + if (query?.UserId != null && query.UserId.Any()) + { + queryable = queryable.Where(data => query.UserId.Contains(data.UserId)); + } + + return await queryable.ToListAsync(); + } + } + + public async Task CreateKey(APIKeyData key) + { + if (key.Type == APIKeyType.Legacy || !string.IsNullOrEmpty(key.StoreId) || string.IsNullOrEmpty(key.UserId)) + { + throw new InvalidOperationException("cannot save a bitpay legacy api key with this repository"); + } + + using (var context = _applicationDbContextFactory.CreateContext()) + { + await context.ApiKeys.AddAsync(key); + await context.SaveChangesAsync(); + } + } + + public async Task Remove(string id, string getUserId) + { + using (var context = _applicationDbContextFactory.CreateContext()) + { + var key = await EntityFrameworkQueryableExtensions.SingleOrDefaultAsync(context.ApiKeys, + data => data.Id == id && data.UserId == getUserId); + context.ApiKeys.Remove(key); + await context.SaveChangesAsync(); + } + } + + public class APIKeyQuery + { + public string[] UserId { get; set; } + } + } +} diff --git a/BTCPayServer/Security/GreenField/GreenFieldAuthenticationHandler.cs b/BTCPayServer/Security/GreenField/GreenFieldAuthenticationHandler.cs new file mode 100644 index 000000000..810d084ca --- /dev/null +++ b/BTCPayServer/Security/GreenField/GreenFieldAuthenticationHandler.cs @@ -0,0 +1,100 @@ +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.Bitpay; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace BTCPayServer.Security.GreenField +{ + public class GreenFieldAuthenticationHandler : AuthenticationHandler + { + private readonly APIKeyRepository _apiKeyRepository; + private readonly IOptionsMonitor _identityOptions; + private readonly SignInManager _signInManager; + private readonly UserManager _userManager; + + public GreenFieldAuthenticationHandler( + APIKeyRepository apiKeyRepository, + IOptionsMonitor identityOptions, + IOptionsMonitor options, + ILoggerFactory logger, + UrlEncoder encoder, + ISystemClock clock, + SignInManager signInManager, + UserManager userManager) : base(options, logger, encoder, clock) + { + _apiKeyRepository = apiKeyRepository; + _identityOptions = identityOptions; + _signInManager = signInManager; + _userManager = userManager; + } + + protected override async Task HandleAuthenticateAsync() + { + var res = await HandleApiKeyAuthenticateResult(); + if (res.None) + { + return await HandleBasicAuthenticateAsync(); + } + + return res; + } + + private async Task HandleApiKeyAuthenticateResult() + { + if (!Context.Request.HttpContext.GetAPIKey(out var apiKey) || string.IsNullOrEmpty(apiKey)) + return AuthenticateResult.NoResult(); + + var key = await _apiKeyRepository.GetKey(apiKey); + + if (key == null) + { + return AuthenticateResult.Fail("ApiKey authentication failed"); + } + + List claims = new List(); + claims.Add(new Claim(_identityOptions.CurrentValue.ClaimsIdentity.UserIdClaimType, key.UserId)); + claims.AddRange(Permission.ToPermissions(key.Permissions).Select(permission => + new Claim(GreenFieldConstants.ClaimTypes.Permission, permission.ToString()))); + return AuthenticateResult.Success(new AuthenticationTicket( + new ClaimsPrincipal(new ClaimsIdentity(claims, GreenFieldConstants.AuthenticationType)), + GreenFieldConstants.AuthenticationType)); + } + + private async Task HandleBasicAuthenticateAsync() + { + string authHeader = Context.Request.Headers["Authorization"]; + + if (authHeader == null || !authHeader.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase)) 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() + { + new Claim(_identityOptions.CurrentValue.ClaimsIdentity.UserIdClaimType, user.Id), + new Claim(GreenFieldConstants.ClaimTypes.Permission, + Permission.Create(Policies.Unrestricted).ToString()) + }; + + return AuthenticateResult.Success(new AuthenticationTicket( + new ClaimsPrincipal(new ClaimsIdentity(claims, GreenFieldConstants.AuthenticationType)), + GreenFieldConstants.AuthenticationType)); + } + } +} diff --git a/BTCPayServer/Security/GreenField/GreenFieldAuthenticationOptions.cs b/BTCPayServer/Security/GreenField/GreenFieldAuthenticationOptions.cs new file mode 100644 index 000000000..ad4aab7bb --- /dev/null +++ b/BTCPayServer/Security/GreenField/GreenFieldAuthenticationOptions.cs @@ -0,0 +1,8 @@ +using Microsoft.AspNetCore.Authentication; + +namespace BTCPayServer.Security.GreenField +{ + public class GreenFieldAuthenticationOptions : AuthenticationSchemeOptions + { + } +} diff --git a/BTCPayServer/Security/GreenField/GreenFieldAuthorizationHandler.cs b/BTCPayServer/Security/GreenField/GreenFieldAuthorizationHandler.cs new file mode 100644 index 000000000..a4f6ed681 --- /dev/null +++ b/BTCPayServer/Security/GreenField/GreenFieldAuthorizationHandler.cs @@ -0,0 +1,96 @@ +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using BTCPayServer.Client; +using BTCPayServer.Data; +using BTCPayServer.Services.Stores; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; + +namespace BTCPayServer.Security.GreenField +{ + public class GreenFieldAuthorizationHandler : AuthorizationHandler + + { + private readonly HttpContext _HttpContext; + private readonly UserManager _userManager; + private readonly StoreRepository _storeRepository; + + public GreenFieldAuthorizationHandler(IHttpContextAccessor httpContextAccessor, + UserManager userManager, + StoreRepository storeRepository) + { + _HttpContext = httpContextAccessor.HttpContext; + _userManager = userManager; + _storeRepository = storeRepository; + } + + protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, + PolicyRequirement requirement) + { + if (context.User.Identity.AuthenticationType != GreenFieldConstants.AuthenticationType) + return; + + bool success = false; + switch (requirement.Policy) + { + case Policies.CanModifyProfile: + case Policies.CanViewProfile: + case Policies.Unrestricted: + success = context.HasPermission(Permission.Create(requirement.Policy)); + break; + + case Policies.CanViewStoreSettings: + case Policies.CanModifyStoreSettings: + var storeId = _HttpContext.GetImplicitStoreId(); + var userid = _userManager.GetUserId(context.User); + // Specific store action + if (storeId != null) + { + if (context.HasPermission(Permission.Create(requirement.Policy, storeId))) + { + if (string.IsNullOrEmpty(userid)) + break; + var store = await _storeRepository.FindStore((string)storeId, userid); + if (store == null) + break; + success = true; + _HttpContext.SetStoreData(store); + } + } + else + { + var stores = await _storeRepository.GetStoresByUserId(userid); + List permissionedStores = new List(); + foreach (var store in stores) + { + if (context.HasPermission(Permission.Create(requirement.Policy, store.Id))) + permissionedStores.Add(store); + } + _HttpContext.SetStoresData(stores.ToArray()); + success = true; + } + break; + case Policies.CanCreateUser: + case Policies.CanModifyServerSettings: + if (context.HasPermission(Permission.Create(requirement.Policy))) + { + var user = await _userManager.GetUserAsync(context.User); + if (user == null) + break; + if (!await _userManager.IsInRoleAsync(user, Roles.ServerAdmin)) + break; + success = true; + } + break; + } + + if (success) + { + context.Succeed(requirement); + } + } + } +} diff --git a/BTCPayServer/Security/GreenField/GreenFieldConstants.cs b/BTCPayServer/Security/GreenField/GreenFieldConstants.cs new file mode 100644 index 000000000..b34385b48 --- /dev/null +++ b/BTCPayServer/Security/GreenField/GreenFieldConstants.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using BTCPayServer.Client; + +namespace BTCPayServer.Security.GreenField +{ + public static class GreenFieldConstants + { + public const string AuthenticationType = "GreenField"; + + public static class ClaimTypes + { + public const string Permission = "APIKey.Permission"; + } + } +} From d219ba5d32f385624ea2dd7e27cd897692731a1d Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Fri, 27 Mar 2020 13:06:41 +0900 Subject: [PATCH 4/5] Split the greenfield authhandler in two classes --- .../Security/AuthenticationSchemes.cs | 4 +- .../Security/GreenField/APIKeyExtensions.cs | 4 +- .../APIKeysAuthenticationHandler.cs | 62 +++++++++++++++++++ ...ndler.cs => BasicAuthenticationHandler.cs} | 45 ++------------ 4 files changed, 74 insertions(+), 41 deletions(-) create mode 100644 BTCPayServer/Security/GreenField/APIKeysAuthenticationHandler.cs rename BTCPayServer/Security/GreenField/{GreenFieldAuthenticationHandler.cs => BasicAuthenticationHandler.cs} (59%) diff --git a/BTCPayServer/Security/AuthenticationSchemes.cs b/BTCPayServer/Security/AuthenticationSchemes.cs index de87c4116..4594f2e09 100644 --- a/BTCPayServer/Security/AuthenticationSchemes.cs +++ b/BTCPayServer/Security/AuthenticationSchemes.cs @@ -4,6 +4,8 @@ { public const string Cookie = "Identity.Application"; public const string Bitpay = "Bitpay"; - public const string Greenfield = "Greenfield"; + public const string Greenfield = "Greenfield.APIKeys,Greenfield.Basic"; + public const string GreenfieldAPIKeys = "Greenfield.APIKeys"; + public const string GreenfieldBasic = "Greenfield.Basic"; } } diff --git a/BTCPayServer/Security/GreenField/APIKeyExtensions.cs b/BTCPayServer/Security/GreenField/APIKeyExtensions.cs index 7b12c0bc5..ef68a0505 100644 --- a/BTCPayServer/Security/GreenField/APIKeyExtensions.cs +++ b/BTCPayServer/Security/GreenField/APIKeyExtensions.cs @@ -26,7 +26,9 @@ namespace BTCPayServer.Security.GreenField public static AuthenticationBuilder AddAPIKeyAuthentication(this AuthenticationBuilder builder) { - builder.AddScheme(AuthenticationSchemes.Greenfield, + builder.AddScheme(AuthenticationSchemes.GreenfieldAPIKeys, + o => { }); + builder.AddScheme(AuthenticationSchemes.GreenfieldBasic, o => { }); return builder; } diff --git a/BTCPayServer/Security/GreenField/APIKeysAuthenticationHandler.cs b/BTCPayServer/Security/GreenField/APIKeysAuthenticationHandler.cs new file mode 100644 index 000000000..ec30b1c13 --- /dev/null +++ b/BTCPayServer/Security/GreenField/APIKeysAuthenticationHandler.cs @@ -0,0 +1,62 @@ +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.Bitpay; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace BTCPayServer.Security.GreenField +{ + public class APIKeysAuthenticationHandler : AuthenticationHandler + { + private readonly APIKeyRepository _apiKeyRepository; + private readonly IOptionsMonitor _identityOptions; + private readonly SignInManager _signInManager; + private readonly UserManager _userManager; + + public APIKeysAuthenticationHandler( + APIKeyRepository apiKeyRepository, + IOptionsMonitor identityOptions, + IOptionsMonitor options, + ILoggerFactory logger, + UrlEncoder encoder, + ISystemClock clock, + SignInManager signInManager, + UserManager userManager) : base(options, logger, encoder, clock) + { + _apiKeyRepository = apiKeyRepository; + _identityOptions = identityOptions; + _signInManager = signInManager; + _userManager = userManager; + } + + protected override async Task HandleAuthenticateAsync() + { + if (!Context.Request.HttpContext.GetAPIKey(out var apiKey) || string.IsNullOrEmpty(apiKey)) + return AuthenticateResult.NoResult(); + + var key = await _apiKeyRepository.GetKey(apiKey); + + if (key == null) + { + return AuthenticateResult.Fail("ApiKey authentication failed"); + } + + List claims = new List(); + claims.Add(new Claim(_identityOptions.CurrentValue.ClaimsIdentity.UserIdClaimType, key.UserId)); + claims.AddRange(Permission.ToPermissions(key.Permissions).Select(permission => + new Claim(GreenFieldConstants.ClaimTypes.Permission, permission.ToString()))); + return AuthenticateResult.Success(new AuthenticationTicket( + new ClaimsPrincipal(new ClaimsIdentity(claims, GreenFieldConstants.AuthenticationType)), + GreenFieldConstants.AuthenticationType)); + } + } +} diff --git a/BTCPayServer/Security/GreenField/GreenFieldAuthenticationHandler.cs b/BTCPayServer/Security/GreenField/BasicAuthenticationHandler.cs similarity index 59% rename from BTCPayServer/Security/GreenField/GreenFieldAuthenticationHandler.cs rename to BTCPayServer/Security/GreenField/BasicAuthenticationHandler.cs index 810d084ca..ee531245a 100644 --- a/BTCPayServer/Security/GreenField/GreenFieldAuthenticationHandler.cs +++ b/BTCPayServer/Security/GreenField/BasicAuthenticationHandler.cs @@ -15,15 +15,13 @@ using Microsoft.Extensions.Options; namespace BTCPayServer.Security.GreenField { - public class GreenFieldAuthenticationHandler : AuthenticationHandler + public class BasicAuthenticationHandler : AuthenticationHandler { - private readonly APIKeyRepository _apiKeyRepository; private readonly IOptionsMonitor _identityOptions; private readonly SignInManager _signInManager; private readonly UserManager _userManager; - public GreenFieldAuthenticationHandler( - APIKeyRepository apiKeyRepository, + public BasicAuthenticationHandler( IOptionsMonitor identityOptions, IOptionsMonitor options, ILoggerFactory logger, @@ -32,49 +30,17 @@ namespace BTCPayServer.Security.GreenField SignInManager signInManager, UserManager userManager) : base(options, logger, encoder, clock) { - _apiKeyRepository = apiKeyRepository; _identityOptions = identityOptions; _signInManager = signInManager; _userManager = userManager; } protected override async Task HandleAuthenticateAsync() - { - var res = await HandleApiKeyAuthenticateResult(); - if (res.None) - { - return await HandleBasicAuthenticateAsync(); - } - - return res; - } - - private async Task HandleApiKeyAuthenticateResult() - { - if (!Context.Request.HttpContext.GetAPIKey(out var apiKey) || string.IsNullOrEmpty(apiKey)) - return AuthenticateResult.NoResult(); - - var key = await _apiKeyRepository.GetKey(apiKey); - - if (key == null) - { - return AuthenticateResult.Fail("ApiKey authentication failed"); - } - - List claims = new List(); - claims.Add(new Claim(_identityOptions.CurrentValue.ClaimsIdentity.UserIdClaimType, key.UserId)); - claims.AddRange(Permission.ToPermissions(key.Permissions).Select(permission => - new Claim(GreenFieldConstants.ClaimTypes.Permission, permission.ToString()))); - return AuthenticateResult.Success(new AuthenticationTicket( - new ClaimsPrincipal(new ClaimsIdentity(claims, GreenFieldConstants.AuthenticationType)), - GreenFieldConstants.AuthenticationType)); - } - - private async Task HandleBasicAuthenticateAsync() { string authHeader = Context.Request.Headers["Authorization"]; - if (authHeader == null || !authHeader.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase)) return AuthenticateResult.NoResult(); + if (authHeader == null || !authHeader.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase)) + return AuthenticateResult.NoResult(); var encodedUsernamePassword = authHeader.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries)[1]?.Trim(); var decodedUsernamePassword = Encoding.UTF8.GetString(Convert.FromBase64String(encodedUsernamePassword)).Split(':'); @@ -82,7 +48,8 @@ namespace BTCPayServer.Security.GreenField var password = decodedUsernamePassword[1]; var result = await _signInManager.PasswordSignInAsync(username, password, true, true); - if (!result.Succeeded) return AuthenticateResult.Fail(result.ToString()); + if (!result.Succeeded) + return AuthenticateResult.Fail(result.ToString()); var user = await _userManager.FindByNameAsync(username); var claims = new List() From 08abda15227e21e9e3fd86151edb4a8c66904719 Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Fri, 27 Mar 2020 13:13:40 +0900 Subject: [PATCH 5/5] Restrict authentication to the APIController to GreenFieldAPIKeys --- BTCPayServer.Tests/GreenfieldAPITests.cs | 6 ++---- BTCPayServer/Controllers/GreenField/ApiKeysController.cs | 4 ++-- BTCPayServer/Controllers/GreenField/UsersController.cs | 6 +++--- BTCPayServer/Views/Manage/AddApiKey.cshtml | 2 +- BTCPayServer/Views/Manage/AuthorizeAPIKey.cshtml | 2 +- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/BTCPayServer.Tests/GreenfieldAPITests.cs b/BTCPayServer.Tests/GreenfieldAPITests.cs index ca6ef6109..9f83f6d5e 100644 --- a/BTCPayServer.Tests/GreenfieldAPITests.cs +++ b/BTCPayServer.Tests/GreenfieldAPITests.cs @@ -46,15 +46,13 @@ namespace BTCPayServer.Tests Assert.Single(apiKeyData.Permissions); //a client using Basic Auth has no business here - await AssertHttpError(404, async () => await clientBasic.GetCurrentAPIKeyInfo()); + await AssertHttpError(401, async () => await clientBasic.GetCurrentAPIKeyInfo()); //revoke current api key await client.RevokeCurrentAPIKeyInfo(); await AssertHttpError(401, async () => await client.GetCurrentAPIKeyInfo()); //a client using Basic Auth has no business here - await AssertHttpError(404, async () => await clientBasic.RevokeCurrentAPIKeyInfo()); - - + await AssertHttpError(401, async () => await clientBasic.RevokeCurrentAPIKeyInfo()); } } diff --git a/BTCPayServer/Controllers/GreenField/ApiKeysController.cs b/BTCPayServer/Controllers/GreenField/ApiKeysController.cs index acb99d415..f272eeada 100644 --- a/BTCPayServer/Controllers/GreenField/ApiKeysController.cs +++ b/BTCPayServer/Controllers/GreenField/ApiKeysController.cs @@ -12,7 +12,7 @@ using BTCPayServer.Security.GreenField; namespace BTCPayServer.Controllers.GreenField { [ApiController] - [Authorize(AuthenticationSchemes = AuthenticationSchemes.Greenfield)] + [Authorize(AuthenticationSchemes = AuthenticationSchemes.GreenfieldAPIKeys)] public class ApiKeysController : ControllerBase { private readonly APIKeyRepository _apiKeyRepository; @@ -36,7 +36,7 @@ namespace BTCPayServer.Controllers.GreenField } [HttpDelete("~/api/v1/api-keys/current")] - [Authorize(Policy = Policies.Unrestricted, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] + [Authorize(Policy = Policies.Unrestricted, AuthenticationSchemes = AuthenticationSchemes.GreenfieldAPIKeys)] public async Task> RevokeKey() { if (!ControllerContext.HttpContext.GetAPIKey(out var apiKey)) diff --git a/BTCPayServer/Controllers/GreenField/UsersController.cs b/BTCPayServer/Controllers/GreenField/UsersController.cs index 873a2deb0..9e56d5dd6 100644 --- a/BTCPayServer/Controllers/GreenField/UsersController.cs +++ b/BTCPayServer/Controllers/GreenField/UsersController.cs @@ -82,7 +82,7 @@ namespace BTCPayServer.Controllers.GreenField // Even if subscription are unlocked, it is forbidden to create admin unauthenticated if (anyAdmin && request.IsAdministrator is true && !isAuth) - return Forbid(AuthenticationSchemes.Greenfield); + return Forbid(AuthenticationSchemes.GreenfieldBasic); // 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 @@ -90,14 +90,14 @@ namespace BTCPayServer.Controllers.GreenField : true; // You need to be admin to create an admin if (request.IsAdministrator is true && !isAdmin) - return Forbid(AuthenticationSchemes.Greenfield); + return Forbid(AuthenticationSchemes.GreenfieldBasic); 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.Greenfield); + return Forbid(AuthenticationSchemes.GreenfieldBasic); } var user = new ApplicationUser diff --git a/BTCPayServer/Views/Manage/AddApiKey.cshtml b/BTCPayServer/Views/Manage/AddApiKey.cshtml index 8c1866aed..c647ac805 100644 --- a/BTCPayServer/Views/Manage/AddApiKey.cshtml +++ b/BTCPayServer/Views/Manage/AddApiKey.cshtml @@ -1,6 +1,6 @@ @using BTCPayServer.Client @using BTCPayServer.Controllers -@using BTCPayServer.Security.APIKeys +@using BTCPayServer.Security.GreenField @model ManageController.AddApiKeyViewModel @{ diff --git a/BTCPayServer/Views/Manage/AuthorizeAPIKey.cshtml b/BTCPayServer/Views/Manage/AuthorizeAPIKey.cshtml index 2c48d067e..105df24b2 100644 --- a/BTCPayServer/Views/Manage/AuthorizeAPIKey.cshtml +++ b/BTCPayServer/Views/Manage/AuthorizeAPIKey.cshtml @@ -1,6 +1,6 @@ @using BTCPayServer.Client @using BTCPayServer.Controllers -@using BTCPayServer.Security.APIKeys +@using BTCPayServer.Security.GreenField @model BTCPayServer.Controllers.ManageController.AuthorizeApiKeysViewModel @{