using GPW.CORE.Data.Controllers; using GPW.CORE.Data.DbModels; using GPW.CORE.Data.DTO; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using NLog; using StackExchange.Redis; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection.PortableExecutable; using System.Text; using System.Threading.Tasks; namespace GPW.CORE.Data.Services { public class AppAuthService : IDisposable { #region Public Fields // diritti (cablòato) public const string RoleSuperAdmin = "MoonPro_SuperAdmin"; public static AppAuthController dbController { get; set; } = null!; public static GPWController GpwDbController { get; set; } = null!; public static AppUserController userController { get; set; } = null!; #endregion Public Fields #region Public Constructors public AppAuthService(IConfiguration configuration, IConnectionMultiplexer redisConnMult) { _configuration = configuration; // cod app CodApp = _configuration.GetValue("ServerConf:CodApp") ?? "GPW.ADM"; Modulo = _configuration.GetValue("ServerConf:Modulo") ?? "GPW"; // 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 DB string connStr = _configuration.GetConnectionString("GPW.DB") ?? ""; if (string.IsNullOrEmpty(connStr)) { Log.Error("ConnString empty!"); } else { dbController = new AppAuthController(configuration); GpwDbController = new GPWController(configuration); userController = new AppUserController(configuration); Log.Info("DbController OK"); } } #endregion Public Constructors #region Public Methods public async Task> AnagKeyValList() { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = GpwDbController.AnagKeyValGetAll(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per AnagKeyValList: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } /// /// 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); dbResult = tempResult ?? new List(); } 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 dbController.Dispose(); GpwDbController.Dispose(); } public async Task FlushRedisCache() { RedisValue pattern = new RedisValue($"{redisBaseAddr}:*"); bool answ = await ExecFlushRedisPattern(pattern); // reset in RAM Vocabolario = new Dictionary(); CheckVoc(); } /// /// 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; } /// /// Menu pagine abilitate dato utente /// /// /// public async Task> MenuUtente(string UserName) { string source = "DB"; List? dbResult = new List(); string currKey = $"{rKeyMenuUser}:{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 { // verifica preliminare vocabolario CheckVoc(); // recupero permessi utente... List userRightList = await PermessiGetByUser(UserName); // converto in voce menù dbResult = userRightList // solo le vere voci di menù x parametri gruppo/numero .Where(x => x.Numero > 0 && x.Gruppo < 999) .OrderBy(g => g.Gruppo) .ThenBy(n => n.Numero) .Select(x => new MenuItemDTO { Url = x.Url, Title = Traduci(x.Nome??x.Url), Descript = Traduci(x.Descrizione ?? ""), Group = x.Gruppo ?? 0, Num = x.Numero ?? 0 }) .ToList(); // salvo in redis... rawData = JsonConvert.SerializeObject(dbResult, JSSettings); await redisDb.StringSetAsync(currKey, rawData, UltraLongCache); } if (dbResult == null) { dbResult = new List(); } sw.Stop(); Log.Debug($"MenuUtente | {source} | {sw.ElapsedMilliseconds} ms"); return dbResult; } public string Traduci(string lemma, string lingua = "IT") { string answ = $"__{lemma}__"; string keyReq = $"{lingua}#{lemma}"; CheckVoc(); // cerco in vocabolario... if (Vocabolario.ContainsKey(keyReq)) { answ = Vocabolario[keyReq]; } return answ; } #endregion Public Methods #region Protected Methods protected void CheckVoc() { if (Vocabolario.Count == 0) { string source = "DB"; string currKey = $"{redisBaseAddr}:MP:VOCAB"; Stopwatch sw = new Stopwatch(); sw.Start(); string? rawData = redisDb.StringGet(currKey); if (!string.IsNullOrEmpty(rawData) && rawData.Length > 2) { source = "REDIS"; var tempResult = JsonConvert.DeserializeObject>(rawData); Vocabolario = tempResult ?? new Dictionary(); } else { Vocabolario = dbController .VocabolarioGetAll() .ToDictionary(x => $"{x.Lingua}#{x.Lemma}", x => x.Traduzione); rawData = JsonConvert.SerializeObject(Vocabolario); redisDb.StringSet(currKey, rawData, UltraLongCache); } sw.Stop(); Log.Trace($"Rilettura Vocabolario | {source} | {sw.ElapsedMilliseconds} ms"); } } #endregion Protected Methods #region Private Fields // gestione key Redis private const string redisBaseAddr = "GPW:ADM"; private const string rKeyDirittiUser = $"{redisBaseAddr}:DIR_USER"; private const string rKeyPermUser = $"{redisBaseAddr}:PERM_USER"; private const string rKeyMenuUser = $"{redisBaseAddr}:MENU_USER"; private static IConfiguration _configuration { get; set; } = null!; private static JsonSerializerSettings? JSSettings; private static Logger Log = LogManager.GetCurrentClassLogger(); private static string Modulo = ""; /// /// Durata cache lunga IN SECONDI /// private int cacheTtlLong = 60 * 5; /// /// Durata cache breve IN SECONDI /// private int cacheTtlShort = 60 * 1; /// /// 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 #region Private Properties 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 #region Private Methods /// /// Esegue flush memoria redis dato pattern /// /// /// private async Task ExecFlushRedisPattern(RedisValue pattern) { bool answ = false; var listEndpoints = redisConn.GetEndPoints(); foreach (var endPoint in listEndpoints) { //var server = redisConnAdmin.GetServer(listEndpoints[0]); var server = redisConn.GetServer(endPoint); if (server != null) { var keyList = server.Keys(redisDb.Database, pattern); foreach (var item in keyList) { await redisDb.KeyDeleteAsync(item); } answ = true; } } return answ; } #endregion Private Methods } }