diff --git a/BTCPayServer.Client/BTCPayServerClient.Misc.cs b/BTCPayServer.Client/BTCPayServerClient.Misc.cs new file mode 100644 index 000000000..258628ca0 --- /dev/null +++ b/BTCPayServer.Client/BTCPayServerClient.Misc.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using BTCPayServer.Client.Models; + +namespace BTCPayServer.Client +{ + public partial class BTCPayServerClient + { + public virtual async Task GetPermissionMetadata(CancellationToken token = default) + { + var response = await _httpClient.SendAsync(CreateHttpRequest("misc/permissions"), token); + return await HandleResponse(response); + } + public virtual async Task GetAvailableLanguages(CancellationToken token = default) + { + var response = await _httpClient.SendAsync(CreateHttpRequest("misc/lang"), token); + return await HandleResponse(response); + } + } +} diff --git a/BTCPayServer.Client/Models/Language.cs b/BTCPayServer.Client/Models/Language.cs new file mode 100644 index 000000000..9e1ac141c --- /dev/null +++ b/BTCPayServer.Client/Models/Language.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json; + +namespace BTCPayServer.Client.Models +{ + public class Language + { + public Language(string code, string displayName) + { + DisplayName = displayName; + Code = code; + } + + [JsonProperty("code")] + public string Code { get; set; } + [JsonProperty("currentLanguage")] + public string DisplayName { get; set; } + } +} diff --git a/BTCPayServer.Client/Models/PermissionMetadata.cs b/BTCPayServer.Client/Models/PermissionMetadata.cs new file mode 100644 index 000000000..cf02a6270 --- /dev/null +++ b/BTCPayServer.Client/Models/PermissionMetadata.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json; + +namespace BTCPayServer.Client.Models +{ + public class PermissionMetadata + { + static PermissionMetadata() + { + Dictionary nodes = new Dictionary(); + foreach (var policy in Client.Policies.AllPolicies) + { + nodes.Add(policy, new PermissionMetadata() { PermissionName = policy }); + } + foreach (var n in nodes) + { + foreach (var policy in Client.Policies.AllPolicies) + { + if (policy.Equals(n.Key, StringComparison.OrdinalIgnoreCase)) + continue; + if (Client.Permission.Create(n.Key).Contains(Client.Permission.Create(policy))) + n.Value.SubPermissions.Add(policy); + } + } + foreach (var n in nodes) + { + n.Value.SubPermissions.Sort(); + } + PermissionNodes = nodes.Values.OrderBy(v => v.PermissionName).ToArray(); + } + public readonly static PermissionMetadata[] PermissionNodes; + [JsonProperty("name")] + public string PermissionName { get; set; } + [JsonProperty("included")] + public List SubPermissions { get; set; } = new List(); + } +} diff --git a/BTCPayServer.Tests/GreenfieldAPITests.cs b/BTCPayServer.Tests/GreenfieldAPITests.cs index 017d5cf51..0f57c1244 100644 --- a/BTCPayServer.Tests/GreenfieldAPITests.cs +++ b/BTCPayServer.Tests/GreenfieldAPITests.cs @@ -69,6 +69,27 @@ namespace BTCPayServer.Tests } } + [Fact(Timeout = TestTimeout)] + [Trait("Integration", "Integration")] + public async Task CanUseMiscAPIs() + { + using (var tester = ServerTester.Create()) + { + await tester.StartAsync(); + var acc = tester.NewAccount(); + await acc.GrantAccessAsync(); + var unrestricted = await acc.CreateClient(); + var langs = await unrestricted.GetAvailableLanguages(); + Assert.NotEmpty(langs); + Assert.NotNull(langs[0].Code); + Assert.NotNull(langs[0].DisplayName); + + var perms = await unrestricted.GetPermissionMetadata(); + Assert.NotEmpty(perms); + var p = perms.First(p => p.PermissionName == "unrestricted"); + Assert.True(p.SubPermissions.Count > 6); + } + } [Fact(Timeout = TestTimeout)] [Trait("Integration", "Integration")] diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs index ed9e7f941..0d0647db4 100644 --- a/BTCPayServer.Tests/UnitTest1.cs +++ b/BTCPayServer.Tests/UnitTest1.cs @@ -323,7 +323,7 @@ namespace BTCPayServer.Tests var acc = tester.NewAccount(); 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.\n"; + "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 = ManageController.AddApiKeyViewModel.PermissionValueItem.PermissionDescriptions.Where(pair => diff --git a/BTCPayServer/Controllers/HomeController.cs b/BTCPayServer/Controllers/HomeController.cs index fc625e565..1095b9fc5 100644 --- a/BTCPayServer/Controllers/HomeController.cs +++ b/BTCPayServer/Controllers/HomeController.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; using BTCPayServer.Abstractions.Constants; +using BTCPayServer.Client; using BTCPayServer.Data; using BTCPayServer.Filters; using BTCPayServer.HostedServices; @@ -14,6 +15,8 @@ using BTCPayServer.Models.StoreViewModels; using BTCPayServer.Security; using BTCPayServer.Services; using BTCPayServer.Services.Apps; +using ExchangeSharp; +using Google.Apis.Auth.OAuth2; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -70,6 +73,14 @@ namespace BTCPayServer.Controllers return Json(LanguageService.GetLanguages(), new JsonSerializerSettings() { Formatting = Formatting.Indented }); } + + [Route("misc/permissions")] + [Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie + "," + AuthenticationSchemes.Greenfield)] + public IActionResult Permissions() + { + return Json(Client.Models.PermissionMetadata.PermissionNodes, new JsonSerializerSettings() { Formatting = Formatting.Indented }); + } + [Route("swagger/v1/swagger.json")] [Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie + "," + AuthenticationSchemes.Greenfield)] public async Task Swagger() diff --git a/BTCPayServer/Services/LanguageService.cs b/BTCPayServer/Services/LanguageService.cs index a72959ce5..5178a8506 100644 --- a/BTCPayServer/Services/LanguageService.cs +++ b/BTCPayServer/Services/LanguageService.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using BTCPayServer.Client.Models; using Microsoft.AspNetCore.Hosting; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -9,20 +10,6 @@ using Newtonsoft.Json.Serialization; namespace BTCPayServer.Services { - public class Language - { - public Language(string code, string displayName) - { - DisplayName = displayName; - Code = code; - } - - [JsonProperty("code")] - public string Code { get; set; } - [JsonProperty("currentLanguage")] - public string DisplayName { get; set; } - } - public class LanguageService { private readonly Language[] _languages; diff --git a/BTCPayServer/wwwroot/swagger/v1/swagger.template.json b/BTCPayServer/wwwroot/swagger/v1/swagger.template.json index 5000aafe7..4b8f58a7b 100644 --- a/BTCPayServer/wwwroot/swagger/v1/swagger.template.json +++ b/BTCPayServer/wwwroot/swagger/v1/swagger.template.json @@ -76,7 +76,7 @@ "securitySchemes": { "API Key": { "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.cancreateuser`: Create new users\n* `btcpay.server.canmodifyserversettings`: Manage your server\n* `btcpay.server.canuseinternallightningnode`: Use the 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.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.canmodifypaymentrequests`: Modify your payment requests\n* `btcpay.store.canviewpaymentrequests`: View your payment requests\n* `btcpay.store.canuselightningnode`: Use the lightning nodes associated with your stores\n* `btcpay.store.cancreatelightninginvoice`: Create invoices 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.\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.cancreateuser`: Create new users\n* `btcpay.server.canmodifyserversettings`: Manage your server\n* `btcpay.server.canuseinternallightningnode`: Use the 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.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.canmodifypaymentrequests`: Modify your payment requests\n* `btcpay.store.canviewpaymentrequests`: View your payment requests\n* `btcpay.store.canuselightningnode`: Use the lightning nodes associated with your stores\n* `btcpay.store.cancreatelightninginvoice`: Create invoices 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", "in": "header", "scheme": "token" diff --git a/BTCPayServer/wwwroot/swagger/v1/swagger.template.misc.json b/BTCPayServer/wwwroot/swagger/v1/swagger.template.misc.json index 729214399..500e410fe 100644 --- a/BTCPayServer/wwwroot/swagger/v1/swagger.template.misc.json +++ b/BTCPayServer/wwwroot/swagger/v1/swagger.template.misc.json @@ -1,9 +1,50 @@ { "paths": { + "/misc/permissions": { + "get": { + "tags": [ + "Miscelleneous" + ], + "summary": "Permissions metadata", + "description": "The metadata of available permissions", + "operationId": "permissionsMetadata", + "responses": { + "200": { + "description": "The metadata of available permissions", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The permission id", + "nullable": false + }, + "included": { + "type": "array", + "description": "Permissions included in this array are also granted by this permission", + "nullable": false, + "items": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "security": [] + } + }, "/misc/lang": { "get": { "tags": [ - "Miscalleneous" + "Miscelleneous" ], "summary": "Language codes", "description": "The supported language codes", @@ -61,7 +102,7 @@ ], "get": { "tags": [ - "Miscalleneous" + "Miscelleneous" ], "summary": "Invoice checkout", "description": "View the checkout page of an invoice", @@ -81,7 +122,7 @@ }, "tags": [ { - "name": "Miscalleneous" + "name": "Miscelleneous" } ] }