A api key can always revoke itself, add a route to delete any api key

This commit is contained in:
nicolas.dorier 2020-03-27 14:46:51 +09:00
parent 39a8c3fe47
commit 6d7b57ea3b
No known key found for this signature in database
GPG key ID: 6618763EF09186FE
5 changed files with 69 additions and 43 deletions

View file

@ -27,5 +27,13 @@ namespace BTCPayServer.Client
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/api-keys/current", null, HttpMethod.Delete), token);
HandleResponse(response);
}
public virtual async Task RevokeAPIKey(string apikey, CancellationToken token = default)
{
if (apikey == null)
throw new ArgumentNullException(nameof(apikey));
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/api-keys/{apikey}", null, HttpMethod.Delete), token);
HandleResponse(response);
}
}
}

View file

@ -37,7 +37,7 @@ namespace BTCPayServer.Tests
var user = tester.NewAccount();
user.GrantAccess();
await user.MakeAdmin();
var client = await user.CreateClient(Policies.Unrestricted);
var client = await user.CreateClient(Policies.CanViewProfile);
var clientBasic = await user.CreateClient();
//Get current api key
var apiKeyData = await client.GetCurrentAPIKeyInfo();
@ -57,7 +57,7 @@ namespace BTCPayServer.Tests
}
[Fact(Timeout = TestTimeout)]
[Trait("Integration", "Integration")]
public async Task CanCreateAPIKeyViaAPI()
public async Task CanCreateAndDeleteAPIKeyViaAPI()
{
using (var tester = ServerTester.Create())
{
@ -80,6 +80,9 @@ namespace BTCPayServer.Tests
Label = "Hello world2",
Permissions = new Permission[] { Permission.Create(Policies.CanViewProfile) }
}));
await unrestricted.RevokeAPIKey(apiKey.ApiKey);
await AssertHttpError(404, async () => await unrestricted.RevokeAPIKey(apiKey.ApiKey));
}
}

View file

@ -56,17 +56,28 @@ namespace BTCPayServer.Controllers.GreenField
}
[HttpDelete("~/api/v1/api-keys/current")]
[Authorize(Policy = Policies.Unrestricted, AuthenticationSchemes = AuthenticationSchemes.GreenfieldAPIKeys)]
public async Task<IActionResult> RevokeKey()
[Authorize(AuthenticationSchemes = AuthenticationSchemes.GreenfieldAPIKeys)]
public Task<IActionResult> RevokeCurrentKey()
{
if (!ControllerContext.HttpContext.GetAPIKey(out var apiKey))
{
return NotFound();
// Should be impossible (we force apikey auth)
return Task.FromResult<IActionResult>(BadRequest());
}
await _apiKeyRepository.Remove(apiKey, _userManager.GetUserId(User));
return Ok();
return RevokeKey(apiKey);
}
[HttpDelete("~/api/v1/api-keys/{apikey}", Order = 1)]
[Authorize(Policy = Policies.Unrestricted, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
public async Task<IActionResult> RevokeKey(string apikey)
{
if (string.IsNullOrEmpty(apikey))
return BadRequest();
if (await _apiKeyRepository.Remove(apikey, _userManager.GetUserId(User)))
return Ok();
else
return NotFound();
}
private static ApiKeyData FromModel(APIKeyData data)
{
return new ApiKeyData()

View file

@ -53,15 +53,18 @@ namespace BTCPayServer.Security.GreenField
}
}
public async Task Remove(string id, string getUserId)
public async Task<bool> Remove(string id, string getUserId)
{
using (var context = _applicationDbContextFactory.CreateContext())
{
var key = await EntityFrameworkQueryableExtensions.SingleOrDefaultAsync(context.ApiKeys,
data => data.Id == id && data.UserId == getUserId);
if (key == null)
return false;
context.ApiKeys.Remove(key);
await context.SaveChangesAsync();
}
return true;
}
public class APIKeyQuery

View file

@ -188,23 +188,48 @@
]
}
},
"/api/v1/api-keys/{apikey}": {
"delete": {
"tags": [
"API Keys"
],
"summary": "Revoke an API Key",
"description": "Revoke the current API key so that it cannot be used anymore",
"parameters": [
{
"name": "apikey",
"in": "path",
"required": true,
"description": "The API Key to revoke",
"schema": { "type": "string" }
}
],
"responses": {
"200": {
"description": "The key has been deleted"
},
"404": {
"description": "The key is not found for this user"
}
},
"security": [
{
"API Key": [ "unrestricted" ],
"Basic": []
}
]
}
},
"/api/v1/api-keys/current": {
"get": {
"tags": [
"API Keys"
],
"summary": "Get current API Key information",
"summary": "Get the current API Key information",
"description": "View information about the current API key",
"responses": {
"200": {
"description": "Information about the current api key",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiKeyData"
}
}
}
"description": "The key has been deleted"
}
},
"security": [
@ -233,7 +258,7 @@
},
"security": [
{
"API Key": [ "unrestricted" ]
"API Key": []
}
]
}
@ -289,30 +314,6 @@
"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" ]
}
]
}
}
},