btcpayserver/BTCPayServer/Controllers/UIManageController.APIKeys.cs

591 lines
30 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Abstractions.Models;
using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Models;
2022-01-14 05:05:23 +01:00
using BTCPayServer.Security.Greenfield;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
2020-03-10 13:30:46 +01:00
using NBitcoin;
using NBitcoin.DataEncoders;
namespace BTCPayServer.Controllers
{
2022-01-07 04:32:00 +01:00
public partial class UIManageController
{
[HttpGet]
public async Task<IActionResult> APIKeys()
{
return View(new ApiKeysViewModel()
{
ApiKeyDatas = await _apiKeyRepository.GetKeys(new APIKeyRepository.APIKeyQuery()
{
2020-03-19 11:11:15 +01:00
UserId = new[] { _userManager.GetUserId(User) }
})
});
}
2020-03-19 11:11:15 +01:00
[HttpGet("~/api-keys/{id}/delete")]
public async Task<IActionResult> DeleteAPIKey(string id)
{
2020-02-26 10:26:38 +01:00
var key = await _apiKeyRepository.GetKey(id);
if (key == null || key.UserId != _userManager.GetUserId(User))
{
return NotFound();
}
return View("Confirm", new ConfirmModel
2020-02-26 10:26:38 +01:00
{
Title = "Delete API key",
DescriptionHtml = true,
Description = $"Any application using the API key <strong>{key.Label ?? key.Id}<strong> will immediately lose access.",
2020-02-26 10:26:38 +01:00
Action = "Delete",
2022-01-14 12:16:28 +01:00
ActionName = nameof(DeleteAPIKeyPost)
2020-02-26 10:26:38 +01:00
});
}
[HttpPost("~/api-keys/{id}/delete")]
public async Task<IActionResult> DeleteAPIKeyPost(string id)
2020-02-26 10:26:38 +01:00
{
var key = await _apiKeyRepository.GetKey(id);
if (key == null || key.UserId != _userManager.GetUserId(User))
{
return NotFound();
}
await _apiKeyRepository.Remove(id, _userManager.GetUserId(User));
TempData.SetStatusMessageModel(new StatusMessageModel()
{
Severity = StatusMessageModel.StatusSeverity.Success,
Message = "API Key removed"
});
return RedirectToAction("APIKeys");
}
[HttpGet]
public async Task<IActionResult> AddApiKey()
{
if (!_btcPayServerEnvironment.IsSecure)
{
TempData.SetStatusMessageModel(new StatusMessageModel()
{
Severity = StatusMessageModel.StatusSeverity.Error,
Message = "Cannot generate api keys while not on https or tor"
});
return RedirectToAction("APIKeys");
}
return View("AddApiKey", await SetViewModelValues(new AddApiKeyViewModel()));
}
2021-12-31 08:59:02 +01:00
[HttpGet("~/api-keys/authorize")]
public async Task<IActionResult> AuthorizeAPIKey(string[] permissions, string applicationName = null, Uri redirect = null,
bool strict = true, bool selectiveStores = false, string applicationIdentifier = null)
{
if (!_btcPayServerEnvironment.IsSecure)
{
TempData.SetStatusMessageModel(new StatusMessageModel()
{
Severity = StatusMessageModel.StatusSeverity.Error,
Message = "Cannot generate api keys while not on https or tor"
});
return RedirectToAction("APIKeys");
}
2020-06-28 10:55:27 +02:00
permissions ??= Array.Empty<string>();
2020-08-04 10:03:51 +02:00
var requestPermissions = Permission.ToPermissions(permissions);
if (redirect?.IsAbsoluteUri is false)
{
redirect = null;
}
if (!string.IsNullOrEmpty(applicationIdentifier) && redirect != null)
{
2021-12-31 08:59:02 +01:00
//check if there is an app identifier that matches and belongs to the current user
var keys = await _apiKeyRepository.GetKeys(new APIKeyRepository.APIKeyQuery()
{
2021-12-31 08:59:02 +01:00
UserId = new[] { _userManager.GetUserId(User) }
});
2020-08-28 09:15:08 +02:00
foreach (var key in keys)
{
2020-08-28 09:15:08 +02:00
var blob = key.GetBlob();
if (blob.ApplicationIdentifier != applicationIdentifier ||
blob.ApplicationAuthority != redirect.AbsoluteUri)
2020-08-28 09:15:08 +02:00
{
continue;
}
//matched the identifier and authority, but we need to check if what the app is requesting in terms of permissions is enough
var alreadyPresentPermissions = Permission.ToPermissions(blob.Permissions)
.GroupBy(permission => permission.Policy);
var fail = false;
foreach (var permission in requestPermissions.GroupBy(permission => permission.Policy))
{
2020-08-28 09:15:08 +02:00
var presentPermission =
alreadyPresentPermissions.SingleOrDefault(grouping => permission.Key == grouping.Key);
if (strict && presentPermission == null)
{
2020-08-28 09:15:08 +02:00
fail = true;
break;
}
2020-08-28 09:15:08 +02:00
if (Policies.IsStorePolicy(permission.Key))
2020-08-04 10:03:51 +02:00
{
2020-08-28 09:15:08 +02:00
if (!selectiveStores &&
permission.Any(permission1 => !string.IsNullOrEmpty(permission1.Scope)))
2020-08-04 10:03:51 +02:00
{
2020-08-28 09:15:08 +02:00
TempData.SetStatusMessageModel(new StatusMessageModel()
{
Severity = StatusMessageModel.StatusSeverity.Error,
Message =
"Cannot request specific store permission when selectiveStores is not enable"
});
return RedirectToAction("APIKeys");
2020-08-04 10:03:51 +02:00
}
2020-08-28 09:15:08 +02:00
else if (!selectiveStores && presentPermission.Any(permission1 =>
!string.IsNullOrEmpty(permission1.Scope)))
2020-08-04 10:03:51 +02:00
{
2020-08-28 09:15:08 +02:00
fail = true;
break;
2020-08-04 10:03:51 +02:00
}
}
2020-08-28 09:15:08 +02:00
}
2020-08-28 09:15:08 +02:00
if (fail)
{
continue;
}
2020-08-28 09:15:08 +02:00
//we have a key that is sufficient, redirect to a page to confirm that it's ok to provide this key to the app.
return View("ConfirmAPIKey",
new AuthorizeApiKeysViewModel()
2020-08-28 09:15:08 +02:00
{
ApiKey = key.Id,
RedirectUrl = redirect,
Label = applicationName,
ApplicationName = applicationName,
SelectiveStores = selectiveStores,
Strict = strict,
Permissions = string.Join(';', permissions),
ApplicationIdentifier = applicationIdentifier
2020-08-28 09:15:08 +02:00
});
}
}
2020-08-28 09:15:08 +02:00
2020-03-23 14:17:17 +01:00
var vm = await SetViewModelValues(new AuthorizeApiKeysViewModel()
{
RedirectUrl = redirect,
Label = applicationName,
ApplicationName = applicationName,
SelectiveStores = selectiveStores,
Strict = strict,
2020-08-04 10:03:51 +02:00
Permissions = string.Join(';', requestPermissions),
ApplicationIdentifier = applicationIdentifier
});
2020-03-23 14:17:17 +01:00
AdjustVMForAuthorization(vm);
2020-06-28 10:55:27 +02:00
return View(vm);
}
2020-03-23 14:17:17 +01:00
private void AdjustVMForAuthorization(AuthorizeApiKeysViewModel vm)
{
var permissions = vm.Permissions?.Split(';') ?? Array.Empty<string>();
var permissionsWithStoreIDs = new List<string>();
/**
* Go over each permission and associated store IDs and
* join them so that permission for a specific store is parsed correctly
*/
2021-12-31 08:59:02 +01:00
for (var i = 0; i < permissions.Length; i++)
{
var currPerm = permissions[i];
var storeIds = vm.PermissionValues[i].SpecificStores.ToArray();
2021-12-31 08:59:02 +01:00
if (storeIds.Length > 0)
{
for (var x = 0; x < storeIds.Length; x++)
{
permissionsWithStoreIDs.Add($"{currPerm}:{storeIds[x]}");
}
2021-12-31 08:59:02 +01:00
}
else
{
permissionsWithStoreIDs.Add(currPerm);
}
}
var parsedPermissions = Permission.ToPermissions(permissionsWithStoreIDs.ToArray()).GroupBy(permission => permission.Policy);
2020-03-23 14:17:17 +01:00
for (var index = vm.PermissionValues.Count - 1; index >= 0; index--)
{
2020-03-23 14:17:17 +01:00
var permissionValue = vm.PermissionValues[index];
var wanted = parsedPermissions?.SingleOrDefault(permission =>
permission.Key.Equals(permissionValue.Permission,
StringComparison.InvariantCultureIgnoreCase));
2020-06-28 10:55:27 +02:00
if (vm.Strict && !(wanted?.Any() ?? false))
{
2020-03-23 14:17:17 +01:00
vm.PermissionValues.RemoveAt(index);
continue;
}
2020-06-28 10:55:27 +02:00
else if (wanted?.Any() ?? false)
2020-03-23 14:17:17 +01:00
{
var commandParts = vm.Command?.Split(':', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
var command = commandParts.Length > 1 ? commandParts[1] : null;
var isPerformingAnAction = command == "change-store-mode" || command == "add-store";
// Don't want to accidentally change mode for the user if they are explicitly performing some action
2021-12-31 08:59:02 +01:00
if (isPerformingAnAction)
{
continue;
}
2020-03-23 14:17:17 +01:00
if (vm.SelectiveStores && Policies.IsStorePolicy(permissionValue.Permission) &&
wanted.Any(permission => !string.IsNullOrEmpty(permission.Scope)))
2020-03-23 14:17:17 +01:00
{
permissionValue.StoreMode = AddApiKeyViewModel.ApiKeyStoreMode.Specific;
permissionValue.SpecificStores = wanted.Select(permission => permission.Scope).ToList();
2020-03-23 14:17:17 +01:00
}
else
2020-03-19 11:11:15 +01:00
{
2020-03-23 14:17:17 +01:00
permissionValue.StoreMode = AddApiKeyViewModel.ApiKeyStoreMode.AllStores;
permissionValue.SpecificStores = new List<string>();
permissionValue.Value = true;
2020-03-19 11:11:15 +01:00
}
}
}
2020-03-23 14:17:17 +01:00
}
2020-03-23 14:17:17 +01:00
[HttpPost("~/api-keys/authorize")]
public async Task<IActionResult> AuthorizeAPIKey([FromForm] AuthorizeApiKeysViewModel viewModel)
{
await SetViewModelValues(viewModel);
2020-06-28 10:55:27 +02:00
2020-03-23 14:17:17 +01:00
AdjustVMForAuthorization(viewModel);
var ar = HandleCommands(viewModel);
2020-06-28 10:55:27 +02:00
2020-03-23 14:17:17 +01:00
if (ar != null)
{
2020-03-23 14:17:17 +01:00
return ar;
}
2020-06-28 10:55:27 +02:00
2020-03-23 14:17:17 +01:00
for (int i = 0; i < viewModel.PermissionValues.Count; i++)
{
if (viewModel.PermissionValues[i].Forbidden && viewModel.Strict)
{
2020-03-23 14:17:17 +01:00
viewModel.PermissionValues[i].Value = false;
ModelState.AddModelError($"{viewModel.PermissionValues}[{i}].Value",
$"The permission '{viewModel.PermissionValues[i].Title}' is required for this application.");
}
2020-03-23 14:17:17 +01:00
if (viewModel.PermissionValues[i].StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.Specific &&
!viewModel.SelectiveStores)
{
2020-03-23 14:17:17 +01:00
viewModel.PermissionValues[i].StoreMode = AddApiKeyViewModel.ApiKeyStoreMode.AllStores;
ModelState.AddModelError($"{viewModel.PermissionValues}[{i}].Value",
$"The permission '{viewModel.PermissionValues[i].Title}' cannot be store specific for this application.");
}
}
2020-06-28 10:55:27 +02:00
if (!ModelState.IsValid)
{
return View(viewModel);
}
2020-06-28 10:55:27 +02:00
var command = viewModel.Command.ToLowerInvariant();
switch (command)
{
case "cancel":
return RedirectToAction("APIKeys");
case "authorize":
case "confirm":
var key = command == "authorize"
? await CreateKey(viewModel, (viewModel.ApplicationIdentifier, viewModel.RedirectUrl?.AbsoluteUri))
: await _apiKeyRepository.GetKey(viewModel.ApiKey);
2021-12-31 08:59:02 +01:00
if (viewModel.RedirectUrl != null)
{
var permissions = key.GetBlob().Permissions;
var redirectVm = new PostRedirectViewModel()
{
FormUrl = viewModel.RedirectUrl.AbsoluteUri,
FormParameters =
{
{ "apiKey", key.Id },
{ "userId", key.UserId },
},
};
foreach (var permission in permissions)
{
redirectVm.FormParameters.Add("permissions[]", permission);
}
return View("PostRedirect", redirectVm);
}
TempData.SetStatusMessageModel(new StatusMessageModel()
{
Severity = StatusMessageModel.StatusSeverity.Success,
2020-04-06 12:22:50 +02:00
Html = $"API key generated! <code class='alert-link'>{key.Id}</code>"
});
2020-03-19 11:11:15 +01:00
return RedirectToAction("APIKeys", new { key = key.Id });
2020-03-19 11:11:15 +01:00
default:
return View(viewModel);
}
}
[HttpPost]
public async Task<IActionResult> AddApiKey(AddApiKeyViewModel viewModel)
{
await SetViewModelValues(viewModel);
var ar = HandleCommands(viewModel);
if (ar != null)
{
return ar;
}
if (!ModelState.IsValid)
{
return View(viewModel);
}
var key = await CreateKey(viewModel);
TempData.SetStatusMessageModel(new StatusMessageModel()
{
Severity = StatusMessageModel.StatusSeverity.Success,
2020-04-06 12:22:50 +02:00
Html = $"API key generated! <code class='alert-link'>{key.Id}</code>"
});
return RedirectToAction("APIKeys");
}
private IActionResult HandleCommands(AddApiKeyViewModel viewModel)
{
2020-03-23 14:17:17 +01:00
if (string.IsNullOrEmpty(viewModel.Command))
{
return null;
}
var parts = viewModel.Command.Split(':', StringSplitOptions.RemoveEmptyEntries);
var permission = parts[0];
if (!Policies.IsStorePolicy(permission))
{
return null;
}
var permissionValueItem = viewModel.PermissionValues.Single(item => item.Permission == permission);
var command = parts[1];
var storeIndex = parts.Length == 3 ? parts[2] : null;
2020-06-28 10:55:27 +02:00
2020-03-23 14:17:17 +01:00
ModelState.Clear();
switch (command)
{
case "change-store-mode":
2020-03-23 14:17:17 +01:00
permissionValueItem.StoreMode = permissionValueItem.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.Specific
? AddApiKeyViewModel.ApiKeyStoreMode.AllStores
: AddApiKeyViewModel.ApiKeyStoreMode.Specific;
2021-09-20 01:42:51 +02:00
// Reset values for "all stores" option to their original values
if (permissionValueItem.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.AllStores)
{
permissionValueItem.SpecificStores = new List<string>();
2021-09-20 01:42:51 +02:00
permissionValueItem.Value = true;
}
2020-03-23 14:17:17 +01:00
if (permissionValueItem.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.Specific &&
!permissionValueItem.SpecificStores.Any() && viewModel.Stores.Any())
{
2020-03-23 14:17:17 +01:00
permissionValueItem.SpecificStores.Add(null);
}
return View(viewModel);
case "add-store":
2020-03-23 14:17:17 +01:00
permissionValueItem.SpecificStores.Add(null);
return View(viewModel);
2020-03-23 14:17:17 +01:00
case "remove-store":
2020-06-28 10:55:27 +02:00
{
if (storeIndex != null)
permissionValueItem.SpecificStores.RemoveAt(int.Parse(storeIndex,
CultureInfo.InvariantCulture));
return View(viewModel);
}
}
2020-06-28 10:55:27 +02:00
return null;
}
2020-08-04 13:10:48 +02:00
private async Task<APIKeyData> CreateKey(AddApiKeyViewModel viewModel, (string appIdentifier, string appAuthority) app = default)
{
var key = new APIKeyData()
{
2020-03-10 13:30:46 +01:00
Id = Encoders.Hex.EncodeData(RandomUtils.GetBytes(20)),
Type = APIKeyType.Permanent,
UserId = _userManager.GetUserId(User),
Label = viewModel.Label,
};
key.SetBlob(new APIKeyBlob()
{
2020-08-04 13:10:48 +02:00
Permissions = GetPermissionsFromViewModel(viewModel).Select(p => p.ToString()).Distinct().ToArray(),
ApplicationAuthority = app.appAuthority,
ApplicationIdentifier = app.appIdentifier
});
await _apiKeyRepository.CreateKey(key);
return key;
}
2020-03-19 11:11:15 +01:00
private IEnumerable<Permission> GetPermissionsFromViewModel(AddApiKeyViewModel viewModel)
{
2020-03-19 11:11:15 +01:00
List<Permission> permissions = new List<Permission>();
2020-03-23 14:17:17 +01:00
foreach (var p in viewModel.PermissionValues.Where(tuple => !tuple.Forbidden))
{
2020-03-23 14:17:17 +01:00
if (Policies.IsStorePolicy(p.Permission))
{
if (p.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.AllStores && p.Value)
{
permissions.Add(Permission.Create(p.Permission));
}
else if (p.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.Specific)
{
permissions.AddRange(p.SpecificStores.Select(s => Permission.Create(p.Permission, s)));
}
}
2020-06-28 10:55:27 +02:00
else if (p.Value && Permission.TryCreatePermission(p.Permission, null, out var pp))
2020-03-19 11:11:15 +01:00
permissions.Add(pp);
}
2020-03-23 14:17:17 +01:00
2020-06-28 10:55:27 +02:00
return permissions.Distinct();
}
private async Task<T> SetViewModelValues<T>(T viewModel) where T : AddApiKeyViewModel
{
viewModel.Stores = await _StoreRepository.GetStoresByUserId(_userManager.GetUserId(User));
2020-03-23 14:17:17 +01:00
var isAdmin = (await _authorizationService.AuthorizeAsync(User, Policies.CanModifyServerSettings))
.Succeeded;
viewModel.PermissionValues ??= Policies.AllPolicies
2020-06-24 03:34:09 +02:00
.Where(p => AddApiKeyViewModel.PermissionValueItem.PermissionDescriptions.ContainsKey(p))
2020-03-23 14:17:17 +01:00
.Select(s => new AddApiKeyViewModel.PermissionValueItem()
{
2020-06-28 10:55:27 +02:00
Permission = s,
Value = false,
2020-03-23 14:17:17 +01:00
Forbidden = Policies.IsServerPolicy(s) && !isAdmin
}).ToList();
2020-03-19 11:11:15 +01:00
if (!isAdmin)
{
foreach (var p in viewModel.PermissionValues.Where(item => item.Permission is null || Policies.IsServerPolicy(item.Permission)))
2020-03-19 11:11:15 +01:00
{
2020-03-23 14:17:17 +01:00
p.Forbidden = true;
2020-03-19 11:11:15 +01:00
}
}
2020-03-23 14:17:17 +01:00
return viewModel;
}
public class AddApiKeyViewModel
{
public string Label { get; set; }
public StoreData[] Stores { get; set; }
public string Command { get; set; }
public List<PermissionValueItem> PermissionValues { get; set; }
public enum ApiKeyStoreMode
{
AllStores,
Specific
}
public class PermissionValueItem
{
2020-03-19 11:11:15 +01:00
public static readonly Dictionary<string, (string Title, string Description)> PermissionDescriptions = new Dictionary<string, (string Title, string Description)>()
{
2020-03-20 05:58:07 +01:00
{BTCPayServer.Client.Policies.Unrestricted, ("Unrestricted access", "The app will have unrestricted access to your account.")},
{BTCPayServer.Client.Policies.CanViewUsers, ("View users", "The app will be able to see all users on this server.")},
2020-03-20 05:58:07 +01:00
{BTCPayServer.Client.Policies.CanCreateUser, ("Create new users", "The app will be able to create new users on this server.")},
2021-04-08 05:40:57 +02:00
{BTCPayServer.Client.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.")},
{BTCPayServer.Client.Policies.CanModifyStoreSettings, ("Modify your stores", "The app will be able to manage invoices on all your stores and modify their settings.")},
{$"{BTCPayServer.Client.Policies.CanModifyStoreSettings}:", ("Manage selected stores", "The app will be able to manage invoices on the selected stores and modify their settings.")},
Exchange api no kraken (#3679) * WIP New APIs for dealing with custodians/exchanges * Simplified things * More API refinements + index.html file for quick viewing * Finishing touches on spec * Switched cryptoCode to paymentMethod as this allows us to differentiate between onchain and lightning * Moved draft API docs to "/docs-draft" * WIP baby steps * Added DB migration for CustodianAccountData * Rough but working POST /v1/api/custodian-account + GET /v1/api/custodian * WIP + early Kraken API client * Moved service registration to proper location * Working create + list custodian accounts + permissions + WIP Kraken client * Kraken API Balances call is working * Added asset balances to response * List Custodian Accounts call does not load assetBalances by default, because it can fail. Can be requested when needed. * Call to get the details of 1 specific custodian account * Added permissions to swagger * Added "tradableAssetPairs" to Kraken custodian response + cache the tradable pairs in memory for 24 hours * Removed unused file * WIP + Moved files to better locations * Updated docs * Working API endpoint to get info on a trade (same response as creating a new trade) * Working API endpoints for Deposit + Trade + untested Withdraw * Delete custodian account * Trading works, better error handling, cleanup * Working withdrawals + New endpoint for getting bid/ask prices * Completed withdrawals + new endpoint for getting info on a past withdrawal to simplify testing, Enums are output as strings, * Better error handling when withdrawing to a wrong destination * WithdrawalAddressName in config is now a string per currency (dictionary) * Added TODOs * Only show the custodian account "config" to users who are allowed * Added the new permissions to the API Keys UI * Renamed KrakenClient to KrakenExchange * WIP Kraken Config Form * Removed files for UI again, will make separate PR later * Fixed docs + Refactored to use PaymentMethod more + Added "name" to custodian account + Using cancelationToken everywhere * Updated withdrawal info docs * First unit test * Complete tests for /api/v1/custodians and /api/v1/custodian-accounts endpoints + Various improvements and fixes * Mock custodian and more exceptions * Many more tests + cleanup, moved files to better locations * More tests * WIP more tests * Greenfield API tests complete * Added missing "Name" column * Cleanup, TODOs and beginning of Kraken Tests * Added Kraken tests using public endpoints + handling of "SATS" currency * Added 1st mocked Kraken API call: GetAssetBalancesAsync * Added assert for bad config * Mocked more Kraken API responses + added CreationDate to withdrawal response * pr review club changes * Make Kraken Custodian a plugin * Re-added User-Agent header as it is required * Fixed bug in market trade on Kraken using a percentage as qty * A short delay so Kraken has the time to execute the market order and we don't fetch the details too quickly. * Merged the draft swagger into the main swagger since it didn't work anymore * Fixed API permissions test * Removed 2 TODOs * Fixed unit test * Remove Kraken Api as it should be separate opt-in plugin * Flatten namespace hierarchy and use InnerExeption instead of OriginalException * Remove useless line * Make sure account is from a specific store * Proper error if custodian code not found * Remove various warnings * Remove various warnings * Handle CustodianApiException through an exception filter * Store custodian-account blob directly * Remove duplications, transform methods into property * Improve docs tags * Make sure the custodianCode saved is canonical * Fix test Co-authored-by: Wouter Samaey <wouter.samaey@storefront.be> Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2022-05-18 07:59:56 +02:00
{BTCPayServer.Client.Policies.CanViewCustodianAccounts, ("View exchange accounts linked to your stores", "The app will be able to see exchange accounts linked to your stores.")},
{$"{BTCPayServer.Client.Policies.CanViewCustodianAccounts}:", ("View exchange accounts linked to selected stores", "The app will be able to see exchange accounts linked to the selected stores.")},
{BTCPayServer.Client.Policies.CanManageCustodianAccounts, ("Manage exchange accounts linked to your stores", "The app will be able to modify exchange accounts linked to your stores.")},
{$"{BTCPayServer.Client.Policies.CanManageCustodianAccounts}:", ("Manage exchange accounts linked to selected stores", "The app will be able to modify exchange accounts linked to selected stores.")},
{BTCPayServer.Client.Policies.CanDepositToCustodianAccounts, ("Deposit funds to exchange accounts linked to your stores", "The app will be able to deposit funds to your exchange accounts.")},
{$"{BTCPayServer.Client.Policies.CanDepositToCustodianAccounts}:", ("Deposit funds to exchange accounts linked to selected stores", "The app will be able to deposit funds to selected store's exchange accounts.")},
{BTCPayServer.Client.Policies.CanWithdrawFromCustodianAccounts, ("Withdraw funds from exchange accounts to your store", "The app will be able to withdraw funds from your exchange accounts to your store.")},
{$"{BTCPayServer.Client.Policies.CanWithdrawFromCustodianAccounts}:", ("Withdraw funds from selected store's exchange accounts", "The app will be able to withdraw funds from your selected store's exchange accounts.")},
{BTCPayServer.Client.Policies.CanTradeCustodianAccount, ("Trade funds on your store's exchange accounts", "The app will be able to trade funds on your store's exchange accounts.")},
{$"{BTCPayServer.Client.Policies.CanTradeCustodianAccount}:", ("Trade funds on selected store's exchange accounts", "The app will be able to trade funds on selected store's exchange accounts.")},
{BTCPayServer.Client.Policies.CanModifyStoreWebhooks, ("Modify stores webhooks", "The app will modify the webhooks of all your stores.")},
{$"{BTCPayServer.Client.Policies.CanModifyStoreWebhooks}:", ("Modify selected stores' webhooks", "The app will modify the webhooks of the selected stores.")},
2020-03-23 14:17:17 +01:00
{BTCPayServer.Client.Policies.CanViewStoreSettings, ("View your stores", "The app will be able to view stores settings.")},
{$"{BTCPayServer.Client.Policies.CanViewStoreSettings}:", ("View your stores", "The app will be able to view the selected stores' settings.")},
{BTCPayServer.Client.Policies.CanModifyServerSettings, ("Manage your server", "The app will have total control on the server settings of your server.")},
2020-03-20 05:58:07 +01:00
{BTCPayServer.Client.Policies.CanViewProfile, ("View your profile", "The app will be able to view your user profile.")},
{BTCPayServer.Client.Policies.CanModifyProfile, ("Manage your profile", "The app will be able to view and modify your user profile.")},
{BTCPayServer.Client.Policies.CanManageNotificationsForUser, ("Manage your notifications", "The app will be able to view and modify your user notifications.")},
{BTCPayServer.Client.Policies.CanViewNotificationsForUser, ("View your notifications", "The app will be able to view your user notifications.")},
2020-03-23 14:17:17 +01:00
{BTCPayServer.Client.Policies.CanCreateInvoice, ("Create an invoice", "The app will be able to create new invoices.")},
{$"{BTCPayServer.Client.Policies.CanCreateInvoice}:", ("Create an invoice", "The app will be able to create new invoices on the selected stores.")},
2020-07-22 13:58:41 +02:00
{BTCPayServer.Client.Policies.CanViewInvoices, ("View invoices", "The app will be able to view invoices.")},
2021-08-15 04:44:37 +02:00
{BTCPayServer.Client.Policies.CanModifyInvoices, ("Modify invoices", "The app will be able to modify and view invoices.")},
2020-07-22 13:58:41 +02:00
{$"{BTCPayServer.Client.Policies.CanViewInvoices}:", ("View invoices", "The app will be able to view invoices on the selected stores.")},
2021-08-15 04:44:37 +02:00
{$"{BTCPayServer.Client.Policies.CanModifyInvoices}:", ("Modify invoices", "The app will be able to modify and view invoices on the selected stores.")},
{BTCPayServer.Client.Policies.CanModifyPaymentRequests, ("Modify your payment requests", "The app will be able to view, modify, delete and create new payment requests on all your stores.")},
{$"{BTCPayServer.Client.Policies.CanModifyPaymentRequests}:", ("Manage selected stores' payment requests", "The app will be able to view, modify, delete and create new payment requests on the selected stores.")},
{BTCPayServer.Client.Policies.CanViewPaymentRequests, ("View your payment requests", "The app will be able to view payment requests.")},
{$"{BTCPayServer.Client.Policies.CanViewPaymentRequests}:", ("View your payment requests", "The app will be able to view the selected stores' payment requests.")},
2022-03-29 20:12:02 +02:00
{BTCPayServer.Client.Policies.CanManagePullPayments, ("Manage your pull payments", "The app will be able to view, modify, delete and create pull payments on all your stores.")},
{$"{BTCPayServer.Client.Policies.CanManagePullPayments}:", ("Manage selected stores' pull payments", "The app will be able to view, modify, delete and create new pull payments on the selected stores.")},
{BTCPayServer.Client.Policies.CanUseInternalLightningNode, ("Use the internal lightning node", "The app will be able to use the internal BTCPay Server lightning node to create BOLT11 invoices, connect to other nodes, open new channels and pay BOLT11 invoices.")},
{BTCPayServer.Client.Policies.CanCreateLightningInvoiceInternalNode, ("Create invoices with internal lightning node", "The app will be able to use the internal BTCPay Server lightning node to create BOLT11 invoices.")},
{BTCPayServer.Client.Policies.CanUseLightningNodeInStore, ("Use the lightning nodes associated with your stores", "The app will be able to use the lightning nodes connected to all your stores to create BOLT11 invoices, connect to other nodes, open new channels and pay BOLT11 invoices.")},
{BTCPayServer.Client.Policies.CanCreateLightningInvoiceInStore, ("Create invoices from the lightning nodes associated with your stores", "The app will be able to use the lightning nodes connected to all your stores to create BOLT11 invoices.")},
{$"{BTCPayServer.Client.Policies.CanUseLightningNodeInStore}:", ("Use the lightning nodes associated with your stores", "The app will be able to use the lightning nodes connected to the selected stores to create BOLT11 invoices, connect to other nodes, open new channels and pay BOLT11 invoices.")},
{$"{BTCPayServer.Client.Policies.CanCreateLightningInvoiceInStore}:", ("Create invoices from the lightning nodes associated with your stores", "The app will be able to use the lightning nodes connected to the selected stores to create BOLT11 invoices.")},
2020-03-19 11:11:15 +01:00
};
public string Title
{
get
{
2020-06-28 10:55:27 +02:00
return PermissionDescriptions[$"{Permission}{(StoreMode == ApiKeyStoreMode.Specific ? ":" : "")}"].Title;
2020-03-19 11:11:15 +01:00
}
}
public string Description
{
get
{
2020-06-28 10:55:27 +02:00
return PermissionDescriptions[$"{Permission}{(StoreMode == ApiKeyStoreMode.Specific ? ":" : "")}"].Description;
2020-03-19 11:11:15 +01:00
}
}
public string Permission { get; set; }
public bool Value { get; set; }
2020-03-19 11:11:15 +01:00
public bool Forbidden { get; set; }
2020-03-23 14:17:17 +01:00
public ApiKeyStoreMode StoreMode { get; set; } = ApiKeyStoreMode.AllStores;
public List<string> SpecificStores { get; set; } = new List<string>();
}
}
public class AuthorizeApiKeysViewModel : AddApiKeyViewModel
{
public string ApplicationName { get; set; }
public string ApplicationIdentifier { get; set; }
public Uri RedirectUrl { get; set; }
public bool Strict { get; set; }
public bool SelectiveStores { get; set; }
public string Permissions { get; set; }
public string ApiKey { get; set; }
}
public class ApiKeysViewModel
{
public List<APIKeyData> ApiKeyDatas { get; set; }
}
}
}