This commit is contained in:
@@ -75,6 +75,7 @@ TODO CLIENT STUFF
|
|||||||
TODO SERVER STUFF
|
TODO SERVER STUFF
|
||||||
|
|
||||||
- Add license violation check - valid number of techs for license in multiple places
|
- Add license violation check - valid number of techs for license in multiple places
|
||||||
|
- do a big seed and see if it fails
|
||||||
|
|
||||||
- Trial license ROCKFISH up it to 1000 techs to cover huge seeding.
|
- Trial license ROCKFISH up it to 1000 techs to cover huge seeding.
|
||||||
|
|
||||||
|
|||||||
@@ -419,7 +419,7 @@ namespace AyaNova
|
|||||||
if (TESTING_REFRESH_DB)
|
if (TESTING_REFRESH_DB)
|
||||||
{
|
{
|
||||||
AyaNova.Core.License.Fetch(apiServerState, dbContext, _log);
|
AyaNova.Core.License.Fetch(apiServerState, dbContext, _log);
|
||||||
Util.Seeder.SeedDatabase(Util.Seeder.SeedLevel.SmallOneManShopTrialDataSet, -8);//#############################################################################################
|
Util.Seeder.SeedDatabase(Util.Seeder.SeedLevel.LargeCorporateMultiRegionalTrialDataSet, -8);//#############################################################################################
|
||||||
}
|
}
|
||||||
//TESTING
|
//TESTING
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ namespace AyaNova.Biz
|
|||||||
/// Process all jobs (stock jobs and those found in operations table)
|
/// Process all jobs (stock jobs and those found in operations table)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
internal static async Task ProcessJobsAsync(AyContext ct)
|
internal static async Task ProcessJobsAsync(AyContext ct, AyaNova.Api.ControllerHelpers.ApiServerState serverState)
|
||||||
{
|
{
|
||||||
//Flush metrics report before anything else happens
|
//Flush metrics report before anything else happens
|
||||||
log.LogTrace("Flushing metrics to reporters");
|
log.LogTrace("Flushing metrics to reporters");
|
||||||
@@ -344,7 +344,18 @@ namespace AyaNova.Biz
|
|||||||
//Health check / metrics
|
//Health check / metrics
|
||||||
await CoreJobMetricsSnapshot.DoJobAsync(ct);
|
await CoreJobMetricsSnapshot.DoJobAsync(ct);
|
||||||
|
|
||||||
//License check??
|
//License check
|
||||||
|
long CurrentActiveCount = UserBiz.ActiveCount;
|
||||||
|
long LicensedUserCount = AyaNova.Core.License.ActiveKey.ActiveNumber;
|
||||||
|
// log.LogInformation("JobsBiz::Checking license active count");
|
||||||
|
if (CurrentActiveCount > LicensedUserCount)
|
||||||
|
{
|
||||||
|
var msg = $"E1020 - Active count exceeded capacity";
|
||||||
|
serverState.SetSystemLock(msg);
|
||||||
|
log.LogCritical(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//Notifications
|
//Notifications
|
||||||
|
|
||||||
|
|||||||
@@ -30,9 +30,14 @@ namespace AyaNova.Biz
|
|||||||
SeedOrImportRelaxedRulesMode = false;//default
|
SeedOrImportRelaxedRulesMode = false;//default
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo:
|
//This is where active tech license consumers are accounted for
|
||||||
//then after that go into widget and anywhere else that there is this associated type code being called for event and search and implement everywhere,
|
internal static long ActiveCount
|
||||||
//then update seeder code to use it and get back on to the main critical path again in the todo
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ServiceProviderProvider.DBContext.User.Where(x => x.Active == true && (x.UserType == UserType.Schedulable || x.UserType == UserType.Subcontractor)).LongCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal static UserBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext)
|
internal static UserBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext)
|
||||||
{
|
{
|
||||||
@@ -53,35 +58,34 @@ namespace AyaNova.Biz
|
|||||||
inObj.Salt = Hasher.GenerateSalt();
|
inObj.Salt = Hasher.GenerateSalt();
|
||||||
inObj.Password = Hasher.hash(inObj.Salt, inObj.Password);
|
inObj.Password = Hasher.hash(inObj.Salt, inObj.Password);
|
||||||
|
|
||||||
|
inObj.OwnerId = UserId;
|
||||||
|
inObj.Tags = TagUtil.NormalizeTags(inObj.Tags);
|
||||||
|
//Seeder sets user options in advance so no need to create them here in that case
|
||||||
|
if (inObj.UserOptions == null)
|
||||||
|
inObj.UserOptions = new UserOptions(UserId);
|
||||||
|
|
||||||
|
|
||||||
Validate(inObj, null);
|
Validate(inObj, null);
|
||||||
if (HasErrors)
|
if (HasErrors)
|
||||||
return null;
|
return null;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//do stuff with User
|
|
||||||
User outObj = inObj;
|
|
||||||
outObj.OwnerId = UserId;
|
|
||||||
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
|
||||||
//Seeder sets user options in advance so no need to create them here in that case
|
|
||||||
if (outObj.UserOptions == null)
|
|
||||||
outObj.UserOptions = new UserOptions(UserId);
|
|
||||||
|
|
||||||
await ct.User.AddAsync(outObj);
|
await ct.User.AddAsync(inObj);
|
||||||
//save to get Id
|
//save to get Id
|
||||||
await ct.SaveChangesAsync();
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
//Handle child and associated items
|
//Handle child and associated items
|
||||||
|
|
||||||
//Log event
|
//Log event
|
||||||
EventLogProcessor.LogEventToDatabase(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), ct);
|
EventLogProcessor.LogEventToDatabase(new Event(UserId, inObj.Id, BizType, AyaEvent.Created), ct);
|
||||||
|
|
||||||
//SEARCH INDEXING
|
//SEARCH INDEXING
|
||||||
//Search.ProcessNewObjectKeywords( UserLocaleId, outObj.Id, BizType, outObj.Name, outObj.EmployeeNumber, outObj.Notes, outObj.Name);
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserLocaleId, inObj.Id, BizType, inObj.Name);
|
||||||
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserLocaleId, outObj.Id, BizType, outObj.Name);
|
SearchParams.AddWord(inObj.Notes).AddWord(inObj.Name).AddWord(inObj.EmployeeNumber).AddWord(inObj.Tags);
|
||||||
SearchParams.AddWord(outObj.Notes).AddWord(outObj.Name).AddWord(outObj.EmployeeNumber).AddWord(outObj.Tags);
|
|
||||||
Search.ProcessNewObjectKeywords(SearchParams);
|
Search.ProcessNewObjectKeywords(SearchParams);
|
||||||
|
|
||||||
return outObj;
|
return inObj;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,36 +97,34 @@ namespace AyaNova.Biz
|
|||||||
//This is a new user so it will have been posted with a password in plaintext which needs to be salted and hashed
|
//This is a new user so it will have been posted with a password in plaintext which needs to be salted and hashed
|
||||||
inObj.Salt = Hasher.GenerateSalt();
|
inObj.Salt = Hasher.GenerateSalt();
|
||||||
inObj.Password = Hasher.hash(inObj.Salt, inObj.Password);
|
inObj.Password = Hasher.hash(inObj.Salt, inObj.Password);
|
||||||
|
inObj.OwnerId = UserId;
|
||||||
|
inObj.Tags = TagUtil.NormalizeTags(inObj.Tags);
|
||||||
|
//Seeder sets user options in advance so no need to create them here in that case
|
||||||
|
if (inObj.UserOptions == null)
|
||||||
|
inObj.UserOptions = new UserOptions(UserId);
|
||||||
|
|
||||||
Validate(inObj, null);
|
Validate(inObj, null);
|
||||||
if (HasErrors)
|
if (HasErrors)
|
||||||
return null;
|
return null;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//do stuff with User
|
|
||||||
User outObj = inObj;
|
|
||||||
outObj.OwnerId = UserId;
|
|
||||||
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
|
||||||
//Seeder sets user options in advance so no need to create them here in that case
|
|
||||||
if (outObj.UserOptions == null)
|
|
||||||
outObj.UserOptions = new UserOptions(UserId);
|
|
||||||
|
|
||||||
TempContext.User.Add(outObj);
|
TempContext.User.Add(inObj);
|
||||||
|
|
||||||
//save to get Id
|
//save to get Id
|
||||||
TempContext.SaveChanges();
|
TempContext.SaveChanges();
|
||||||
|
|
||||||
//Handle child and associated items
|
//Handle child and associated items
|
||||||
|
|
||||||
//Log event
|
//Log event
|
||||||
EventLogProcessor.LogEventToDatabase(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), TempContext);
|
EventLogProcessor.LogEventToDatabase(new Event(UserId, inObj.Id, BizType, AyaEvent.Created), TempContext);
|
||||||
|
|
||||||
//SEARCH INDEXING
|
//SEARCH INDEXING
|
||||||
// Search.ProcessNewObjectKeywords(UserLocaleId, outObj.Id, BizType, outObj.Name, outObj.EmployeeNumber, outObj.Notes, outObj.Name);
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserLocaleId, inObj.Id, BizType, inObj.Name);
|
||||||
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserLocaleId, outObj.Id, BizType, outObj.Name);
|
SearchParams.AddWord(inObj.Notes).AddWord(inObj.Name).AddWord(inObj.EmployeeNumber).AddWord(inObj.Tags);
|
||||||
SearchParams.AddWord(outObj.Notes).AddWord(outObj.Name).AddWord(outObj.EmployeeNumber).AddWord(outObj.Tags);
|
|
||||||
Search.ProcessNewObjectKeywords(SearchParams);
|
Search.ProcessNewObjectKeywords(SearchParams);
|
||||||
|
|
||||||
return outObj;
|
return inObj;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -229,51 +231,6 @@ namespace AyaNova.Biz
|
|||||||
//get picklist (paged)
|
//get picklist (paged)
|
||||||
internal ApiPagedResponse<NameIdItem> GetPickList(IUrlHelper Url, string routeName, PagingOptions pagingOptions)
|
internal ApiPagedResponse<NameIdItem> GetPickList(IUrlHelper Url, string routeName, PagingOptions pagingOptions)
|
||||||
{
|
{
|
||||||
// pagingOptions.Offset = pagingOptions.Offset ?? PagingOptions.DefaultOffset;
|
|
||||||
// pagingOptions.Limit = pagingOptions.Limit ?? PagingOptions.DefaultLimit;
|
|
||||||
|
|
||||||
// NameIdItem[] items;
|
|
||||||
// int totalRecordCount = 0;
|
|
||||||
|
|
||||||
// if (!string.IsNullOrWhiteSpace(q))
|
|
||||||
// {
|
|
||||||
// items = await ct.User
|
|
||||||
// .AsNoTracking()
|
|
||||||
// .Where(m => EF.Functions.ILike(m.Name, q))
|
|
||||||
// .OrderBy(m => m.Name)
|
|
||||||
// .Skip(pagingOptions.Offset.Value)
|
|
||||||
// .Take(pagingOptions.Limit.Value)
|
|
||||||
// .Select(m => new NameIdItem()
|
|
||||||
// {
|
|
||||||
// Id = m.Id,
|
|
||||||
// Name = m.Name
|
|
||||||
// }).ToArrayAsync();
|
|
||||||
|
|
||||||
// totalRecordCount = await ct.User.Where(m => EF.Functions.ILike(m.Name, q)).CountAsync();
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// items = await ct.User
|
|
||||||
// .AsNoTracking()
|
|
||||||
// .OrderBy(m => m.Name)
|
|
||||||
// .Skip(pagingOptions.Offset.Value)
|
|
||||||
// .Take(pagingOptions.Limit.Value)
|
|
||||||
// .Select(m => new NameIdItem()
|
|
||||||
// {
|
|
||||||
// Id = m.Id,
|
|
||||||
// Name = m.Name
|
|
||||||
// }).ToArrayAsync();
|
|
||||||
|
|
||||||
// totalRecordCount = await ct.User.CountAsync();
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// var pageLinks = new PaginationLinkBuilder(Url, routeName, null, pagingOptions, totalRecordCount).PagingLinksObject();
|
|
||||||
|
|
||||||
// ApiPagedResponse<NameIdItem> pr = new ApiPagedResponse<NameIdItem>(items, pageLinks);
|
|
||||||
// return pr;
|
|
||||||
|
|
||||||
pagingOptions.Offset = pagingOptions.Offset ?? PagingOptions.DefaultOffset;
|
pagingOptions.Offset = pagingOptions.Offset ?? PagingOptions.DefaultOffset;
|
||||||
pagingOptions.Limit = pagingOptions.Limit ?? PagingOptions.DefaultLimit;
|
pagingOptions.Limit = pagingOptions.Limit ?? PagingOptions.DefaultLimit;
|
||||||
|
|
||||||
@@ -287,8 +244,6 @@ namespace AyaNova.Biz
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
//UPDATE
|
//UPDATE
|
||||||
//
|
//
|
||||||
@@ -301,24 +256,24 @@ namespace AyaNova.Biz
|
|||||||
inObj.OwnerId = dbObj.OwnerId;
|
inObj.OwnerId = dbObj.OwnerId;
|
||||||
|
|
||||||
//Get a snapshot of the original db value object before changes
|
//Get a snapshot of the original db value object before changes
|
||||||
User SnapshotObj = new User();
|
User SnapshotOfOriginalDBObj = new User();
|
||||||
CopyObject.Copy(dbObj, SnapshotObj);
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
//Update the db object with the PUT object values
|
//Update the db object with the PUT object values
|
||||||
CopyObject.Copy(inObj, dbObj, "Id, Salt");
|
CopyObject.Copy(inObj, dbObj, "Id, Salt");
|
||||||
dbObj.Tags = TagUtil.NormalizeTags(dbObj.Tags);
|
dbObj.Tags = TagUtil.NormalizeTags(dbObj.Tags);
|
||||||
|
|
||||||
//Is the user updating the password?
|
//Is the user updating the password?
|
||||||
if (!string.IsNullOrWhiteSpace(inObj.Password) && SnapshotObj.Password != inObj.Password)
|
if (!string.IsNullOrWhiteSpace(inObj.Password) && SnapshotOfOriginalDBObj.Password != inObj.Password)
|
||||||
{
|
{
|
||||||
//YES password is being updated:
|
//YES password is being updated:
|
||||||
dbObj.Password = Hasher.hash(SnapshotObj.Salt, inObj.Password);
|
dbObj.Password = Hasher.hash(SnapshotOfOriginalDBObj.Salt, inObj.Password);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//No, use the snapshot password value
|
//No, use the snapshot password value
|
||||||
dbObj.Password = SnapshotObj.Password;
|
dbObj.Password = SnapshotOfOriginalDBObj.Password;
|
||||||
dbObj.Salt = SnapshotObj.Salt;
|
dbObj.Salt = SnapshotOfOriginalDBObj.Salt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -326,7 +281,7 @@ namespace AyaNova.Biz
|
|||||||
//this will allow EF to check it out
|
//this will allow EF to check it out
|
||||||
ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = inObj.ConcurrencyToken;
|
ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = inObj.ConcurrencyToken;
|
||||||
|
|
||||||
Validate(dbObj, SnapshotObj);
|
Validate(dbObj, SnapshotOfOriginalDBObj);
|
||||||
if (HasErrors)
|
if (HasErrors)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -349,22 +304,22 @@ namespace AyaNova.Biz
|
|||||||
if (!ValidateJsonPatch<User>.Validate(this, objectPatch)) return false;
|
if (!ValidateJsonPatch<User>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
//make a snapshot of the original for validation but update the original to preserve workflow
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
User snapshotObj = new User();
|
User SnapshotOfOriginalDBObj = new User();
|
||||||
CopyObject.Copy(dbObj, snapshotObj);
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
//Do the patching
|
//Do the patching
|
||||||
objectPatch.ApplyTo(dbObj);
|
objectPatch.ApplyTo(dbObj);
|
||||||
dbObj.Tags = TagUtil.NormalizeTags(dbObj.Tags);
|
dbObj.Tags = TagUtil.NormalizeTags(dbObj.Tags);
|
||||||
|
|
||||||
//Is the user patching the password?
|
//Is the user patching the password?
|
||||||
if (!string.IsNullOrWhiteSpace(dbObj.Password) && dbObj.Password != snapshotObj.Password)
|
if (!string.IsNullOrWhiteSpace(dbObj.Password) && dbObj.Password != SnapshotOfOriginalDBObj.Password)
|
||||||
{
|
{
|
||||||
//YES password is being updated:
|
//YES password is being updated:
|
||||||
dbObj.Password = Hasher.hash(dbObj.Salt, dbObj.Password);
|
dbObj.Password = Hasher.hash(dbObj.Salt, dbObj.Password);
|
||||||
}
|
}
|
||||||
|
|
||||||
ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = concurrencyToken;
|
ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = concurrencyToken;
|
||||||
Validate(dbObj, snapshotObj);
|
Validate(dbObj, SnapshotOfOriginalDBObj);
|
||||||
if (HasErrors)
|
if (HasErrors)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -424,11 +379,36 @@ namespace AyaNova.Biz
|
|||||||
bool isNew = currentObj == null;
|
bool isNew = currentObj == null;
|
||||||
|
|
||||||
|
|
||||||
if (isNew) //Yes, no currentObj
|
|
||||||
{
|
|
||||||
|
|
||||||
|
//do we need to check the license situation?
|
||||||
|
if (proposedObj.IsTech && proposedObj.Active)
|
||||||
|
{
|
||||||
|
//Yes, it might be affected depending on things
|
||||||
|
long CurrentActiveCount = UserBiz.ActiveCount;
|
||||||
|
long LicensedUserCount = AyaNova.Core.License.ActiveKey.ActiveNumber;
|
||||||
|
|
||||||
|
if (isNew)
|
||||||
|
{
|
||||||
|
//This operation is about to consume one more license, check that we are not at the limit already
|
||||||
|
CheckActiveForValidation(CurrentActiveCount, LicensedUserCount);
|
||||||
|
}
|
||||||
|
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
|
||||||
|
CheckActiveForValidation(CurrentActiveCount, LicensedUserCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: check user count if not new to see if affected that way
|
||||||
|
//also check user count in general to see if it's exceeded
|
||||||
|
//And maybe check it in login as well as a good central spot or wherever makes sense
|
||||||
|
|
||||||
//OwnerId required
|
//OwnerId required
|
||||||
if (!isNew)
|
if (!isNew)
|
||||||
{
|
{
|
||||||
@@ -543,6 +523,14 @@ namespace AyaNova.Biz
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CheckActiveForValidation(long CurrentActiveCount, long LicensedUserCount)
|
||||||
|
{
|
||||||
|
if (CurrentActiveCount >= LicensedUserCount)
|
||||||
|
{
|
||||||
|
AddError(ValidationErrorType.InvalidOperation, null, "LT:ErrorSecurityUserCapacity");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//Can delete?
|
//Can delete?
|
||||||
private void ValidateCanDelete(User inObj)
|
private void ValidateCanDelete(User inObj)
|
||||||
@@ -560,7 +548,7 @@ namespace AyaNova.Biz
|
|||||||
//There's only one rule - have they done anything eventlog worthy yet?
|
//There's only one rule - have they done anything eventlog worthy yet?
|
||||||
if (ct.Event.Select(m => m).Where(m => m.OwnerId == inObj.Id).Count() > 0)
|
if (ct.Event.Select(m => m).Where(m => m.OwnerId == inObj.Id).Count() > 0)
|
||||||
{
|
{
|
||||||
AddError(ValidationErrorType.InvalidOperation, "user", "[E_ACTIVE_NOT_DELETABLE] This user shows activity in the database and can not be deleted. Set inactive instead.");
|
AddError(ValidationErrorType.InvalidOperation, "user", "LT:ErrorDBForeignKeyViolation");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -332,6 +332,8 @@ namespace AyaNova.Biz
|
|||||||
//run validation and biz rules
|
//run validation and biz rules
|
||||||
if (isNew)
|
if (isNew)
|
||||||
{
|
{
|
||||||
|
//WARNING: this is not really the "current" object, it's been modified already by caller
|
||||||
|
|
||||||
// //NEW widgets must be active
|
// //NEW widgets must be active
|
||||||
// if (inObj.Active == null || ((bool)inObj.Active) == false)
|
// if (inObj.Active == null || ((bool)inObj.Active) == false)
|
||||||
// {
|
// {
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ namespace AyaNova.Generator
|
|||||||
//=================================================================
|
//=================================================================
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await JobsBiz.ProcessJobsAsync(ct);
|
await JobsBiz.ProcessJobsAsync(ct, serverState);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -46,7 +46,15 @@ namespace AyaNova.Models
|
|||||||
|
|
||||||
public User()
|
public User()
|
||||||
{
|
{
|
||||||
Tags = new List<string>();
|
Tags = new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsTech
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.UserType == UserType.Subcontractor || this.UserType == UserType.Schedulable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,13 @@ namespace AyaNova.Core
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long ActiveNumber
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return GetLicenseFeature(SERVICE_TECHS_FEATURE_NAME).Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check for the existance of license feature
|
/// Check for the existance of license feature
|
||||||
@@ -446,7 +453,7 @@ namespace AyaNova.Core
|
|||||||
|
|
||||||
if (ldb.Key == "none")
|
if (ldb.Key == "none")
|
||||||
{
|
{
|
||||||
var msg = "License key not found in database, running in unlicensed mode";
|
var msg = "E1020 - License key not found in database, running in unlicensed mode";
|
||||||
apiServerState.SetSystemLock(msg);
|
apiServerState.SetSystemLock(msg);
|
||||||
log.LogWarning(msg);
|
log.LogWarning(msg);
|
||||||
return;
|
return;
|
||||||
@@ -456,9 +463,9 @@ namespace AyaNova.Core
|
|||||||
AyaNovaLicenseKey k = Parse(ldb.Key, log);
|
AyaNovaLicenseKey k = Parse(ldb.Key, log);
|
||||||
if (k == null)
|
if (k == null)
|
||||||
{
|
{
|
||||||
var msg = "Error: License key in database is not valid, running in unlicensed mode";
|
var msg = "E1020 - License key in database is not valid, running in unlicensed mode";
|
||||||
apiServerState.SetSystemLock(msg);
|
apiServerState.SetSystemLock(msg);
|
||||||
log.LogError(msg);
|
log.LogCritical(msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,9 +473,18 @@ namespace AyaNova.Core
|
|||||||
|
|
||||||
if (_ActiveLicense.LicenseExpired)
|
if (_ActiveLicense.LicenseExpired)
|
||||||
{
|
{
|
||||||
var msg = $"License key expired {DateUtil.ServerDateTimeString(_ActiveLicense.LicenseExpiration)}";
|
var msg = $"E1020 - License key expired {DateUtil.ServerDateTimeString(_ActiveLicense.LicenseExpiration)}";
|
||||||
apiServerState.SetSystemLock(msg);
|
apiServerState.SetSystemLock(msg);
|
||||||
log.LogWarning(msg);
|
log.LogCritical(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Has someone been trying funny business with the active techs in the db?
|
||||||
|
if (AyaNova.Biz.UserBiz.ActiveCount > _ActiveLicense.ActiveNumber)
|
||||||
|
{
|
||||||
|
var msg = $"E1020 - Active count exceeded capacity";
|
||||||
|
apiServerState.SetSystemLock(msg);
|
||||||
|
log.LogCritical(msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -484,7 +500,8 @@ namespace AyaNova.Core
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
var msg = "E1020 - Error initializing license key";
|
var msg = "E1020 - Error initializing license key";
|
||||||
log.LogError(ex, msg);
|
log.LogCritical(ex, msg);
|
||||||
|
apiServerState.SetSystemLock(msg);
|
||||||
throw new ApplicationException(msg, ex);
|
throw new ApplicationException(msg, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -503,7 +520,7 @@ namespace AyaNova.Core
|
|||||||
|
|
||||||
if (ParsedNewKey == null)
|
if (ParsedNewKey == null)
|
||||||
{
|
{
|
||||||
throw new ApplicationException("License.Install -> key could not be parsed");
|
throw new ApplicationException("E1020 - License.Install -> key could not be parsed");
|
||||||
}
|
}
|
||||||
|
|
||||||
//Can't install a trial into a non-empty db
|
//Can't install a trial into a non-empty db
|
||||||
@@ -512,6 +529,12 @@ namespace AyaNova.Core
|
|||||||
throw new ApplicationException("E1020 - Can't install a trial key into a non empty AyaNova database. Erase the database first.");
|
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?
|
||||||
|
if (AyaNova.Biz.UserBiz.ActiveCount > ParsedNewKey.GetLicenseFeature(SERVICE_TECHS_FEATURE_NAME).Count)
|
||||||
|
{
|
||||||
|
throw new ApplicationException("E1020 - Can't install key, too many active techs and / or subcontractors in database. Deactivate enough to install key.");
|
||||||
|
}
|
||||||
|
|
||||||
//Update current license
|
//Update current license
|
||||||
CurrentInDbKeyRecord.Key = RawTextNewKey;
|
CurrentInDbKeyRecord.Key = RawTextNewKey;
|
||||||
//LOOKAT: reason, resultcode etc
|
//LOOKAT: reason, resultcode etc
|
||||||
@@ -547,7 +570,7 @@ namespace AyaNova.Core
|
|||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(k))
|
if (string.IsNullOrWhiteSpace(k))
|
||||||
{
|
{
|
||||||
throw new ApplicationException("License.Parse -> License key is empty and can't be validated");
|
throw new ApplicationException("E1020 - License.Parse -> License key is empty and can't be validated");
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -557,7 +580,7 @@ namespace AyaNova.Core
|
|||||||
!k.Contains("[SIGNATURE") ||
|
!k.Contains("[SIGNATURE") ||
|
||||||
!k.Contains("SIGNATURE]"))
|
!k.Contains("SIGNATURE]"))
|
||||||
{
|
{
|
||||||
throw new ApplicationException("License.Parse -> License key is missing required delimiters");
|
throw new ApplicationException("E1020 - License.Parse -> License key is missing required delimiters");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,7 +611,7 @@ EQIDAQAB
|
|||||||
signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
|
signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
|
||||||
if (!signer.VerifySignature(expectedSig))
|
if (!signer.VerifySignature(expectedSig))
|
||||||
{
|
{
|
||||||
throw new ApplicationException("License.Parse -> License key failed integrity check and is not valid");
|
throw new ApplicationException("E1020 - License.Parse -> License key failed integrity check and is not valid");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion check signature
|
#endregion check signature
|
||||||
@@ -598,7 +621,7 @@ EQIDAQAB
|
|||||||
|
|
||||||
key.LicenseFormat = (string)token.SelectToken("Key.LicenseFormat");
|
key.LicenseFormat = (string)token.SelectToken("Key.LicenseFormat");
|
||||||
if (key.LicenseFormat != "2018")
|
if (key.LicenseFormat != "2018")
|
||||||
throw new ApplicationException($"License.Parse -> License key format {key.LicenseFormat} not recognized");
|
throw new ApplicationException($"E1020 - License.Parse -> License key format {key.LicenseFormat} not recognized");
|
||||||
key.Id = (string)token.SelectToken("Key.Id");
|
key.Id = (string)token.SelectToken("Key.Id");
|
||||||
key.RegisteredTo = (string)token.SelectToken("Key.RegisteredTo");
|
key.RegisteredTo = (string)token.SelectToken("Key.RegisteredTo");
|
||||||
key.DbId = (Guid)token.SelectToken("Key.DBID");
|
key.DbId = (Guid)token.SelectToken("Key.DBID");
|
||||||
|
|||||||
Reference in New Issue
Block a user