mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2024-11-19 01:43:50 +01:00
Greenfield: Admins can create/delete API keys of any user (#4680)
* Greenfield: Admins can create/delete API keys of any user * Greenfield: Improve doc for scoped apikey (Close #4673) * Fix permissions hierarchy * Update BTCPayServer.Client/Permissions.cs * Fix tests --------- Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
This commit is contained in:
parent
d14dafc871
commit
4ae05272c3
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -22,6 +23,15 @@ namespace BTCPayServer.Client
|
|||||||
return await HandleResponse<ApiKeyData>(response);
|
return await HandleResponse<ApiKeyData>(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual async Task<ApiKeyData> CreateAPIKey(string userId, CreateApiKeyRequest request, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (request == null)
|
||||||
|
throw new ArgumentNullException(nameof(request));
|
||||||
|
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/{userId}/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);
|
||||||
@ -35,5 +45,14 @@ namespace BTCPayServer.Client
|
|||||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/api-keys/{apikey}", null, HttpMethod.Delete), token);
|
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/api-keys/{apikey}", null, HttpMethod.Delete), token);
|
||||||
await HandleResponse(response);
|
await HandleResponse(response);
|
||||||
}
|
}
|
||||||
|
public virtual async Task RevokeAPIKey(string userId, string apikey, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (apikey == null)
|
||||||
|
throw new ArgumentNullException(nameof(apikey));
|
||||||
|
if (userId is null)
|
||||||
|
throw new ArgumentNullException(nameof(userId));
|
||||||
|
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/{userId}/api-keys/{apikey}", null, HttpMethod.Delete), token);
|
||||||
|
await HandleResponse(response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ namespace BTCPayServer.Client
|
|||||||
public const string CanViewNotificationsForUser = "btcpay.user.canviewnotificationsforuser";
|
public const string CanViewNotificationsForUser = "btcpay.user.canviewnotificationsforuser";
|
||||||
public const string CanViewUsers = "btcpay.server.canviewusers";
|
public const string CanViewUsers = "btcpay.server.canviewusers";
|
||||||
public const string CanCreateUser = "btcpay.server.cancreateuser";
|
public const string CanCreateUser = "btcpay.server.cancreateuser";
|
||||||
|
public const string CanManageUsers = "btcpay.server.canmanageusers";
|
||||||
public const string CanDeleteUser = "btcpay.user.candeleteuser";
|
public const string CanDeleteUser = "btcpay.user.candeleteuser";
|
||||||
public const string CanManagePullPayments = "btcpay.store.canmanagepullpayments";
|
public const string CanManagePullPayments = "btcpay.store.canmanagepullpayments";
|
||||||
public const string CanCreatePullPayments = "btcpay.store.cancreatepullpayments";
|
public const string CanCreatePullPayments = "btcpay.store.cancreatepullpayments";
|
||||||
@ -73,6 +74,7 @@ namespace BTCPayServer.Client
|
|||||||
yield return CanDepositToCustodianAccounts;
|
yield return CanDepositToCustodianAccounts;
|
||||||
yield return CanWithdrawFromCustodianAccounts;
|
yield return CanWithdrawFromCustodianAccounts;
|
||||||
yield return CanTradeCustodianAccount;
|
yield return CanTradeCustodianAccount;
|
||||||
|
yield return CanManageUsers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static bool IsValidPolicy(string policy)
|
public static bool IsValidPolicy(string policy)
|
||||||
@ -206,15 +208,23 @@ namespace BTCPayServer.Client
|
|||||||
private static void Init()
|
private static void Init()
|
||||||
{
|
{
|
||||||
PolicyHasChild(Policies.CanModifyStoreSettings,
|
PolicyHasChild(Policies.CanModifyStoreSettings,
|
||||||
Policies.CanManageCustodianAccounts, Policies.CanManagePullPayments, Policies.CanModifyInvoices, Policies.CanViewStoreSettings, Policies.CanModifyStoreWebhooks, Policies.CanModifyPaymentRequests );
|
Policies.CanManageCustodianAccounts,
|
||||||
|
Policies.CanManagePullPayments,
|
||||||
|
Policies.CanModifyInvoices,
|
||||||
|
Policies.CanViewStoreSettings,
|
||||||
|
Policies.CanModifyStoreWebhooks,
|
||||||
|
Policies.CanModifyPaymentRequests);
|
||||||
|
|
||||||
|
PolicyHasChild(Policies.CanManageUsers, Policies.CanCreateUser);
|
||||||
PolicyHasChild(Policies.CanManagePullPayments, Policies.CanCreatePullPayments );
|
PolicyHasChild(Policies.CanManagePullPayments, Policies.CanCreatePullPayments );
|
||||||
PolicyHasChild(Policies.CanCreatePullPayments, Policies.CanCreateNonApprovedPullPayments );
|
PolicyHasChild(Policies.CanCreatePullPayments, Policies.CanCreateNonApprovedPullPayments );
|
||||||
PolicyHasChild(Policies.CanModifyPaymentRequests, Policies.CanViewPaymentRequests );
|
PolicyHasChild(Policies.CanModifyPaymentRequests, Policies.CanViewPaymentRequests );
|
||||||
PolicyHasChild(Policies.CanModifyProfile, Policies.CanViewProfile );
|
PolicyHasChild(Policies.CanModifyProfile, Policies.CanViewProfile );
|
||||||
PolicyHasChild(Policies.CanUseLightningNodeInStore, Policies.CanViewLightningInvoiceInStore, Policies.CanCreateLightningInvoiceInStore );
|
PolicyHasChild(Policies.CanUseLightningNodeInStore, Policies.CanViewLightningInvoiceInStore, Policies.CanCreateLightningInvoiceInStore );
|
||||||
PolicyHasChild(Policies.CanManageNotificationsForUser, Policies.CanViewNotificationsForUser );
|
PolicyHasChild(Policies.CanManageNotificationsForUser, Policies.CanViewNotificationsForUser );
|
||||||
PolicyHasChild(Policies.CanModifyServerSettings, Policies.CanUseInternalLightningNode );
|
PolicyHasChild(Policies.CanModifyServerSettings,
|
||||||
|
Policies.CanUseInternalLightningNode,
|
||||||
|
Policies.CanManageUsers);
|
||||||
PolicyHasChild(Policies.CanUseInternalLightningNode, Policies.CanCreateLightningInvoiceInternalNode,Policies.CanViewLightningInvoiceInternalNode );
|
PolicyHasChild(Policies.CanUseInternalLightningNode, Policies.CanCreateLightningInvoiceInternalNode,Policies.CanViewLightningInvoiceInternalNode );
|
||||||
PolicyHasChild(Policies.CanManageCustodianAccounts, Policies.CanViewCustodianAccounts );
|
PolicyHasChild(Policies.CanManageCustodianAccounts, Policies.CanViewCustodianAccounts );
|
||||||
PolicyHasChild(Policies.CanModifyInvoices, Policies.CanViewInvoices, Policies.CanCreateInvoice );
|
PolicyHasChild(Policies.CanModifyInvoices, Policies.CanViewInvoices, Policies.CanCreateInvoice );
|
||||||
|
@ -190,6 +190,41 @@ namespace BTCPayServer.Tests
|
|||||||
|
|
||||||
await unrestricted.RevokeAPIKey(apiKey.ApiKey);
|
await unrestricted.RevokeAPIKey(apiKey.ApiKey);
|
||||||
await AssertAPIError("apikey-not-found", () => unrestricted.RevokeAPIKey(apiKey.ApiKey));
|
await AssertAPIError("apikey-not-found", () => unrestricted.RevokeAPIKey(apiKey.ApiKey));
|
||||||
|
|
||||||
|
|
||||||
|
// Admin create API key to new user
|
||||||
|
acc = tester.NewAccount();
|
||||||
|
await acc.GrantAccessAsync(isAdmin: true);
|
||||||
|
unrestricted = await acc.CreateClient();
|
||||||
|
var newUser = await unrestricted.CreateUser(new CreateApplicationUserRequest() { Email = Utils.GenerateEmail(), Password = "Kitten0@" });
|
||||||
|
var newUserAPIKey = await unrestricted.CreateAPIKey(newUser.Id, new CreateApiKeyRequest()
|
||||||
|
{
|
||||||
|
Label = "Hello world",
|
||||||
|
Permissions = new Permission[] { Permission.Create(Policies.CanViewProfile) }
|
||||||
|
});
|
||||||
|
var newUserClient = acc.CreateClientFromAPIKey(newUserAPIKey.ApiKey);
|
||||||
|
Assert.Equal(newUser.Id, (await newUserClient.GetCurrentUser()).Id);
|
||||||
|
// Admin delete it
|
||||||
|
await unrestricted.RevokeAPIKey(newUser.Id, newUserAPIKey.ApiKey);
|
||||||
|
await Assert.ThrowsAsync<GreenfieldAPIException>(() => newUserClient.GetCurrentUser());
|
||||||
|
|
||||||
|
// Admin create store
|
||||||
|
var store = await unrestricted.CreateStore(new CreateStoreRequest() { Name = "Pouet lol" });
|
||||||
|
|
||||||
|
// Grant right to another user
|
||||||
|
newUserAPIKey = await unrestricted.CreateAPIKey(newUser.Id, new CreateApiKeyRequest()
|
||||||
|
{
|
||||||
|
Label = "Hello world",
|
||||||
|
Permissions = new Permission[] { Permission.Create(Policies.CanViewInvoices, store.Id) },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Despite the grant, the user shouldn't be able to get the invoices!
|
||||||
|
newUserClient = acc.CreateClientFromAPIKey(newUserAPIKey.ApiKey);
|
||||||
|
await Assert.ThrowsAsync<GreenfieldAPIException>(() => newUserClient.GetInvoices(store.Id));
|
||||||
|
|
||||||
|
// if user is a guest or owner, then it should be ok
|
||||||
|
await unrestricted.AddStoreUser(store.Id, new StoreUserData() { UserId = newUser.Id, Role = "Guest" });
|
||||||
|
await newUserClient.GetInvoices(store.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Timeout = TestTimeout)]
|
[Fact(Timeout = TestTimeout)]
|
||||||
|
@ -219,7 +219,7 @@ namespace BTCPayServer.Tests
|
|||||||
var account = parent.PayTester.GetController<UIAccountController>();
|
var account = parent.PayTester.GetController<UIAccountController>();
|
||||||
RegisterDetails = new RegisterViewModel()
|
RegisterDetails = new RegisterViewModel()
|
||||||
{
|
{
|
||||||
Email = Guid.NewGuid() + "@toto.com",
|
Email = Utils.GenerateEmail(),
|
||||||
ConfirmPassword = "Kitten0@",
|
ConfirmPassword = "Kitten0@",
|
||||||
Password = "Kitten0@",
|
Password = "Kitten0@",
|
||||||
IsAdmin = isAdmin
|
IsAdmin = isAdmin
|
||||||
|
@ -160,25 +160,7 @@ namespace BTCPayServer.Tests
|
|||||||
await tester.StartAsync();
|
await tester.StartAsync();
|
||||||
var acc = tester.NewAccount();
|
var acc = tester.NewAccount();
|
||||||
|
|
||||||
var description =
|
var description = UtilitiesTests.GetSecuritySchemeDescription();
|
||||||
"BTCPay Server supports authenticating and authorizing users through an API Key that is generated by them. Send the API Key as a header value to Authorization with the format: `token {token}`. For a smoother experience, you can generate a url that redirects users to an API key creation screen.\n\n The following permissions are available to the context of the user creating the API Key:\n\n#OTHERPERMISSIONS#\n\nThe following permissions are available if the user is an administrator:\n\n#SERVERPERMISSIONS#\n\nThe following permissions applies to all stores of the user, you can limit to a specific store with the following format: `btcpay.store.cancreateinvoice:6HSHAEU4iYWtjxtyRs9KyPjM9GAQp8kw2T9VWbGG1FnZ`:\n\n#STOREPERMISSIONS#\n\nNote that API Keys only limits permission of a user and can never expand it. If an API Key has the permission `btcpay.server.canmodifyserversettings` but that the user account creating this API Key is not administrator, the API Key will not be able to modify the server settings.\nSome permissions may include other permissions, see [this operation](#operation/permissionsMetadata).\n";
|
|
||||||
|
|
||||||
var storePolicies =
|
|
||||||
UIManageController.AddApiKeyViewModel.PermissionValueItem.PermissionDescriptions.Where(pair =>
|
|
||||||
Policies.IsStorePolicy(pair.Key) && !pair.Key.EndsWith(":", StringComparison.InvariantCulture));
|
|
||||||
var serverPolicies =
|
|
||||||
UIManageController.AddApiKeyViewModel.PermissionValueItem.PermissionDescriptions.Where(pair =>
|
|
||||||
Policies.IsServerPolicy(pair.Key));
|
|
||||||
var otherPolicies =
|
|
||||||
UIManageController.AddApiKeyViewModel.PermissionValueItem.PermissionDescriptions.Where(pair =>
|
|
||||||
!Policies.IsStorePolicy(pair.Key) && !Policies.IsServerPolicy(pair.Key));
|
|
||||||
|
|
||||||
description = description.Replace("#OTHERPERMISSIONS#",
|
|
||||||
string.Join("\n", otherPolicies.Select(pair => $"* `{pair.Key}`: {pair.Value.Title}")))
|
|
||||||
.Replace("#SERVERPERMISSIONS#",
|
|
||||||
string.Join("\n", serverPolicies.Select(pair => $"* `{pair.Key}`: {pair.Value.Title}")))
|
|
||||||
.Replace("#STOREPERMISSIONS#",
|
|
||||||
string.Join("\n", storePolicies.Select(pair => $"* `{pair.Key}`: {pair.Value.Title}")));
|
|
||||||
TestLogs.LogInformation(description);
|
TestLogs.LogInformation(description);
|
||||||
|
|
||||||
var sresp = Assert
|
var sresp = Assert
|
||||||
@ -187,7 +169,11 @@ namespace BTCPayServer.Tests
|
|||||||
|
|
||||||
JObject json = JObject.Parse(sresp);
|
JObject json = JObject.Parse(sresp);
|
||||||
|
|
||||||
Assert.Equal(description, json["components"]["securitySchemes"]["API_Key"]["description"].Value<string>());
|
// If this test fail, run `UpdateSwagger` once.
|
||||||
|
if (description != json["components"]["securitySchemes"]["API_Key"]["description"].Value<string>())
|
||||||
|
{
|
||||||
|
Assert.False(true, "Please run manually the test `UpdateSwagger` once");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Amazon.Runtime.Internal;
|
using BTCPayServer.Client;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Controllers;
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.WindowsAzure.Storage.RetryPolicies;
|
|
||||||
using NBitcoin.DataEncoders;
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@ -20,6 +16,39 @@ namespace BTCPayServer.Tests
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class UtilitiesTests
|
public class UtilitiesTests
|
||||||
{
|
{
|
||||||
|
internal static string GetSecuritySchemeDescription()
|
||||||
|
{
|
||||||
|
var description =
|
||||||
|
"BTCPay Server supports authenticating and authorizing users through an API Key that is generated by them. Send the API Key as a header value to Authorization with the format: `token {token}`. For a smoother experience, you can generate a url that redirects users to an API key creation screen.\n\n The following permissions are available to the context of the user creating the API Key:\n\n#OTHERPERMISSIONS#\n\nThe following permissions are available if the user is an administrator:\n\n#SERVERPERMISSIONS#\n\nThe following permissions applies to all stores of the user, you can limit to a specific store with the following format: `btcpay.store.cancreateinvoice:6HSHAEU4iYWtjxtyRs9KyPjM9GAQp8kw2T9VWbGG1FnZ`:\n\n#STOREPERMISSIONS#\n\nNote that API Keys only limits permission of a user and can never expand it. If an API Key has the permission `btcpay.server.canmodifyserversettings` but that the user account creating this API Key is not administrator, the API Key will not be able to modify the server settings.\nSome permissions may include other permissions, see [this operation](#operation/permissionsMetadata).\n";
|
||||||
|
|
||||||
|
var storePolicies =
|
||||||
|
UIManageController.AddApiKeyViewModel.PermissionValueItem.PermissionDescriptions.Where(pair =>
|
||||||
|
Policies.IsStorePolicy(pair.Key) && !pair.Key.EndsWith(":", StringComparison.InvariantCulture));
|
||||||
|
var serverPolicies =
|
||||||
|
UIManageController.AddApiKeyViewModel.PermissionValueItem.PermissionDescriptions.Where(pair =>
|
||||||
|
Policies.IsServerPolicy(pair.Key));
|
||||||
|
var otherPolicies =
|
||||||
|
UIManageController.AddApiKeyViewModel.PermissionValueItem.PermissionDescriptions.Where(pair =>
|
||||||
|
!Policies.IsStorePolicy(pair.Key) && !Policies.IsServerPolicy(pair.Key));
|
||||||
|
|
||||||
|
description = description.Replace("#OTHERPERMISSIONS#",
|
||||||
|
string.Join("\n", otherPolicies.Select(pair => $"* `{pair.Key}`: {pair.Value.Title}")))
|
||||||
|
.Replace("#SERVERPERMISSIONS#",
|
||||||
|
string.Join("\n", serverPolicies.Select(pair => $"* `{pair.Key}`: {pair.Value.Title}")))
|
||||||
|
.Replace("#STOREPERMISSIONS#",
|
||||||
|
string.Join("\n", storePolicies.Select(pair => $"* `{pair.Key}`: {pair.Value.Title}")));
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
[Trait("Utilities", "Utilities")]
|
||||||
|
[Fact]
|
||||||
|
public void UpdateSwagger()
|
||||||
|
{
|
||||||
|
var filePath = Path.Combine(TestUtils.TryGetSolutionDirectoryInfo().FullName, "BTCPayServer", "wwwroot", "swagger", "v1", "swagger.template.json");
|
||||||
|
var o = JObject.Parse(File.ReadAllText(filePath));
|
||||||
|
o["components"]["securitySchemes"]["API_Key"]["description"] = GetSecuritySchemeDescription();
|
||||||
|
File.WriteAllText(filePath, o.ToString(Newtonsoft.Json.Formatting.Indented));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Download transifex transactions and put them in BTCPayServer\wwwroot\locales
|
/// Download transifex transactions and put them in BTCPayServer\wwwroot\locales
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -77,5 +77,10 @@ namespace BTCPayServer.Tests
|
|||||||
}
|
}
|
||||||
// depending on your use case, consider throwing an exception here
|
// depending on your use case, consider throwing an exception here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GenerateEmail()
|
||||||
|
{
|
||||||
|
return Guid.NewGuid() + "@toto.com";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,14 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
|
|
||||||
[HttpPost("~/api/v1/api-keys")]
|
[HttpPost("~/api/v1/api-keys")]
|
||||||
[Authorize(Policy = Policies.Unrestricted, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
[Authorize(Policy = Policies.Unrestricted, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||||
public async Task<IActionResult> CreateKey(CreateApiKeyRequest request)
|
public Task<IActionResult> CreateAPIKey(CreateApiKeyRequest request)
|
||||||
|
{
|
||||||
|
return CreateUserAPIKey(_userManager.GetUserId(User), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("~/api/v1/users/{userId}/api-keys")]
|
||||||
|
[Authorize(Policy = Policies.CanManageUsers, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||||
|
public async Task<IActionResult> CreateUserAPIKey(string userId, CreateApiKeyRequest request)
|
||||||
{
|
{
|
||||||
request ??= new CreateApiKeyRequest();
|
request ??= new CreateApiKeyRequest();
|
||||||
request.Permissions ??= System.Array.Empty<Permission>();
|
request.Permissions ??= System.Array.Empty<Permission>();
|
||||||
@ -52,7 +59,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
{
|
{
|
||||||
Id = Encoders.Hex.EncodeData(RandomUtils.GetBytes(20)),
|
Id = Encoders.Hex.EncodeData(RandomUtils.GetBytes(20)),
|
||||||
Type = APIKeyType.Permanent,
|
Type = APIKeyType.Permanent,
|
||||||
UserId = _userManager.GetUserId(User),
|
UserId = userId,
|
||||||
Label = request.Label
|
Label = request.Label
|
||||||
};
|
};
|
||||||
key.SetBlob(new APIKeyBlob()
|
key.SetBlob(new APIKeyBlob()
|
||||||
@ -72,19 +79,27 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
// Should be impossible (we force apikey auth)
|
// Should be impossible (we force apikey auth)
|
||||||
return Task.FromResult<IActionResult>(BadRequest());
|
return Task.FromResult<IActionResult>(BadRequest());
|
||||||
}
|
}
|
||||||
return RevokeKey(apiKey);
|
return RevokeAPIKey(apiKey);
|
||||||
}
|
}
|
||||||
[HttpDelete("~/api/v1/api-keys/{apikey}", Order = 1)]
|
[HttpDelete("~/api/v1/api-keys/{apikey}", Order = 1)]
|
||||||
[Authorize(Policy = Policies.Unrestricted, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
[Authorize(Policy = Policies.Unrestricted, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||||
public async Task<IActionResult> RevokeKey(string apikey)
|
public Task<IActionResult> RevokeAPIKey(string apikey)
|
||||||
|
{
|
||||||
|
return RevokeAPIKey(_userManager.GetUserId(User), apikey);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("~/api/v1/users/{userId}/api-keys/{apikey}", Order = 1)]
|
||||||
|
[Authorize(Policy = Policies.CanManageUsers, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||||
|
public async Task<IActionResult> RevokeAPIKey(string userId, string apikey)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(apikey) &&
|
if (!string.IsNullOrEmpty(apikey) &&
|
||||||
await _apiKeyRepository.Remove(apikey, _userManager.GetUserId(User)))
|
await _apiKeyRepository.Remove(apikey, userId))
|
||||||
return Ok();
|
return Ok();
|
||||||
else
|
else
|
||||||
return this.CreateAPIError("apikey-not-found", "This apikey does not exists");
|
return this.CreateAPIError("apikey-not-found", "This apikey does not exists");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static ApiKeyData FromModel(APIKeyData data)
|
private static ApiKeyData FromModel(APIKeyData data)
|
||||||
{
|
{
|
||||||
return new ApiKeyData()
|
return new ApiKeyData()
|
||||||
|
@ -670,7 +670,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
public override async Task<ApiKeyData> CreateAPIKey(CreateApiKeyRequest request,
|
public override async Task<ApiKeyData> CreateAPIKey(CreateApiKeyRequest request,
|
||||||
CancellationToken token = default)
|
CancellationToken token = default)
|
||||||
{
|
{
|
||||||
return GetFromActionResult<ApiKeyData>(await GetController<GreenfieldApiKeysController>().CreateKey(request));
|
return GetFromActionResult<ApiKeyData>(await GetController<GreenfieldApiKeysController>().CreateAPIKey(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task RevokeCurrentAPIKeyInfo(CancellationToken token = default)
|
public override async Task RevokeCurrentAPIKeyInfo(CancellationToken token = default)
|
||||||
@ -680,7 +680,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
|
|
||||||
public override async Task RevokeAPIKey(string apikey, CancellationToken token = default)
|
public override async Task RevokeAPIKey(string apikey, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
HandleActionResult(await GetController<GreenfieldApiKeysController>().RevokeKey(apikey));
|
HandleActionResult(await GetController<GreenfieldApiKeysController>().RevokeAPIKey(apikey));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<IEnumerable<NotificationData>> GetNotifications(bool? seen = null,
|
public override async Task<IEnumerable<NotificationData>> GetNotifications(bool? seen = null,
|
||||||
|
@ -509,6 +509,7 @@ namespace BTCPayServer.Controllers
|
|||||||
{Policies.Unrestricted, ("Unrestricted access", "The app will have unrestricted access to your account.")},
|
{Policies.Unrestricted, ("Unrestricted access", "The app will have unrestricted access to your account.")},
|
||||||
{Policies.CanViewUsers, ("View users", "The app will be able to see all users on this server.")},
|
{Policies.CanViewUsers, ("View users", "The app will be able to see all users on this server.")},
|
||||||
{Policies.CanCreateUser, ("Create new users", "The app will be able to create new users on this server.")},
|
{Policies.CanCreateUser, ("Create new users", "The app will be able to create new users on this server.")},
|
||||||
|
{Policies.CanManageUsers, ("Manage users", "The app will be able to create/delete API keys for users.")},
|
||||||
{Policies.CanDeleteUser, ("Delete user", "The app will be able to delete the user to whom it is assigned. Admin users can delete any user without this permission.")},
|
{Policies.CanDeleteUser, ("Delete user", "The app will be able to delete the user to whom it is assigned. Admin users can delete any user without this permission.")},
|
||||||
{Policies.CanModifyStoreSettings, ("Modify your stores", "The app will be able to manage invoices on all your stores and modify their settings.")},
|
{Policies.CanModifyStoreSettings, ("Modify your stores", "The app will be able to manage invoices on all your stores and modify their settings.")},
|
||||||
{$"{Policies.CanModifyStoreSettings}:", ("Manage selected stores", "The app will be able to manage invoices on the selected stores and modify their settings.")},
|
{$"{Policies.CanModifyStoreSettings}:", ("Manage selected stores", "The app will be able to manage invoices on the selected stores and modify their settings.")},
|
||||||
|
@ -47,7 +47,6 @@ namespace BTCPayServer.Security.Greenfield
|
|||||||
{
|
{
|
||||||
throw new InvalidOperationException("cannot save a bitpay legacy api key with this repository");
|
throw new InvalidOperationException("cannot save a bitpay legacy api key with this repository");
|
||||||
}
|
}
|
||||||
|
|
||||||
using var context = _applicationDbContextFactory.CreateContext();
|
using var context = _applicationDbContextFactory.CreateContext();
|
||||||
await context.ApiKeys.AddAsync(key);
|
await context.ApiKeys.AddAsync(key);
|
||||||
await context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
@ -33,6 +33,46 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/users/{userId}/api-keys/{apikey}": {
|
||||||
|
"delete": {
|
||||||
|
"operationId": "ApiKeys_DeleteUserApiKey",
|
||||||
|
"tags": [
|
||||||
|
"API Keys"
|
||||||
|
],
|
||||||
|
"summary": "Revoke an API Key of target user",
|
||||||
|
"description": "Revoke the API key of a target user so that it cannot be used anymore",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "userId",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"description": "The target user",
|
||||||
|
"schema": { "type": "string" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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": {
|
"/api/v1/api-keys/current": {
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "ApiKeys_GetCurrentApiKey",
|
"operationId": "ApiKeys_GetCurrentApiKey",
|
||||||
@ -65,7 +105,8 @@
|
|||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"API_Key": []
|
"API_Key": [ "btcpay.server.canmanageusers" ],
|
||||||
|
"Basic": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -178,6 +219,89 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/users/{userId}/api-keys": {
|
||||||
|
"post": {
|
||||||
|
"operationId": "ApiKeys_CreateUserApiKey",
|
||||||
|
"tags": [
|
||||||
|
"API Keys"
|
||||||
|
],
|
||||||
|
"summary": "Create a new API Key for a user",
|
||||||
|
"description": "Create a new API Key for a user",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "userId",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"description": "The target user",
|
||||||
|
"schema": { "type": "string" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Information about the new api key",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ApiKeyData"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Missing authorization",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ProblemDetails"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"description": "Unexpected error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ProblemDetails"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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": [ "btcpay.server.canmanageusers" ],
|
||||||
|
"Basic": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
@ -198,11 +322,12 @@
|
|||||||
},
|
},
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"description": "The permissions associated to this API Key",
|
"description": "The permissions associated to this API Key (can be scoped to a specific store)",
|
||||||
"nullable": false,
|
"nullable": false,
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
},
|
||||||
|
"example": [ "btcpay.server.canmanageusers", "btcpay.server.canmanageusers:2KxSpc9V5zDWfUbvgYiZuAfka4wUhGF96F75Ao8y4zHP" ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,12 +99,20 @@
|
|||||||
"example": 90
|
"example": 90
|
||||||
},
|
},
|
||||||
"TimeSpanSeconds": {
|
"TimeSpanSeconds": {
|
||||||
"allOf": [ { "$ref": "#/components/schemas/TimeSpan" } ],
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/TimeSpan"
|
||||||
|
}
|
||||||
|
],
|
||||||
"format": "seconds",
|
"format": "seconds",
|
||||||
"description": "A span of times in seconds"
|
"description": "A span of times in seconds"
|
||||||
},
|
},
|
||||||
"TimeSpanMinutes": {
|
"TimeSpanMinutes": {
|
||||||
"allOf": [ { "$ref": "#/components/schemas/TimeSpan" } ],
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/TimeSpan"
|
||||||
|
}
|
||||||
|
],
|
||||||
"format": "minutes",
|
"format": "minutes",
|
||||||
"description": "A span of times in minutes"
|
"description": "A span of times in minutes"
|
||||||
}
|
}
|
||||||
@ -112,7 +120,7 @@
|
|||||||
"securitySchemes": {
|
"securitySchemes": {
|
||||||
"API_Key": {
|
"API_Key": {
|
||||||
"type": "apiKey",
|
"type": "apiKey",
|
||||||
"description": "BTCPay Server supports authenticating and authorizing users through an API Key that is generated by them. Send the API Key as a header value to Authorization with the format: `token {token}`. For a smoother experience, you can generate a url that redirects users to an API key creation screen.\n\n The following permissions are available to the context of the user creating the API Key:\n\n* `unrestricted`: Unrestricted access\n* `btcpay.user.candeleteuser`: Delete user\n* `btcpay.user.canviewprofile`: View your profile\n* `btcpay.user.canmodifyprofile`: Manage your profile\n* `btcpay.user.canmanagenotificationsforuser`: Manage your notifications\n* `btcpay.user.canviewnotificationsforuser`: View your notifications\n\nThe following permissions are available if the user is an administrator:\n\n* `btcpay.server.canviewusers`: View users\n* `btcpay.server.cancreateuser`: Create new users\n* `btcpay.server.canmodifyserversettings`: Manage your server\n* `btcpay.server.canuseinternallightningnode`: Use the internal lightning node\n* `btcpay.server.canviewlightninginvoiceinternalnode`: View invoices from internal lightning node\n* `btcpay.server.cancreatelightninginvoiceinternalnode`: Create invoices with internal lightning node\n\nThe following permissions applies to all stores of the user, you can limit to a specific store with the following format: `btcpay.store.cancreateinvoice:6HSHAEU4iYWtjxtyRs9KyPjM9GAQp8kw2T9VWbGG1FnZ`:\n\n* `btcpay.store.canmodifystoresettings`: Modify your stores\n* `btcpay.store.canviewcustodianaccounts`: View exchange accounts linked to your stores\n* `btcpay.store.canmanagecustodianaccounts`: Manage exchange accounts linked to your stores\n* `btcpay.store.candeposittocustodianaccount`: Deposit funds to exchange accounts linked to your stores\n* `btcpay.store.canwithdrawfromcustodianaccount`: Withdraw funds from exchange accounts to your store\n* `btcpay.store.cantradecustodianaccount`: Trade funds on your store's exchange accounts\n* `btcpay.store.webhooks.canmodifywebhooks`: Modify stores webhooks\n* `btcpay.store.canviewstoresettings`: View your stores\n* `btcpay.store.cancreateinvoice`: Create an invoice\n* `btcpay.store.canviewinvoices`: View invoices\n* `btcpay.store.canmodifyinvoices`: Modify invoices\n* `btcpay.store.canmodifypaymentrequests`: Modify your payment requests\n* `btcpay.store.canviewpaymentrequests`: View your payment requests\n* `btcpay.store.canmanagepullpayments`: Manage your pull payments\n* `btcpay.store.cancreatepullpayments`: Create pull payments\n* `btcpay.store.cancreatenonapprovedpullpayments`: Create non-approved pull payments\n* `btcpay.store.canuselightningnode`: Use the lightning nodes associated with your stores\n* `btcpay.store.canviewlightninginvoice`: View the lightning invoices associated with your stores\n* `btcpay.store.cancreatelightninginvoice`: Create invoices from the lightning nodes associated with your stores\n\nNote that API Keys only limits permission of a user and can never expand it. If an API Key has the permission `btcpay.server.canmodifyserversettings` but that the user account creating this API Key is not administrator, the API Key will not be able to modify the server settings.\nSome permissions may include other permissions, see [this operation](#operation/permissionsMetadata).\n",
|
"description": "BTCPay Server supports authenticating and authorizing users through an API Key that is generated by them. Send the API Key as a header value to Authorization with the format: `token {token}`. For a smoother experience, you can generate a url that redirects users to an API key creation screen.\n\n The following permissions are available to the context of the user creating the API Key:\n\n* `unrestricted`: Unrestricted access\n* `btcpay.user.candeleteuser`: Delete user\n* `btcpay.user.canviewprofile`: View your profile\n* `btcpay.user.canmodifyprofile`: Manage your profile\n* `btcpay.user.canmanagenotificationsforuser`: Manage your notifications\n* `btcpay.user.canviewnotificationsforuser`: View your notifications\n\nThe following permissions are available if the user is an administrator:\n\n* `btcpay.server.canviewusers`: View users\n* `btcpay.server.cancreateuser`: Create new users\n* `btcpay.server.canmanageusers`: Manage users\n* `btcpay.server.canmodifyserversettings`: Manage your server\n* `btcpay.server.canuseinternallightningnode`: Use the internal lightning node\n* `btcpay.server.canviewlightninginvoiceinternalnode`: View invoices from internal lightning node\n* `btcpay.server.cancreatelightninginvoiceinternalnode`: Create invoices with internal lightning node\n\nThe following permissions applies to all stores of the user, you can limit to a specific store with the following format: `btcpay.store.cancreateinvoice:6HSHAEU4iYWtjxtyRs9KyPjM9GAQp8kw2T9VWbGG1FnZ`:\n\n* `btcpay.store.canmodifystoresettings`: Modify your stores\n* `btcpay.store.canviewcustodianaccounts`: View exchange accounts linked to your stores\n* `btcpay.store.canmanagecustodianaccounts`: Manage exchange accounts linked to your stores\n* `btcpay.store.candeposittocustodianaccount`: Deposit funds to exchange accounts linked to your stores\n* `btcpay.store.canwithdrawfromcustodianaccount`: Withdraw funds from exchange accounts to your store\n* `btcpay.store.cantradecustodianaccount`: Trade funds on your store's exchange accounts\n* `btcpay.store.webhooks.canmodifywebhooks`: Modify stores webhooks\n* `btcpay.store.canviewstoresettings`: View your stores\n* `btcpay.store.cancreateinvoice`: Create an invoice\n* `btcpay.store.canviewinvoices`: View invoices\n* `btcpay.store.canmodifyinvoices`: Modify invoices\n* `btcpay.store.canmodifypaymentrequests`: Modify your payment requests\n* `btcpay.store.canviewpaymentrequests`: View your payment requests\n* `btcpay.store.canmanagepullpayments`: Manage your pull payments\n* `btcpay.store.cancreatepullpayments`: Create pull payments\n* `btcpay.store.cancreatenonapprovedpullpayments`: Create non-approved pull payments\n* `btcpay.store.canuselightningnode`: Use the lightning nodes associated with your stores\n* `btcpay.store.canviewlightninginvoice`: View the lightning invoices associated with your stores\n* `btcpay.store.cancreatelightninginvoice`: Create invoices from the lightning nodes associated with your stores\n\nNote that API Keys only limits permission of a user and can never expand it. If an API Key has the permission `btcpay.server.canmodifyserversettings` but that the user account creating this API Key is not administrator, the API Key will not be able to modify the server settings.\nSome permissions may include other permissions, see [this operation](#operation/permissionsMetadata).\n",
|
||||||
"name": "Authorization",
|
"name": "Authorization",
|
||||||
"in": "header"
|
"in": "header"
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user