licensing system change, all internal active, no more scheduleable resources / tech licensing

This commit is contained in:
2022-09-12 21:57:31 +00:00
parent c5a8f1f09c
commit d3d06766c4
7 changed files with 135 additions and 162 deletions

View File

@@ -685,7 +685,7 @@ namespace AyaNova
//Log the active user counts so it's in the log record
//I'm doing all three even though some not relevant in certain scenarios in case of user switching between subscription/perpetual as another source of information
_newLog.LogInformation($"Active tech Users - {UserBiz.ActiveTechUserCountAsync().Result}");
//_newLog.LogInformation($"Active tech Users - {UserBiz.ActiveTechUserCountAsync().Result}");
_newLog.LogInformation($"Active Customer contact Users - {UserBiz.ActiveCustomerContactUserCountAsync().Result}");
_newLog.LogInformation($"Active internal staff Users - {UserBiz.ActiveInternalUserCountAsync().Result}");

View File

@@ -26,18 +26,18 @@ namespace AyaNova.Biz
}
//This is where active tech license consumers are accounted for
internal static async Task<long> ActiveTechUserCountAsync()
{
using (AyContext ct = ServiceProviderProvider.DBContext)
{
var ret = await ct.User.AsNoTracking().Where(z => z.Active == true && (
z.UserType == UserType.Service ||
z.UserType == UserType.ServiceContractor)).LongCountAsync();
return ret;
}
}
// internal static async Task<long> ActiveTechUserCountAsync()
// {
// using (AyContext ct = ServiceProviderProvider.DBContext)
// {
// var ret = await ct.User.AsNoTracking().Where(z => z.Active == true && (
// z.UserType == UserType.Service ||
// z.UserType == UserType.ServiceContractor)).LongCountAsync();
// return ret;
// }
// }
//This is where SUBSCRIPTION active internal (non customer) license consumers are accounted for as they consume resources if they can login
//This is where SUBSCRIPTION AND PERPETUAL active internal (non customer) license consumers are accounted for
internal static async Task<long> ActiveInternalUserCountAsync()
{
using (AyContext ct = ServiceProviderProvider.DBContext)
@@ -75,46 +75,46 @@ namespace AyaNova.Biz
}
}
//Called by license processor when use downgrades to lesser amount of techs
internal static async Task DeActivateExcessiveTechs(long KeepThisManyActiveTechs, ILogger _log)
{
var TotalActiveTechs = await ActiveTechUserCountAsync();
int CountOfTechsToSetInactive = (int)(TotalActiveTechs - KeepThisManyActiveTechs);
if (CountOfTechsToSetInactive < 1) return;
_log.LogInformation($"New license is a downgrade with fewer scheduleable resources, deactivating {CountOfTechsToSetInactive} scheduleable users automatically so the license can be installed");
//bugbug: exactly wrong, deactivating the most recently logged in over some who never logged in, could be that null login is causing it?
using (AyContext ct = ServiceProviderProvider.DBContext)
{
// //Called by license processor when use downgrades to lesser amount of techs
// internal static async Task DeActivateExcessiveTechs(long KeepThisManyActiveTechs, ILogger _log)
// {
// var TotalActiveTechs = await ActiveTechUserCountAsync();
// int CountOfTechsToSetInactive = (int)(TotalActiveTechs - KeepThisManyActiveTechs);
// if (CountOfTechsToSetInactive < 1) return;
// _log.LogInformation($"New license is a downgrade with fewer scheduleable resources, deactivating {CountOfTechsToSetInactive} scheduleable users automatically so the license can be installed");
// //bugbug: exactly wrong, deactivating the most recently logged in over some who never logged in, could be that null login is causing it?
// using (AyContext ct = ServiceProviderProvider.DBContext)
// {
//Algorithm: For deactivation favor subcontractors first over servicetechs, favor ones that have no login records and finally favor by oldest last login
//theory is to catch the least likely to be currently active techs
// //Algorithm: For deactivation favor subcontractors first over servicetechs, favor ones that have no login records and finally favor by oldest last login
// //theory is to catch the least likely to be currently active techs
var NoLoginTechList = await ct.User.Where(z => z.Active == true && z.LastLogin == null && (
z.UserType == UserType.Service ||
z.UserType == UserType.ServiceContractor)).OrderByDescending(z => z.UserType).Take(CountOfTechsToSetInactive).ToListAsync();
// var NoLoginTechList = await ct.User.Where(z => z.Active == true && z.LastLogin == null && (
// z.UserType == UserType.Service ||
// z.UserType == UserType.ServiceContractor)).OrderByDescending(z => z.UserType).Take(CountOfTechsToSetInactive).ToListAsync();
CountOfTechsToSetInactive -= NoLoginTechList.Count();
foreach (User u in NoLoginTechList)
{
u.Active = false;
var msg = $"User {u.Name} with no prior login automatically set to inactive to free up downgraded license";
await NotifyEventHelper.AddOpsProblemEvent(msg);
_log.LogInformation(msg);
}
await ct.SaveChangesAsync();
// CountOfTechsToSetInactive -= NoLoginTechList.Count();
// foreach (User u in NoLoginTechList)
// {
// u.Active = false;
// var msg = $"User {u.Name} with no prior login automatically set to inactive to free up downgraded license";
// await NotifyEventHelper.AddOpsProblemEvent(msg);
// _log.LogInformation(msg);
// }
// await ct.SaveChangesAsync();
var HasLoginTechList = await ct.User.Where(z => z.Active == true && z.LastLogin != null && (
z.UserType == UserType.Service ||
z.UserType == UserType.ServiceContractor)).OrderByDescending(z => z.UserType).ThenBy(z => z.LastLogin).Take(CountOfTechsToSetInactive).ToListAsync();
foreach (User u in HasLoginTechList)
{
u.Active = false;
_log.LogInformation($"User {u.Name} who last logged in {u.LastLogin} automatically set to inactive to free up license");
}
await ct.SaveChangesAsync();
}
}
// var HasLoginTechList = await ct.User.Where(z => z.Active == true && z.LastLogin != null && (
// z.UserType == UserType.Service ||
// z.UserType == UserType.ServiceContractor)).OrderByDescending(z => z.UserType).ThenBy(z => z.LastLogin).Take(CountOfTechsToSetInactive).ToListAsync();
// foreach (User u in HasLoginTechList)
// {
// u.Active = false;
// _log.LogInformation($"User {u.Name} who last logged in {u.LastLogin} automatically set to inactive to free up license");
// }
// await ct.SaveChangesAsync();
// }
// }
internal static void ResetSuperUserPassword()
{
@@ -810,26 +810,51 @@ namespace AyaNova.Biz
}
#else
//Check the PERPETUAL license allows this
//TECHS ONLY
if (proposedObj.IsTech && proposedObj.Active)
// //TECHS ONLY
// if (proposedObj.IsTech && proposedObj.Active)
// {
// //Yes, it might be affected depending on things
// long CurrentActiveTechCount = await UserBiz.ActiveTechUserCountAsync();
// long LicensedTechCount = AyaNova.Core.License.ActiveKey.ActiveTechsCount;
// if (isNew)
// {
// //This operation is about to consume one more license, check that we are not at the limit already
// await AddErrorIfAtLicenseLimitAnyBuildType(CurrentActiveTechCount, LicensedTechCount);
// }
// else
// {
// //did anything that might affect licensing change?
// if (!currentObj.IsTech || (!currentObj.Active))//currently not a tech or if it is it's not active
// {
// //going from non tech to tech and active
// //Yes, this is about to consume one more license, check that we are not at the limit already
// await AddErrorIfAtLicenseLimitAnyBuildType(CurrentActiveTechCount, LicensedTechCount);
// }
// }
// }
//INTERNAL USERS
//ACTIVE internal users consume a license
if (!proposedObj.IsOutsideCustomerContactTypeUser && proposedObj.Active)
{
//Yes, it might be affected depending on things
long CurrentActiveTechCount = await UserBiz.ActiveTechUserCountAsync();
long LicensedTechCount = AyaNova.Core.License.ActiveKey.ActiveTechsCount;
long CurrentActiveInternalUserCount = await UserBiz.ActiveInternalUserCountAsync();
long LicensedInternalUserCount = AyaNova.Core.License.ActiveKey.ActiveInternalUsersCount;
if (isNew)
{
//This operation is about to consume one more license, check that we are not at the limit already
await AddErrorIfAtLicenseLimitAnyBuildType(CurrentActiveTechCount, LicensedTechCount);
await AddErrorIfAtLicenseLimitAnyBuildType(CurrentActiveInternalUserCount, LicensedInternalUserCount);
}
else
{
//did anything that might affect licensing change?
if (!currentObj.IsTech || (!currentObj.Active))//currently not a tech or if it is it's not active
if (currentObj.IsOutsideCustomerContactTypeUser || (!currentObj.Active))//currently not a inside user or if it is it's not active
{
//going from non tech to tech and active
//going from non inside to inside and/or to active
//Yes, this is about to consume one more license, check that we are not at the limit already
await AddErrorIfAtLicenseLimitAnyBuildType(CurrentActiveTechCount, LicensedTechCount);
await AddErrorIfAtLicenseLimitAnyBuildType(CurrentActiveInternalUserCount, LicensedInternalUserCount);
}
}
}

