diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 542ab049..755f4210 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -296,8 +296,6 @@ STAT:release: paths: - publish/ script: - - dotnet publish -c Release -o ./publish MP.Land/MP.Land.csproj - - dotnet publish -c Release -o ./publish MP.Prog/MP.Prog.csproj - dotnet publish -c Release -o ./publish MP.Stats/MP.Stats.csproj diff --git a/Egw.Core/Egw.Core.csproj b/Egw.Core/Egw.Core.csproj new file mode 100644 index 00000000..132c02c5 --- /dev/null +++ b/Egw.Core/Egw.Core.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/Egw.Core/LiManObj.cs b/Egw.Core/LiManObj.cs new file mode 100644 index 00000000..c6e409f8 --- /dev/null +++ b/Egw.Core/LiManObj.cs @@ -0,0 +1,220 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Egw.Core +{ + public class LiManObj + { + #region Public Enums + + public enum StatoRichiesta + { + ND = 0, + + Richiesta, + + Valutazione, + + Approvata, + + Rifiutata + } + + public enum TipoLicenza + { + ND = 0, + + /// + /// Licenza LEgacy Steamware + /// + GLS, + + /// + /// Master Key License, che ha una data di scadenza globale ed un token = numero di utenti/token massimi associati + /// + MasterKey, + + /// + /// UserKey License (licenza che consuma un token utente della licenza master) - es GPW + /// + UserKey, + + /// + /// Chiave tiupo Checksum basata su licenza masster + checksum MD5 di una serie di dati (child licenses) + /// + CheckSumKey + } + + #endregion Public Enums + + #region Public Classes + + public class ApplicativoDTO + { + #region Public Properties + + public string Chiave { get; set; } = ""; + public string CodApp { get; set; } = ""; + public string CodInst { get; set; } = ""; + public DateTime DataEnigma { get; set; } = DateTime.Today.AddYears(-1); + public string Descrizione { get; set; } = ""; + public string Enigma { get; set; } = ""; + public int IdxLic { get; set; } = 0; + + public bool IsActive + { + get => (Scadenza.Subtract(DateTime.Today).TotalDays > 0); + } + + public bool Locked { get; set; } = false; + public int NumLicenze { get; set; } = 0; + public int NumLicenzeAttive { get; set; } = 0; + public string Payload { get; set; } = ""; + public DateTime Scadenza { get; set; } = DateTime.Today.AddYears(-1); + public TipoLicenza Tipo { get; set; } = TipoLicenza.ND; + + #endregion Public Properties + } + + public class AttivazioneDTO + { + #region Public Properties + + public string Chiave { get; set; } = ""; + public string CodApp { get; set; } = ""; + public string CodImpiego { get; set; } = ""; + public string CodInst { get; set; } = ""; + public string Descrizione { get; set; } = ""; + public int IdxLic { get; set; } = 0; + public int IdxSubLic { get; set; } = 0; + public TipoLicenza Tipo { get; set; } = TipoLicenza.UserKey; + public DateTime VetoUnlock { get; set; } = DateTime.Today.AddMonths(2); + + #endregion Public Properties + } + + public class LicenseCoord + { + #region Public Properties + + public string CodApp { get; set; } = ""; + public string CodInst { get; set; } = ""; + public string Enigma { get; set; } = ""; + public string MasterKey { get; set; } = ""; + + #endregion Public Properties + } + + public class SupportRequest + { + #region Public Properties + + public string CodApp { get; set; } = ""; + public string CodImp { get; set; } = ""; + public string CodInst { get; set; } = ""; + public string ContactEmail { get; set; } = ""; + public string ContactName { get; set; } = ""; + public string ContactPhone { get; set; } = ""; + public int idxSubLic { get; set; } = 0; + + public bool IsValid + { + get => !string.IsNullOrEmpty(MasterKey) && !string.IsNullOrEmpty(ContactName) && !string.IsNullOrEmpty(ContactEmail) && !string.IsNullOrEmpty(CodInst) && !string.IsNullOrEmpty(CodApp); + } + + public string MasterKey { get; set; } = ""; + public string ReqBody { get; set; } = ""; + + #endregion Public Properties + } + + /// + /// Oggetto Ticket + /// + public class TicketDTO + { + #region Public Properties + + /// + /// Codice univoco della sub licenza (opzionale) + /// + public string CodImpiego { get; set; } = ""; + + /// + /// Contatto email del cliente richiedente + /// + public string ContactEmail { get; set; } = ""; + + /// + /// Contatto del cliente richiedente + /// + public string ContactName { get; set; } = ""; + + /// + /// Contatto telefonico del cliente richiedente + /// + public string ContactPhone { get; set; } = ""; + + public DateTime DtReq { get; set; } = DateTime.Now; + + /// + /// IDX licenza master + /// + public int IdxLic { get; set; } = 0; + + /// + /// IDX licenza child (opzionale) + /// + public int IdxSubLic { get; set; } = 0; + + public int IdxTicket { get; set; } = 0; + + /// + /// Motivazione della richiesta + /// + public string ReqBody { get; set; } = ""; + + /// + /// Stato richiesta + /// + public StatoRichiesta Status { get; set; } = StatoRichiesta.ND; + + /// + /// Risposta alla richiesta + /// + public string SupplAnsw { get; set; } = ""; + + /// + /// Email del responsabile dell'azione (interno - supplier) + /// + public string SupplEmail { get; set; } = ""; + + /// + /// Cod dell'user responsabile dell'azione (interno - supplier) + /// + public string SupplUserCode { get; set; } = ""; + + /// + /// Tipologia di licenza gestita + /// + public TipoLicenza Tipo { get; set; } = TipoLicenza.UserKey; + + #endregion Public Properties + } + + public class UserLicenseRequest + { + #region Public Properties + + public string MasterKey { get; set; } = ""; + public Dictionary ParamDict { get; set; } = new Dictionary(); + + #endregion Public Properties + } + + #endregion Public Classes + } +} diff --git a/Egw.Core/SteamCrypto.cs b/Egw.Core/SteamCrypto.cs new file mode 100644 index 00000000..1f021d25 --- /dev/null +++ b/Egw.Core/SteamCrypto.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace Egw.Core +{ + /// + /// utils x cifrature e Crypto + /// + public class SteamCrypto + { + #region Public Methods + + /// + /// decifra un messaggio con una password + /// + /// + /// + /// + public static string DecryptString(string Message, string Passphrase) + { + string answ = Message; + byte[] Results = null; + UTF8Encoding UTF8 = new UTF8Encoding(); + + // Step 1. We hash the passphrase using MD5 + // We use the MD5 hash generator as the result is a 128 bit byte array + // which is a valid length for the TripleDES encoder we use below + + MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider(); + byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase)); + + // Step 2. Create a new TripleDESCryptoServiceProvider object + TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider(); + + // Step 3. Setup the decoder + TDESAlgorithm.Key = TDESKey; + TDESAlgorithm.Mode = CipherMode.ECB; + TDESAlgorithm.Padding = PaddingMode.PKCS7; + + // Step 4. Convert the input string to a byte[] + byte[] DataToDecrypt = null; + try + { + DataToDecrypt = Convert.FromBase64String(Message); + } + catch + { } + if (DataToDecrypt != null) + { + // Step 5. Attempt to decrypt the string + try + { + ICryptoTransform Decryptor = TDESAlgorithm.CreateDecryptor(); + Results = Decryptor.TransformFinalBlock(DataToDecrypt, 0, DataToDecrypt.Length); + } + finally + { + // Clear the TripleDes and Hashprovider services of any sensitive information + TDESAlgorithm.Clear(); + HashProvider.Clear(); + } + // Step 6. Return the decrypted string in UTF8 format + answ = UTF8.GetString(Results); + } + return answ; + } + + /// + /// cifra un messaggio con una password + /// + /// + /// + /// + public static string EncryptString(string Message, string Passphrase) + { + byte[] Results; + UTF8Encoding UTF8 = new UTF8Encoding(); + + // Step 1. We hash the passphrase using MD5 + // We use the MD5 hash generator as the result is a 128 bit byte array + // which is a valid length for the TripleDES encoder we use below + + MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider(); + byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase)); + + // Step 2. Create a new TripleDESCryptoServiceProvider object + TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider(); + + // Step 3. Setup the encoder + TDESAlgorithm.Key = TDESKey; + TDESAlgorithm.Mode = CipherMode.ECB; + TDESAlgorithm.Padding = PaddingMode.PKCS7; + + // Step 4. Convert the input string to a byte[] + byte[] DataToEncrypt = UTF8.GetBytes(Message); + + // Step 5. Attempt to encrypt the string + try + { + ICryptoTransform Encryptor = TDESAlgorithm.CreateEncryptor(); + Results = Encryptor.TransformFinalBlock(DataToEncrypt, 0, DataToEncrypt.Length); + } + finally + { + // Clear the TripleDes and Hashprovider services of any sensitive information + TDESAlgorithm.Clear(); + HashProvider.Clear(); + } + + // Step 6. Return the encrypted string as a base64 encoded string + return Convert.ToBase64String(Results); + } + + /// + /// genera hash di una stringa in MD5 (es x hash gravatar) + /// + /// + /// + public static string getHashStringMD5(string Message) + { + string hash = ""; + using (MD5 md5Hash = MD5.Create()) + { + hash = GetMd5Hash(md5Hash, Message); + } + return hash; + } + + /// + /// Crea un hash MD5 + /// + /// + /// + /// + public static string GetMd5Hash(MD5 md5Hash, string input) + { + // Convert the input string to a byte array and compute the hash. + byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input)); + + // Create a new Stringbuilder to collect the bytes + // and create a string. + StringBuilder sBuilder = new StringBuilder(); + + // Loop through each byte of the hashed data + // and format each one as a hexadecimal string. + for (int i = 0; i < data.Length; i++) + { + sBuilder.Append(data[i].ToString("x2")); + } + + // Return the hexadecimal string. + return sBuilder.ToString(); + } + + /// + /// Verify a hash against a string. + /// + /// + /// + /// + /// + public static bool VerifyMd5Hash(MD5 md5Hash, string input, string hash) + { + // Hash the input. + string hashOfInput = GetMd5Hash(md5Hash, input); + + // Create a StringComparer an compare the hashes. + StringComparer comparer = StringComparer.OrdinalIgnoreCase; + + if (0 == comparer.Compare(hashOfInput, hash)) + { + return true; + } + else + { + return false; + } + } + + #endregion Public Methods + } +} \ No newline at end of file diff --git a/Egw.Core/licenseManGLS.cs b/Egw.Core/licenseManGLS.cs new file mode 100644 index 00000000..616ba977 --- /dev/null +++ b/Egw.Core/licenseManGLS.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Egw.Core +{ + /// + /// Gestione licenze applicativi GLS (Legacy SteamWare) + /// + public class licenseManGLS + { + #region Public Methods + + /// + /// restituisce data decodificata da authKey + applicazione + cliente... + /// + /// The cliente. + /// The applicativo. + /// The licenze. + /// The authentication key. + /// + public static DateTime expiryDateByAuthKey(string cliente, string applicativo, int licenze, string authKey) + { + DateTime answ = DateTime.Today.AddYears(-10); + + string plainAuthKey = ""; + try + { + string passPhrase = string.Format("{0}|{1}", cliente.PadLeft(50, ':'), applicativo); + plainAuthKey = SteamCrypto.DecryptString(authKey, passPhrase); // uso combinazione cliente+applicativo come passphrase! + answ = Convert.ToDateTime(plainAuthKey.Replace(string.Format("{0}#{1}-", cliente, applicativo.PadLeft(20, '-')), "").Replace(string.Format("%{0}%", licenze), "")); + } + catch (Exception exc) + { + //logger.lg.scriviLog(string.Format("Errore decodifica auth key:{0}AuthKey: {1}{0}cliente:{2}{0}applicativo:{3}{0}errore:{4}", Environment.NewLine, authKey, cliente, applicativo, exc), tipoLog.EXCEPTION); + } + return answ; + } + + /// + /// Fornisce chiave MD5 x un cliente/applicativo/expiryDate + /// + /// + /// + /// + /// + /// + public static string getAuthKey(string cliente, string applicativo, int licenze, DateTime expiryDate) + { + string answ = ""; + // algoritmo MD5 formato cliente#applicativo#expDate, via SQLdiventa + // SELECT CONVERT(VARCHAR(32), HashBytes('MD5', 'ETS#GPW#2013/12/31'), 2) + string plainAuthKey = string.Format("{0}#{1}-{2}%{3}%", cliente, applicativo.PadLeft(20, '-'), expiryDate.ToString("yyyy/MM/dd"), licenze); + string passPhrase = string.Format("{0}|{1}", cliente.PadLeft(50, ':'), applicativo); + answ = SteamCrypto.EncryptString(plainAuthKey, passPhrase); // uso combinazione cliente+applicativo come passphrase! + return answ; + } + + /// + /// Fornisce chiave MD5 x una chiave secondaria/di checksum data dai parametri in ingresso + /// MasterKey/string[] chiavi singole child/expiryDate + /// + /// Chiave master da cui si parte + /// Payload che contiene le chiavi SUB (child) riferite alla master in formato JSon (compresso/no indent) + /// + public static string getChecksumKey(string MasterKey, string Payload) + { + string answ = ""; + answ = SteamCrypto.EncryptString(Payload, MasterKey); + return answ; + } + + /// + /// numero di licenze attive per cliente/applicativo + /// + /// + /// + /// + public static int getLicenseNum(string cliente, string applicativo) + { + // !!!FARE!!! chiamata a webservice 1/mese + int answ = 1; + // molto hard-coded e discutibile... licenze "perenni" + switch (cliente) + { + default: + answ = 1; + break; + } + + return answ; + } + + #endregion Public Methods + } +} diff --git a/MP-LAND.sln b/MP-LAND.sln index f4e0ede6..85cb3e47 100644 --- a/MP-LAND.sln +++ b/MP-LAND.sln @@ -1,12 +1,14 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31229.75 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32112.339 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MP.Land", "MP.Land\MP.Land.csproj", "{D949AB45-9B65-4594-A97E-182BC3831707}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MP.AppAuth", "MP.AppAuth\MP.AppAuth.csproj", "{E8B1E617-87BC-4638-A8B6-04EEBA3B8F47}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Egw.Core", "Egw.Core\Egw.Core.csproj", "{D3D348EF-1313-43DF-94FB-28CD38B68212}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {E8B1E617-87BC-4638-A8B6-04EEBA3B8F47}.Debug|Any CPU.Build.0 = Debug|Any CPU {E8B1E617-87BC-4638-A8B6-04EEBA3B8F47}.Release|Any CPU.ActiveCfg = Release|Any CPU {E8B1E617-87BC-4638-A8B6-04EEBA3B8F47}.Release|Any CPU.Build.0 = Release|Any CPU + {D3D348EF-1313-43DF-94FB-28CD38B68212}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D3D348EF-1313-43DF-94FB-28CD38B68212}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D3D348EF-1313-43DF-94FB-28CD38B68212}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D3D348EF-1313-43DF-94FB-28CD38B68212}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/MP.AppAuth/Controllers/MPController .cs b/MP.AppAuth/Controllers/MPController .cs index 81c577ca..794b7d64 100644 --- a/MP.AppAuth/Controllers/MPController .cs +++ b/MP.AppAuth/Controllers/MPController .cs @@ -42,9 +42,9 @@ namespace MP.AppAuth.Controllers /// Elenco Record x AnagKeyValue /// /// - public List AnagKeyValuesGetAll() + public List AnagKeyValuesGetAll() { - List dbResult = new List(); + List dbResult = new List(); using (MoonProContext localDbCtx = new MoonProContext(_configuration)) { dbResult = localDbCtx diff --git a/MP.AppAuth/MP.AppAuth.csproj b/MP.AppAuth/MP.AppAuth.csproj index 757a2c47..13d4315a 100644 --- a/MP.AppAuth/MP.AppAuth.csproj +++ b/MP.AppAuth/MP.AppAuth.csproj @@ -1,7 +1,7 @@ - net5.0 + net6.0 @@ -23,4 +23,8 @@ + + + + diff --git a/MP.AppAuth/Models/AnagKeyValue.cs b/MP.AppAuth/Models/AnagKeyValueModel.cs similarity index 93% rename from MP.AppAuth/Models/AnagKeyValue.cs rename to MP.AppAuth/Models/AnagKeyValueModel.cs index d2be5358..6efcc279 100644 --- a/MP.AppAuth/Models/AnagKeyValue.cs +++ b/MP.AppAuth/Models/AnagKeyValueModel.cs @@ -11,7 +11,7 @@ namespace MP.AppAuth.Models // This is here so CodeMaid doesn't reorganize this document // [Table("AnagKeyValue")] - public partial class AnagKeyValue + public partial class AnagKeyValueModel { #region Public Properties diff --git a/MP.AppAuth/MoonProContext.cs b/MP.AppAuth/MoonProContext.cs index 7c4d02d8..09f1d31c 100644 --- a/MP.AppAuth/MoonProContext.cs +++ b/MP.AppAuth/MoonProContext.cs @@ -63,7 +63,7 @@ namespace MP.AppAuth public virtual DbSet AnagraficaOperatoris { get; set; } public virtual DbSet Configs { get; set; } public virtual DbSet DatiMacchines { get; set; } - public virtual DbSet DbSetAnagKeyValues { get; set; } + public virtual DbSet DbSetAnagKeyValues { get; set; } public virtual DbSet FamigliaTipoIngressis { get; set; } public virtual DbSet FamiglieMacchines { get; set; } public virtual DbSet KeepAlives { get; set; } @@ -151,7 +151,7 @@ namespace MP.AppAuth entity.Property(e => e.Descrizione).HasMaxLength(500); }); - modelBuilder.Entity(entity => + modelBuilder.Entity(entity => { entity.HasKey(e => e.NomeVar); diff --git a/MP.Land/Components/HomeLink.razor b/MP.Land/Components/HomeLink.razor index b1e34f83..272ed60c 100644 --- a/MP.Land/Components/HomeLink.razor +++ b/MP.Land/Components/HomeLink.razor @@ -3,8 +3,8 @@ @using Microsoft.Extensions.Configuration @inject IConfiguration Configuration - @inject AppAuthService DataService +@inject LicenseService LicServ
@@ -65,10 +65,62 @@ [Parameter] public UpdMan CurrItem { get; set; } + protected List AKVList + { + get + { + return LicServ.AKVList; + } + set + { + LicServ.AKVList = value; + } + } + + + protected override async Task OnInitializedAsync() + { + // check init AKV + if (AKVList == null || AKVList.Count == 0) + { + AKVList = await DataService.AnagKeyValList(); + LicServ.InitAkv(); + } + } + + + protected string getAKVString(string nomeVar) + { + string answ = ""; + if (AKVList != null) + { + var currRec = AKVList.FirstOrDefault(x => x.NomeVar == nomeVar); + if (currRec != null) + { + answ = currRec.ValString; + } + } + return answ; + } + + protected AnagKeyValueModel getAKVRec(string nomeVar) + { + AnagKeyValueModel answ = new AnagKeyValueModel(); + if (AKVList != null) + { + answ = AKVList.FirstOrDefault(x => x.NomeVar == nomeVar); + } + return answ; + } + protected bool authOk() { - bool answ = !string.IsNullOrEmpty(CurrItem.LicenseKey) && CurrItem.IsAuth; - return answ; + bool allOk = !string.IsNullOrEmpty(CurrItem.LicenseKey); + if (allOk) + { + allOk = LicServ.checkLicenseActive(CurrItem.LicenseKey); + } + return allOk; } protected string fullUrl(string relUrl) diff --git a/MP.Land/Components/SingleDownload.razor b/MP.Land/Components/SingleDownload.razor index 4530ffe6..46f26b33 100644 --- a/MP.Land/Components/SingleDownload.razor +++ b/MP.Land/Components/SingleDownload.razor @@ -1,4 +1,7 @@ -
+@using MP.Land.Data +@inject LicenseService LicServ + +
@if (authOk()) {
diff --git a/MP.Land/Components/SingleDownload.razor.cs b/MP.Land/Components/SingleDownload.razor.cs index 809037ad..be21c56a 100644 --- a/MP.Land/Components/SingleDownload.razor.cs +++ b/MP.Land/Components/SingleDownload.razor.cs @@ -42,7 +42,11 @@ namespace MP.Land.Components protected bool authOk() { - bool answ = !string.IsNullOrEmpty(CurrItem.LicenseKey) && CurrItem.IsAuth; + bool answ = !string.IsNullOrEmpty(CurrItem.LicenseKey); + if (answ) + { + answ = LicServ.checkLicenseActive(CurrItem.LicenseKey); + } return answ; } diff --git a/MP.Land/Data/AppAuthService.cs b/MP.Land/Data/AppAuthService.cs index 6f586b39..55ac68a1 100644 --- a/MP.Land/Data/AppAuthService.cs +++ b/MP.Land/Data/AppAuthService.cs @@ -173,9 +173,9 @@ namespace MP.Land.Data return await Task.FromResult(dbResult); } - public async Task> AnagKeyValList() + public async Task> AnagKeyValList() { - List dbResult = new List(); + List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = MpDbController.AnagKeyValuesGetAll(); @@ -185,6 +185,12 @@ namespace MP.Land.Data return await Task.FromResult(dbResult); } + public async Task ResetCache() + { + string cacheKey = ":MP:VOCAB"; + await distributedCache.RemoveAsync(cacheKey); + } + #endregion Public Methods } } \ No newline at end of file diff --git a/MP.Land/Data/LicenseService.cs b/MP.Land/Data/LicenseService.cs new file mode 100644 index 00000000..9a103d9f --- /dev/null +++ b/MP.Land/Data/LicenseService.cs @@ -0,0 +1,367 @@ +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 System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web; + +namespace MP.Land.Data +{ + /// + /// Servizi e dati condivisi a livello applicazione + /// + public class LicenseService + { + #region Private Fields + + private static IConfiguration? _configuration; + private static ILogger? _logger; + + /// + /// URL dell'API x chiamate gestione licenze + /// + private static string apiUrl = "https://liman.egalware.com/ELM.API/"; + + private readonly IDistributedCache distributedCache; + + /// + /// Elenco obj in cache + /// + private List cachedDataList = new List(); + + /// + /// Fattorte conversione cache sliding --> 1 h + /// + private int cacheFact = 12; + + /// + /// Durata assoluta massima della cache IN SECONDI + /// + private int chAbsExp = 60 * 5; + + /// + /// Durata della cache IN SECONDI in modalità inattiva (non acceduta) prima di venire rimossa + /// NON estende oltre il tempo massimo di validità della cache (chAbsExp) + /// + private int chSliExp = 60 * 1; + + #endregion Private Fields + + #region Protected Fields + + /// + /// Chiave redis x attivazioni della licenza + /// + protected const string rKeyAttByLic = "LongCache:AttByLic"; + + #endregion Protected Fields + + #region Public Constructors + + /// + /// Init classe + /// + /// + /// + /// + public LicenseService(IConfiguration configuration, ILogger logger, IDistributedCache distributedCache) + { + _logger = logger; + _configuration = configuration; + this.distributedCache = distributedCache; + } + + #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); + + public string Installazione { get; set; } = ""; + + public string MasterKey { get; set; } = ""; + + 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 Private Methods + + /// + /// Opzioni cache con moltiplicatore durata risp durata base (1/5 minuti) + /// + /// + /// + private DistributedCacheEntryOptions cacheOpt(int multFact) + { + var numSecAbsExp = chAbsExp * multFact; + var numSecSliExp = chSliExp * multFact; + return new DistributedCacheEntryOptions().SetAbsoluteExpiration(DateTime.Now.AddSeconds(numSecAbsExp)).SetSlidingExpiration(TimeSpan.FromSeconds(numSecSliExp)); + } + + /// + /// 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); + } + + private void ReportUpdated() + { + if (EA_InfoUpdated != null) + { + EA_InfoUpdated?.Invoke(); + } + } + + #endregion Private Methods + + #region Protected Methods + + /// + /// 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) + { + string answ = ""; + var redisDataList = await distributedCache.GetAsync(rKey); + if (redisDataList != null) + { + answ = Encoding.UTF8.GetString(redisDataList); + } + return answ; + } + + /// + /// Salvataggio chiave in redis + /// + /// + /// + /// + /// + protected async Task setRSV(string rKey, string rVal, int cacheMult) + { + bool fatto = false; + var redisDataList = Encoding.UTF8.GetBytes(rVal); + await distributedCache.SetAsync(rKey, redisDataList, cacheOpt(cacheMult)); + fatto = true; + return fatto; + } + + /// + /// Salvataggio chiave in redis + /// + /// + /// + /// + /// + protected async Task setRSV(string rKey, int rValInt, int cacheMult) + { + bool fatto = false; + var redisDataList = Encoding.UTF8.GetBytes($"{rValInt}"); + await distributedCache.SetAsync(rKey, redisDataList, cacheOpt(cacheMult)); + fatto = true; + return fatto; + } + + /// + /// Registra in cache chiave se non fosse già in elenco + /// + /// + protected void trackCache(string newKey) + { + if (!cachedDataList.Contains(newKey)) + { + cachedDataList.Add(newKey); + } + } + + #endregion Protected Methods + + #region Public Methods + + public async Task> ActivListCache() + { + List dbResult = new List(); + string cacheKey = $"{rKeyAttByLic}:{MasterKey}"; + trackCache(cacheKey); + 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 bool InitAkv() + { + bool fatto = false; + Installazione = getAVKStr("Installazione"); + MasterKey = getAVKStr(Applicazione); + fatto = !string.IsNullOrEmpty($"{Installazione}{MasterKey}"); + return fatto; + } + + /// + /// Init della classe con variabili di base da Redis/DB + /// + public async Task RefreshLicense() + { + bool fatto = false; + var onlineAct = await OnlineActivationList(); + if (onlineAct != null) + { + if (onlineAct.Count > 0) + { + // scadenza info a 15 gg... + int numDays = 15; + 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); + await setRSV(cacheKey, rawData, numDays * cacheFact * 24); + fatto = true; + if (EA_InfoUpdated != null) + { + EA_InfoUpdated?.Invoke(); + } + return fatto; + } + + /// + /// 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) + { + numLic = (int)recLic.ValInt; + // verifico scadenza licenza! + DateTime scadenza = licenseManGLS.expiryDateByAuthKey(Installazione, recLic.NomeVar, numLic, authKey); + answ = scadenza > DateTime.Today; + } + else + { + _logger.LogInformation($"Record non trovato per {authKey}"); + } + } + return answ; + } + + #endregion Public Methods + } +} diff --git a/MP.Land/MP.Land.csproj b/MP.Land/MP.Land.csproj index 9672a391..454dd721 100644 --- a/MP.Land/MP.Land.csproj +++ b/MP.Land/MP.Land.csproj @@ -1,9 +1,9 @@ - net5.0 + net6.0 MP.Land - 6.14.2112.2315 + 6.14.2202.0112 @@ -49,6 +49,7 @@ + diff --git a/MP.Land/Pages/RefreshData.razor b/MP.Land/Pages/RefreshData.razor new file mode 100644 index 00000000..d38688b9 --- /dev/null +++ b/MP.Land/Pages/RefreshData.razor @@ -0,0 +1,29 @@ +@page "/RefreshData" + +@using MP.Land.Components +@using MP.Land.Data + +@inject AppAuthService DataService +@inject LicenseService LicServ +@inject NavigationManager NavManager + +
+
+ +
+
+ + +@code { + protected override async Task OnInitializedAsync() + { + await Task.Delay(100); + LicServ.AKVList = new List(); + await Task.Delay(100); + await DataService.ResetCache(); + await Task.Delay(100); + + // redireziono + NavManager.NavigateTo(""); + } +} diff --git a/MP.Land/Pages/UpdateManager.razor b/MP.Land/Pages/UpdateManager.razor index e16386b0..df011226 100644 --- a/MP.Land/Pages/UpdateManager.razor +++ b/MP.Land/Pages/UpdateManager.razor @@ -2,6 +2,7 @@ @using MP.Land.Data @using MP.Land.Components +@inject LicenseService LicServ
diff --git a/MP.Land/Pages/UpdateManager.razor.cs b/MP.Land/Pages/UpdateManager.razor.cs index 8976d086..38c6feaa 100644 --- a/MP.Land/Pages/UpdateManager.razor.cs +++ b/MP.Land/Pages/UpdateManager.razor.cs @@ -88,8 +88,18 @@ namespace MP.Land.Pages Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); // ciclo su tutti quelli con licenza valida... - var authList = ListRecords - .Where(x => !string.IsNullOrEmpty(x.LicenseKey) && x.IsAuth).ToList(); + List rawList = ListRecords + .Where(x => !string.IsNullOrEmpty(x.LicenseKey)).ToList(); + List authList = new List(); + + // ciclo SOLO tra quelli davvero autorizzati... + foreach (var item in rawList) + { + if (LicServ.checkLicenseActive(item.LicenseKey)) + { + authList.Add(item); + } + } numTot = authList.Count; foreach (var item in authList) { diff --git a/MP.Land/Resources/ChangeLog.html b/MP.Land/Resources/ChangeLog.html index 3fcbcdb1..5899bbe8 100644 --- a/MP.Land/Resources/ChangeLog.html +++ b/MP.Land/Resources/ChangeLog.html @@ -1,6 +1,6 @@ Modulo gestione Programmi MAPO -

Versione: 6.14.2112.2315

+

Versione: 6.14.2202.0112


Note di rilascio:
    diff --git a/MP.Land/Resources/VersNum.txt b/MP.Land/Resources/VersNum.txt index aae3317e..053e3aa2 100644 --- a/MP.Land/Resources/VersNum.txt +++ b/MP.Land/Resources/VersNum.txt @@ -1 +1 @@ -6.14.2112.2315 +6.14.2202.0112 diff --git a/MP.Land/Resources/manifest.xml b/MP.Land/Resources/manifest.xml index 26b3c9a1..e0e7c169 100644 --- a/MP.Land/Resources/manifest.xml +++ b/MP.Land/Resources/manifest.xml @@ -1,6 +1,6 @@ - 6.14.2112.2315 + 6.14.2202.0112 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 e12aecc8..83676332 100644 --- a/MP.Land/Shared/NavMenu.razor +++ b/MP.Land/Shared/NavMenu.razor @@ -42,6 +42,11 @@ System Info +
diff --git a/MP.Land/Startup.cs b/MP.Land/Startup.cs index 65b36ec6..c599a8b6 100644 --- a/MP.Land/Startup.cs +++ b/MP.Land/Startup.cs @@ -116,6 +116,7 @@ namespace MP.Land services.AddServerSideBlazor(); services.AddSingleton(Configuration); + services.AddSingleton(); services.AddScoped(); services.AddScoped(); diff --git a/MP.Prog/MP.Prog.csproj b/MP.Prog/MP.Prog.csproj index fc403dad..64fe4797 100644 --- a/MP.Prog/MP.Prog.csproj +++ b/MP.Prog/MP.Prog.csproj @@ -3,7 +3,7 @@ net5.0 MP.Prog - 6.14.2110.1510 + 6.14.2112.2315 diff --git a/MP.Prog/Resources/ChangeLog.html b/MP.Prog/Resources/ChangeLog.html index 4a3675e6..3fcbcdb1 100644 --- a/MP.Prog/Resources/ChangeLog.html +++ b/MP.Prog/Resources/ChangeLog.html @@ -1,6 +1,6 @@ Modulo gestione Programmi MAPO -

Versione: 6.14.2110.1510

+

Versione: 6.14.2112.2315


Note di rilascio:
    diff --git a/MP.Prog/Resources/VersNum.txt b/MP.Prog/Resources/VersNum.txt index 79db2e6b..aae3317e 100644 --- a/MP.Prog/Resources/VersNum.txt +++ b/MP.Prog/Resources/VersNum.txt @@ -1 +1 @@ -6.14.2110.1510 +6.14.2112.2315 diff --git a/MP.Prog/Resources/manifest.xml b/MP.Prog/Resources/manifest.xml index f63dfc9d..4acad0c0 100644 --- a/MP.Prog/Resources/manifest.xml +++ b/MP.Prog/Resources/manifest.xml @@ -1,6 +1,6 @@ - 6.14.2110.1510 + 6.14.2112.2315 https://nexus.steamware.net/repository/SWS/MP-PROG/stable/LAST/MP.Prog.zip https://nexus.steamware.net/repository/SWS/MP-PROG/stable/LAST/ChangeLog.html false diff --git a/MP.Stats/MP.Stats.csproj b/MP.Stats/MP.Stats.csproj index 988f9641..6278df50 100644 --- a/MP.Stats/MP.Stats.csproj +++ b/MP.Stats/MP.Stats.csproj @@ -4,7 +4,7 @@ net5.0 MP.Stats 826e877c-ba70-4253-84cb-d0b1cafd4440 - 6.14.2109.3019 + 6.14.2201.1815 diff --git a/MP.Stats/Resources/ChangeLog.html b/MP.Stats/Resources/ChangeLog.html index 12e9f1a1..019b94c3 100644 --- a/MP.Stats/Resources/ChangeLog.html +++ b/MP.Stats/Resources/ChangeLog.html @@ -1,6 +1,6 @@ Modulo statistiche MAPO -

    Versione: 6.14.2109.3019

    +

    Versione: 6.14.2201.1815


    Note di rilascio:
      diff --git a/MP.Stats/Resources/VersNum.txt b/MP.Stats/Resources/VersNum.txt index 4e302d02..9d7e2e60 100644 --- a/MP.Stats/Resources/VersNum.txt +++ b/MP.Stats/Resources/VersNum.txt @@ -1 +1 @@ -6.14.2109.3019 +6.14.2201.1815 diff --git a/MP.Stats/Resources/manifest.xml b/MP.Stats/Resources/manifest.xml index d3b9d8fe..039efe7e 100644 --- a/MP.Stats/Resources/manifest.xml +++ b/MP.Stats/Resources/manifest.xml @@ -1,6 +1,6 @@ - 6.14.2109.3019 + 6.14.2201.1815 https://nexus.steamware.net/repository/SWS/MP-STATS/stable/LAST/MP.Stats.zip https://nexus.steamware.net/repository/SWS/MP-STATS/stable/LAST/ChangeLog.html false