diff --git a/MP.Land/Data/AppAuthService.cs b/MP.Land/Data/AppAuthService.cs index a4c803b7..665a7977 100644 --- a/MP.Land/Data/AppAuthService.cs +++ b/MP.Land/Data/AppAuthService.cs @@ -12,6 +12,8 @@ using System.Text; using Newtonsoft.Json; using System.Diagnostics.Eventing.Reader; using MP.AppAuth.Models; +using StackExchange.Redis; +using MP.AppAuth.Controllers; namespace MP.Land.Data { @@ -19,17 +21,36 @@ namespace MP.Land.Data { #region Public Fields + // diritti (cablòato) + public const string RoleSuperAdmin = "MoonPro_SuperAdmin"; + public static AppAuth.Controllers.AppAuthController dbController; public static AppAuth.Controllers.MPController MpDbController; + public static AppAuth.Controllers.MPUserController userController; #endregion Public Fields #region Public Constructors - public AppAuthService(IConfiguration configuration, ILogger logger, IMemoryCache memoryCache, IDistributedCache distributedCache) + public AppAuthService(IConfiguration configuration, ILogger logger, IConnectionMultiplexer redisConnMult, IMemoryCache memoryCache, IDistributedCache distributedCache) { _logger = logger; _configuration = configuration; + + // cod app + CodApp = _configuration.GetValue("ServerConf:CodApp"); + Modulo = _configuration.GetValue("ServerConf:Modulo"); + + // Conf cache + redisConn = redisConnMult; + redisDb = this.redisConn.GetDatabase(); + + // json serializer... FIX errore loop circolare https://www.ryadel.com/en/jsonserializationexception-self-referencing-loop-detected-error-fix-entity-framework-asp-net-core/ + JSSettings = new JsonSerializerSettings() + { + ReferenceLoopHandling = ReferenceLoopHandling.Ignore + }; + // conf cache this.memoryCache = memoryCache; this.distributedCache = distributedCache; @@ -41,8 +62,9 @@ namespace MP.Land.Data } else { - dbController = new AppAuth.Controllers.AppAuthController(configuration); - MpDbController = new AppAuth.Controllers.MPController(configuration); + dbController = new AppAuthController(configuration); + MpDbController = new MPController(configuration); + userController = new MPUserController(configuration); _logger.LogInformation("DbController OK"); } } @@ -200,6 +222,48 @@ namespace MP.Land.Data return await Task.FromResult(answ); } + /// + /// Elenco diritti dato utente + /// + /// + /// + public async Task> DirittiGetByUser(string UserName) + { + string source = "DB"; + List? dbResult = new List(); + string currKey = $"{rKeyDirittiUser}:{UserName}"; + Stopwatch sw = new Stopwatch(); + sw.Start(); + string? rawData = await redisDb.StringGetAsync(currKey); + if (!string.IsNullOrEmpty(rawData) && rawData.Length > 2) + { + source = "REDIS"; + var tempResult = JsonConvert.DeserializeObject>(rawData); + if (tempResult == null) + { + dbResult = new List(); + } + else + { + dbResult = tempResult; + } + } + else + { + // recupero diritti utente + dbResult = userController.DirittiUtente(UserName, Modulo); + rawData = JsonConvert.SerializeObject(dbResult, JSSettings); + await redisDb.StringSetAsync(currKey, rawData, UltraLongCache); + } + if (dbResult == null) + { + dbResult = new List(); + } + sw.Stop(); + Log.Debug($"DirittiGetByUser | {source} | {sw.ElapsedMilliseconds} ms"); + return dbResult; + } + public void Dispose() { // Clear database controller @@ -207,6 +271,56 @@ namespace MP.Land.Data MpDbController.Dispose(); } + /// + /// Elenco permessi dato utente + /// + /// + /// + public async Task> PermessiGetByUser(string UserName) + { + string source = "DB"; + List? dbResult = new List(); + string currKey = $"{rKeyPermUser}:{UserName}"; + Stopwatch sw = new Stopwatch(); + sw.Start(); + string? rawData = await redisDb.StringGetAsync(currKey); + if (!string.IsNullOrEmpty(rawData)) + { + source = "REDIS"; + var tempResult = JsonConvert.DeserializeObject>(rawData); + if (tempResult == null) + { + dbResult = new List(); + } + else + { + dbResult = tempResult; + } + } + else + { + // recupero diritti utente + var userRightList = userController.DirittiUtente(UserName, Modulo); + // proietto come funzioni... + var ListFunc = userRightList.Select(x => x.Funzione).ToList(); + // trasformo i permessi utente + if (ListFunc == null) + { + ListFunc = new List(); + } + dbResult = dbController.PermessiGetByFunc(ListFunc); + rawData = JsonConvert.SerializeObject(dbResult, JSSettings); + await redisDb.StringSetAsync(currKey, rawData, UltraLongCache); + } + if (dbResult == null) + { + dbResult = new List(); + } + sw.Stop(); + Log.Debug($"PermessiGetByUser | {source} | {sw.ElapsedMilliseconds} ms"); + return dbResult; + } + public async Task ResetCache() { string cacheKey = ":MP:VOCAB"; @@ -324,18 +438,32 @@ namespace MP.Land.Data #region Private Fields + // gestione key Redis + private const string redisBaseAddr = "MP:LAND"; + + private const string rKeyDirittiUser = $"{redisBaseAddr}:DIR_USER"; + private const string rKeyPermUser = $"{redisBaseAddr}:PERM_USER"; private static IConfiguration _configuration; - private static ILogger _logger; - private static List ElencoMacchine = new List(); - + private static JsonSerializerSettings? JSSettings; private static Logger Log = LogManager.GetCurrentClassLogger(); + private static string Modulo = ""; private readonly IDistributedCache distributedCache; private readonly IMemoryCache memoryCache; + /// + /// Durata cache lunga IN SECONDI + /// + private int cacheTtlLong = 60 * 5; + + /// + /// Durata cache breve IN SECONDI + /// + private int cacheTtlShort = 60 * 1; + /// /// Durata assoluta massima della cache /// @@ -347,6 +475,18 @@ namespace MP.Land.Data /// private int chSliExp = 5; + /// + /// Oggetto per connessione a REDIS + /// + private IConnectionMultiplexer redisConn; + + //ISubscriber sub = redis.GetSubscriber(); + /// + /// Oggetto DB redis da impiegare x chiamate R/W + /// + private IDatabase redisDb = null!; + + private Random rnd = new Random(); private Dictionary Vocabolario = new Dictionary(); #endregion Private Fields @@ -369,6 +509,32 @@ namespace MP.Land.Data } } + private string CodApp { get; set; } = ""; + + /// + /// Durata cache lunga (+ perturbazione percentuale +/-10%) + /// + private TimeSpan FastCache + { + get => TimeSpan.FromSeconds(cacheTtlShort * rnd.Next(900, 1100) / 1000); + } + + /// + /// Durata cache lunga (+ perturbazione percentuale +/-10%) + /// + private TimeSpan LongCache + { + get => TimeSpan.FromSeconds(cacheTtlLong * rnd.Next(900, 1100) / 1000); + } + + /// + /// Durata cache lunga (+ perturbazione percentuale +/-10%) + /// + private TimeSpan UltraLongCache + { + get => TimeSpan.FromSeconds(cacheTtlLong * 10 * rnd.Next(900, 1100) / 1000); + } + #endregion Private Properties } } \ No newline at end of file diff --git a/MP.Land/MP.Land.csproj b/MP.Land/MP.Land.csproj index 2c47ece8..1d5ae9dc 100644 --- a/MP.Land/MP.Land.csproj +++ b/MP.Land/MP.Land.csproj @@ -3,7 +3,7 @@ net6.0 MP.Land - 6.16.2407.3117 + 6.16.2409.0317 diff --git a/MP.Land/Resources/ChangeLog.html b/MP.Land/Resources/ChangeLog.html index 68770304..9f01dd6e 100644 --- a/MP.Land/Resources/ChangeLog.html +++ b/MP.Land/Resources/ChangeLog.html @@ -1,6 +1,6 @@ Modulo Tablet MAPO - DotNet6 -

Versione: 6.16.2407.3117

+

Versione: 6.16.2409.0317


Note di rilascio:
    diff --git a/MP.Land/Resources/VersNum.txt b/MP.Land/Resources/VersNum.txt index db65b563..ca913415 100644 --- a/MP.Land/Resources/VersNum.txt +++ b/MP.Land/Resources/VersNum.txt @@ -1 +1 @@ -6.16.2407.3117 +6.16.2409.0317 diff --git a/MP.Land/Resources/manifest.xml b/MP.Land/Resources/manifest.xml index f294da22..92dd3a11 100644 --- a/MP.Land/Resources/manifest.xml +++ b/MP.Land/Resources/manifest.xml @@ -1,6 +1,6 @@ - 6.16.2407.3117 + 6.16.2409.0317 https://nexus.steamware.net/repository/SWS/MP-LAND/stable/LAST/MP.Land.zip https://nexus.steamware.net/repository/SWS/MP-LAND/stable/LAST/ChangeLog.html false diff --git a/MP.Land/Shared/NavMenu.razor b/MP.Land/Shared/NavMenu.razor index 613c30f2..547e2abb 100644 --- a/MP.Land/Shared/NavMenu.razor +++ b/MP.Land/Shared/NavMenu.razor @@ -9,56 +9,56 @@
    -@code { - private bool collapseNavMenu = true; - - private string NavMenuCssClass => collapseNavMenu ? "collapse" : null; - - private void ToggleNavMenu() - { - collapseNavMenu = !collapseNavMenu; - } -} \ No newline at end of file diff --git a/MP.Land/Shared/NavMenu.razor.cs b/MP.Land/Shared/NavMenu.razor.cs new file mode 100644 index 00000000..4e8ed084 --- /dev/null +++ b/MP.Land/Shared/NavMenu.razor.cs @@ -0,0 +1,150 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Configuration; +using MP.AppAuth.Models; +using MP.Land.Data; +using System.Collections.Generic; +using System.Configuration; +using System.Linq; +using System.Threading.Tasks; + +namespace MP.Land.Shared +{ + public partial class NavMenu + { + #region Protected Properties + + [Inject] + protected AuthenticationStateProvider AuthStateProvider { get; set; } = null!; + + [Inject] + protected AppAuthService DataService { get; set; } + + protected bool IsSuperAdmin + { + get => HasRight(AppAuthService.RoleSuperAdmin); + } + + [Inject] + protected NavigationManager NavManager { get; set; } = null!; + + protected string pageName + { + get + { + string pName = NavManager.ToBaseRelativePath(NavManager.Uri).ToLower(); + if (pName.Contains("?")) + { + pName = pName.Substring(0, pName.IndexOf("?")); + } + return pName; + } + } + + protected override async Task OnInitializedAsync() + { + await ReloadData(); + } + + protected List UserPerm { get; set; } = new List(); + protected List UserRight { get; set; } = new List(); + + #endregion Protected Properties + + #region Protected Methods + + protected bool HasRight(string codFunz) + { + bool answ = false; + if (UserRight != null && UserRight.Count > 0) + { + answ = UserRight + .Where(x => x.Funzione.Equals(codFunz, System.StringComparison.InvariantCultureIgnoreCase)) + .Count() > 0; + } + return answ; + } + + #endregion Protected Methods + + #region Private Fields + + private bool collapseNavMenu = true; + + private bool isLoading { get; set; } = false; + private string SafePages = ""; + private string userName = ""; + + #endregion Private Fields + + #region Private Properties + + [Inject] + private IConfiguration ConfMan { get; set; } = null!; + + private string NavMenuCssClass => collapseNavMenu ? "collapse" : null; + + #endregion Private Properties + + #region Private Methods + + private async Task checkAuth() + { + // verifico pagina tra i permessi, se manca --> rimando a pagina unauth... se contiene + // index --> salto + if (!pageName.Contains("index")) + { + bool isAuth = false; + if (UserPerm != null) + { + isAuth = UserPerm.Where(x => x.Url.ToLower() == pageName).Count() > 0; + bool pageIsSafe = SafePages.ToLower().Contains($"|{pageName.ToLower()}|"); + if (!pageIsSafe && !isAuth) + { + NavManager.NavigateTo("Unauthorized", true); + } + } + } + } + + private async Task ReloadData() + { + isLoading = true; + await Task.Delay(1); + // sistemo elenco pagine safe... + SafePages = ConfMan.GetValue("Application:SafePages").ToLower(); + var authState = await AuthStateProvider.GetAuthenticationStateAsync(); + var user = authState.User; + if (user.Identity != null && user.Identity.IsAuthenticated) + { + userName = $"{user.Identity.Name}"; + } + else + { + userName = "N.A."; + } + // carico diritti... + var domUser = userName.Split("\\"); + if (domUser.Length > 0) + { + string dominio = domUser[0]; + string uName = domUser[1]; + UserRight = await DataService.DirittiGetByUser(uName); + UserPerm = await DataService.PermessiGetByUser(uName); + } + await checkAuth(); + await Task.Delay(1); + isLoading = false; + await Task.Delay(1); + //await InvokeAsync(StateHasChanged); + } + + private void ToggleNavMenu() + { + collapseNavMenu = !collapseNavMenu; + } + + #endregion Private Methods + } +} \ No newline at end of file diff --git a/MP.Land/Startup.cs b/MP.Land/Startup.cs index 0fd913d8..96c09590 100644 --- a/MP.Land/Startup.cs +++ b/MP.Land/Startup.cs @@ -11,8 +11,10 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using MP.Land.Data; +using StackExchange.Redis; using System; using System.Collections.Generic; +using System.Configuration; using System.Globalization; using System.Linq; using System.Threading.Tasks; @@ -124,6 +126,13 @@ namespace MP.Land options.InstanceName = "MP:Land"; }); + // REDIS setup + string connStringRedis = Configuration.GetConnectionString("Redis"); + string redisSrvAddr = connStringRedis.Substring(0, connStringRedis.IndexOf(":")); + // avvio oggetto shared x redis... + var redisMultiplexer = ConnectionMultiplexer.Connect(connStringRedis); + + services.AddLocalization(); services.AddRazorPages(); @@ -135,6 +144,7 @@ namespace MP.Land services.AddScoped(); services.AddScoped(); + services.AddSingleton(redisMultiplexer); services.AddBlazoredLocalStorage(); services.AddBlazoredSessionStorage(); diff --git a/MP.Land/appsettings.Production.json b/MP.Land/appsettings.Production.json index 61a2b439..9958c471 100644 --- a/MP.Land/appsettings.Production.json +++ b/MP.Land/appsettings.Production.json @@ -14,6 +14,7 @@ "ConnectionStrings": { "DefaultConnection": "Server=SQL2016DEV;Database=MoonPro;Trusted_Connection=True;MultipleActiveResultSets=true", "MP.Land": "Server=SQL2016DEV;Database=MoonPro;User ID=sa;Password=keyhammer16;integrated security=False;MultipleActiveResultSets=True;App=MP.Land;", + "MP.Land.Auth": "Server=SQL2016DEV;Database=MoonPro_Anagrafica;User ID=sa;Password=keyhammer16;integrated security=False;MultipleActiveResultSets=True;App=MP.Land;", "Redis": "localhost:26379,serviceName=devel,defaultDatabase=5,keepAlive=180,asyncTimeout=5000" }, "ServerConf": { diff --git a/MP.Land/appsettings.json b/MP.Land/appsettings.json index 8276633e..511ac405 100644 --- a/MP.Land/appsettings.json +++ b/MP.Land/appsettings.json @@ -13,10 +13,16 @@ "ConnectionStrings": { "DefaultConnection": "Server=SQL2016DEV;Database=MoonPro;Trusted_Connection=True;MultipleActiveResultSets=true", "MP.Land": "Server=SQL2016DEV;Database=MoonPro;User ID=sa;Password=keyhammer16;integrated security=False;MultipleActiveResultSets=True;App=MP.Land;", + "MP.Land.Auth": "Server=SQL2016DEV;Database=MoonPro_Anagrafica;User ID=sa;Password=keyhammer16;integrated security=False;MultipleActiveResultSets=True;App=MP.Land;", //"MP.Land": "Server=SQL2016DEV;Database=MoonPro_ONE;User ID=sa;Password=keyhammer16;integrated security=False;MultipleActiveResultSets=True;App=MP.Land;", "Redis": "localhost:26379, serviceName=devel, defaultDatabase=1, keepAlive=180, connectTimeout=5000, syncTimeout=5000, asyncTimeout=5000, abortConnect=false, ssl=false, allowAdmin=true" }, + "Application": { + "SafePages": "||LAND|Home|Index|About|Help|Unauthorized|" + }, "ServerConf": { + "CodApp": "MP-LAND", + "Modulo": "MoonPro", "BaseUrl": "https://localhost:44309/", "downloadPath": "C:\\Steamware\\installers\\MP" }