View File

@@ -76,11 +76,18 @@ namespace AyaNova.Biz
}
#else
if (await UserBiz.ActiveTechUserCountAsync() > AyaNova.Core.License.ActiveKey.ActiveTechsCount)
// if (await UserBiz.ActiveTechUserCountAsync() > AyaNova.Core.License.ActiveKey.ActiveTechsCount)
// {
// //Warning: magic string, do not change this, triggers special login procedures to fix license issue
// AyaNova.Util.ServiceProviderProvider.ServerState.SetSystemLock(AyaNova.Core.License.SERVER_STATE_LOCKOUT_DUE_TO_LICENSE_EXCEEDED_CAPACITY_ERROR);
// log.LogCritical(AyaNova.Core.License.SERVER_STATE_LOCKOUT_DUE_TO_LICENSE_EXCEEDED_CAPACITY_ERROR);
// return;
// }
if (await AyaNova.Biz.UserBiz.ActiveInternalUserCountAsync() > AyaNova.Core.License.ActiveKey.ActiveInternalUsersCount)
{
//Warning: magic string, do not change this, triggers special login procedures to fix license issue
AyaNova.Util.ServiceProviderProvider.ServerState.SetSystemLock(AyaNova.Core.License.SERVER_STATE_LOCKOUT_DUE_TO_LICENSE_EXCEEDED_CAPACITY_ERROR);
log.LogCritical(AyaNova.Core.License.SERVER_STATE_LOCKOUT_DUE_TO_LICENSE_EXCEEDED_CAPACITY_ERROR);
var msg = $"{AyaNova.Core.License.SERVER_STATE_LOCKOUT_DUE_TO_LICENSE_EXCEEDED_CAPACITY_ERROR} (internal staff User count)";
AyaNova.Util.ServiceProviderProvider.ServerState.SetSystemLock(msg);
log.LogCritical(msg);
return;
}
#endif

