From a98dfbc08de6ba066ff2ba558332b2c11e18f472 Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Thu, 11 Jun 2020 19:52:47 +0000 Subject: [PATCH] --- .../AyaNova/Controllers/ApiRootController.cs | 1 + server/AyaNova/models/AyContext.cs | 5 +- server/AyaNova/models/SchemaVersion.cs | 24 +++++++ server/AyaNova/util/AySchema.cs | 6 +- server/AyaNova/util/License.cs | 71 +++++++++---------- 5 files changed, 63 insertions(+), 44 deletions(-) create mode 100644 server/AyaNova/models/SchemaVersion.cs diff --git a/server/AyaNova/Controllers/ApiRootController.cs b/server/AyaNova/Controllers/ApiRootController.cs index 4238c4c5..b7398f92 100644 --- a/server/AyaNova/Controllers/ApiRootController.cs +++ b/server/AyaNova/Controllers/ApiRootController.cs @@ -444,6 +444,7 @@ namespace AyaNova.Api.Controllers DBSchemaVersion = AySchema.currentSchema, ServerLocalTime = DateUtil.ServerDateTimeString(System.DateTime.UtcNow), ServerTimeZone = TimeZoneInfo.Local.Id, + ServerDbId= AyaNova.Core.License.ServerDbId, License = AyaNova.Core.License.LicenseInfoAsJson } }); diff --git a/server/AyaNova/models/AyContext.cs b/server/AyaNova/models/AyContext.cs index b02a63b5..eebf4ff4 100644 --- a/server/AyaNova/models/AyContext.cs +++ b/server/AyaNova/models/AyContext.cs @@ -5,7 +5,8 @@ namespace AyaNova.Models { public partial class AyContext : DbContext { - public virtual DbSet MetricMM { get; set; } + public virtual DbSet SchemaVersion { get; set; } + public virtual DbSet MetricMM { get; set; } public virtual DbSet MetricDD { get; set; } public virtual DbSet User { get; set; } public virtual DbSet UserOptions { get; set; } @@ -127,7 +128,7 @@ namespace AyaNova.Models modelBuilder.Entity().Property(z => z.Serial).UseIdentityByDefaultColumn(); //modelBuilder.Entity().Property(z => z.Serial).UseIdentityByDefaultColumn(); - + //modelBuilder.Entity().HasNoKey(); /////////////////////////////////////////////////////////////////////// diff --git a/server/AyaNova/models/SchemaVersion.cs b/server/AyaNova/models/SchemaVersion.cs new file mode 100644 index 00000000..6bb1380a --- /dev/null +++ b/server/AyaNova/models/SchemaVersion.cs @@ -0,0 +1,24 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace AyaNova.Models +{ + + /// + /// AyaNova Schema version table + /// + public class SchemaVersion + { + [Required] + [Key] + public Guid Id { get; set; } + public int Schema { get; set; } + + + + //ef core requires this + public SchemaVersion() { } + + + }//eoc +}//eons diff --git a/server/AyaNova/util/AySchema.cs b/server/AyaNova/util/AySchema.cs index 466dce2b..4a7aca5d 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::EmptyBizDataFromDatabaseForSeedingOrImporting WHEN NEW TABLES ADDED!!!! private const int DESIRED_SCHEMA_LEVEL = 11; - internal const long EXPECTED_COLUMN_COUNT = 323; + internal const long EXPECTED_COLUMN_COUNT = 324; internal const long EXPECTED_INDEX_COUNT = 134; //!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImporting WHEN NEW TABLES ADDED!!!! @@ -173,10 +173,10 @@ namespace AyaNova.Util using (var cm = ct.Database.GetDbConnection().CreateCommand()) { await ct.Database.OpenConnectionAsync(); - cm.CommandText = "CREATE TABLE aschemaversion (schema INTEGER NOT NULL);"; + cm.CommandText = "CREATE TABLE aschemaversion (schema INTEGER NOT NULL, id uuid not null);"; await cm.ExecuteNonQueryAsync(); - cm.CommandText = "insert into aschemaversion (schema) values (1);"; + cm.CommandText = $"insert into aschemaversion (schema, id) values (1,'{Guid.NewGuid()}');"; await cm.ExecuteNonQueryAsync(); await ct.Database.CloseConnectionAsync(); diff --git a/server/AyaNova/util/License.cs b/server/AyaNova/util/License.cs index b7b92e16..d303fe86 100644 --- a/server/AyaNova/util/License.cs +++ b/server/AyaNova/util/License.cs @@ -52,15 +52,15 @@ namespace AyaNova.Core //Trial key magic number for development and testing, all other guids will be fully licensed - private static Guid TEST_TRIAL_KEY_DBID = new Guid("{A6D18A8A-5613-4979-99DA-80D07641A2FE}"); + // private static Guid TEST_TRIAL_KEY_DBID = new Guid("{A6D18A8A-5613-4979-99DA-80D07641A2FE}"); //Current license key, can be empty private static AyaNovaLicenseKey _ActiveLicense = new AyaNovaLicenseKey(); - //The one and only DBID - private static Guid DbId { get; set; } + //The license dbid, separate from teh server dbid + private static Guid LicenseDbId { get; set; } #region license classes @@ -153,7 +153,6 @@ namespace AyaNova.Core { get { - return LicenseStatus.Revoked; if (string.IsNullOrWhiteSpace(RegisteredTo) || RegisteredTo == UNLICENSED_TOKEN) return LicenseStatus.NONE; @@ -287,7 +286,8 @@ namespace AyaNova.Core #region Exposed properties - + //The database id value stored in the schema table + internal static Guid ServerDbId { get; private set; } /// /// Fetch a summary of the license key for displaying to the end user @@ -302,7 +302,7 @@ namespace AyaNova.Core if (ActiveKey.IsEmpty) { sb.AppendLine(UNLICENSED_TOKEN); - sb.AppendLine($"DB ID: {DbId}"); + sb.AppendLine($"DB ID: {LicenseDbId}"); } else { @@ -311,7 +311,7 @@ namespace AyaNova.Core sb.AppendLine($"Registered to: {ActiveKey.RegisteredTo}"); sb.AppendLine($"Key ID: {ActiveKey.Id}"); - sb.AppendLine($"DB ID: {DbId}"); + sb.AppendLine($"DB ID: {LicenseDbId}"); sb.AppendLine($"Type: {(ActiveKey.RentalLicense ? "Service" : "Perpetual")}"); if (ActiveKey.WillExpire) sb.AppendLine($"License expires: {DateUtil.ServerDateTimeString(ActiveKey.LicenseExpiration)}"); @@ -347,7 +347,7 @@ namespace AyaNova.Core if (ActiveKey.IsEmpty) { - return $"UNLICENSED, DB ID: {DbId}"; + return $"UNLICENSED, DB ID: {LicenseDbId}"; } else { @@ -357,7 +357,7 @@ namespace AyaNova.Core sb.Append($"regto: {ActiveKey.RegisteredTo}, "); sb.Append($"keyid: {ActiveKey.Id}, "); - sb.Append($"dbid: {DbId}, "); + sb.Append($"dbid: {LicenseDbId}, "); sb.Append($"type: {(ActiveKey.RentalLicense ? "service" : "perpetual")}, "); if (ActiveKey.WillExpire) sb.Append($"exp: {DateUtil.ServerDateTimeString(ActiveKey.LicenseExpiration)}, "); @@ -438,27 +438,16 @@ namespace AyaNova.Core /// Result string internal static async Task RequestTrialAsync(string email, string regto, ILogger log) { - //BEFORE_RELEASE: DO THE FOLLOWING BEFORE RELEASE: - //TODO: TESTING REMOVE BEFORE RELEASE - //for test purposes if this route is hit and this code executed then the dbid is temporarily changed to the special trial request - //dbid so I can test remotely without hassle - //TO USE: just hit the trial key request route once then the license fetch route and it should be easy peasy - log.LogCritical("WARNING License::RequestTrial - DEVELOPMENT TEST FORCING TRIAL DB KEY ID. UPDATE BEFORE RELEASE!!"); - DbId = TEST_TRIAL_KEY_DBID; - //TESTING - - Microsoft.AspNetCore.Http.Extensions.QueryBuilder q = new Microsoft.AspNetCore.Http.Extensions.QueryBuilder(); - q.Add("dbid", DbId.ToString()); + q.Add("dbid", LicenseDbId.ToString()); q.Add("email", email); q.Add("regto", regto); - log.LogDebug($"Requesting trial license for DBID {DbId.ToString()}"); + log.LogDebug($"Requesting trial license for DBID {LicenseDbId.ToString()}"); string sUrl = $"{LICENSE_SERVER_URL}rvr" + q.ToQueryString(); try { - //var res = await _Client.GetStringAsync(sUrl); var res = await ServiceProviderProvider.HttpClientFactory.CreateClient().GetStringAsync(sUrl); return res; } @@ -467,7 +456,6 @@ namespace AyaNova.Core var msg = "E1020 - Error requesting trial license key see log for details"; log.LogError(ex, msg); return msg; - // throw new ApplicationException(msg, ex); } } #endregion trial license request handling @@ -482,18 +470,9 @@ namespace AyaNova.Core /// Result string internal static async Task FetchKeyAsync(AyaNova.Api.ControllerHelpers.ApiServerState apiServerState, AyContext ct, ILogger log) { - log.LogDebug($"Fetching license for DBID {DbId.ToString()}"); - string sUrl = $"{LICENSE_SERVER_URL}rvf/{DbId.ToString()}"; + log.LogDebug($"Fetching license for DBID {LicenseDbId.ToString()}"); + string sUrl = $"{LICENSE_SERVER_URL}rvf/{LicenseDbId.ToString()}"; - //########################################################################################################## - //TODO: RELEASE WARNING: this needs to be dealt with before production release - if (ServerBootConfig.AYANOVA_SERVER_TEST_MODE) - { - log.LogInformation("Server is in test mode, fetching trial key"); - sUrl = $"{LICENSE_SERVER_URL}rvf/{TEST_TRIAL_KEY_DBID.ToString()}"; - // log.LogInformation(sUrl); - } - //########################################################################################################## try { @@ -519,8 +498,8 @@ namespace AyaNova.Core /// - /// Initialize the key - /// Handle if first boot scenario to tag DB ID etc + /// Initialize the license + /// /// internal static async Task InitializeAsync(AyaNova.Api.ControllerHelpers.ApiServerState apiServerState, AyContext ct, ILogger log) { @@ -528,6 +507,18 @@ namespace AyaNova.Core try { + //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) + { + //cryptic message deliberately, this is probably caused by someone trying to circumvent licensing + var msg = "E1030 - Database integrity check failed (2). Contact support."; + apiServerState.SetOpsOnly(msg); + log.LogCritical(msg); + return; + } + ServerDbId = schema.Id; + //Fetch key from db as no tracking so doesn't hang round if need to immediately clear and then re-add the key Models.License ldb = await ct.License.AsNoTracking().SingleOrDefaultAsync(); @@ -535,7 +526,7 @@ namespace AyaNova.Core if (ldb == null) { ldb = new Models.License(); - ldb.DbId = Guid.NewGuid();//<--Permanent and unchanging DB ID for this database, the license must match this + ldb.DbId = ServerDbId; ldb.Key = "none"; ct.License.Add(ldb); await ct.SaveChangesAsync(); @@ -544,7 +535,7 @@ namespace AyaNova.Core //ensure DB ID if (ldb.DbId == Guid.Empty) { - ldb.DbId = Guid.NewGuid();//<--Permanent and unchanging DB ID for this database, the license must match this + ldb.DbId = ServerDbId; //Convert the no tracking record fetched above to tracking //this is required because a prior call to initialize before dumping the db would mean the license is still in memory in the context ct.Entry(ldb).State = Microsoft.EntityFrameworkCore.EntityState.Modified; @@ -552,7 +543,7 @@ namespace AyaNova.Core } //Get it early and set it here so that it can be displayed early to the user even if not licensed - DbId = ldb.DbId; + LicenseDbId = ldb.DbId; if (ldb.Key == "none") { @@ -739,6 +730,8 @@ EQIDAQAB key.Id = (string)token.SelectToken("Key.Id"); key.RegisteredTo = (string)token.SelectToken("Key.RegisteredTo"); key.DbId = (Guid)token.SelectToken("Key.DBID"); + if (key.DbId != ServerDbId) + throw new ApplicationException($"E1020 - License.Parse -> License key does not match this server"); key.LicenseExpiration = (DateTime)token.SelectToken("Key.LicenseExpiration"); key.MaintenanceExpiration = (DateTime)token.SelectToken("Key.MaintenanceExpiration");