diff --git a/docs/8.0/ayanova/docs/ops-install-linux-desktop.md b/docs/8.0/ayanova/docs/ops-install-linux-desktop.md index 56be4b45..d4e9ea47 100644 --- a/docs/8.0/ayanova/docs/ops-install-linux-desktop.md +++ b/docs/8.0/ayanova/docs/ops-install-linux-desktop.md @@ -90,7 +90,7 @@ sudo mkdir /usr/bin/ayanova [https://www.ayanova.com/download/ayanova-linux-x64-desktop.zip](https://www.ayanova.com/download/ayanova-linux-x64-desktop.zip) -Download the the AyaNova server binary files folder created in the previous step or copy it there from the terminal: +Download the AyaNova server binary files to the folder created in the previous step or copy it there from the terminal: ```bash sudo cp ayanova-linux-x64-desktop.zip /usr/bin/ayanova/ diff --git a/server/AyaNova/Controllers/AuthController.cs b/server/AyaNova/Controllers/AuthController.cs index 2cc872de..b9b5da43 100644 --- a/server/AyaNova/Controllers/AuthController.cs +++ b/server/AyaNova/Controllers/AuthController.cs @@ -71,6 +71,12 @@ namespace AyaNova.Api.Controllers //NOTE: lockout or other login impacting state is processed later in ReturnUserCredsOnSuccessfulAuthentication() because many of those states need to have exceptions once the user is known //or return alternate result of auth etc + if (Core.License.LicenseConsentRequired) + { + await Task.Delay(AyaNova.Util.ServerBootConfig.FAILED_AUTH_DELAY); + return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED, "generalerror", "License agreement consent required")); + } + if (string.IsNullOrWhiteSpace(creds.Login) || string.IsNullOrWhiteSpace(creds.Password)) { //Make a failed pw wait diff --git a/server/AyaNova/Controllers/LicenseController.cs b/server/AyaNova/Controllers/LicenseController.cs index bcb71b2a..c4f8824b 100644 --- a/server/AyaNova/Controllers/LicenseController.cs +++ b/server/AyaNova/Controllers/LicenseController.cs @@ -274,8 +274,31 @@ namespace AyaNova.Api.Controllers } + /// + /// + /// + /// + /// HTTP 204 No Content result code on success or fail code with explanation + [AllowAnonymous] + [HttpPost("lc")] + [ApiExplorerSettings(IgnoreApi = true)] + public async Task lc([FromBody] string acceptCode) + { + //END USER LICENSE AGREEMENT ROUTE ONLY CALLED FROM WEBAPP AND HIDDEN FROM VIEW AS A ROUTE + //SuperUser only and must have accept code + if (string.IsNullOrWhiteSpace(acceptCode) || acceptCode.ToLowerInvariant() != "iaccepttheagreement") + return StatusCode(403, new ApiNotAuthorizedResponse()); + + await Core.License.FlagEULA(ct, log); + + //Log + await EventLogProcessor.LogEventToDatabaseAsync(new Event(1, 0, AyaType.Global, AyaEvent.Modified, "End user license agreement consent obtained"), ct); + + return NoContent(); + } + //------------------------------------------------------ diff --git a/server/AyaNova/Controllers/NotifyController.cs b/server/AyaNova/Controllers/NotifyController.cs index 6d425e89..f4bed2be 100644 --- a/server/AyaNova/Controllers/NotifyController.cs +++ b/server/AyaNova/Controllers/NotifyController.cs @@ -54,9 +54,10 @@ namespace AyaNova.Api.Controllers var logo = await ct.Logo.AsNoTracking().SingleOrDefaultAsync(); if (logo == null) { - return Ok(ApiOkResponse.Response(new { eval = showSampleLogins, ll = false, ml = false, sl = false })); + return Ok(ApiOkResponse.Response(new { eval = showSampleLogins, ll = false, ml = false, sl = false, lcr = AyaNova.Core.License.LicenseConsentRequired })); } - return Ok(ApiOkResponse.Response(new { eval = showSampleLogins, ll = logo.Large != null ? true : false, ml = logo.Medium != null ? true : false, sl = logo.Small != null ? true : false })); + return Ok(ApiOkResponse.Response( + new { eval = showSampleLogins, ll = logo.Large != null ? true : false, ml = logo.Medium != null ? true : false, sl = logo.Small != null ? true : false, lcr = AyaNova.Core.License.LicenseConsentRequired })); } @@ -69,7 +70,7 @@ namespace AyaNova.Api.Controllers public async Task GetNewCount() { var UserId = UserIdFromContext.Id(HttpContext.Items); - if (serverState.IsClosed && UserId!=1)//bypass for superuser to fix fundamental problems + if (serverState.IsClosed && UserId != 1)//bypass for superuser to fix fundamental problems return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); return Ok(ApiOkResponse.Response(await ct.InAppNotification.CountAsync(z => z.UserId == UserId && z.Fetched == false))); diff --git a/server/AyaNova/generator/CoreJobLicense.cs b/server/AyaNova/generator/CoreJobLicense.cs index efb27879..ee1d05d2 100644 --- a/server/AyaNova/generator/CoreJobLicense.cs +++ b/server/AyaNova/generator/CoreJobLicense.cs @@ -1,10 +1,7 @@ using System; -using System.Linq; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using AyaNova.Models; -using AyaNova.Core; using AyaNova.Util; namespace AyaNova.Biz diff --git a/server/AyaNova/models/License.cs b/server/AyaNova/models/License.cs index ebf695a7..7eff4816 100644 --- a/server/AyaNova/models/License.cs +++ b/server/AyaNova/models/License.cs @@ -7,6 +7,8 @@ namespace AyaNova.Models { public long Id { get; set; } public string Key { get; set; } - public string DbId { get; set; } + public string DbId { get; set; } + public bool LicenseAgree { get; set; }//flag indicating end user license agreement consent + } } diff --git a/server/AyaNova/util/AySchema.cs b/server/AyaNova/util/AySchema.cs index d65c78ba..4ff39411 100644 --- a/server/AyaNova/util/AySchema.cs +++ b/server/AyaNova/util/AySchema.cs @@ -22,16 +22,16 @@ namespace AyaNova.Util //!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImportingAsync WHEN NEW TABLES ADDED!!!! private const int DESIRED_SCHEMA_LEVEL = 1; - internal const long EXPECTED_COLUMN_COUNT = 1324; + internal const long EXPECTED_COLUMN_COUNT = 1325; internal const long EXPECTED_INDEX_COUNT = 153; - internal const long EXPECTED_CHECK_CONSTRAINTS = 519; + internal const long EXPECTED_CHECK_CONSTRAINTS = 520; internal const long EXPECTED_FOREIGN_KEY_CONSTRAINTS = 198; internal const long EXPECTED_VIEWS = 11; internal const long EXPECTED_ROUTINES = 2; //!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImportingAsync WHEN NEW TABLES ADDED!!!! - ///////////////////////////////////////// C1324:I153:CC519:FC198:V11:R2 + ///////////////////////////////////////// C1325:I153:CC520:FC198:V11:R2 @@ -624,7 +624,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); await AyaNova.Biz.PrimeData.PrimeSuperUserAccount(ct); //Add user table - await ExecQueryAsync("CREATE TABLE alicense (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, dbid TEXT, key TEXT NOT NULL)"); + await ExecQueryAsync("CREATE TABLE alicense (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, licenseagree BOOL NOT NULL, dbid TEXT, key TEXT NOT NULL)"); await ExecQueryAsync("CREATE TABLE afileattachment (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, " + "attachtoobjectid BIGINT NOT NULL, attachtoatype INTEGER NOT NULL, " diff --git a/server/AyaNova/util/License.cs b/server/AyaNova/util/License.cs index 3e669105..3bb533ce 100644 --- a/server/AyaNova/util/License.cs +++ b/server/AyaNova/util/License.cs @@ -73,6 +73,7 @@ namespace AyaNova.Core //The license dbid, separate from the server dbid private static string LicenseDbId { get; set; } + #region license classes //DTO object returned on license query @@ -184,6 +185,8 @@ namespace AyaNova.Core } } + + //Has any kind of valid license that is active //used for auth route checking to allow for fixing this issue public bool KeyDoesNotNeedAttention @@ -257,6 +260,7 @@ namespace AyaNova.Core public List Features { get; set; } + } #endregion @@ -302,6 +306,9 @@ namespace AyaNova.Core //The database id value stored in the schema table internal static string ServerDbId { get; private set; } + internal static bool LicenseConsentRequired { get; private set; } + + /// /// Fetch a summary of the license key for displaying to the end user /// @@ -443,10 +450,32 @@ namespace AyaNova.Core } } - - #endregion + #region EULA consent + /// + /// Set consent flag for license agreement + /// + internal static async Task FlagEULA(AyContext ct, ILogger log) + { + try + { + var CurrentInDbKeyRecord = await ct.License.OrderBy(z => z.Id).FirstOrDefaultAsync(); + if (CurrentInDbKeyRecord == null) + throw new ApplicationException("E1020 - Can't update EULA agreement, no key record found"); + //Update current license + CurrentInDbKeyRecord.LicenseAgree = true; + await ct.SaveChangesAsync(); + } + catch (Exception ex) + { + var msg = "E1020 - Error during EULA agreement flagging"; + log.LogError(ex, msg); + throw new ApplicationException(msg, ex); + } + LicenseConsentRequired = false; + } + #endregion #region Trial license request handling /// @@ -484,7 +513,7 @@ namespace AyaNova.Core #endregion trial license request handling - #region License fetching and handling + #region License fetching and handling public class dtoFetchRequest { @@ -657,6 +686,7 @@ namespace AyaNova.Core ldb = new Models.License(); ldb.DbId = ServerDbId; ldb.Key = "none"; + ldb.LicenseAgree = false; ct.License.Add(ldb); await ct.SaveChangesAsync(); } @@ -666,6 +696,7 @@ namespace AyaNova.Core if (string.IsNullOrWhiteSpace(ldb.DbId)) { ldb.DbId = ServerDbId; + ldb.LicenseAgree = false; //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; @@ -675,10 +706,13 @@ namespace AyaNova.Core //Get it early and set it here so that it can be displayed early to the user even if not licensed LicenseDbId = ldb.DbId; + //someone must agree to the license on first login from the client, this stores that + LicenseConsentRequired = !ldb.LicenseAgree; + if (ldb.Key == "none") { var msg = "E1020 - License key not found in database, running in unlicensed mode"; - apiServerState.SetSystemLock(msg); + apiServerState.SetSystemLock(msg); log.LogWarning(msg); return; } @@ -790,6 +824,8 @@ namespace AyaNova.Core } + + #endregion #region PARSE and Validate key