add to client, fix tests and doc

This commit is contained in:
Kukks
2020-03-20 17:14:47 +01:00
parent 9d99c32305
commit f8f358ebdb
7 changed files with 48 additions and 11 deletions

View File

@@ -14,6 +14,8 @@ namespace BTCPayServer.Client
{ {
private readonly string _apiKey; private readonly string _apiKey;
private readonly Uri _btcpayHost; private readonly Uri _btcpayHost;
private readonly string _username;
private readonly string _password;
private readonly HttpClient _httpClient; private readonly HttpClient _httpClient;
public string APIKey => _apiKey; public string APIKey => _apiKey;
@@ -32,6 +34,15 @@ namespace BTCPayServer.Client
_httpClient = httpClient ?? new HttpClient(); _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) protected void HandleResponse(HttpResponseMessage message)
{ {
message.EnsureSuccessStatusCode(); message.EnsureSuccessStatusCode();
@@ -56,6 +67,10 @@ namespace BTCPayServer.Client
var httpRequest = new HttpRequestMessage(method ?? HttpMethod.Get, uriBuilder.Uri); var httpRequest = new HttpRequestMessage(method ?? HttpMethod.Get, uriBuilder.Uri);
if (_apiKey != null) if (_apiKey != null)
httpRequest.Headers.Authorization = new AuthenticationHeaderValue("token", _apiKey); 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; return httpRequest;

View File

@@ -38,15 +38,23 @@ namespace BTCPayServer.Tests
user.GrantAccess(); user.GrantAccess();
await user.MakeAdmin(); await user.MakeAdmin();
var client = await user.CreateClient(Policies.Unrestricted); var client = await user.CreateClient(Policies.Unrestricted);
var clientBasic = await user.CreateClient();
//Get current api key //Get current api key
var apiKeyData = await client.GetCurrentAPIKeyInfo(); var apiKeyData = await client.GetCurrentAPIKeyInfo();
Assert.NotNull(apiKeyData); Assert.NotNull(apiKeyData);
Assert.Equal(client.APIKey, apiKeyData.ApiKey); Assert.Equal(client.APIKey, apiKeyData.ApiKey);
Assert.Single(apiKeyData.Permissions); Assert.Single(apiKeyData.Permissions);
//a client using Basic Auth has no business here
await AssertHttpError(401, async () => await clientBasic.GetCurrentAPIKeyInfo());
//revoke current api key //revoke current api key
await client.RevokeCurrentAPIKeyInfo(); await client.RevokeCurrentAPIKeyInfo();
await AssertHttpError(401, async () => await client.GetCurrentAPIKeyInfo()); 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.UserId = user1.Id;
user1Acc.IsAdmin = false; user1Acc.IsAdmin = false;
var user1Client = await user1Acc.CreateClient(Policies.CanModifyServerSettings); var user1Client = await user1Acc.CreateClient(Policies.CanModifyServerSettings);
var user1ClientBasic = await user1Acc.CreateClient();
// User1 trying to get server management would still fail to create user // 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" })); 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 // User1 should be able to create user if subscription unlocked
await settings.UpdateSetting<PoliciesSettings>(new PoliciesSettings() { LockSubscription = false }); await settings.UpdateSetting<PoliciesSettings>(new PoliciesSettings() { LockSubscription = false });
await user1Client.CreateUser(new CreateApplicationUserRequest() { Email = "test8@gmail.com", Password = "afewfoiewiou" }); 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 // But not an admin
await AssertHttpError(403, async () => await user1Client.CreateUser(new CreateApplicationUserRequest() { Email = "admin8@gmail.com", Password = "afewfoiewiou", IsAdministrator = true })); 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 clientProfile = await user.CreateClient(Policies.CanModifyProfile);
var clientServer = await user.CreateClient(Policies.CanCreateUser, Policies.CanViewProfile); var clientServer = await user.CreateClient(Policies.CanCreateUser, Policies.CanViewProfile);
var clientInsufficient = await user.CreateClient(Policies.CanModifyStoreSettings); var clientInsufficient = await user.CreateClient(Policies.CanModifyStoreSettings);
var clientBasic = await user.CreateClient();
var apiKeyProfileUserData = await clientProfile.GetCurrentUser(); var apiKeyProfileUserData = await clientProfile.GetCurrentUser();
@@ -149,6 +160,7 @@ namespace BTCPayServer.Tests
await Assert.ThrowsAsync<HttpRequestException>(async () => await clientInsufficient.GetCurrentUser()); await Assert.ThrowsAsync<HttpRequestException>(async () => await clientInsufficient.GetCurrentUser());
await clientServer.GetCurrentUser(); await clientServer.GetCurrentUser();
await clientProfile.GetCurrentUser(); await clientProfile.GetCurrentUser();
await clientBasic.GetCurrentUser();
await Assert.ThrowsAsync<HttpRequestException>(async () => await clientInsufficient.CreateUser(new CreateApplicationUserRequest() await Assert.ThrowsAsync<HttpRequestException>(async () => await clientInsufficient.CreateUser(new CreateApplicationUserRequest()
{ {
@@ -163,6 +175,13 @@ namespace BTCPayServer.Tests
}); });
Assert.NotNull(newUser); 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<HttpRequestException>(async () => await clientServer.CreateUser(new CreateApplicationUserRequest() await Assert.ThrowsAsync<HttpRequestException>(async () => await clientServer.CreateUser(new CreateApplicationUserRequest()
{ {
Email = $"{Guid.NewGuid()}", Email = $"{Guid.NewGuid()}",

View File

@@ -49,6 +49,11 @@ namespace BTCPayServer.Tests
IsAdmin = true; IsAdmin = true;
} }
public async Task<BTCPayServerClient> CreateClient()
{
return new BTCPayServerClient(parent.PayTester.ServerUri, RegisterDetails.Email, RegisterDetails.Password);
}
public async Task<BTCPayServerClient> CreateClient(params string[] permissions) public async Task<BTCPayServerClient> CreateClient(params string[] permissions)
{ {
var manageController = parent.PayTester.GetController<ManageController>(UserId, StoreId, IsAdmin); var manageController = parent.PayTester.GetController<ManageController>(UserId, StoreId, IsAdmin);

View File

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

View File

@@ -82,7 +82,7 @@ namespace BTCPayServer.Controllers.RestApi
// Even if subscription are unlocked, it is forbidden to create admin unauthenticated // Even if subscription are unlocked, it is forbidden to create admin unauthenticated
if (anyAdmin && request.IsAdministrator is true && !isAuth) 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 // 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 bool isAdmin = anyAdmin ? (await _authorizationService.AuthorizeAsync(User, null, new PolicyRequirement(Policies.CanModifyServerSettings))).Succeeded
&& (await _authorizationService.AuthorizeAsync(User, null, new PolicyRequirement(Policies.Unrestricted))).Succeeded && (await _authorizationService.AuthorizeAsync(User, null, new PolicyRequirement(Policies.Unrestricted))).Succeeded
@@ -90,14 +90,14 @@ namespace BTCPayServer.Controllers.RestApi
: true; : true;
// You need to be admin to create an admin // You need to be admin to create an admin
if (request.IsAdministrator is true && !isAdmin) if (request.IsAdministrator is true && !isAdmin)
return Forbid(AuthenticationSchemes.ApiKeyOrBasic); return Forbid(AuthenticationSchemes.ApiKey, AuthenticationSchemes.Basic);
if (!isAdmin && policies.LockSubscription) if (!isAdmin && policies.LockSubscription)
{ {
// If we are not admin and subscriptions are locked, we need to check the Policies.CanCreateUser.Key permission // 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; var canCreateUser = (await _authorizationService.AuthorizeAsync(User, null, new PolicyRequirement(Policies.CanCreateUser))).Succeeded;
if (!isAuth || !canCreateUser) if (!isAuth || !canCreateUser)
return Forbid(AuthenticationSchemes.ApiKeyOrBasic); return Forbid(AuthenticationSchemes.ApiKey, AuthenticationSchemes.Basic);
} }
var user = new ApplicationUser var user = new ApplicationUser

View File

@@ -6,6 +6,6 @@
public const string Bitpay = "Bitpay"; public const string Bitpay = "Bitpay";
public const string ApiKey = "GreenfieldApiKey"; public const string ApiKey = "GreenfieldApiKey";
public const string Basic = "Basic"; public const string Basic = "Basic";
public const string ApiKeyOrBasic = "Basic,GreenfieldApiKey"; public const string ApiKeyOrBasic = ApiKey + "," + Basic;
} }
} }

View File

@@ -209,8 +209,7 @@
}, },
"security": [ "security": [
{ {
"GreenField Authentication": [], "GreenField Authentication": []
"Basic": []
} }
] ]
}, },
@@ -234,8 +233,7 @@
}, },
"security": [ "security": [
{ {
"GreenField Authentication": [ "unrestricted" ], "GreenField Authentication": [ "unrestricted" ]
"Basic": []
} }
] ]
} }