Merge pull request #1408 from Kukks/api/api-key-uu-refactor

Refactor UI for Add APIKey/Authorize
This commit is contained in:
Nicolas Dorier
2020-04-10 22:00:32 +09:00
committed by GitHub
6 changed files with 334 additions and 309 deletions

View File

@@ -93,65 +93,93 @@ namespace BTCPayServer.Controllers
});
return RedirectToAction("APIKeys");
}
permissions ??= Array.Empty<string>();
var vm = await SetViewModelValues(new AuthorizeApiKeysViewModel(Permission.ToPermissions(permissions))
var parsedPermissions = Permission.ToPermissions(permissions).GroupBy(permission => permission.Policy);
var vm = await SetViewModelValues(new AuthorizeApiKeysViewModel()
{
Label = applicationName,
ApplicationName = applicationName,
SelectiveStores = selectiveStores,
Strict = strict,
Permissions = string.Join(';', parsedPermissions.SelectMany(grouping => grouping.Select(permission => permission.ToString())))
});
AdjustVMForAuthorization(vm);
return View(vm);
}
private void AdjustVMForAuthorization(AuthorizeApiKeysViewModel vm)
{
var parsedPermissions = Permission.ToPermissions(vm.Permissions.Split(';')).GroupBy(permission => permission.Policy);
for (var index = vm.PermissionValues.Count - 1; index >= 0; index--)
{
var permissionValue = vm.PermissionValues[index];
var wanted = parsedPermissions?.SingleOrDefault(permission =>
permission.Key.Equals(permissionValue.Permission,
StringComparison.InvariantCultureIgnoreCase));
if (vm.Strict && !(wanted?.Any()??false))
{
vm.PermissionValues.RemoveAt(index);
continue;
}
else if (wanted?.Any()??false)
{
if (vm.SelectiveStores && Policies.IsStorePolicy(permissionValue.Permission) &&
wanted.Any(permission => !string.IsNullOrEmpty(permission.StoreId)))
{
permissionValue.StoreMode = AddApiKeyViewModel.ApiKeyStoreMode.Specific;
permissionValue.SpecificStores = wanted.Select(permission => permission.StoreId).ToList();
}
else
{
permissionValue.StoreMode = AddApiKeyViewModel.ApiKeyStoreMode.AllStores;
permissionValue.SpecificStores = new List<string>();
permissionValue.Value = true;
}
}
}
}
[HttpPost("~/api-keys/authorize")]
public async Task<IActionResult> AuthorizeAPIKey([FromForm] AuthorizeApiKeysViewModel viewModel)
{
await SetViewModelValues(viewModel);
AdjustVMForAuthorization(viewModel);
var ar = HandleCommands(viewModel);
if (ar != null)
{
return ar;
}
if (viewModel.Strict)
for (int i = 0; i < viewModel.PermissionValues.Count; i++)
{
for (int i = 0; i < viewModel.PermissionValues.Count; i++)
if (viewModel.PermissionValues[i].Forbidden && viewModel.Strict)
{
if (viewModel.PermissionValues[i].Forbidden)
{
ModelState.AddModelError($"{viewModel.PermissionValues}[{i}].Value",
$"The permission '{viewModel.PermissionValues[i].Title}' is required for this application.");
}
viewModel.PermissionValues[i].Value = false;
ModelState.AddModelError($"{viewModel.PermissionValues}[{i}].Value",
$"The permission '{viewModel.PermissionValues[i].Title}' is required for this application.");
}
if (viewModel.PermissionValues[i].StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.Specific &&
!viewModel.SelectiveStores)
{
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.");
}
}
var permissions = Permission.ToPermissions(viewModel.Permissions.Split(';')).ToHashSet();
if (permissions.Contains(Permission.Create(Policies.CanModifyStoreSettings)))
{
if (!viewModel.SelectiveStores &&
viewModel.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.Specific)
{
viewModel.StoreMode = AddApiKeyViewModel.ApiKeyStoreMode.AllStores;
ModelState.AddModelError(nameof(viewModel.StoreManagementPermission),
"This application does not allow selective store permissions.");
}
if (!viewModel.StoreManagementPermission.Value && !viewModel.SpecificStores.Any() && viewModel.Strict)
{
ModelState.AddModelError(nameof(viewModel.StoreManagementPermission),
$"This permission '{viewModel.StoreManagementPermission.Title}' is required for this application.");
}
}
if (!ModelState.IsValid)
{
return View(viewModel);
}
switch (viewModel.Command.ToLowerInvariant())
{
case "no":
@@ -197,35 +225,48 @@ namespace BTCPayServer.Controllers
}
private IActionResult HandleCommands(AddApiKeyViewModel viewModel)
{
switch (viewModel.Command)
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;
ModelState.Clear();
switch (command)
{
case "change-store-mode":
viewModel.StoreMode = viewModel.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.Specific
permissionValueItem.StoreMode = permissionValueItem.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.Specific
? AddApiKeyViewModel.ApiKeyStoreMode.AllStores
: AddApiKeyViewModel.ApiKeyStoreMode.Specific;
if (viewModel.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.Specific &&
!viewModel.SpecificStores.Any() && viewModel.Stores.Any())
if (permissionValueItem.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.Specific &&
!permissionValueItem.SpecificStores.Any() && viewModel.Stores.Any())
{
viewModel.SpecificStores.Add(null);
permissionValueItem.SpecificStores.Add(null);
}
return View(viewModel);
case "add-store":
viewModel.SpecificStores.Add(null);
permissionValueItem.SpecificStores.Add(null);
return View(viewModel);
case string x when x.StartsWith("remove-store", StringComparison.InvariantCultureIgnoreCase):
{
ModelState.Clear();
var index = int.Parse(
viewModel.Command.Substring(
viewModel.Command.IndexOf(":", StringComparison.InvariantCultureIgnoreCase) + 1),
CultureInfo.InvariantCulture);
viewModel.SpecificStores.RemoveAt(index);
return View(viewModel);
}
case "remove-store":
{
if (storeIndex != null)
permissionValueItem.SpecificStores.RemoveAt(int.Parse(storeIndex,
CultureInfo.InvariantCulture));
return View(viewModel);
}
}
return null;
}
@@ -249,95 +290,56 @@ namespace BTCPayServer.Controllers
private IEnumerable<Permission> GetPermissionsFromViewModel(AddApiKeyViewModel viewModel)
{
List<Permission> permissions = new List<Permission>();
foreach (var p in viewModel.PermissionValues.Where(tuple => tuple.Value && !tuple.Forbidden))
foreach (var p in viewModel.PermissionValues.Where(tuple => !tuple.Forbidden))
{
if (Permission.TryCreatePermission(p.Permission, null, out var pp))
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)));
}
}
else if (p.Value && Permission.TryCreatePermission(p.Permission, null, out var pp))
permissions.Add(pp);
}
if (viewModel.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.AllStores && viewModel.StoreManagementPermission.Value)
{
permissions.Add(Permission.Create(Policies.CanModifyStoreSettings));
}
else if (viewModel.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.Specific)
{
permissions.AddRange(viewModel.SpecificStores.Select(s => Permission.Create(Policies.CanModifyStoreSettings, s)));
}
return permissions.Distinct();
}
private async Task<T> SetViewModelValues<T>(T viewModel) where T : AddApiKeyViewModel
{
viewModel.Stores = await _StoreRepository.GetStoresByUserId(_userManager.GetUserId(User));
var isAdmin = (await _authorizationService.AuthorizeAsync(User, Policies.CanModifyServerSettings)).Succeeded;
viewModel.PermissionValues ??= Policies.AllPolicies.Where(p => p != Policies.CanModifyStoreSettings)
.Select(s => new AddApiKeyViewModel.PermissionValueItem() { Permission = s, Value = false }).ToList();
var isAdmin = (await _authorizationService.AuthorizeAsync(User, Policies.CanModifyServerSettings))
.Succeeded;
viewModel.PermissionValues ??= Policies.AllPolicies
.Select(s => new AddApiKeyViewModel.PermissionValueItem()
{
Permission = s,
Value = false,
Forbidden = Policies.IsServerPolicy(s) && !isAdmin
}).ToList();
if (!isAdmin)
{
foreach (var p in viewModel.PermissionValues)
foreach (var p in viewModel.PermissionValues.Where(item => Policies.IsServerPolicy(item.Permission)))
{
if (p.Permission == Policies.CanCreateUser ||
p.Permission == Policies.CanModifyServerSettings)
{
p.Forbidden = true;
}
p.Forbidden = true;
}
}
return viewModel;
}
public class AddApiKeyViewModel
{
public AddApiKeyViewModel()
{
StoreManagementPermission = new PermissionValueItem()
{
Permission = Policies.CanModifyStoreSettings,
Value = false
};
StoreManagementSelectivePermission = new PermissionValueItem()
{
Permission = $"{Policies.CanModifyStoreSettings}:",
Value = true
};
}
public AddApiKeyViewModel(IEnumerable<Permission> permissions):this()
{
StoreManagementPermission.Value = permissions.Any(p => p.Policy == Policies.CanModifyStoreSettings && p.StoreId == null);
PermissionValues = permissions.Where(p => p.Policy != Policies.CanModifyStoreSettings)
.Select(p => new PermissionValueItem() { Permission = p.ToString(), Value = true })
.ToList();
}
public IEnumerable<Permission> GetPermissions()
{
if (!(PermissionValues is null))
{
foreach (var p in PermissionValues.Where(o => o.Value))
{
if (Permission.TryCreatePermission(p.Permission, null, out var pp))
yield return pp;
}
}
if (this.StoreMode == ApiKeyStoreMode.AllStores)
{
if (StoreManagementPermission.Value)
yield return Permission.Create(Policies.CanModifyStoreSettings);
}
else if (this.StoreMode == ApiKeyStoreMode.Specific && SpecificStores is List<string>)
{
foreach (var p in SpecificStores)
{
if (Permission.TryCreatePermission(Policies.CanModifyStoreSettings, p, out var pp))
yield return pp;
}
}
}
public string Label { get; set; }
public StoreData[] Stores { get; set; }
public ApiKeyStoreMode StoreMode { get; set; }
public List<string> SpecificStores { get; set; } = new List<string>();
public PermissionValueItem StoreManagementPermission { get; set; }
public PermissionValueItem StoreManagementSelectivePermission { get; set; }
public string Command { get; set; }
public List<PermissionValueItem> PermissionValues { get; set; }
@@ -354,43 +356,40 @@ namespace BTCPayServer.Controllers
{BTCPayServer.Client.Policies.Unrestricted, ("Unrestricted access", "The app will have unrestricted access to your account.")},
{BTCPayServer.Client.Policies.CanCreateUser, ("Create new users", "The app will be able to create new users on this server.")},
{BTCPayServer.Client.Policies.CanModifyStoreSettings, ("Modify your stores", "The app will be able to create, view and modify, delete and create new invoices on the all your stores.")},
{BTCPayServer.Client.Policies.CanViewStoreSettings, ("View your stores", "The app will be able to view stores settings.")},
{$"{BTCPayServer.Client.Policies.CanModifyStoreSettings}:", ("Manage selected stores", "The app will be able to view, modify, delete and create new invoices on the selected stores.")},
{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")},
{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.CanCreateInvoice, ("Create an invoice", "The app will be able to create new invoice.")},
{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.")},
};
public string Title
{
get
{
return PermissionDescriptions[Permission].Title;
return PermissionDescriptions[$"{Permission}{(StoreMode == ApiKeyStoreMode.Specific? ":": "")}"].Title;
}
}
public string Description
{
get
{
return PermissionDescriptions[Permission].Description;
return PermissionDescriptions[$"{Permission}{(StoreMode == ApiKeyStoreMode.Specific? ":": "")}"].Description;
}
}
public string Permission { get; set; }
public bool Value { get; set; }
public bool Forbidden { get; set; }
public ApiKeyStoreMode StoreMode { get; set; } = ApiKeyStoreMode.AllStores;
public List<string> SpecificStores { get; set; } = new List<string>();
}
}
public class AuthorizeApiKeysViewModel : AddApiKeyViewModel
{
public AuthorizeApiKeysViewModel()
{
}
public AuthorizeApiKeysViewModel(IEnumerable<Permission> permissions) : base(permissions)
{
Permissions = string.Join(';', permissions.Select(p => p.ToString()).ToArray());
}
public string ApplicationName { get; set; }
public bool Strict { get; set; }
public bool SelectiveStores { get; set; }