View File

@@ -62,11 +62,6 @@ namespace AyaNova.Core
//REVOKED token
private const string REVOKED_TOKEN = "REVOKED";
//Scheduleable users
private const string SERVICE_TECHS_FEATURE_NAME = "ServiceTechs";
//ActiveInternalUsers subscription license
private const string ACTIVE_INTERNAL_USERS_FEATURE_NAME = "ActiveInternalUsers";
@@ -141,13 +136,13 @@ namespace AyaNova.Core
return null;
}
public long ActiveTechsCount
{
get
{
return GetLicenseFeature(SERVICE_TECHS_FEATURE_NAME)?.Count ?? 0;
}
}
// public long ActiveTechsCount
// {
// get
// {
// return GetLicenseFeature(SERVICE_TECHS_FEATURE_NAME)?.Count ?? 0;
// }
// }
public long ActiveInternalUsersCount
{
@@ -285,42 +280,6 @@ namespace AyaNova.Core
}
#endregion
#region sample v8 key
// private static string SAMPLE_KEY = @"[KEY
// {
// ""Key"": {
// ""LicenseFormat"": ""8"",
// ""Id"": ""34-1516288681"", <----Customer id followed by key serial id
// ""RegisteredTo"": ""Super TestCo"", or "REVOKED" if revoked
// ""DBID"": ""df558559-7f8a-4c7b-955c-959ebcdf71f3"",
// ""LicenseExpiration"": ""2019-01-18T07:18:01.2329138-08:00"", <--- UTC, DateTime if perpetual license 1/1/5555 indicates not expiring
// ""MaintenanceExpiration"": ""2019-01-18T07:18:01.2329138-08:00"", <-- UTC, DateTime support and updates subscription runs out, applies to all features
// ""Features"": {
// ""Feature"": [
// {
// ""Name"": ""Scheduleable users"",
// ""Count"":""10"",
// },
// {
// ""Name"": ""Accounting""
// },
// {
// ""Name"": ""TrialMode""<---means is a trial key
// },
// {
// ""Name"": ""Subscription"" <----Means it's an SAAS/Rental key
// }
// ]
// }
// }
// }
// KEY]
// [SIGNATURE
// HEcY3JCVwK9HFXEFnldUEPXP4Q7xoZfMZfOfx1cYmfVF3MVWePyZ9dqVZcY7pk3RmR1BbhQdhpljsYLl+ZLTRhNa54M0EFa/bQnBnbwYZ70EQl8fz8WOczYTEBo7Sm5EyC6gSHtYZu7yRwBvhQzpeMGth5uWnlfPb0dMm0DQM7PaqhdWWW9GCSOdZmFcxkFQ8ERLDZhVMbd8PJKyLvZ+sGMrmYTAIoL0tqa7nrxYkM71uJRTAmQ0gEl4bJdxiV825U1J+buNQuTZdacZKEPSjQQkYou10jRbReUmP2vDpvu+nA1xdJe4b5LlyQL+jiIXH17lf93xlCUb0UkDpu8iNQ==
// SIGNATURE]\";
#endregion
#region Exposed properties
@@ -459,7 +418,7 @@ namespace AyaNova.Core
#else
build = "Perpetual",
#endif
activeTechUserCount = AyaNova.Biz.UserBiz.ActiveTechUserCountAsync().Result,
// activeTechUserCount = AyaNova.Biz.UserBiz.ActiveTechUserCountAsync().Result,
activeCustomerContactUserCount = AyaNova.Biz.UserBiz.ActiveCustomerContactUserCountAsync().Result,
activeInternalUserCount = AyaNova.Biz.UserBiz.ActiveInternalUserCountAsync().Result
}
@@ -850,10 +809,17 @@ namespace AyaNova.Core
#else
if (await AyaNova.Biz.UserBiz.ActiveTechUserCountAsync() > _ActiveLicense.ActiveTechsCount)
// if (await AyaNova.Biz.UserBiz.ActiveTechUserCountAsync() > _ActiveLicense.ActiveTechsCount)
// {
// apiServerState.SetSystemLock(SERVER_STATE_LOCKOUT_DUE_TO_LICENSE_EXCEEDED_CAPACITY_ERROR);
// log.LogCritical(SERVER_STATE_LOCKOUT_DUE_TO_LICENSE_EXCEEDED_CAPACITY_ERROR);
// return;
//}
if (await AyaNova.Biz.UserBiz.ActiveInternalUserCountAsync() > _ActiveLicense.ActiveInternalUsersCount)
{
apiServerState.SetSystemLock(SERVER_STATE_LOCKOUT_DUE_TO_LICENSE_EXCEEDED_CAPACITY_ERROR);
log.LogCritical(SERVER_STATE_LOCKOUT_DUE_TO_LICENSE_EXCEEDED_CAPACITY_ERROR);
var msg = $"{SERVER_STATE_LOCKOUT_DUE_TO_LICENSE_EXCEEDED_CAPACITY_ERROR} (internal staff User count)";
apiServerState.SetSystemLock(msg);
log.LogCritical(msg);
return;
}
#endif
@@ -933,15 +899,29 @@ namespace AyaNova.Core
#else
//PERPETUAL, vet the TECHCOUNT - new license causes exceeding count?
long NewTechCount = ParsedNewKey.ActiveTechsCount;
if (await AyaNova.Biz.UserBiz.ActiveTechUserCountAsync() > NewTechCount)
{
//attempt to set enough of the eldest last login techs to inactive
await AyaNova.Biz.UserBiz.DeActivateExcessiveTechs(NewTechCount, log);
// long NewTechCount = ParsedNewKey.ActiveTechsCount;
// if (await AyaNova.Biz.UserBiz.ActiveTechUserCountAsync() > NewTechCount)
// {
// //attempt to set enough of the eldest last login techs to inactive
// await AyaNova.Biz.UserBiz.DeActivateExcessiveTechs(NewTechCount, log);
if (await AyaNova.Biz.UserBiz.ActiveTechUserCountAsync() > NewTechCount)
throw new ApplicationException("E1020 - Can't install key, too many active techs and / or subcontractors in database. Deactivate enough to install key.");
// if (await AyaNova.Biz.UserBiz.ActiveTechUserCountAsync() > NewTechCount)
// throw new ApplicationException("E1020 - Can't install key, too many active techs and / or subcontractors in database. Deactivate enough to install key.");
// }
long NewInsideUserLicensedCount = ParsedNewKey.ActiveInternalUsersCount;
long ExistingActiveInsideUserCount = await AyaNova.Biz.UserBiz.ActiveInternalUserCountAsync();
string err = "E1020 - Can't install license: ";
bool throwit = false;
if (ExistingActiveInsideUserCount > NewInsideUserLicensedCount)
{
throwit = true;
err += $"{ExistingActiveInsideUserCount} active internal users of {NewInsideUserLicensedCount} permitted";
}
if (throwit)
throw new ApplicationException(err);
#endif
}