BTCPayServer.Client library + Revoke API Key

This commit is contained in:
Kukks 2020-03-02 16:50:28 +01:00
parent c74f52a61c
commit 233fa8a4a1
18 changed files with 285 additions and 136 deletions

View file

@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,22 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
{
public partial class BTCPayServerClient
{
public virtual async Task<ApiKeyData> GetCurrentAPIKeyInfo(CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/api-keys/current"), token);
return await HandleResponse<ApiKeyData>(response);
}
public virtual async Task RevokeCurrentAPIKeyInfo(CancellationToken token = default)
{
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/api-keys/current", null, HttpMethod.Delete), token);
HandleResponse(response);
}
}
}

View file

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
namespace BTCPayServer.Client
{
public partial class BTCPayServerClient
{
public static Uri GenerateAuthorizeUri(Uri btcpayHost, string[] permissions, bool strict = true,
bool selectiveStores = false)
{
var result = new UriBuilder(btcpayHost);
result.Path = "api-keys/authorize";
AppendPayloadToQuery(result,
new Dictionary<string, object>()
{
{"strict", strict}, {"selectiveStores", selectiveStores}, {"permissions", permissions}
});
return result.Uri;
}
}
}

View file

@ -0,0 +1,96 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Threading.Tasks;
namespace BTCPayServer.Client
{
public partial class BTCPayServerClient
{
private readonly string _apiKey;
private readonly Uri _btcpayHost;
private readonly HttpClient _httpClient;
private readonly JsonSerializerOptions _serializerOptions = new JsonSerializerOptions()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
public BTCPayServerClient(Uri btcpayHost, string APIKey, HttpClient httpClient = null)
{
_apiKey = APIKey;
_btcpayHost = btcpayHost;
_httpClient = httpClient ?? new HttpClient();
}
protected void HandleResponse(HttpResponseMessage message)
{
message.EnsureSuccessStatusCode();
}
protected async Task<T> HandleResponse<T>(HttpResponseMessage message)
{
HandleResponse(message);
return JsonSerializer.Deserialize<T>(await message.Content.ReadAsStringAsync(), _serializerOptions);
}
protected virtual HttpRequestMessage CreateHttpRequest(string path,
Dictionary<string, object> queryPayload = null,
HttpMethod method = null)
{
UriBuilder uriBuilder = new UriBuilder(_btcpayHost) {Path = path};
if (queryPayload != null && queryPayload.Any())
{
AppendPayloadToQuery(uriBuilder, queryPayload);
}
var httpRequest = new HttpRequestMessage(method ?? HttpMethod.Get, uriBuilder.Uri);
httpRequest.Headers.Authorization = new AuthenticationHeaderValue("token", _apiKey);
return httpRequest;
}
protected virtual HttpRequestMessage CreateHttpRequest<T>(string path,
Dictionary<string, object> queryPayload = null,
T bodyPayload = default, HttpMethod method = null)
{
var request = CreateHttpRequest(path, queryPayload, method);
if (typeof(T).IsPrimitive || !EqualityComparer<T>.Default.Equals(bodyPayload, default(T)))
{
request.Content = new StringContent(JsonSerializer.Serialize(bodyPayload, _serializerOptions));
}
return request;
}
private static void AppendPayloadToQuery(UriBuilder uri, Dictionary<string, object> payload)
{
if (uri.Query.Length > 1)
uri.Query += "&";
foreach (KeyValuePair<string, object> keyValuePair in payload)
{
UriBuilder uriBuilder = uri;
if (keyValuePair.Value.GetType().GetInterfaces().Contains((typeof(IEnumerable))))
{
foreach (var item in (IEnumerable)keyValuePair.Value)
{
uriBuilder.Query = uriBuilder.Query + Uri.EscapeDataString(keyValuePair.Key) + "=" +
Uri.EscapeDataString(item.ToString()) + "&";
}
}
else
{
uriBuilder.Query = uriBuilder.Query + Uri.EscapeDataString(keyValuePair.Key) + "=" +
Uri.EscapeDataString(keyValuePair.Value.ToString()) + "&";
}
}
uri.Query = uri.Query.Trim('&');
}
}
}

View file

@ -0,0 +1,10 @@
namespace BTCPayServer.Client.Models
{
public class ApiKeyData
{
public string ApiKey { get; set; }
public string Label { get; set; }
public string UserId { get; set; }
public string[] Permissions { get; set; }
}
}

