using Egw.Core; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using MP.AppAuth.Models; using Newtonsoft.Json; using RestSharp; using StackExchange.Redis; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web; #nullable enable namespace MP.Land.Data { /// /// Servizi e dati condivisi a livello applicazione /// public class LicenseService { #region Public Constructors /// /// Init classe /// /// /// /// public LicenseService(IConfiguration configuration, ILogger logger, IConnectionMultiplexer redisConnMult) { _logger = logger; _configuration = configuration; // 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 }; } #endregion Public Constructors #region Public Events public event Action EA_InfoUpdated = null!; #endregion Public Events #region Public Properties public List ActivList { get; set; } = new List(); public List AKVList { get; set; } = new List(); public string Applicazione { get; set; } = ""; public bool HasActivData { get { bool answ = ValidData; // se ok controllo che ci siano attivazioni if (answ) { // provo classe locale... answ = ActivList != null && ActivList.Count > 0; // se non le avessi carico! if (!answ) { var pUpd = Task.Run(async () => { ActivList = await ActivListCache(); }); pUpd.Wait(); } } return answ; } } public DateTime infoExpiry { get; set; } = DateTime.Today.AddDays(1); /// /// Codice cliente/installazione /// public string Installazione { get; set; } = ""; /// /// Master key licenza principale /// public string MasterKey { get; set; } = ""; /// /// Numero licenze da DB /// public int NumLicDb { get; set; } = -1; /// /// Numero licenze da auth remota /// public int NumLicRemote { get; set; } = -1; public bool ValidData { get { // controllo valori string base bool checkData = !string.IsNullOrEmpty(Installazione) && !string.IsNullOrEmpty(Applicazione) && !string.IsNullOrEmpty(MasterKey) && ActivList != null && ActivList.Count > 0; return checkData; } } #endregion Public Properties #region Public Methods public async Task> ActivListCache() { List dbResult = new List(); string cacheKey = $"{rKeyAttByLic}:{MasterKey}"; string rawData = await getRSV(cacheKey); if (!string.IsNullOrEmpty(rawData)) { var cacheRes = JsonConvert.DeserializeObject?>(rawData); if (cacheRes != null) { dbResult = cacheRes; } } return await Task.FromResult(dbResult); } /// /// Verifica attivazione licenza /// /// /// public bool checkLicenseActive(string authKey) { bool answ = false; //cerco anche nelle info AKV if (AKVList != null) { var recLic = AKVList.Where(x => x.ValString == authKey).FirstOrDefault(); int numLic = 0; //cerco in record if (recLic != null && recLic.ValInt != null) { numLic = (int)recLic.ValInt; // verifico scadenza licenza! DateTime scadenza = licenseManGLS.expiryDateByAuthKey(Installazione, recLic.NomeVar, numLic, authKey); answ = scadenza > DateTime.Today; } else { _logger.LogInformation($"checkLicenseActive | Record non trovato per {authKey}"); } } return answ; } /// /// Stato server gestione licenze /// public async Task checkLimanServer() { string answ = "ND"; // cerco online RestClient client = new RestClient(apiUrl); var request = new RestRequest($"api/health", Method.Get); var response = await client.GetAsync(request); // controllo risposta if (response.StatusCode == System.Net.HttpStatusCode.OK) { // verifico risposta if (response.Content != null) { answ = response.Content.Replace("\"", ""); } } return await Task.FromResult(answ); } /// /// Verifica scadenza licenza /// /// /// public DateTime getLicenseExpiry(string authKey) { DateTime answ = DateTime.Today.AddDays(-1); //cerco anche nelle info AKV if (AKVList != null) { var recLic = AKVList.Where(x => x.ValString == authKey).FirstOrDefault(); int numLic = 0; //cerco in record if (recLic != null && recLic.ValInt != null) { numLic = (int)recLic.ValInt; // verifico scadenza licenza! DateTime scadenza = licenseManGLS.expiryDateByAuthKey(Installazione, recLic.NomeVar, numLic, authKey); answ = scadenza; } else { _logger.LogInformation($"getLicenseExpiry | Record non trovato per {authKey}"); } } return answ; } /// /// Init della classe con variabili di base da Redis/DB /// public bool InitAkv() { bool fatto = false; Applicazione = "MAPO"; Installazione = getAVKStr("Installazione"); MasterKey = getAVKStr(Applicazione); NumLicDb = getAVKInt(Applicazione); fatto = !string.IsNullOrEmpty($"{Installazione}{MasterKey}"); return fatto; } public async Task> LicAppCache() { List dbResult = new List(); string cacheKey = $"{rkeyAppInfo}:{MasterKey}"; string rawData = await getRSV(cacheKey); if (!string.IsNullOrEmpty(rawData)) { var cacheRes = JsonConvert.DeserializeObject?>(rawData); if (cacheRes != null) { dbResult = cacheRes; } } return await Task.FromResult(dbResult); } /// /// Init della classe con variabili di base da Redis/DB /// public async Task RefreshLicense() { bool fatto = false; // scadenza info a 15 gg... int numDays = 15; // dati applicativo var appData = await OnlineAppInfo(); if (appData != null) { if (appData.Count > 0) { fatto = await setAppInfo(appData, numDays); // salvo info licenza... NumLicRemote = appData[0].NumLicenze; } } // dati attivazioni var onlineAct = await OnlineActivationList(); if (onlineAct != null) { if (onlineAct.Count > 0) { infoExpiry = DateTime.Now.AddDays(numDays); ActivList = onlineAct; fatto = await setActivList(onlineAct, numDays); } } await Task.Delay(1); return fatto; } public async Task setActivList(List newActList, int numDays) { bool fatto = false; string cacheKey = $"{rKeyAttByLic}:{MasterKey}"; var rawData = JsonConvert.SerializeObject(newActList); TimeSpan cacheTs = TimeSpan.FromDays(numDays); await setRSV(cacheKey, rawData, cacheTs); fatto = true; if (EA_InfoUpdated != null) { EA_InfoUpdated?.Invoke(); } return fatto; } public async Task setAppInfo(List newAppInfo, int numDays) { bool fatto = false; string cacheKey = $"{rkeyAppInfo}:{MasterKey}"; var rawData = JsonConvert.SerializeObject(newAppInfo); TimeSpan cacheTs = TimeSpan.FromDays(numDays); await setRSV(cacheKey, rawData, cacheTs); fatto = true; if (EA_InfoUpdated != null) { EA_InfoUpdated?.Invoke(); } return fatto; } #endregion Public Methods #region Protected Fields /// /// Chiave redis x attivazioni della licenza /// protected const string rKeyAttByLic = "LongCache:AttByLic"; #endregion Protected Fields #region Protected Methods /// /// Cerca di recuperare valore INT da elenco AKV /// /// Chiave AKV richiesta /// protected int getAVKInt(string varReq) { int answ = -9999; if (AKVList != null && AKVList.Count > 0) { var currRec = AKVList.Where(x => x.NomeVar == varReq).FirstOrDefault(); if (currRec != null) { answ = currRec.ValInt ?? 0; } } return answ; } /// /// Cerca di recuperare valore string da elenco AKV /// /// Chiave AKV richiesta /// protected string getAVKStr(string varReq) { string answ = ""; if (AKVList != null && AKVList.Count > 0) { var currRec = AKVList.Where(x => x.NomeVar == varReq).FirstOrDefault(); if (currRec != null) { answ = $"{currRec.ValString}"; } } return answ; } /// /// Recupero chiave da redis /// /// /// protected async Task getRSV(string rKey) { var rawData = await redisDb.StringGetAsync(rKey); string answ = rawData.HasValue ? $"{rawData}" : ""; return answ; } /// /// Salvataggio chiave in redis /// /// /// /// /// protected async Task setRSV(string rKey, string rVal, TimeSpan cacheTS) { bool fatto = await redisDb.StringSetAsync(rKey, rVal, cacheTS); return fatto; } /// /// Salvataggio chiave in redis /// /// /// /// /// protected async Task setRSV(string rKey, int rValInt, TimeSpan cacheTS) { bool fatto = await setRSV(rkeyAppInfo, $"{rValInt}", cacheTS); return fatto; } #endregion Protected Methods #region Private Fields private static IConfiguration? _configuration; /// /// URL dell'API x chiamate gestione licenze /// private static string apiUrl = "https://liman.egalware.com/ELM.API/"; private static JsonSerializerSettings? JSSettings; /// /// Chiave redis x info della licenza /// private static string rkeyAppInfo = "LongCache:AppInfo"; /// /// 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(); #endregion Private Fields #region Private Properties private static ILogger _logger { get; set; } = null!; #endregion Private Properties #region Private Methods /// /// Elenco attivazioni attuali /// private async Task?> OnlineActivationList() { List? answ = new List(); // cerco online RestClient client = new RestClient(apiUrl); //client.Authenticator = new HttpBasicAuthenticator("username", "password"); string MKeyEnc = HttpUtility.UrlEncode(MasterKey); var request = new RestRequest($"api/attivazioni/?chiave={MKeyEnc}", Method.Get); var response = await client.GetAsync(request); // controllo risposta if (response.StatusCode == System.Net.HttpStatusCode.OK) { // salvo in redis contenuto serializzato string rawData = $"{response.Content}"; answ = JsonConvert.DeserializeObject?>(rawData); } return await Task.FromResult(answ); } /// /// Recupera info licenza da remoto /// private async Task> OnlineAppInfo() { List? answ = new List(); // cerco online RestClient client = new RestClient(apiUrl); string MKeyEnc = HttpUtility.UrlEncode(MasterKey); //string mKey = System.Net.WebUtility.UrlEncode(MasterKey); string reqUrl = $"api/licenza/{Installazione}?CodApp={Applicazione}&Chiave={MKeyEnc}"; var request = new RestRequest(reqUrl, Method.Get); var response = await client.GetAsync(request); // controllo risposta if (response.StatusCode == System.Net.HttpStatusCode.OK) { // verifico risposta string rawData = $"{response.Content}"; answ = JsonConvert.DeserializeObject?>(rawData); } // restituisce valori o insieme vuoto return await Task.FromResult(answ ?? new List()); } private void ReportUpdated() { if (EA_InfoUpdated != null) { EA_InfoUpdated?.Invoke(); } } #endregion Private Methods } }