diff --git a/devdocs/todo.txt b/devdocs/todo.txt index fd69dfea..e0d75c7b 100644 --- a/devdocs/todo.txt +++ b/devdocs/todo.txt @@ -48,6 +48,8 @@ todo: customer contact consuming a subscription license always because it doesn' todo: TEST subscription and perpetual license code + Does new boot code still work when no database? + Does new boot code work to prevent running if wrong build or maint date expired vs build date? Does it prevent changing a user to active? Does it prevent changing a user type? (direct db meddling) Does it prevent installing a new license when too few for existing active? diff --git a/server/AyaNova/DataList/InsideUserDataList.cs b/server/AyaNova/DataList/InsideUserDataList.cs index 9212c4e5..09b2eec9 100644 --- a/server/AyaNova/DataList/InsideUserDataList.cs +++ b/server/AyaNova/DataList/InsideUserDataList.cs @@ -47,6 +47,14 @@ namespace AyaNova.DataList SqlValueColumnName = "auser.active" }); + FieldDefinitions.Add(new DataListFieldDefinition + { + TKey = "AllowLogin", + FieldKey = "allowlogin", + UiFieldDataType = (int)UiFieldDataType.Bool, + SqlValueColumnName = "auser.allowlogin" + }); + FieldDefinitions.Add(new DataListFieldDefinition { TKey = "UserType", diff --git a/server/AyaNova/DataList/OutsideUserDataList.cs b/server/AyaNova/DataList/OutsideUserDataList.cs index 96cc49d9..bf4e1a38 100644 --- a/server/AyaNova/DataList/OutsideUserDataList.cs +++ b/server/AyaNova/DataList/OutsideUserDataList.cs @@ -15,7 +15,7 @@ namespace AyaNova.DataList + "left join atranslation on auseroptions.translationid = atranslation.id"; var RoleSet = BizRoles.GetRoleSet(DefaultListAType); AllowedRoles = RoleSet.ReadFullRecord | RoleSet.Change; - DefaultColumns = new List() { "name", "active", "usercustomer", "userheadoffice", "lastlogin" }; + DefaultColumns = new List() { "name", "active", "allowlogin", "usercustomer", "userheadoffice", "lastlogin" }; DefaultSortBy = new Dictionary() { { "name", "+" } }; FieldDefinitions = new List(); @@ -30,7 +30,7 @@ namespace AyaNova.DataList IsRowId = true }); - + FieldDefinitions.Add(new DataListFieldDefinition { @@ -40,6 +40,16 @@ namespace AyaNova.DataList SqlValueColumnName = "auser.active" }); + + FieldDefinitions.Add(new DataListFieldDefinition + { + TKey = "AllowLogin", + FieldKey = "allowlogin", + UiFieldDataType = (int)UiFieldDataType.Bool, + SqlValueColumnName = "auser.allowlogin" + }); + + FieldDefinitions.Add(new DataListFieldDefinition { TKey = "UserType", diff --git a/server/AyaNova/Startup.cs b/server/AyaNova/Startup.cs index bb474d85..c46dc1b7 100644 --- a/server/AyaNova/Startup.cs +++ b/server/AyaNova/Startup.cs @@ -602,28 +602,11 @@ namespace AyaNova } - //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 or perpetual / subscription vs build type 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 type mismatch 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 HOWEVER they can do the case 4170 thing with the flag AYANOVA_REMOVE_LICENSE_FROM_DB - - //NOTE: AYANOVA_REMOVE_LICENSE_FROM_DB boot flag can be used to work around this issue - bool licenseChecked = false; + //BOOT safety check to prevent schema update if user has installed version of AyaNova they are not licensed for try { - //NOTE: a completely missing db will trigger an exception on this line, we expect that and will be fine as the schema check will run below and create the tables required - var tempSchema = dbContext.SchemaVersion.AsNoTracking().SingleOrDefault(); - var tempLicense = dbContext.License.AsNoTracking().SingleOrDefault(); - - 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 - - AyaNova.Core.License.InitializeAsync(apiServerState, dbContext, _newLog).Wait(); - licenseChecked = true; - } + //this will either return without throwing which is ok or will throw and may or may not indicate need to be prevented from further boot + AyaNova.Core.License.BootSafetyCheck(dbContext, _newLog); } catch (Exception ex) { @@ -631,7 +614,7 @@ namespace AyaNova //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(AyaNova.Core.License.LICENSE_MISMATCH_TO_BUILD_ERROR)) + if (ex.Message.Contains("1020") && ex.Message.Contains(AyaNova.Core.License.LICENSE_MISMATCH_TO_BUILD_ERROR))//DO NOT CHANGE THIS MAGIC STRING KEY { throw new Exception("AyaNova did not start to protect the integrity of your data, see the console and / or error log for details"); } @@ -652,9 +635,8 @@ namespace AyaNova AySchema.EXPECTED_ROUTINES, _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(); + //Initialize license + AyaNova.Core.License.InitializeAsync(apiServerState, dbContext, _newLog).Wait(); //Set static global biz settings _newLog.LogDebug("Global settings"); diff --git a/server/AyaNova/util/AySchema.cs b/server/AyaNova/util/AySchema.cs index 1649ca59..08c24a23 100644 --- a/server/AyaNova/util/AySchema.cs +++ b/server/AyaNova/util/AySchema.cs @@ -22,7 +22,7 @@ namespace AyaNova.Util //!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImportingAsync WHEN NEW TABLES ADDED!!!! private const int DESIRED_SCHEMA_LEVEL = 6; - internal const long EXPECTED_COLUMN_COUNT = 1376; + internal const long EXPECTED_COLUMN_COUNT = 1377; internal const long EXPECTED_INDEX_COUNT = 161; internal const long EXPECTED_CHECK_CONSTRAINTS = 561; internal const long EXPECTED_FOREIGN_KEY_CONSTRAINTS = 204; @@ -31,7 +31,7 @@ namespace AyaNova.Util //!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImportingAsync WHEN NEW TABLES ADDED!!!! - ///////////////////////////////////////// (C1376:I161:CC561:FC204:V11:R2) + ///////////////////////////////////////// C1377:I161:CC561:FC204:V11:R2 /* @@ -1521,7 +1521,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); LogUpdateMessage(log); await ExecQueryAsync("ALTER TABLE auser ADD column allowlogin BOOL"); - await ExecQueryAsync("UPDTE TABLE auser SET allowlogin=true WHERE active=true"); + await ExecQueryAsync("UPDATE auser SET allowlogin=true WHERE active=true"); //english translations diff --git a/server/AyaNova/util/License.cs b/server/AyaNova/util/License.cs index dc8a1e47..773d14df 100644 --- a/server/AyaNova/util/License.cs +++ b/server/AyaNova/util/License.cs @@ -686,6 +686,48 @@ namespace AyaNova.Core } + + /// + /// Minimal no side effects check if ok to proceed with boot + /// and do schema updates etc, this is like a pre license initialize + /// called from startup.cs to ensure the safety of the db in case + /// the user has installed a version they are not entitled to + /// which would permanently change their db + /// + internal static void BootSafetyCheck(AyContext ct, ILogger log) + { + //this is necessary to accomodate checking build date against subscription or perpetual / subscription vs build type and preventing schema update if they upgrade but are not entitled to + //so they don't damage their database saving us having to walk them through a potentially flawed restore + //if there is a build date issue or a license type mismatch 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 HOWEVER they can do the case 4170 thing with the flag AYANOVA_REMOVE_LICENSE_FROM_DB + + //verify the build date and version match this build + log.LogDebug("Boot database safety check"); + //NOTE: a completely missing db will trigger an exception on this line, we expect that and will be fine as the caller in startup will understand this scenario + //and proceed with the first boot schema update + var schema = ct.SchemaVersion.AsNoTracking().SingleOrDefault(); + var ldb = ct.License.AsNoTracking().SingleOrDefault(); + + if (schema == null || ldb == null || string.IsNullOrWhiteSpace(schema.Id)) + return;//no key record at all or no schema, no need to prevent the normal boot up + + //is there an actual license in the key? + if (ldb.Key == "none") + return;//nope, let it do it's thing and schema update if necessary + + //we have a schema and a license, check it now for build date vs maintenance expiry check + //parse will try to parse the key and will check the build type and maint date so if it bombs here it will throw and startup.cs will properly understand that + ServerDbId = schema.Id; + Parse(ldb.Key, log); + + // AyaNova.Core.License.InitializeAsync(apiServerState, dbContext, _newLog).Wait(); + + + + } + + /// /// Initialize the license /// @@ -698,7 +740,6 @@ namespace AyaNova.Core { //First fetch the schema db id for the servers database, the license must match var schema = await ct.SchemaVersion.AsNoTracking().SingleOrDefaultAsync(); - //if (schema == null || schema.Id == Guid.Empty) if (schema == null || string.IsNullOrWhiteSpace(schema.Id)) { //cryptic message deliberately, this is probably caused by someone trying to circumvent licensing @@ -923,7 +964,7 @@ namespace AyaNova.Core { AyaNovaLicenseKey key = new AyaNovaLicenseKey(); - log.LogDebug("Validating license"); + log.LogDebug("Parsing and validating license"); if (string.IsNullOrWhiteSpace(k)) {