Uldate filtraggio ruoli e nomi utente

This commit is contained in:
Samuele Locatelli
2024-07-15 12:04:28 +02:00
parent b522bd4ec0
commit 623d9d3e6d
16 changed files with 782 additions and 15 deletions
+13 -1
View File
@@ -385,12 +385,24 @@ namespace LiMan.DB.Controllers
return answ;
}
public List<AuthUserModel> AuthUserAll()
{
List<AuthUserModel> dbResult = new List<AuthUserModel>();
using (LMDbContext localDbCtx = new LMDbContext(_configuration))
{
dbResult = localDbCtx
.DbSetUsers
.Include(c => c.Claims)
.ThenInclude(r => r.RoleNav)
.ToList();
}
return dbResult;
}
public List<AuthUserModel> AuthUserGetFilt(string userName)
{
List<AuthUserModel> dbResult = new List<AuthUserModel>();
using (LMDbContext localDbCtx = new LMDbContext(_configuration))
{
DateTime oggi = DateTime.Today;
dbResult = localDbCtx
.DbSetUsers
.Where(x => x.Username == userName)
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>License Manager</i>
<h4>Versione: 1.1.2407.1511</h4>
<h4>Versione: 1.1.2407.1512</h4>
<br />
Note di rilascio:
<ul>
+1 -1
View File
@@ -1 +1 @@
1.1.2407.1511
1.1.2407.1512
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>1.1.2407.1511</version>
<version>1.1.2407.1512</version>
<url>https://nexus.steamware.net/repository/SWS/LiMan/stable/LAST/LiMan.Transfer.zip</url>
<changelog>https://nexus.steamware.net/repository/SWS/LiMan/stable/LAST/ChangeLog.html</changelog>
<mandatory>false</mandatory>
+198
View File
@@ -0,0 +1,198 @@
@if (ShowPopup)
{
<div class="modal" tabindex="-1" style="display:block" role="dialog">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Edit User</h3>
<!-- Button to close the popup -->
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="ClosePopup">
</button>
</div>
<!-- Edit form for the current user -->
<div class="modal-body">
@* <div class="row">
<div class="col-6">
<div class="input-group mb-2">
<span class="input-group-text"><i class="far fa-envelope"></i></span>
<input class="form-control" type="text" placeholder="Email" @bind="objUser.Email" />
<div class="input-group-text">
<input class="form-control2" type="checkbox" title="Email Confirmed" @bind="objUser.EmailConfirmed" />
</div>
</div>
<div class="input-group mb-2">
<span class="input-group-text"><i class="fas fa-key"></i></span>
<input class="form-control" type="password" placeholder="Password" @bind="objUser.PasswordHash" />
</div>
<div class="input-group mb-2">
<span class="input-group-text"><i class="fas fa-user-tag"></i></span>
@if (MultiRoleEnab)
{
<select @bind="CurrUserRoles" multiple class="form-control">
@foreach (var option in RolesList)
{
<option value="@option">
@option
</option>
}
</select>
}
else
{
<select class="form-select" @bind="@CurrentUserRole">
@foreach (var option in RolesList)
{
<option value="@option">
@option
</option>
}
</select>
}
</div>
<div class="input-group mb-2">
@if (MultiClaimEnab)
{
<CmpClaimEdit Value="CurrentUserClaims"></CmpClaimEdit>
}
else
{
<span class="input-group-text"><i class="fas fa-tag"></i></span>
<select class="form-select" @bind="@CurrentUserClaimType">
@foreach (var option in ClaimsList)
{
<option value="@option">
@option
</option>
}
</select>
<select class="form-select" @bind="@CurrentUserClaimVal">
<option value="0">--- Selezionare ---</option>
@foreach (var option in ClaimValList)
{
<option value="@option.Key">
@option.Value
</option>
}
</select>
}
</div>
<span style="color:red">@strError</span>
<hr />
<div class="row">
<div class="col-6">
<button class="btn w-100 btn-success" @onclick="SaveUser" title="Save"><i class="fas fa-check"></i> Save</button>
</div>
<div class="col-6">
<button class="btn w-100 btn-primary" @onclick="ClosePopup"><i class="far fa-window-close"></i> Close</button>
</div>
</div>
</div>
<div class="col-6">
<!-- Only show Id if not a new user -->
@if (objUser.Id != "")
{
<QrCodeDisplayJS rawCode="@qrCodeVal" Height="450" Width="450"></QrCodeDisplayJS>
}
<CopyToClipboard Text="@qrCodeVal"></CopyToClipboard>
</div>
</div> *@
</div>
</div>
</div>
</div>
}
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between">
<div class="px-1 text-truncate">
<h3>User Administration</h3>
</div>
<div class="px-1">
<div class="input-group">
<span class="input-group-text"><i class="fas fa-user-tag"></i></span>
<select class="form-select" @bind="@FiltUserRole">
<option value="0">--- Tutti ---</option>
@foreach (var item in RolesList)
{
<option value="@item.RoleID">
@item.Ruolo
</option>
}
</select>
@* <AuthorizeView>
<Authorized>
@if (@context.User.IsInRole("SuperAdmin"))
{
<button class="btn btn-success btn-block" @onclick="AddNewUser"><i class="fa-solid fa-user-plus"></i> Add User</button>
}
</Authorized>
</AuthorizeView> *@
</div>
</div>
</div>
</div>
<div class="card-body">
<table class="table table-sm table-striped table-responsive-md">
<thead>
<tr>
<th></th>
<th>AD User</th>
<th>Cognome</th>
<th>Nome</th>
<th>Ruolo</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var user in UsersList)
{
<tr>
<td>
@if (!ShowPopup)
{
<button class="btn btn-sm btn-primary" @onclick="(() => EditUser(user))" title="Edit">
<i class="fas fa-pen"></i>
</button>
}
else
{
<button class="btn btn-sm btn-secondary disabled" title="Edit">
<i class="fas fa-pen"></i>
</button>
}
</td>
<td>@user.Username</td>
<td>
@user.Cognome
</td>
<td>
@user.Nome
</td>
<td>
@ShowRoles(user.Claims.ToList())
</td>
<td>
@if (!ShowPopup)
{
<button class="btn btn-sm btn-danger" @onclick="(() => DeleteUser(user))" title="Delete">
<i class="fas fa-trash"></i>
</button>
}
else
{
<button class="btn btn-sm btn-secondary disabled" title="Delete">
<i class="fas fa-trash"></i>
</button>
}
</td>
</tr>
}
</tbody>
</table>
</div>
<div class="card-footer">
<DataPager PageSize="@numRecord" currPage="@currPage" numRecordChanged="SetNumRec" numPageChanged="SetPage" totalCount="@totalCount" showLoading="@isLoading"></DataPager>
</div>
</div>
+466
View File
@@ -0,0 +1,466 @@
using LiMan.UI.Data;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System;
using LiMan.DB.DBModels;
namespace LiMan.UI.Components
{
public partial class UserMan : IDisposable
{
#region Public Methods
public void Dispose()
{
AppMService.EA_SearchUpdated -= AppMService_EA_SearchUpdated;
}
[Inject]
protected LiManDataService DataService { get; set; } = null!;
/// <summary>
/// Recupera elenco utenti
/// </summary>
public async Task GetUsers()
{
// clear any error messages
strError = "";
UsersAll = await DataService.AuthUserAll();
// filtro visualizzazione x tipo SE richeisto
if (FiltUserRole > 0)
{
UsersAll = UsersAll.Where(x => x.Claims.Any(c => c.RoleID == FiltUserRole)).ToList();
}
// se c'è search,,,
if (!string.IsNullOrEmpty(currSearch))
{
UsersAll = UsersAll
.Where(x => x.Cognome.Contains(currSearch, StringComparison.CurrentCultureIgnoreCase)
|| x.Nome.Contains(currSearch, StringComparison.CurrentCultureIgnoreCase)
|| x.Username.Contains(currSearch, StringComparison.CurrentCultureIgnoreCase))
.ToList();
}
totalCount = UsersAll.Count;
UsersList = UsersAll
.Skip(numRecord * (currPage - 1)).Take(numRecord)
.ToList();
}
public string ShowRoles(List<AuthClaimModel> ClaimList)
{
string answ = "";
foreach (var item in ClaimList)
{
answ += $"{item.RoleNav.Ruolo}, ";
}
if (answ.Length > 2)
{
answ = answ.Substring(0, answ.Length - 2);
}
return answ;
}
#endregion Public Methods
#region Protected Properties
[Inject]
protected MessageService AppMService { get; set; } = null!;
protected int totalCount { get; set; } = 0;
#endregion Protected Properties
#region Protected Methods
protected void DoSelect(AuthUserModel? selItem)
{
#if false
if (selItem != null)
{
ProjDbId = selItem.ProjDbId;
}
else
{
ProjDbId = 0;
}
E_ProjSel.InvokeAsync(selItem);
#endif
}
protected override async Task OnInitializedAsync()
{
currSearch = "";
string rawConf = Configuration["RuntimeOpt:MultiRoleEnab"];
if (rawConf != null)
{
bool.TryParse(rawConf, out MultiRoleEnab);
}
AppMService.EA_SearchUpdated += AppMService_EA_SearchUpdated;
// lettura dati
await GetUsers();
await refreshLists();
}
protected async Task SetNumRec(int newNum)
{
numRecord = newNum;
currPage = 1;
await InvokeAsync(ReloadData);
}
protected async Task SetPage(int newNum)
{
currPage = newNum;
DoSelect(null);
await InvokeAsync(ReloadData);
}
#endregion Protected Methods
#region Private Fields
private const string ADMIN_ROLE = "Admin";
private const string UNDEF_ROLE = "Undef";
private string currSearch = "";
/// <summary>
/// Abilitazione gestione ROLES multipli
/// </summary>
private bool MultiRoleEnab = true;
/// <summary>
/// User corrente
/// </summary>
private IdentityUser objUser = new IdentityUser();
private string qrCodeVal = "";
/// <summary>
/// Elenco ROLES da mostrare in dropdown durante editing (usare DB?)
/// </summary>
private List<AuthRoleModel> RolesList = new List<AuthRoleModel>();
// To enable showing the Popup
private bool ShowPopup = false;
// To hold any possible errors
private string strError = "";
/// <summary>
/// Collezione utenti (totale)
/// </summary>
private List<AuthUserModel> UsersAll = new List<AuthUserModel>();
/// <summary>
/// Collezione utenti
/// </summary>
private List<AuthUserModel> UsersList = new List<AuthUserModel>();
#endregion Private Fields
#region Private Properties
private int _filtUserRole { get; set; } = 0;
[Inject]
private IConfiguration Configuration { get; set; } = null!;
/// <summary>
/// Ruolo di default (User!!!)
/// </summary>
private string CurrentUserRole { get; set; } = "User";
private int currPage { get; set; } = 1;
/// <summary>
/// Ruoli correnti utente
/// </summary>
private string[] CurrUserRoles { get; set; } = new string[] { "User" };
/// <summary>
/// Gestione filtraggio dati
/// </summary>
private int FiltUserRole
{
get
{
return _filtUserRole;
}
set
{
if (_filtUserRole != value)
{
_filtUserRole = value;
var pUpd = Task.Run(async () => await ReloadData());
pUpd.Wait();
}
}
}
private bool isLoading { get; set; } = false;
private int numRecord { get; set; } = 10;
#endregion Private Properties
#region Private Methods
private void AddNewUser()
{
// Make new user
objUser = new IdentityUser();
objUser.PasswordHash = "*****";
// Set Id to blank so we know it is a new record
objUser.Id = "";
// Open the Popup
ShowPopup = true;
}
private async void AppMService_EA_SearchUpdated()
{
currSearch = AppMService.SearchVal;
await ReloadData();
}
private async Task ClosePopup()
{
// Close the Popup
ShowPopup = false;
// Refresh Users
await GetUsers();
}
private async Task DeleteUser(AuthUserModel _currRec)
{
#if false
// Close the Popup
ShowPopup = false;
if (!await JSRuntime.InvokeAsync<bool>("confirm", "Sicuro di voler eliminare l'utente selezionato??"))
return;
// Get the user
var user = await _UserManager.FindByIdAsync(_IdentityUser.Id);
if (user != null)
{
// Delete the user
await _UserManager.DeleteAsync(user);
}
#endif
// Refresh Users
await GetUsers();
}
private void EditUser(AuthUserModel _currRec)
{
// Open the Popup
ShowPopup = true;
}
private async Task refreshLists()
{
RolesList = await DataService.AuthRolesGetAll();
}
private async Task ReloadData()
{
isLoading = true;
await GetUsers();
isLoading = false;
}
private async Task SaveUser()
{
try
{
#if false
// Is this an existing user?
if (objUser.Id != "")
{
// Get the user
var user = await _UserManager.FindByIdAsync(objUser.Id);
// Update Email + check email
user.Email = objUser.Email;
user.EmailConfirmed = objUser.EmailConfirmed;
// Update the user
await _UserManager.UpdateAsync(user);
// Only update password if the current value is not the default value
if (objUser.PasswordHash != "*****")
{
var resetToken =
await _UserManager.GeneratePasswordResetTokenAsync(user);
var passworduser =
await _UserManager.ResetPasswordAsync(
user,
resetToken,
objUser.PasswordHash);
if (!passworduser.Succeeded)
{
if (passworduser.Errors.FirstOrDefault() != null)
{
strError =
passworduser
.Errors
.FirstOrDefault()
.Description;
}
else
{
strError = "Password error";
}
// Keep the popup opened
return;
}
}
// Gestione salvataggio ruoli... SE VARIATO...
var UserRoles = await _UserManager.GetRolesAsync(user);
if (!MultiRoleEnab)
{
if (UserRoles != null && UserRoles.Count > 0)
{
var oldRole = UserRoles.FirstOrDefault();
if (!CurrentUserRole.Equals(oldRole))
{
// recupero il ruolo attuale e lo rimuovo
await _UserManager.RemoveFromRoleAsync(user, oldRole);
}
// aggiungo il nuovo ruolo
await _UserManager.AddToRoleAsync(user, CurrentUserRole);
}
else
{
// aggiungo il nuovo ruolo
await _UserManager.AddToRoleAsync(user, CurrentUserRole);
}
}
else
{
// verifico ruoli costanti...
var listOk = UserRoles.Intersect(CurrUserRoles).ToList();
var list2del = UserRoles.Except(listOk).ToList();
var list2add = CurrUserRoles.Except(listOk).ToList();
// elimino
foreach (var item in list2del)
{
await _UserManager.RemoveFromRoleAsync(user, item);
}
// aggiungo
foreach (var item in list2add)
{
await _UserManager.AddToRoleAsync(user, item);
}
}
// gestione salvataggio Claims
var UserClaims = await _UserManager.GetClaimsAsync(user);
if (!MultiClaimEnab)
{
if (UserClaims != null && UserClaims.Count > 0)
{
var oldClaim = UserClaims.FirstOrDefault();
if (!oldClaim.Equals(CurrentUserClaim))
{
// recupero il ruolo attuale e lo rimuovo
await _UserManager.RemoveClaimAsync(user, oldClaim);
}
// aggiungo il nuovo ruolo
await _UserManager.AddClaimAsync(user, CurrentUserClaim);
}
else
{
// aggiungo il nuovo ruolo
await _UserManager.AddClaimAsync(user, CurrentUserClaim);
}
}
else
{
// elimino i vecchi claims non presenti nel nuovo...
var listClaimsMod = CurrentUserClaims.Select(x => new Claim(x.Type, x.Value)).ToList();
var listOk = UserClaims.Intersect(listClaimsMod).ToList();
var list2del = UserClaims.Except(listOk).ToList();
var list2add = listClaimsMod.Except(listOk).ToList();
// elimino
foreach (var item in list2del)
{
await _UserManager.RemoveClaimAsync(user, item);
}
// aggiungo
foreach (var item in list2add)
{
await _UserManager.AddClaimAsync(user, item);
}
}
}
else
{
// Insert new user
var NewUser =
new IdentityUser
{
UserName = objUser.Email,
Email = objUser.Email
};
var CreateResult = await _UserManager.CreateAsync(NewUser, objUser.PasswordHash);
if (!CreateResult.Succeeded)
{
if (CreateResult
.Errors
.FirstOrDefault() != null)
{
strError =
CreateResult
.Errors
.FirstOrDefault()
.Description;
}
else
{
strError = "Create error";
}
// Keep the popup opened
return;
}
else
{
// aggiungo a ruolo undef
await _UserManager.AddToRoleAsync(NewUser, UNDEF_ROLE);
// aggiungo claim undef...
await _UserManager.AddClaimAsync(NewUser, CurrentUserClaim);
}
}
// Close the Popup
ShowPopup = false;
// refresh cache...
await MTService.ResetUserDataCache();
#endif
// Refresh Users
await GetUsers();
}
catch (Exception ex)
{
strError = ex.GetBaseException().Message;
}
}
#endregion Private Methods
}
}
+55 -1
View File
@@ -372,6 +372,60 @@ namespace LiMan.UI.Data
return fatto;
}
/// <summary>
/// Recupera elenco Utenti (tutti)
/// </summary>
/// <returns></returns>
public async Task<List<AuthUserModel>> AuthUserAll()
{
string source = "DB";
List<AuthUserModel>? dbResult = new List<AuthUserModel>();
try
{
string currKey = $"{Const.rKeyConfig}:Auth:UsersList";
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
string? rawData = await redisDb.StringGetAsync(currKey);
if (!string.IsNullOrEmpty(rawData))
{
source = "REDIS";
var tempResult = JsonConvert.DeserializeObject<List<AuthUserModel>>(rawData);
if (tempResult == null)
{
dbResult = new List<AuthUserModel>();
}
else
{
dbResult = tempResult;
}
}
else
{
dbResult = dbControllerNext.AuthUserAll();
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, UltraLongCache);
// per evitare loopback uso deserialize...
var tempResult = JsonConvert.DeserializeObject<List<AuthUserModel>>(rawData);
if (tempResult != null)
{
dbResult = tempResult;
}
}
if (dbResult == null)
{
dbResult = new List<AuthUserModel>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"AuthUserAll | {source} in: {ts.TotalMilliseconds} ms");
}
catch (Exception exc)
{
Log.Error($"Error during AuthUserAll:{Environment.NewLine}{exc}");
}
return dbResult;
}
/// <summary>
/// Recupera elenco Utenti UserName (idealmente 1...)
/// </summary>
@@ -383,7 +437,7 @@ namespace LiMan.UI.Data
List<AuthUserModel>? dbResult = new List<AuthUserModel>();
try
{
string currKey = $"{Const.rKeyConfig}:Auth:Users:{userName}";
string currKey = $"{Const.rKeyConfig}:Auth:User:{userName}";
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
string? rawData = await redisDb.StringGetAsync(currKey);
+1 -1
View File
@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Version>1.1.2407.1511</Version>
<Version>1.1.2407.1512</Version>
<RootNamespace>LiMan.UI</RootNamespace>
<AssemblyName>LiMan.UI</AssemblyName>
</PropertyGroup>
+4
View File
@@ -0,0 +1,4 @@
@page "/UserAdmin"
@attribute [Authorize]
<UserMan></UserMan>
+26
View File
@@ -0,0 +1,26 @@
using LiMan.UI.Data;
using Microsoft.AspNetCore.Components;
namespace LiMan.UI.Pages
{
public partial class UserAdmin
{
#region Protected Properties
[Inject]
protected MessageService AppMService { get; set; } = null!;
#endregion Protected Properties
#region Protected Methods
protected override void OnInitialized()
{
AppMService.ShowSearch = false;
AppMService.PageName = "Gestione Utenti";
AppMService.PageIcon = "fas fa-users";
}
#endregion Protected Methods
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>License Manager</i>
<h4>Versione: 1.1.2407.1511</h4>
<h4>Versione: 1.1.2407.1512</h4>
<br /> Note di rilascio:
<ul>
<li>
+1 -1
View File
@@ -1 +1 @@
1.1.2407.1511
1.1.2407.1512
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>1.1.2407.1511</version>
<version>1.1.2407.1512</version>
<url>https://nexus.steamware.net/repository/SWS/LiMan/stable/LAST/LiMan.UI.zip</url>
<changelog>https://nexus.steamware.net/repository/SWS/LiMan/stable/LAST/ChangeLog.html</changelog>
<mandatory>false</mandatory>
+10 -5
View File
@@ -14,6 +14,11 @@
<NavMenuItem IconClass="fas fa-home" Text="Home" />
</NavLink>
</li>
<li class="nav-item px-2">
<NavLink class="nav-link" href="LiManCadCam">
<NavMenuItem IconClass="fas fa-search" Text="Search Licenze CAD/CAM" />
</NavLink>
</li>
@if (UserHasClaim("SuperUser") || UserHasClaim("Admin"))
{
<li class="nav-item px-2">
@@ -46,12 +51,12 @@
<NavMenuItem IconClass="fas fa-lock" Text="Licenze STW" />
</NavLink>
</li>
<li class="nav-item px-2">
<NavLink class="nav-link" href="UserAdmin">
<NavMenuItem IconClass="fas fa-users" Text="Gestione Utenti" />
</NavLink>
</li>
}
<li class="nav-item px-2">
<NavLink class="nav-link" href="LiManCadCam">
<NavMenuItem IconClass="fas fa-search" Text="Search Licenze CAD/CAM" />
</NavLink>
</li>
<li class="nav-item px-2">
<NavLink class="nav-link" href="Reset">
<NavMenuItem IconClass="fas fa-sync" Text="Reset cache" />
+1
View File
@@ -8,6 +8,7 @@
@using Microsoft.JSInterop
@using LiMan.UI
@using LiMan.UI.Shared
@using LiMan.UI.Components
@using EgwCoreLib
@using EgwCoreLib.Razor
@using EgwCoreLib.Utils
+2 -1
View File
@@ -35,7 +35,8 @@
"ProcOp": "ceo@steamware.net"
},
"RuntimeOpt": {
"BaseUrl": "/ELM.UI"
"BaseUrl": "/ELM.UI",
"MultiRoleEnab": true
},
"ApiUrl": "https://liman.egalware.com",
"HostOs": "Win"