Files
btcpayserver/BTCPayServer.Tests/UtilitiesTests.cs
Nicolas Dorier 4ae05272c3 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>
2023-02-24 16:19:03 +09:00

157 lines
8.2 KiB
C#

using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using BTCPayServer.Client;
using BTCPayServer.Controllers;
using Newtonsoft.Json.Linq;
using Xunit;
namespace BTCPayServer.Tests
{
/// <summary>
/// This class hold easy to run utilities for dev time
/// </summary>
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>
/// Download transifex transactions and put them in BTCPayServer\wwwroot\locales
/// </summary>
[FactWithSecret("TransifexAPIToken")]
[Trait("Utilities", "Utilities")]
public async Task PullTransifexTranslations()
{
// 1. Generate an API Token on https://www.transifex.com/user/settings/api/
// 2. Run "dotnet user-secrets set TransifexAPIToken <youapitoken>"
var client = new TransifexClient(FactWithSecretAttribute.GetFromSecrets("TransifexAPIToken"));
var proj = "o:btcpayserver:p:btcpayserver";
var resource = $"{proj}:r:enjson";
var json = await client.GetTransifexAsync($"https://rest.api.transifex.com/resource_language_stats?filter[project]={proj}&filter[resource]={resource}");
var langs = json["data"].Select(o => o["id"].Value<string>().Split(':').Last()).ToArray();
json = await client.GetTransifexAsync($"https://rest.api.transifex.com/resource_strings?filter[resource]={resource}");
var hashToKeys = json["data"]
.ToDictionary(
o => o["id"].Value<string>().Split(':').Last(),
o => o["attributes"]["key"].Value<string>().Replace("\\.", "."));
var translations = new ConcurrentDictionary<string, JObject>();
translations.TryAdd("en",
new JObject(
json["data"]
.Select(o => new JProperty(
o["attributes"]["key"].Value<string>().Replace("\\.", "."),
o["attributes"]["strings"]["other"].Value<string>())))
);
var langsDir = Path.Combine(TestUtils.TryGetSolutionDirectoryInfo().FullName, "BTCPayServer", "wwwroot", "locales");
Task.WaitAll(langs.Select(async l =>
{
if (l == "en")
return;
retry:
var j = await client.GetTransifexAsync($"https://rest.api.transifex.com/resource_translations?filter[resource]={resource}&filter[language]=l:{l}");
try
{
var jobj = new JObject(
j["data"].Select(o => (Key: hashToKeys[o["id"].Value<string>().Split(':')[^3]], Strings: o["attributes"]["strings"]))
.Select(o =>
new JProperty(
o.Key,
o.Strings.Type == JTokenType.Null ? translations["en"][o.Key].Value<string>() : o.Strings["other"].Value<string>()
)));
if (l == "ne_NP")
l = "np_NP";
if (l == "zh_CN")
l = "zh-SP";
if (l == "kk")
l = "kk-KZ";
var langCode = l.Replace("_", "-");
jobj["code"] = langCode;
if ((string)jobj["currentLanguage"] == "English")
return; // Not translated
if ((string)jobj["currentLanguage"] == "disable")
return; // Not translated
if (jobj["InvoiceExpired_Body_3"].Value<string>() == translations["en"]["InvoiceExpired_Body_3"].Value<string>())
{
jobj["InvoiceExpired_Body_3"] = string.Empty;
}
translations.TryAdd(langCode, jobj);
}
catch
{
goto retry;
}
}).ToArray());
foreach (var t in translations)
{
t.Value.AddFirst(new JProperty("NOTICE_WARN", "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/"));
var content = t.Value.ToString(Newtonsoft.Json.Formatting.Indented);
File.WriteAllText(Path.Combine(langsDir, $"{t.Key}.json"), content);
}
}
}
public class TransifexClient
{
public TransifexClient(string apiToken)
{
Client = new HttpClient();
APIToken = apiToken;
}
public HttpClient Client { get; }
public string APIToken { get; }
public async Task<JObject> GetTransifexAsync(string uri)
{
var message = new HttpRequestMessage(HttpMethod.Get, uri);
message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", APIToken);
message.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/vnd.api+json"));
var response = await Client.SendAsync(message);
var str = await response.Content.ReadAsStringAsync();
return JObject.Parse(str);
}
}
}