mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-22 06:21:44 +01:00
GreenField: Create API Key
This commit is contained in:
parent
927c09ff7b
commit
39a8c3fe47
6 changed files with 143 additions and 4 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -12,7 +13,15 @@ namespace BTCPayServer.Client
|
||||||
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/api-keys/current"), token);
|
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/api-keys/current"), token);
|
||||||
return await HandleResponse<ApiKeyData>(response);
|
return await HandleResponse<ApiKeyData>(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual async Task<ApiKeyData> CreateAPIKey(CreateApiKeyRequest request, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (request == null)
|
||||||
|
throw new ArgumentNullException(nameof(request));
|
||||||
|
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/api-keys", bodyPayload: request, method: HttpMethod.Post), token);
|
||||||
|
return await HandleResponse<ApiKeyData>(response);
|
||||||
|
}
|
||||||
|
|
||||||
public virtual async Task RevokeCurrentAPIKeyInfo(CancellationToken token = default)
|
public virtual async Task RevokeCurrentAPIKeyInfo(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/api-keys/current", null, HttpMethod.Delete), token);
|
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/api-keys/current", null, HttpMethod.Delete), token);
|
||||||
|
|
|
@ -55,6 +55,33 @@ namespace BTCPayServer.Tests
|
||||||
await AssertHttpError(401, async () => await clientBasic.RevokeCurrentAPIKeyInfo());
|
await AssertHttpError(401, async () => await clientBasic.RevokeCurrentAPIKeyInfo());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
[Fact(Timeout = TestTimeout)]
|
||||||
|
[Trait("Integration", "Integration")]
|
||||||
|
public async Task CanCreateAPIKeyViaAPI()
|
||||||
|
{
|
||||||
|
using (var tester = ServerTester.Create())
|
||||||
|
{
|
||||||
|
await tester.StartAsync();
|
||||||
|
var acc = tester.NewAccount();
|
||||||
|
await acc.GrantAccessAsync();
|
||||||
|
var unrestricted = await acc.CreateClient();
|
||||||
|
var apiKey = await unrestricted.CreateAPIKey(new CreateApiKeyRequest()
|
||||||
|
{
|
||||||
|
Label = "Hello world",
|
||||||
|
Permissions = new Permission[] { Permission.Create(Policies.CanViewProfile) }
|
||||||
|
});
|
||||||
|
Assert.Equal("Hello world", apiKey.Label);
|
||||||
|
var p = Assert.Single(apiKey.Permissions);
|
||||||
|
Assert.Equal(Policies.CanViewProfile, p.Policy);
|
||||||
|
|
||||||
|
var restricted = acc.CreateClientFromAPIKey(apiKey.ApiKey);
|
||||||
|
await AssertHttpError(403, async () => await restricted.CreateAPIKey(new CreateApiKeyRequest()
|
||||||
|
{
|
||||||
|
Label = "Hello world2",
|
||||||
|
Permissions = new Permission[] { Permission.Create(Policies.CanViewProfile) }
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact(Timeout = TestTimeout)]
|
[Fact(Timeout = TestTimeout)]
|
||||||
[Trait("Integration", "Integration")]
|
[Trait("Integration", "Integration")]
|
||||||
|
|
|
@ -87,6 +87,12 @@ namespace BTCPayServer.Tests
|
||||||
Assert.IsType<ViewResult>(await store.RequestPairing(pairingCode.ToString()));
|
Assert.IsType<ViewResult>(await store.RequestPairing(pairingCode.ToString()));
|
||||||
await store.Pair(pairingCode.ToString(), StoreId);
|
await store.Pair(pairingCode.ToString(), StoreId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BTCPayServerClient CreateClientFromAPIKey(string apiKey)
|
||||||
|
{
|
||||||
|
return new BTCPayServerClient(parent.PayTester.ServerUri, apiKey);
|
||||||
|
}
|
||||||
|
|
||||||
public void CreateStore()
|
public void CreateStore()
|
||||||
{
|
{
|
||||||
CreateStoreAsync().GetAwaiter().GetResult();
|
CreateStoreAsync().GetAwaiter().GetResult();
|
||||||
|
|
|
@ -223,5 +223,5 @@
|
||||||
<_ContentIncludedByDefault Remove="Views\Authorization\Authorize.cshtml" />
|
<_ContentIncludedByDefault Remove="Views\Authorization\Authorize.cshtml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ProjectExtensions><VisualStudio><UserProperties /></VisualStudio></ProjectExtensions>
|
<ProjectExtensions><VisualStudio><UserProperties wwwroot_4swagger_4v1_4swagger_1template_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" /></VisualStudio></ProjectExtensions>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -8,6 +8,8 @@ using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using BTCPayServer.Security.GreenField;
|
using BTCPayServer.Security.GreenField;
|
||||||
|
using NBitcoin.DataEncoders;
|
||||||
|
using NBitcoin;
|
||||||
|
|
||||||
namespace BTCPayServer.Controllers.GreenField
|
namespace BTCPayServer.Controllers.GreenField
|
||||||
{
|
{
|
||||||
|
@ -35,9 +37,27 @@ namespace BTCPayServer.Controllers.GreenField
|
||||||
return Ok(FromModel(data));
|
return Ok(FromModel(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("~/api/v1/api-keys")]
|
||||||
|
[Authorize(Policy = Policies.Unrestricted, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||||
|
public async Task<ActionResult<ApiKeyData>> CreateKey(CreateApiKeyRequest request)
|
||||||
|
{
|
||||||
|
if (request is null)
|
||||||
|
return BadRequest();
|
||||||
|
var key = new APIKeyData()
|
||||||
|
{
|
||||||
|
Id = Encoders.Hex.EncodeData(RandomUtils.GetBytes(20)),
|
||||||
|
Type = APIKeyType.Permanent,
|
||||||
|
UserId = _userManager.GetUserId(User),
|
||||||
|
Label = request.Label
|
||||||
|
};
|
||||||
|
key.Permissions = string.Join(";", request.Permissions.Select(p => p.ToString()).Distinct().ToArray());
|
||||||
|
await _apiKeyRepository.CreateKey(key);
|
||||||
|
return Ok(FromModel(key));
|
||||||
|
}
|
||||||
|
|
||||||
[HttpDelete("~/api/v1/api-keys/current")]
|
[HttpDelete("~/api/v1/api-keys/current")]
|
||||||
[Authorize(Policy = Policies.Unrestricted, AuthenticationSchemes = AuthenticationSchemes.GreenfieldAPIKeys)]
|
[Authorize(Policy = Policies.Unrestricted, AuthenticationSchemes = AuthenticationSchemes.GreenfieldAPIKeys)]
|
||||||
public async Task<ActionResult<ApiKeyData>> RevokeKey()
|
public async Task<IActionResult> RevokeKey()
|
||||||
{
|
{
|
||||||
if (!ControllerContext.HttpContext.GetAPIKey(out var apiKey))
|
if (!ControllerContext.HttpContext.GetAPIKey(out var apiKey))
|
||||||
{
|
{
|
||||||
|
|
|
@ -77,7 +77,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"security": [ ]
|
"security": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/v1/users/me": {
|
"/api/v1/users/me": {
|
||||||
|
@ -237,6 +237,83 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/api-keys": {
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"API Keys"
|
||||||
|
],
|
||||||
|
"summary": "Create a new API Key",
|
||||||
|
"description": "Create a new API Key",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Information about the new api key",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ApiKeyData"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"requestBody": {
|
||||||
|
"x-name": "request",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"label": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The label of the new API Key",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"permissions": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "The permissions granted to this API Key (See API Key Authentication)",
|
||||||
|
"nullable": true,
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"API Key": [ "unrestricted" ],
|
||||||
|
"Basic": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"tags": [
|
||||||
|
"API Keys"
|
||||||
|
],
|
||||||
|
"summary": "Revoke the current API Key",
|
||||||
|
"description": "Revoke the current API key so that it cannot be used anymore",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The key was revoked and is no longer usable",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ApiKeyData"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"API Key": [ "unrestricted" ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
|
|
Loading…
Add table
Reference in a new issue