View file

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
namespace BTCPayServer.Client
{
public static class Permissions
{
public const string ServerManagement = nameof(ServerManagement);
public const string StoreManagement = nameof(StoreManagement);
public static string GetStorePermission(string storeId) => $"{nameof(StoreManagement)}:{storeId}";
public static IEnumerable<string> ExtractStorePermissionsIds(IEnumerable<string> permissions) => permissions
.Where(s => s.StartsWith($"{nameof(StoreManagement)}:", StringComparison.InvariantCulture))
.Select(s => s.Split(":")[1]);
}
}

View file

@ -4,6 +4,7 @@ using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Security.APIKeys;
using BTCPayServer.Tests.Logging;
@ -67,8 +68,8 @@ namespace BTCPayServer.Tests
var superApiKey = s.AssertHappyMessage().FindElement(By.TagName("code")).Text;
//this api key has access to everything
await TestApiAgainstAccessToken(superApiKey, tester, user, APIKeyConstants.Permissions.ServerManagement,
APIKeyConstants.Permissions.StoreManagement);
await TestApiAgainstAccessToken(superApiKey, tester, user, Permissions.ServerManagement,
Permissions.StoreManagement);
s.Driver.FindElement(By.Id("AddApiKey")).Click();
@ -76,7 +77,7 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("Generate")).Click();
var serverOnlyApiKey = s.AssertHappyMessage().FindElement(By.TagName("code")).Text;
await TestApiAgainstAccessToken(serverOnlyApiKey, tester, user,
APIKeyConstants.Permissions.ServerManagement);
Permissions.ServerManagement);
s.Driver.FindElement(By.Id("AddApiKey")).Click();
@ -84,7 +85,7 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("Generate")).Click();
var allStoreOnlyApiKey = s.AssertHappyMessage().FindElement(By.TagName("code")).Text;
await TestApiAgainstAccessToken(allStoreOnlyApiKey, tester, user,
APIKeyConstants.Permissions.StoreManagement);
Permissions.StoreManagement);
s.Driver.FindElement(By.Id("AddApiKey")).Click();
s.Driver.FindElement(By.CssSelector("button[value=change-store-mode]")).Click();
@ -96,7 +97,7 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("Generate")).Click();
var selectiveStoreApiKey = s.AssertHappyMessage().FindElement(By.TagName("code")).Text;
await TestApiAgainstAccessToken(selectiveStoreApiKey, tester, user,
APIKeyConstants.Permissions.GetStorePermission(storeId));
Permissions.GetStorePermission(storeId));
s.Driver.FindElement(By.Id("AddApiKey")).Click();
s.Driver.FindElement(By.Id("Generate")).Click();
@ -117,31 +118,8 @@ namespace BTCPayServer.Tests
//permissions
//strict
//selectiveStores
UriBuilder authorize = new UriBuilder(tester.PayTester.ServerUri);
authorize.Path = "api-keys/authorize";
authorize.AppendPayloadToQuery(new Dictionary<string, object>()
{
{"redirect", "https://local.local/callback"},
{"applicationName", "kukksappname"},
{"strict", true},
{"selectiveStores", false},
{
"permissions",
new[]
{
APIKeyConstants.Permissions.StoreManagement,
APIKeyConstants.Permissions.ServerManagement
}
},
});
var authUrl = authorize.ToString();
var perms = new[]
{
APIKeyConstants.Permissions.StoreManagement, APIKeyConstants.Permissions.ServerManagement
};
authUrl = authUrl.Replace("permissions=System.String%5B%5D",
string.Join("&", perms.Select(s1 => $"permissions={s1}")));
var authUrl = BTCPayServerClient.GenerateAuthorizeUri(tester.PayTester.ServerUri,
new[] {Permissions.StoreManagement, Permissions.ServerManagement}).ToString();
s.Driver.Navigate().GoToUrl(authUrl);
s.Driver.PageSource.Contains("kukksappname");
Assert.NotNull(s.Driver.FindElement(By.Id("StoreManagementPermission")).GetAttribute("readonly"));
@ -159,28 +137,9 @@ namespace BTCPayServer.Tests
await TestApiAgainstAccessToken(results.Single(pair => pair.Key == "key").Value, tester, user,
(await apiKeyRepo.GetKey(results.Single(pair => pair.Key == "key").Value)).GetPermissions());
authorize = new UriBuilder(tester.PayTester.ServerUri);
authorize.Path = "api-keys/authorize";
authorize.AppendPayloadToQuery(new Dictionary<string, object>()
{
{"strict", false},
{"selectiveStores", true},
{
"permissions",
new[]
{
APIKeyConstants.Permissions.StoreManagement,
APIKeyConstants.Permissions.ServerManagement
}
}
});
authUrl = authorize.ToString();
perms = new[]
{
APIKeyConstants.Permissions.StoreManagement, APIKeyConstants.Permissions.ServerManagement
};
authUrl = authUrl.Replace("permissions=System.String%5B%5D",
string.Join("&", perms.Select(s1 => $"permissions={s1}")));
authUrl = BTCPayServerClient.GenerateAuthorizeUri(tester.PayTester.ServerUri,
new[] {Permissions.StoreManagement, Permissions.ServerManagement}).ToString();
s.Driver.Navigate().GoToUrl(authUrl);
Assert.DoesNotContain("kukksappname", s.Driver.PageSource);
@ -214,8 +173,8 @@ namespace BTCPayServer.Tests
var secondUser = tester.NewAccount();
secondUser.GrantAccess();
var selectiveStorePermissions = APIKeyConstants.Permissions.ExtractStorePermissionsIds(permissions);
if (permissions.Contains(APIKeyConstants.Permissions.StoreManagement) || selectiveStorePermissions.Any())
var selectiveStorePermissions = Permissions.ExtractStorePermissionsIds(permissions);
if (permissions.Contains(Permissions.StoreManagement) || selectiveStorePermissions.Any())
{
var resultStores =
await TestApiAgainstAccessToken<StoreData[]>(accessToken, $"{TestApiPath}/me/stores",
@ -231,7 +190,7 @@ namespace BTCPayServer.Tests
data => data.Id.Equals(selectiveStorePermission, StringComparison.InvariantCultureIgnoreCase));
}
if (permissions.Contains(APIKeyConstants.Permissions.StoreManagement))
if (permissions.Contains(Permissions.StoreManagement))
{
Assert.True(await TestApiAgainstAccessToken<bool>(accessToken,
$"{TestApiPath}/me/stores/actions",
@ -272,7 +231,7 @@ namespace BTCPayServer.Tests
tester.PayTester.HttpClient);
});
if (permissions.Contains(APIKeyConstants.Permissions.ServerManagement))
if (permissions.Contains(Permissions.ServerManagement))
{
Assert.True(await TestApiAgainstAccessToken<bool>(accessToken,
$"{TestApiPath}/me/is-admin",

View file

@ -1,14 +1,10 @@
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using BTCPayServer.Client;
using BTCPayServer.Controllers;
using BTCPayServer.Controllers.RestApi.ApiKeys;
using BTCPayServer.Data;
using BTCPayServer.Security.APIKeys;
using BTCPayServer.Tests.Logging;
using Microsoft.AspNet.SignalR.Client;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
using Xunit;
using Xunit.Abstractions;
@ -37,17 +33,20 @@ namespace BTCPayServer.Tests
user.GrantAccess();
await user.MakeAdmin();
string apiKey = await GenerateAPIKey(tester, user);
var client = new BTCPayServerClient(tester.PayTester.ServerUri, apiKey);
//Get current api key
var request = new HttpRequestMessage(HttpMethod.Get, "api/v1/api-keys/current");
request.Headers.Authorization = new AuthenticationHeaderValue("token", apiKey);
var result = await tester.PayTester.HttpClient.SendAsync(request);
Assert.True(result.IsSuccessStatusCode);
var apiKeyData = JObject.Parse(await result.Content.ReadAsStringAsync()).ToObject<ApiKeyData>();
var apiKeyData = await client.GetCurrentAPIKeyInfo();
Assert.NotNull(apiKeyData);
Assert.Equal(apiKey, apiKeyData.ApiKey);
Assert.Equal(user.UserId, apiKeyData.UserId);
Assert.Equal(2, apiKeyData.Permissions.Length);
//revoke current api key
await client.RevokeCurrentAPIKeyInfo();
await Assert.ThrowsAsync<HttpRequestException>(async () =>
{
await client.GetCurrentAPIKeyInfo();
});
}
}

View file

@ -124,6 +124,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BTCPayServer.Client\BTCPayServer.Client.csproj" />
<ProjectReference Include="..\BTCPayServer.Data\BTCPayServer.Data.csproj" />
<ProjectReference Include="..\BTCPayServer.Rating\BTCPayServer.Rating.csproj" />
<ProjectReference Include="..\BTCPayServer.Common\BTCPayServer.Common.csproj" />

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Hosting.OpenApi;
using BTCPayServer.Models;
@ -109,8 +110,8 @@ namespace BTCPayServer.Controllers
var vm = await SetViewModelValues(new AuthorizeApiKeysViewModel()
{
Label = applicationName,
ServerManagementPermission = permissions.Contains(APIKeyConstants.Permissions.ServerManagement),
StoreManagementPermission = permissions.Contains(APIKeyConstants.Permissions.StoreManagement),
ServerManagementPermission = permissions.Contains(Permissions.ServerManagement),
StoreManagementPermission = permissions.Contains(Permissions.StoreManagement),
PermissionsFormatted = permissions,
ApplicationName = applicationName,
SelectiveStores = selectiveStores,
@ -133,7 +134,7 @@ namespace BTCPayServer.Controllers
}
if (viewModel.PermissionsFormatted.Contains(APIKeyConstants.Permissions.ServerManagement))
if (viewModel.PermissionsFormatted.Contains(Permissions.ServerManagement))
{
if (!viewModel.IsServerAdmin && viewModel.ServerManagementPermission)
{
@ -147,7 +148,7 @@ namespace BTCPayServer.Controllers
}
}
if (viewModel.PermissionsFormatted.Contains(APIKeyConstants.Permissions.StoreManagement))
if (viewModel.PermissionsFormatted.Contains(Permissions.StoreManagement))
{
if (!viewModel.SelectiveStores &&
viewModel.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.Specific)
@ -265,16 +266,16 @@ namespace BTCPayServer.Controllers
if (viewModel.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.Specific)
{
permissions.AddRange(viewModel.SpecificStores.Select(APIKeyConstants.Permissions.GetStorePermission));
permissions.AddRange(viewModel.SpecificStores.Select(Permissions.GetStorePermission));
}
else if (viewModel.StoreManagementPermission)
{
permissions.Add(APIKeyConstants.Permissions.StoreManagement);
permissions.Add(Permissions.StoreManagement);
}
if (viewModel.IsServerAdmin && viewModel.ServerManagementPermission)
{
permissions.Add(APIKeyConstants.Permissions.ServerManagement);
permissions.Add(Permissions.ServerManagement);
}
return permissions;

View file

@ -1,23 +0,0 @@
using BTCPayServer.Data;
namespace BTCPayServer.Controllers.RestApi.ApiKeys
{
public class ApiKeyData
{
public string ApiKey { get; set; }
public string Label { get; set; }
public string UserId { get; set; }
public string[] Permissions { get; set; }
public static ApiKeyData FromModel(APIKeyData data)
{
return new ApiKeyData()
{
Permissions = data.GetPermissions(),
ApiKey = data.Id,
UserId = data.UserId,
Label = data.Label
};
}
}
}

View file

@ -1,9 +1,12 @@
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
using BTCPayServer.Data;
using BTCPayServer.Hosting.OpenApi;
using BTCPayServer.Security;
using BTCPayServer.Security.APIKeys;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using NSwag.Annotations;
@ -16,10 +19,12 @@ namespace BTCPayServer.Controllers.RestApi.ApiKeys
public class ApiKeysController : ControllerBase
{
private readonly APIKeyRepository _apiKeyRepository;
private readonly UserManager<ApplicationUser> _userManager;
public ApiKeysController(APIKeyRepository apiKeyRepository)
public ApiKeysController(APIKeyRepository apiKeyRepository, UserManager<ApplicationUser> userManager)
{
_apiKeyRepository = apiKeyRepository;
_userManager = userManager;
}
[OpenApiOperation("Get current API Key information", "View information about the current API key")]
@ -31,7 +36,30 @@ namespace BTCPayServer.Controllers.RestApi.ApiKeys
{
ControllerContext.HttpContext.GetAPIKey(out var apiKey);
var data = await _apiKeyRepository.GetKey(apiKey);
return Ok(ApiKeyData.FromModel(data));
return Ok(FromModel(data));
}
[OpenApiOperation("Revoke the current API Key", "Revoke the current API key so that it cannot be used anymore")]
[SwaggerResponse(StatusCodes.Status200OK, typeof(ApiKeyData),
Description = "The key was revoked and is no longer usable")]
[HttpDelete("~/api/v1/api-keys/current")]
[HttpDelete("~/api/v1/users/me/api-keys/current")]
public async Task<ActionResult<ApiKeyData>> RevokeKey()
{
ControllerContext.HttpContext.GetAPIKey(out var apiKey);
await _apiKeyRepository.Remove(apiKey, _userManager.GetUserId(User));
return Ok();
}
private static ApiKeyData FromModel(APIKeyData data)
{
return new ApiKeyData()
{
Permissions = data.GetPermissions(),
ApiKey = data.Id,
UserId = data.UserId,
Label = data.Label
};
}
}
}

View file

@ -1,5 +1,6 @@
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Authorization;
@ -35,14 +36,14 @@ namespace BTCPayServer.Security.APIKeys
{
case Policies.CanListStoreSettings.Key:
var selectiveStorePermissions =
APIKeyConstants.Permissions.ExtractStorePermissionsIds(context.GetPermissions());
success = context.HasPermissions(APIKeyConstants.Permissions.StoreManagement) ||
Permissions.ExtractStorePermissionsIds(context.GetPermissions());
success = context.HasPermissions(Permissions.StoreManagement) ||
selectiveStorePermissions.Any();
break;
case Policies.CanModifyStoreSettings.Key:
string storeId = _HttpContext.GetImplicitStoreId();
if (!context.HasPermissions(APIKeyConstants.Permissions.StoreManagement) &&
!context.HasPermissions(APIKeyConstants.Permissions.GetStorePermission(storeId)))
if (!context.HasPermissions(Permissions.StoreManagement) &&
!context.HasPermissions(Permissions.GetStorePermission(storeId)))
break;
if (storeId == null)
@ -63,7 +64,7 @@ namespace BTCPayServer.Security.APIKeys
break;
case Policies.CanModifyServerSettings.Key:
if (!context.HasPermissions(APIKeyConstants.Permissions.ServerManagement))
if (!context.HasPermissions(Permissions.ServerManagement))
break;
// For this authorization, we stil check in database because it is super sensitive.
var user = await _userManager.GetUserAsync(context.User);

View file

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
namespace BTCPayServer.Security.APIKeys
{
@ -16,21 +13,13 @@ namespace BTCPayServer.Security.APIKeys
public static class Permissions
{
public const string ServerManagement = nameof(ServerManagement);
public const string StoreManagement = nameof(StoreManagement);
public static readonly Dictionary<string, (string Title, string Description)> PermissionDescriptions = new Dictionary<string, (string Title, string Description)>()
{
{StoreManagement, ("Manage your stores", "The app will be able to create, modify and delete all your stores.")},
{$"{nameof(StoreManagement)}:", ("Manage selected stores", "The app will be able to modify and delete selected stores.")},
{ServerManagement, ("Manage your server", "The app will have total control on your server")},
{Client.Permissions.StoreManagement, ("Manage your stores", "The app will be able to create, modify and delete all your stores.")},
{$"{nameof(Client.Permissions.StoreManagement)}:", ("Manage selected stores", "The app will be able to modify and delete selected stores.")},
{Client.Permissions.ServerManagement, ("Manage your server", "The app will have total control on your server")},
};
public static string GetStorePermission(string storeId) => $"{nameof(StoreManagement)}:{storeId}";
public static IEnumerable<string> ExtractStorePermissionsIds(IEnumerable<string> permissions) => permissions
.Where(s => s.StartsWith($"{nameof(StoreManagement)}:", StringComparison.InvariantCulture))
.Select(s => s.Split(":")[1]);
}
}
}

View file

@ -2,6 +2,7 @@ using System;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Security.Bitpay;
using BTCPayServer.Services.Stores;
@ -35,12 +36,12 @@ namespace BTCPayServer.Security.APIKeys
claimsPrincipal.Claims.Where(claim => claim.Type == APIKeyConstants.ClaimTypes.Permissions)
.Select(claim => claim.Value).ToList();
if (permissions.Contains(APIKeyConstants.Permissions.StoreManagement))
if (permissions.Contains(Permissions.StoreManagement))
{
return storeRepository.GetStoresByUserId(userManager.GetUserId(claimsPrincipal));
}
var storeIds = APIKeyConstants.Permissions.ExtractStorePermissionsIds(permissions);
var storeIds = Permissions.ExtractStorePermissionsIds(permissions);
return storeRepository.GetStoresByUserId(userManager.GetUserId(claimsPrincipal), storeIds);
}

View file

@ -1,6 +1,7 @@
@using BTCPayServer.Client
@using BTCPayServer.Controllers
@using BTCPayServer.Security.APIKeys
@model BTCPayServer.Controllers.ManageController.AddApiKeyViewModel
@model BTCPayServer.Controllers.ManageController.AuthorizeApiKeysViewModel
@{
ViewData.SetActivePageAndTitle(ManageNavPages.APIKeys, "Add API Key");
@ -39,9 +40,9 @@
{
<div class="list-group-item form-group">
<input asp-for="ServerManagementPermission" class="form-check-inline"/>
<label asp-for="ServerManagementPermission" class="h5">@GetTitle(APIKeyConstants.Permissions.ServerManagement)</label>
<label asp-for="ServerManagementPermission" class="h5">@GetTitle(Permissions.ServerManagement)</label>
<span asp-validation-for="ServerManagementPermission" class="text-danger"></span>
<p>@GetDescription(APIKeyConstants.Permissions.ServerManagement).</p>
<p>@GetDescription(Permissions.ServerManagement).</p>
</div>
}
@if (Model.StoreMode == ManageController.AddApiKeyViewModel.ApiKeyStoreMode.AllStores)
@ -49,9 +50,9 @@
<div class="list-group-item form-group">
@Html.CheckBoxFor(model => model.StoreManagementPermission, new Dictionary<string, string>() {{"class", "form-check-inline"}})
<label asp-for="StoreManagementPermission" class="h5">@GetTitle(APIKeyConstants.Permissions.StoreManagement)</label>
<label asp-for="StoreManagementPermission" class="h5">@GetTitle(Permissions.StoreManagement)</label>
<span asp-validation-for="StoreManagementPermission" class="text-danger"></span>
<p class="mb-0">@GetDescription(APIKeyConstants.Permissions.StoreManagement).</p>
<p class="mb-0">@GetDescription(Permissions.StoreManagement).</p>
<button type="submit" class="btn btn-link" name="command" value="change-store-mode">Give permission to specific stores instead</button>
</div>
}
@ -59,8 +60,8 @@
{
<div class="list-group-item p-0 border-0 mb-2">
<li class="list-group-item ">
<h5 class="mb-1">@GetTitle(APIKeyConstants.Permissions.StoreManagement + ":")</h5>
<p class="mb-0">@GetDescription(APIKeyConstants.Permissions.StoreManagement + ":").</p>
<h5 class="mb-1">@GetTitle(Permissions.StoreManagement + ":")</h5>
<p class="mb-0">@GetDescription(Permissions.StoreManagement + ":").</p>
<button type="submit" class="btn btn-link" name="command" value="change-store-mode">Give permission to all stores instead</button>
</li>
@if (!Model.Stores.Any())

View file

@ -1,3 +1,4 @@
@using BTCPayServer.Client
@using BTCPayServer.Controllers
@using BTCPayServer.Security.APIKeys
@model BTCPayServer.Controllers.ManageController.AuthorizeApiKeysViewModel
@ -48,11 +49,11 @@
<p >There are no associated permissions to the API key being requested here. The application cannot do anything with your BTCPay account other than validating your account exists.</p>
</div>
}
@if (Model.PermissionsFormatted.Contains(APIKeyConstants.Permissions.ServerManagement) && (Model.IsServerAdmin || Model.Strict))
@if (Model.PermissionsFormatted.Contains(Permissions.ServerManagement) && (Model.IsServerAdmin || Model.Strict))
{
<div class="list-group-item form-group">
<input asp-for="ServerManagementPermission" class="form-check-inline" readonly="@(Model.Strict || !Model.IsServerAdmin)"/>
<label asp-for="ServerManagementPermission" class="h5">@GetTitle(APIKeyConstants.Permissions.ServerManagement)</label>
<label asp-for="ServerManagementPermission" class="h5">@GetTitle(Permissions.ServerManagement)</label>
@if (!Model.IsServerAdmin)
{
<span class="text-danger">
@ -61,19 +62,19 @@
}
<span asp-validation-for="ServerManagementPermission" class="text-danger"></span>
<p>@GetDescription(APIKeyConstants.Permissions.ServerManagement).</p>
<p>@GetDescription(Permissions.ServerManagement).</p>
</div>
}
@if (Model.PermissionsFormatted.Contains(APIKeyConstants.Permissions.StoreManagement))
@if (Model.PermissionsFormatted.Contains(Permissions.StoreManagement))
{
@if (Model.StoreMode == ManageController.AddApiKeyViewModel.ApiKeyStoreMode.AllStores)
{
<div class="list-group-item form-group">
<input type="checkbox" asp-for="StoreManagementPermission" class="form-check-inline" readonly="@Model.Strict"/>
<label asp-for="StoreManagementPermission" class="h5">@GetTitle(APIKeyConstants.Permissions.StoreManagement)</label>
<label asp-for="StoreManagementPermission" class="h5">@GetTitle(Permissions.StoreManagement)</label>
<span asp-validation-for="StoreManagementPermission" class="text-danger"></span>
<p class="mb-0">@GetDescription(APIKeyConstants.Permissions.StoreManagement).</p>
<p class="mb-0">@GetDescription(Permissions.StoreManagement).</p>
@if (Model.SelectiveStores)
{
<button type="submit" class="btn btn-link" name="command" value="change-store-mode">Give permission to specific stores instead</button>
@ -84,8 +85,8 @@
{
<div class="list-group-item p-0 border-0 mb-2">
<li class="list-group-item">
<h5 class="mb-1">@GetTitle(APIKeyConstants.Permissions.StoreManagement + ":")</h5>
<p class="mb-0">@GetDescription(APIKeyConstants.Permissions.StoreManagement + ":").</p>
<h5 class="mb-1">@GetTitle(Permissions.StoreManagement + ":")</h5>
<p class="mb-0">@GetDescription(Permissions.StoreManagement + ":").</p>
<button type="submit" class="btn btn-link" name="command" value="change-store-mode">Give permission to all stores instead</button>
</li>
@if (!Model.Stores.Any())

View file

@ -25,6 +25,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BTCPayServer.Common", "BTCP
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BTCPayServer.Data", "BTCPayServer.Data\BTCPayServer.Data.csproj", "{4D7A865D-3945-4C70-9CC8-B09A274A697E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Client", "BTCPayServer.Client\BTCPayServer.Client.csproj", "{21A13304-7168-49A0-86C2-0A1A9453E9C7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -95,6 +97,18 @@ Global
{4D7A865D-3945-4C70-9CC8-B09A274A697E}.Release|x64.Build.0 = Release|Any CPU
{4D7A865D-3945-4C70-9CC8-B09A274A697E}.Release|x86.ActiveCfg = Release|Any CPU
{4D7A865D-3945-4C70-9CC8-B09A274A697E}.Release|x86.Build.0 = Release|Any CPU
{21A13304-7168-49A0-86C2-0A1A9453E9C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{21A13304-7168-49A0-86C2-0A1A9453E9C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{21A13304-7168-49A0-86C2-0A1A9453E9C7}.Debug|x64.ActiveCfg = Debug|Any CPU
{21A13304-7168-49A0-86C2-0A1A9453E9C7}.Debug|x64.Build.0 = Debug|Any CPU
{21A13304-7168-49A0-86C2-0A1A9453E9C7}.Debug|x86.ActiveCfg = Debug|Any CPU
{21A13304-7168-49A0-86C2-0A1A9453E9C7}.Debug|x86.Build.0 = Debug|Any CPU
{21A13304-7168-49A0-86C2-0A1A9453E9C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{21A13304-7168-49A0-86C2-0A1A9453E9C7}.Release|Any CPU.Build.0 = Release|Any CPU
{21A13304-7168-49A0-86C2-0A1A9453E9C7}.Release|x64.ActiveCfg = Release|Any CPU
{21A13304-7168-49A0-86C2-0A1A9453E9C7}.Release|x64.Build.0 = Release|Any CPU
{21A13304-7168-49A0-86C2-0A1A9453E9C7}.Release|x86.ActiveCfg = Release|Any CPU
{21A13304-7168-49A0-86C2-0A1A9453E9C7}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE