From 7bcbbdb5ce48785942dc1822ce044d1c2b633e73 Mon Sep 17 00:00:00 2001 From: Alexander Shabarshov Date: Wed, 12 Nov 2025 15:12:07 +0000 Subject: [PATCH] Support for non-admin commands to be available to non-admin users --- .../Components/Commands/CmdBase.razor | 13 +++- .../Pages/Admin/AdminCommands.razor | 78 +++++++++++++++---- Rms.Risk.Mango/Pages/User/Delete.razor | 2 +- Rms.Risk.Mango/Pages/User/Update.razor | 2 +- Rms.Risk.Mango/Services/TempFileStorage.cs | 18 ++--- Rms.Risk.Mango/Shared/NavMenu.razor | 1 + 6 files changed, 81 insertions(+), 33 deletions(-) diff --git a/Rms.Risk.Mango/Components/Commands/CmdBase.razor b/Rms.Risk.Mango/Components/Commands/CmdBase.razor index d6a56fb..2371c92 100644 --- a/Rms.Risk.Mango/Components/Commands/CmdBase.razor +++ b/Rms.Risk.Mango/Components/Commands/CmdBase.razor @@ -20,9 +20,10 @@ *@ [Inject] protected IUserSession UserSession { get; set; } = null!; - public class CommandParams(string name, Action changed) + public class CommandParams(string name, CmdType commandType, Action changed) { - public string Name { get; } = name; + public string Name { get; } = name; + public CmdType CommandType { get; } = commandType; public string CommandJson { @@ -148,7 +149,7 @@ Validate(); Parameters.ResultChanged = _ => OnResultReceived(); - + StateHasChanged(); } } @@ -227,4 +228,10 @@ return result; } + public MongoCommandAttribute? GetCommandAttribute() => + GetType().GetCustomAttributes(typeof(MongoCommandAttribute), true) + .OfType() + .FirstOrDefault(); + + public CmdType CommandType => GetCommandAttribute()?.Type ?? CmdType.Admin; } diff --git a/Rms.Risk.Mango/Pages/Admin/AdminCommands.razor b/Rms.Risk.Mango/Pages/Admin/AdminCommands.razor index 36edf25..8359cdd 100644 --- a/Rms.Risk.Mango/Pages/Admin/AdminCommands.razor +++ b/Rms.Risk.Mango/Pages/Admin/AdminCommands.razor @@ -6,9 +6,11 @@ @using System.Reflection @using Rms.Risk.Mango.Components.Commands -@inject NavigationManager NavigationManager -@inject IUserSession UserSession -@inject IJSRuntime JsRuntime +@inject NavigationManager NavigationManager +@inject IUserSession UserSession +@inject IJSRuntime JsRuntime +@inject AuthenticationStateProvider AuthProvider; +@inject IAuthorizationService AuthService; @* * dbMango @@ -84,7 +86,7 @@

Commands

