From f8f358ebdbb987d901659d9910db31acec6a0a3d Mon Sep 17 00:00:00 2001 From: Kukks Date: Fri, 20 Mar 2020 17:14:47 +0100 Subject: [PATCH] add to client, fix tests and doc --- BTCPayServer.Client/BTCPayServerClient.cs | 15 +++++++++++++ BTCPayServer.Tests/GreenfieldAPITests.cs | 21 ++++++++++++++++++- BTCPayServer.Tests/TestAccount.cs | 5 +++++ .../Controllers/RestApi/ApiKeysController.cs | 4 ++-- .../Controllers/RestApi/UsersController.cs | 6 +++--- .../Security/AuthenticationSchemes.cs | 2 +- .../wwwroot/swagger/v1/swagger.template.json | 6 ++---- 7 files changed, 48 insertions(+), 11 deletions(-) diff --git a/BTCPayServer.Client/BTCPayServerClient.cs b/BTCPayServer.Client/BTCPayServerClient.cs index e84031fa1..a5fa230f4 100644 --- a/BTCPayServer.Client/BTCPayServerClient.cs +++ b/BTCPayServer.Client/BTCPayServerClient.cs @@ -14,6 +14,8 @@ namespace BTCPayServer.Client { private readonly string _apiKey; private readonly Uri _btcpayHost; + private readonly string _username; + private readonly string _password; private readonly HttpClient _httpClient; public string APIKey => _apiKey; @@ -31,6 +33,15 @@ namespace BTCPayServer.Client _btcpayHost = btcpayHost; _httpClient = httpClient ?? new HttpClient(); } + + public BTCPayServerClient(Uri btcpayHost, string username, string password, HttpClient httpClient = null) + { + _apiKey = APIKey; + _btcpayHost = btcpayHost; + _username = username; + _password = password; + _httpClient = httpClient ?? new HttpClient(); + } protected void HandleResponse(HttpResponseMessage message) { @@ -56,6 +67,10 @@ namespace BTCPayServer.Client var httpRequest = new HttpRequestMessage(method ?? HttpMethod.Get, uriBuilder.Uri); if (_apiKey != null) httpRequest.Headers.Authorization = new AuthenticationHeaderValue("token", _apiKey); + else if (!string.IsNullOrEmpty(_username)) + { + httpRequest.Headers.Authorization = new AuthenticationHeaderValue("Basic", System.Convert.ToBase64String(Encoding.ASCII.GetBytes(_username + ":" + _password))); + } return httpRequest; diff --git a/BTCPayServer.Tests/GreenfieldAPITests.cs b/BTCPayServer.Tests/GreenfieldAPITests.cs index bea94c54f..5c1129da5 100644 --- a/BTCPayServer.Tests/GreenfieldAPITests.cs +++ b/BTCPayServer.Tests/GreenfieldAPITests.cs @@ -38,15 +38,23 @@ namespace BTCPayServer.Tests user.GrantAccess(); await user.MakeAdmin(); var client = await user.CreateClient(Policies.Unrestricted); + var clientBasic = await user.CreateClient(); //Get current api key var apiKeyData = await client.GetCurrentAPIKeyInfo(); Assert.NotNull(apiKeyData); Assert.Equal(client.APIKey, apiKeyData.ApiKey); Assert.Single(apiKeyData.Permissions); - + + //a client using Basic Auth has no business here + 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(401, async () => await clientBasic.RevokeCurrentAPIKeyInfo()); + + } } @@ -108,12 +116,14 @@ namespace BTCPayServer.Tests user1Acc.UserId = user1.Id; user1Acc.IsAdmin = false; var user1Client = await user1Acc.CreateClient(Policies.CanModifyServerSettings); + var user1ClientBasic = await user1Acc.CreateClient(); // User1 trying to get server management would still fail to create user await AssertHttpError(403, async () => await user1Client.CreateUser(new CreateApplicationUserRequest() { Email = "test8@gmail.com", Password = "afewfoiewiou" })); // User1 should be able to create user if subscription unlocked await settings.UpdateSetting(new PoliciesSettings() { LockSubscription = false }); await user1Client.CreateUser(new CreateApplicationUserRequest() { Email = "test8@gmail.com", Password = "afewfoiewiou" }); + await user1ClientBasic.CreateUser(new CreateApplicationUserRequest() { Email = "testaa8@gmail.com", Password = "afewfoiewiou" }); // But not an admin await AssertHttpError(403, async () => await user1Client.CreateUser(new CreateApplicationUserRequest() { Email = "admin8@gmail.com", Password = "afewfoiewiou", IsAdministrator = true })); } @@ -139,6 +149,7 @@ namespace BTCPayServer.Tests var clientProfile = await user.CreateClient(Policies.CanModifyProfile); var clientServer = await user.CreateClient(Policies.CanCreateUser, Policies.CanViewProfile); var clientInsufficient = await user.CreateClient(Policies.CanModifyStoreSettings); + var clientBasic = await user.CreateClient(); var apiKeyProfileUserData = await clientProfile.GetCurrentUser(); @@ -149,6 +160,7 @@ namespace BTCPayServer.Tests await Assert.ThrowsAsync(async () => await clientInsufficient.GetCurrentUser()); await clientServer.GetCurrentUser(); await clientProfile.GetCurrentUser(); + await clientBasic.GetCurrentUser(); await Assert.ThrowsAsync(async () => await clientInsufficient.CreateUser(new CreateApplicationUserRequest() { @@ -163,6 +175,13 @@ namespace BTCPayServer.Tests }); Assert.NotNull(newUser); + var newUser2 = await clientBasic.CreateUser(new CreateApplicationUserRequest() + { + Email = $"{Guid.NewGuid()}@g.com", + Password = Guid.NewGuid().ToString() + }); + Assert.NotNull(newUser2); + await Assert.ThrowsAsync(async () => await clientServer.CreateUser(new CreateApplicationUserRequest() { Email = $"{Guid.NewGuid()}", diff --git a/BTCPayServer.Tests/TestAccount.cs b/BTCPayServer.Tests/TestAccount.cs index 9ecfe14e4..c0ef1a4b9 100644 --- a/BTCPayServer.Tests/TestAccount.cs +++ b/BTCPayServer.Tests/TestAccount.cs @@ -49,6 +49,11 @@ namespace BTCPayServer.Tests IsAdmin = true; } + public async Task CreateClient() + { + return new BTCPayServerClient(parent.PayTester.ServerUri, RegisterDetails.Email, RegisterDetails.Password); + } + public async Task CreateClient(params string[] permissions) { var manageController = parent.PayTester.GetController(UserId, StoreId, IsAdmin); diff --git a/BTCPayServer/Controllers/RestApi/ApiKeysController.cs b/BTCPayServer/Controllers/RestApi/ApiKeysController.cs index fff308122..95324ebf9 100644 --- a/BTCPayServer/Controllers/RestApi/ApiKeysController.cs +++ b/BTCPayServer/Controllers/RestApi/ApiKeysController.cs @@ -12,7 +12,7 @@ using Microsoft.AspNetCore.Mvc; namespace BTCPayServer.Controllers.RestApi { [ApiController] - [Authorize(AuthenticationSchemes = AuthenticationSchemes.ApiKeyOrBasic)] + [Authorize(AuthenticationSchemes = AuthenticationSchemes.ApiKey)] public class ApiKeysController : ControllerBase { private readonly APIKeyRepository _apiKeyRepository; @@ -33,7 +33,7 @@ namespace BTCPayServer.Controllers.RestApi } [HttpDelete("~/api/v1/api-keys/current")] - [Authorize(Policy = Policies.Unrestricted, AuthenticationSchemes = AuthenticationSchemes.ApiKeyOrBasic)] + [Authorize(Policy = Policies.Unrestricted, AuthenticationSchemes = AuthenticationSchemes.ApiKey)] public async Task> RevokeKey() { ControllerContext.HttpContext.GetAPIKey(out var apiKey); diff --git a/BTCPayServer/Controllers/RestApi/UsersController.cs b/BTCPayServer/Controllers/RestApi/UsersController.cs index 301d9df46..25711540a 100644 --- a/BTCPayServer/Controllers/RestApi/UsersController.cs +++ b/BTCPayServer/Controllers/RestApi/UsersController.cs @@ -82,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.ApiKeyOrBasic); + return Forbid(AuthenticationSchemes.ApiKey, AuthenticationSchemes.Basic); // 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.RestApi : true; // You need to be admin to create an admin if (request.IsAdministrator is true && !isAdmin) - return Forbid(AuthenticationSchemes.ApiKeyOrBasic); + return Forbid(AuthenticationSchemes.ApiKey, AuthenticationSchemes.Basic); 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.ApiKeyOrBasic); + return Forbid(AuthenticationSchemes.ApiKey, AuthenticationSchemes.Basic); } var user = new ApplicationUser diff --git a/BTCPayServer/Security/AuthenticationSchemes.cs b/BTCPayServer/Security/AuthenticationSchemes.cs index e5d8683f9..5f34a7caa 100644 --- a/BTCPayServer/Security/AuthenticationSchemes.cs +++ b/BTCPayServer/Security/AuthenticationSchemes.cs @@ -6,6 +6,6 @@ public const string Bitpay = "Bitpay"; public const string ApiKey = "GreenfieldApiKey"; public const string Basic = "Basic"; - public const string ApiKeyOrBasic = "Basic,GreenfieldApiKey"; + public const string ApiKeyOrBasic = ApiKey + "," + Basic; } } diff --git a/BTCPayServer/wwwroot/swagger/v1/swagger.template.json b/BTCPayServer/wwwroot/swagger/v1/swagger.template.json index 8d7dfb820..7d167d9d9 100644 --- a/BTCPayServer/wwwroot/swagger/v1/swagger.template.json +++ b/BTCPayServer/wwwroot/swagger/v1/swagger.template.json @@ -209,8 +209,7 @@ }, "security": [ { - "GreenField Authentication": [], - "Basic": [] + "GreenField Authentication": [] } ] }, @@ -234,8 +233,7 @@ }, "security": [ { - "GreenField Authentication": [ "unrestricted" ], - "Basic": [] + "GreenField Authentication": [ "unrestricted" ] } ] }