diff --git a/devdocs/todo.txt b/devdocs/todo.txt index 83fd5880..d0ff4ec3 100644 --- a/devdocs/todo.txt +++ b/devdocs/todo.txt @@ -12,7 +12,12 @@ Updates: it should also have some license route fetch prevention i.e. license has updates end date so server won't send if expired? Or something along those lines?? Or maybe that would be bad if they downgrade and want to put back in the license they paid for (restore from backup?) - +Update scenarios to handle + boot with newer version than licensed and no db at all + in this case it's ok to do the schema update and install the fresh empty db then fetch the license + LICENSE FETCH SHOULD COMPARE DATES BEFORE INSTALLING A FETCHED LICENSE AND BALK IF MISMATCH + Boot with newer version and have existing db with outdated schema + TODO: it should check the license version *before* the schema update in order to not break anything todo: RELEASE FINAL RELEASE @@ -23,7 +28,7 @@ todo: RELEASE FINAL RELEASE test carefully once done reversion everything to 8.0.1 for official release Post in /next download folder for testing before posting fully Re-run the smoke tests and e2e tests before posting 8.0.1 officially - + todo: test on every platform every installer and be certain they work as advertised, yes this is a pain but it's necessary diff --git a/docs/8.0/ayanova/mkdocs.yml b/docs/8.0/ayanova/mkdocs.yml index c830af9b..5b90eb9e 100644 --- a/docs/8.0/ayanova/mkdocs.yml +++ b/docs/8.0/ayanova/mkdocs.yml @@ -7,7 +7,7 @@ theme: site_name: AyaNova manual site_dir: '../../../server/AyaNova/wwwroot/docs' strict: true -copyright: Copyright © 2022 Ground Zero Tech-Works Inc. REV-2022-08-04 +copyright: Copyright © 2022 Ground Zero Tech-Works Inc. REV-2022-08-05 extra: generator: false # Extensions diff --git a/server/AyaNova/AyaNova.csproj b/server/AyaNova/AyaNova.csproj index 1fbbc141..0643e1c4 100644 --- a/server/AyaNova/AyaNova.csproj +++ b/server/AyaNova/AyaNova.csproj @@ -9,6 +9,7 @@ ayanova.ico bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml 1591 + False diff --git a/server/AyaNova/Program.cs b/server/AyaNova/Program.cs index 8fb55e2d..84845048 100644 --- a/server/AyaNova/Program.cs +++ b/server/AyaNova/Program.cs @@ -208,6 +208,11 @@ namespace AyaNova logger.Info($"AYANOVA SERVER {AyaNovaVersion.VersionString} BOOTING"); +#if (DEBUG) + logger.Info($"### Linker timestamp is {Util.FileUtil.GetLinkerTimestampUtc(System.Reflection.Assembly.GetExecutingAssembly())}"); + +#endif + //log configuration logger.Info($"Config {DiagConfig}"); logger.Debug($"Full configuration is {config.GetDebugView()}"); @@ -294,7 +299,7 @@ namespace AyaNova catch (Exception e) { logger.Fatal(e, "E1090 - AyaNova server can't start due to unexpected exception during initialization"); - throw; + //throw; } finally { diff --git a/server/AyaNova/Startup.cs b/server/AyaNova/Startup.cs index 6f501fb8..0c451376 100644 --- a/server/AyaNova/Startup.cs +++ b/server/AyaNova/Startup.cs @@ -593,7 +593,35 @@ namespace AyaNova //log each item individually from runtime parameters - + //Initialize license unless it doesn't exist yet then wait and do it after schema update and fingerprint + //this is necessary to accomodate checking build date against subscription and preventing schema update if they upgrade but are not entitled to + //so they don't fuck their database + //if there is a build date issue or a license issue it will fail with an exception, log to log file, log to console and not go beyond the license check preserving the db + //Note: case 4160 is to build an external license fetcher utility to allow a user to upgrade without uninstalling the newer version by purchasing a new sub and installing the key out of AyaNova + //If they don't want to purchase then they must downgrade + var tempSchema = dbContext.SchemaVersion.AsNoTracking().SingleOrDefault(); + var tempLicense = dbContext.License.AsNoTracking().SingleOrDefault(); + bool licenseChecked = false; + if (tempSchema != null && tempLicense != null && !string.IsNullOrWhiteSpace(tempSchema.Id)) + { + //we have a schema and a license, check it now thus triggering build date vs maintenance expiry check + try + { + AyaNova.Core.License.InitializeAsync(apiServerState, dbContext, _newLog).Wait(); + licenseChecked = true; + } + catch (Exception ex) + { + //Only re-throw if it's a 1020 error with the text VERSION-TOO-NEW included, any other error allow it to keep doing it's thing below before the second license init + //our exception is buried inside multiple inner exceptions but it's the innermost so drill down into it + while (ex.InnerException != null) + ex = ex.InnerException; + if (ex.Message.Contains("1020") && ex.Message.Contains("VERSION-TOO-NEW ")) + { + throw new Exception("AyaNova did not start to protect the integrity of your data, see the console and / or error log for details"); + } + } + } @@ -602,7 +630,6 @@ namespace AyaNova AySchema.CheckAndUpdateAsync(dbContext, _newLog).Wait(); - //Check database integrity _newLog.LogDebug("DB integrity check"); DbUtil.CheckFingerPrintAsync(AySchema.EXPECTED_COLUMN_COUNT, @@ -613,8 +640,9 @@ namespace AyaNova AySchema.EXPECTED_ROUTINES, _newLog).Wait(); - //Initialize license - AyaNova.Core.License.InitializeAsync(apiServerState, dbContext, _newLog).Wait(); + //Initialize license if not already done (due to there being no db at all yet) + if (!licenseChecked) + AyaNova.Core.License.InitializeAsync(apiServerState, dbContext, _newLog).Wait(); //Set static global biz settings _newLog.LogDebug("Global settings"); diff --git a/server/AyaNova/util/FileUtil.cs b/server/AyaNova/util/FileUtil.cs index e03b681d..8ea5c0d3 100644 --- a/server/AyaNova/util/FileUtil.cs +++ b/server/AyaNova/util/FileUtil.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging; using Microsoft.EntityFrameworkCore; using AyaNova.Models; using AyaNova.Biz; +using System.Reflection; namespace AyaNova.Util { @@ -481,7 +482,7 @@ namespace AyaNova.Util AttachToAType = attachToObject.AType, LastModified = lastModified, Size = FileSize, - AttachedByUserId=attachedByUserId + AttachedByUserId = attachedByUserId }; @@ -828,7 +829,7 @@ namespace AyaNova.Util { return string.Empty; } - + //Linux ~ home folder special handling here if (path.Contains('~')) { @@ -841,6 +842,34 @@ namespace AyaNova.Util } #endregion general utilities + + #region licensing related utility to qualify upgradability + //https://www.meziantou.net/getting-the-date-of-build-of-a-dotnet-assembly-at-runtime.htm + public static DateTime GetLinkerTimestampUtc(Assembly assembly) + { + var location = assembly.Location; + return GetLinkerTimestampUtc(location); + } + + public static DateTime GetLinkerTimestampUtc(string filePath) + { + const int peHeaderOffset = 60; + const int linkerTimestampOffset = 8; + var bytes = new byte[2048]; + + using (var file = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + file.Read(bytes, 0, bytes.Length); + } + + var headerPos = BitConverter.ToInt32(bytes, peHeaderOffset); + var secondsSince1970 = BitConverter.ToInt32(bytes, headerPos + linkerTimestampOffset); + var dt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + return dt.AddSeconds(secondsSince1970); + } + #endregion + + }//eoc }//eons \ No newline at end of file diff --git a/server/AyaNova/util/ImportUtil.cs b/server/AyaNova/util/ImportUtil.cs index 25ba66f7..ef885bf7 100644 --- a/server/AyaNova/util/ImportUtil.cs +++ b/server/AyaNova/util/ImportUtil.cs @@ -1,8 +1,6 @@ using System; using System.Reflection; -using System.Linq; using System.Collections.Generic; -using Newtonsoft.Json.Linq; namespace AyaNova.Util { @@ -63,7 +61,8 @@ namespace AyaNova.Util } - + + }//eoc diff --git a/server/AyaNova/util/License.cs b/server/AyaNova/util/License.cs index 4dc71880..ccf7c526 100644 --- a/server/AyaNova/util/License.cs +++ b/server/AyaNova/util/License.cs @@ -799,7 +799,7 @@ namespace AyaNova.Core throw new ApplicationException("E1020 - Can't install a trial key into a non empty AyaNova database. Erase the database first."); } - //TODO: TECHCOUNT - new license causes exceeding count? + //TECHCOUNT - new license causes exceeding count? long NewTechCount = ParsedNewKey.GetLicenseFeature(SERVICE_TECHS_FEATURE_NAME).Count; if (await AyaNova.Biz.UserBiz.ActiveCountAsync() > NewTechCount) { @@ -925,6 +925,15 @@ EQIDAQAB #endregion get values + + //Check if attempting to use a build of AyaNova that is newer than maintenance subscription expiry + if (MExBB(Util.FileUtil.GetLinkerTimestampUtc(System.Reflection.Assembly.GetExecutingAssembly()), key)) + { + Console.WriteLine("E1020 - Not licensed for this version of AyaNova. Fix: downgrade back to previous version in use or contact technical support for options."); + + //NOTE: VERSION-TOO-NEW bit below is checked for in startup.cs DO NOT remove this without fixing the side effects + throw new ApplicationException("E1020 VERSION-TOO-NEW - Not licensed for this version of AyaNova. Fix: downgrade back to previous version in use or contact technical support for options."); + } //All is well return key return key; @@ -942,6 +951,12 @@ EQIDAQAB + #region Validate Build date against maintenance date in license + //MaintenanceExpirationBeforeBuild? + internal static bool MExBB(DateTime dtB, AyaNovaLicenseKey k) => k.MaintenanceExpiration < dtB; + #endregion + + }//eoc