- +
@@ -100,10 +102,10 @@ - @foreach (var (group, controls) in _availableCommands) + @foreach (var (group, controls) in _userAvailableCommands) { - @foreach (var (name, control) in controls) + @foreach (var (name, _, control) in controls) { var parameters = new Dictionary() { @@ -184,7 +186,7 @@ } } = "Statistics"; - private static string[] _groupsOrder = + private static readonly string[] _groupsOrder = [ "Informational", "Admin", @@ -195,23 +197,30 @@ private string Timeout { get; set; } = "20"; - private string Error { get; set; } = ""; + private string Error { get; set; } = ""; + private bool IsReady { get; set; } - private bool IsReady { get; set; } private bool IsReadyToRun => IsReady && CanExecute && !string.IsNullOrWhiteSpace(CommandJson); private EditContext? _editContext; private EditContext EditContext => _editContext!; private string CommandName => _commandParams[SelectedCommand].Name; + private CmdType CommandType => _commandParams[SelectedCommand].CommandType; private string CommandJson => _commandParams[SelectedCommand].CommandJson; private bool CanExecute => _commandParams[SelectedCommand].CanExecute; private bool NeedConfirmation => _commandParams[SelectedCommand].NeedConfirmation; private CmdBase.CommandParams.DatabaseConnectionType RequiredConnectionType => _commandParams[SelectedCommand].RequiredConnectionType; - private static readonly List<(string Group, List<(string Name, Type Control)> Controls)> _availableCommands; - private Dictionary _commandParams = null!; - private readonly Dictionary _shardConnectionStrings = []; + private static readonly List<(string Group, List<(string Name, CmdType CommandType, Type Control)> Controls)> _availableCommands; + private List<(string Group, List<(string Name, CmdType CommandType, Type Control)> Controls)> _userAvailableCommands = []; + + private Dictionary _commandParams = null!; + private readonly Dictionary _shardConnectionStrings = []; + + private bool _isWrite; + private bool _isRead; + private bool _isAdmin; private string Shard { get; set; } = ""; private IReadOnlyCollection Shards => _shardConnectionStrings.Keys.OrderBy(x => x).ToList(); @@ -232,7 +241,7 @@ }) .Where(x => x.Attr != null) .GroupBy(x => x.Attr!.Group) - .Select(x => (Group: x.Key, Commands: x.OrderBy(y => y.Attr!.Name).Select(y => (y.Attr!.Name, Control: y.Type) ).ToList())) + .Select(x => (Group: x.Key, Commands: x.OrderBy(y => y.Attr!.Name).Select(y => (y.Attr!.Name, CommandType: y.Attr.Type, Control: y.Type) ).ToList())) .Where(x => _groupsOrder.Contains(x.Group)) .OrderBy(x => Array.IndexOf(_groupsOrder, x.Group)) .ToList() @@ -248,7 +257,7 @@ _commandParams = new( _availableCommands .SelectMany(x => x.Controls) - .Select(x => new KeyValuePair(x.Name, new (x.Name, OnChanged)) + .Select(x => new KeyValuePair(x.Name, new (x.Name, x.CommandType, OnChanged)) ) ); } @@ -258,7 +267,7 @@ InvokeAsync(StateHasChanged); } - protected override void OnAfterRender(bool firstRender) + protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender) return; @@ -280,6 +289,8 @@ SelectedCollection = CollectionStr; SyncUrl(); + await Authorize(); + SelectAvailableCommands(); _ = Task.Run(async () => { @@ -299,6 +310,41 @@ StateHasChanged(); } + private void SelectAvailableCommands() + { + _userAvailableCommands = _availableCommands + .Select(x => + x with { + Controls = x.Controls + .Where(y => + y.CommandType switch + { + CmdType.Admin => _isAdmin, + CmdType.Read => _isRead, + CmdType.Write => _isWrite, + _ => false + }) + .ToList() + }) + .Where(x => x.Controls.Count > 0) + .ToList(); + } + + private async Task Authorize() + { + var user = (await AuthProvider.GetAuthenticationStateAsync()).User; + + var readTask = AuthService.AuthorizeAsync(user, UserSession.Database, "ReadAccess"); + var writeTask = AuthService.AuthorizeAsync(user, UserSession.Database, "WriteAccess"); + var adminTask = AuthService.AuthorizeAsync(user, UserSession.Database, "AdminAccess"); + + var results = await Task.WhenAll(readTask, writeTask, adminTask); + + _isRead = results[0].Succeeded; + _isWrite = results[1].Succeeded; + _isAdmin = results[2].Succeeded; + } + private async Task LoadCollections() { var admin = UserSession.MongoDbAdmin; @@ -372,7 +418,7 @@ return; } - var ticket = await CanExecuteCommand(); + var ticket = CommandType == CmdType.Read ? "N/A" : await CanExecuteCommand(); if (string.IsNullOrWhiteSpace(ticket)) return; diff --git a/Rms.Risk.Mango/Pages/User/Delete.razor b/Rms.Risk.Mango/Pages/User/Delete.razor index 6e5bec0..aab448b 100644 --- a/Rms.Risk.Mango/Pages/User/Delete.razor +++ b/Rms.Risk.Mango/Pages/User/Delete.razor @@ -155,7 +155,7 @@ protected override void OnInitialized() { _editContext = new(this); - _commandParams = new("Delete Documents", OnChanged); + _commandParams = new("Delete Documents", CmdType.Write, OnChanged); } private void OnChanged(CmdBase.CommandParams arg) diff --git a/Rms.Risk.Mango/Pages/User/Update.razor b/Rms.Risk.Mango/Pages/User/Update.razor index ba8639e..9108a46 100644 --- a/Rms.Risk.Mango/Pages/User/Update.razor +++ b/Rms.Risk.Mango/Pages/User/Update.razor @@ -155,7 +155,7 @@ protected override void OnInitialized() { _editContext = new(this); - _commandParams = new("Update Documents", OnChanged); + _commandParams = new("Update Documents", CmdType.Write, OnChanged); } private void OnChanged(CmdBase.CommandParams arg) diff --git a/Rms.Risk.Mango/Services/TempFileStorage.cs b/Rms.Risk.Mango/Services/TempFileStorage.cs index c1ba903..137dfce 100644 --- a/Rms.Risk.Mango/Services/TempFileStorage.cs +++ b/Rms.Risk.Mango/Services/TempFileStorage.cs @@ -96,7 +96,7 @@ public class TempFileStorage : ITempFileStorage, IDisposable { if ( Directory.Exists( TempFolder ) ) { - SafeDeleteFolder( TempFolder ); + FileUtils.SafeDeleteFolder( TempFolder ); } } @@ -114,20 +114,14 @@ public class TempFileStorage : ITempFileStorage, IDisposable foreach ( var name in outdatedFolders ) { - SafeDeleteFolder(name); + _log.Debug( $"Deleting temporary Folder=\"{name}\"" ); + var sw = Stopwatch.StartNew(); + + FileUtils.SafeDeleteFolder(name); + _log.Debug( $"Temporary Folder=\"{name}\" deleted. Elapsed=\"{sw.Elapsed:g}\"" ); } } - private static void SafeDeleteFolder(string name) - { - _log.Debug( $"Deleting temporary Folder=\"{name}\"" ); - var sw = Stopwatch.StartNew(); - - Directory.Delete(name); - _log.Debug( $"Temporary Folder=\"{name}\" deleted. Elapsed=\"{sw.Elapsed:g}\"" ); - } - - public string TempFolder { get; } public string LocalPersistentFolder { get; } diff --git a/Rms.Risk.Mango/Shared/NavMenu.razor b/Rms.Risk.Mango/Shared/NavMenu.razor index 941bab0..f349997 100644 --- a/Rms.Risk.Mango/Shared/NavMenu.razor +++ b/Rms.Risk.Mango/Shared/NavMenu.razor @@ -66,6 +66,7 @@