using System; using System.Collections.Generic; using System.Linq; using System.Text; using Sarand.Net.Standard.Base.Core; using Sarand.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Utils; namespace Sarand.Net.Standard.Base.Security { //======================================================================= public abstract class SaSecurityItem : SacsHierarchyItem { //------------------------------------------------------------------- // Definitions. // Password Salt constants - must be kept up to date with legacy application. private static String PasswordSalt_MsSql = "MsSql"; //------------------------------------------------------------------- // Columns. public enum Column { Pos = 0, Id, ItemType, ItemType_Desc, Code, EmailAddress, Description, Location, LastLogin_Date, OwnerLinkCount, TargetLinkCount, TargetLinkCount_Groups, OwnerLinkCount_Rights, Notes, Status, Status_Desc, Status_Ind, CreateOn_Date, UpdateOn_Date, } //------------------------------------------------------------------- /// Properties other than common. public class DataProperties { public SaSecurity.ItemType ItemType { get; set; } = SaSecurity.ItemType.Unknown; public String Code { get; set; } = String.Empty; public String EmailAddress { get; set; } = String.Empty; public String Description { get; set; } = String.Empty; public String Location { get; set; } = String.Empty; public int DataItemType { get; set; } = -1; // Security. public String PasswordEncrypted { get; set; } = String.Empty; public String PasswordSalt { get; set; } = String.Empty; public String PasswordHash { get; set; } = String.Empty; public List PasswordHistory { get; } = new List(); public String PasswordToken { get; set; } = String.Empty; public DateTime? PasswordChangedAt { get; set; } = DateTime.MinValue; // Login. public DateTime? LastLogin { get; set; } = DateTime.MinValue; public DateTime? LastInvalidLoginAt { get; set; } = DateTime.MinValue; public short InvalidLoginCount { get; set; } } //------------------------------------------------------------------- // Data. public new class Data : SacsHierarchyItem.Data { // Data members. private readonly DataProperties data = new DataProperties(); // Construction. public Data() { Status = DefaultStatus; } // Properties. public SaSecurity.ItemType ItemType { get { return data.ItemType; } set { CheckChanged((int)data.ItemType, (int)value); data.ItemType = value; } } public String Code { get { return data.Code; } set { CheckChanged(data.Code, value); data.Code = value; } } public String EmailAddress { get { return data.EmailAddress; } set { CheckChanged(data.EmailAddress, value); data.EmailAddress = value; } } public String Description { get { return data.Description; } set { CheckChanged(data.Description, value); data.Description = value; } } public String Location { get { return data.Location; } set { CheckChanged(data.Location, value); data.Location = value; } } public int DataItemType { get { return data.DataItemType; } set { CheckChanged(data.DataItemType, value); data.DataItemType = value; } } // Security. public String PasswordEncrypted { get { return data.PasswordEncrypted; } set { CheckChanged(data.PasswordEncrypted, value); data.PasswordEncrypted = value; } } public String PasswordSalt { get { return data.PasswordSalt; } set { CheckChanged(data.PasswordSalt, value); data.PasswordSalt = value; } } public String PasswordHash { get { return data.PasswordHash; } set { CheckChanged(data.PasswordHash, value); data.PasswordHash = value; } } public List PasswordHistory { get { return data.PasswordHistory; } set { CheckChanged(data.PasswordHistory, value); data.PasswordHistory.Clear(); data.PasswordHistory.AddRange((value ?? new List()).FindAll(item => !StringUtil.IsEmpty(StringUtil.TrimAll(item)))); } } public String PasswordToken { get { return data.PasswordToken; } set { CheckChanged(data.PasswordToken, value); data.PasswordToken = value; } } public DateTime? PasswordChangedAt { get { return data.PasswordChangedAt; } set { CheckChangedNullable(data.PasswordChangedAt, value); data.PasswordChangedAt = value; } } // Login. public DateTime? LastLogin { get { return data.LastLogin; } set { CheckChangedNullable(data.LastLogin, value); data.LastLogin = value; } } public DateTime? LastInvalidLoginAt { get { return data.LastInvalidLoginAt; } set { CheckChangedNullable(data.LastInvalidLoginAt, value); data.LastInvalidLoginAt = value; } } public short InvalidLoginCount { get { return data.InvalidLoginCount; } set { CheckChanged(data.InvalidLoginCount, value); data.InvalidLoginCount = value; } } // Flags. public bool AuthenticatedUser { get { return GetFlag(Flag_AuthenticatedUser); } set { SetFlag(Flag_AuthenticatedUser, value); } } // Population. internal void PopulateFrom(Data data) { ItemType = data.ItemType; Code = data.Code; EmailAddress = data.EmailAddress; Description = data.Description; Location = data.Location; DataItemType = data.DataItemType; // Security. PasswordEncrypted = data.PasswordEncrypted; PasswordSalt = data.PasswordSalt; PasswordHash = data.PasswordHash; PasswordHistory = data.PasswordHistory; PasswordToken = data.PasswordToken; PasswordChangedAt = data.PasswordChangedAt; // Login. LastLogin = data.LastLogin; LastInvalidLoginAt = data.LastInvalidLoginAt; InvalidLoginCount = data.InvalidLoginCount; base.PopulateFrom(data); } public Data Clone() { Data data = new Data(); data.PopulateFrom(this); return data; } } //------------------------------------------------------------------- // Error field - identify properties with validation error. public new class ErrField : SacsHierarchyItem.ErrField { // Identifies the field that has invalid data. public ValidValue ItemType { get; set; } = ValidValue.Valid; public ValidValue Code { get; set; } = ValidValue.Valid; public ValidValue EmailAddress { get; set; } = ValidValue.Valid; public ValidValue Description { get; set; } = ValidValue.Valid; public ValidValue Location { get; set; } = ValidValue.Valid; // Security. public ValidValue Password { get; set; } = ValidValue.Valid; public ValidValue PasswordHistory { get; set; } = ValidValue.Valid; public ValidValue PasswordToken { get; set; } = ValidValue.Valid; public ValidValue PasswordChanged { get; set; } = ValidValue.Valid; // Login. public ValidValue LastLogin { get; set; } = ValidValue.Valid; public ValidValue LastInvalidLogin { get; set; } = ValidValue.Valid; public ValidValue InvalidLoginCount { get; set; } = ValidValue.Valid; } //------------------------------------------------------------------- // Data members. private readonly Data data = new Data(); // Flags. public static readonly Byte Flag_AuthenticatedUser = 0x01; //------------------------------------------------------------------- // Construction. public SaSecurityItem(SysId id, SaSecurity.ItemType securityItemType) : base(id) { base.SetData(data); ItemType = securityItemType; Status = DefaultStatus; } //------------------------------------------------------------------- // Status. private static readonly List securityItemStatuses = new List() { SaStatus.Enabled, SaStatus.Disabled, }; public static SaStatus DefaultStatus { get { return EnabledStatus; } } public static SaStatus EnabledStatus { get { return SaStatus.Enabled; } } public static SaStatus DisabledStatus { get { return SaStatus.Disabled; } } public static List ValidStatuses { get { return new List(securityItemStatuses); } } public static DataValue.TriStateType GetStatusTriState(SaStatus status) { switch (status) { case SaStatus.Enabled: return DataValue.TriStateType.Green; case SaStatus.Disabled: return DataValue.TriStateType.Red; case SaStatus.Deleted: return DataValue.TriStateType.Purple; } return DataValue.TriStateType.Unknown; } //------------------------------------------------------------------- // Data properties. public Data GetData() { return data.Clone(); } public void SetData(Data data) { // Properties that cannot be changed if previously specified. if (SysId.IsValid(Id) && SysId.ToInt(Id) != SysId.ToInt(data.Id)) throw new NotSupportedException($"{SysUtil.Fn} Id cannot be changed"); if ((int)data.ItemType > 0 && data.ItemType != ItemType) throw new NotSupportedException($"{SysUtil.Fn} Item Type cannot be changed"); // Populate individual properties to ensure that 'Changed' indicator is kept up to date. this.data.PopulateFrom(data); } //------------------------------------------------------------------- // Properties. public SaSecurity.ItemType ItemType { get { return data.ItemType; } protected set { data.ItemType = value; } } public String Code { get { return data.Code; } set { data.Code = value; } } public String EmailAddress { get { return data.EmailAddress; } set { data.EmailAddress = value; } } public String Description { get { return data.Description; } set { data.Description = value; } } public String Location { get { return data.Location; } set { data.Location = value; } } public int DataItemType { get { return data.DataItemType; } set { data.DataItemType = value; } } // Security. public String PasswordSalt { get { return data.PasswordSalt; } protected set { data.PasswordSalt = value; } } public String PasswordHash { get { return data.PasswordHash; } protected set { data.PasswordHash = value; } } [Obsolete("Avoid using the original C++ password encryption - switch to hashed password instead.")] public String PasswordEncrypted { get { return data.PasswordEncrypted; } protected set { data.PasswordEncrypted = value; } } public String PasswordToken { get { return data.PasswordToken; } set { data.PasswordToken = value; } } public List PasswordHistory { get { return data.PasswordHistory; } set { data.PasswordHistory = value; } } public DateTime? PasswordChangedAt { get { return data.PasswordChangedAt; } set { data.PasswordChangedAt = value; } } // Login. public DateTime? LastLogin { get { return data.LastLogin; } set { data.LastLogin = value; } } public DateTime? LastInvalidLoginAt { get { return data.LastInvalidLoginAt; } set { data.LastInvalidLoginAt = value; } } public short InvalidLoginCount { get { return data.InvalidLoginCount; } set { data.InvalidLoginCount = value; } } //------------------------------------------------------------------- // Flags. public bool AuthenticatedUser { get { return GetFlag(Flag_AuthenticatedUser); } set { SetFlag(Flag_AuthenticatedUser, value); } } //------------------------------------------------------------------- // Password utilities. /// Extract password storage method from data. public SaSecurity.PasswordStorageMethod GetPasswordMethod() { if (PasswordSalt == PasswordSalt_MsSql && !StringUtil.IsBlank(PasswordHash)) return SaSecurity.PasswordStorageMethod.MsSql; return SaSecurity.PasswordStorageMethod.Legacy; } public void SetEncodedPassword(SaSecurity.PasswordStorageMethod method, String encodedPassword) { if (method == SaSecurity.PasswordStorageMethod.MsSql) { #pragma warning disable CS0618 // Deprecated property PasswordEncrypted = ""; #pragma warning restore CS0618 // Deprecated property PasswordSalt = PasswordSalt_MsSql; PasswordHash = encodedPassword; } else { #pragma warning disable CS0618 // Deprecated property PasswordEncrypted = encodedPassword; #pragma warning restore CS0618 // Deprecated property PasswordSalt = ""; PasswordHash = ""; } } //------------------------------------------------------------------- // List parameters. public enum StatusCat { Any, EnabledOnly, DisabledOnly, } /// /// Item list parameters. /// public new class Params : SacsHierarchyItem.Params { public HashSet IdsIncluded { get; } = new HashSet(); public HashSet IdsExcluded { get; } = new HashSet(); public SaSecurity.ItemType SecurityItemType { get; set; } = SaSecurity.ItemType.Any; public String Code { get; set; } public String CodePartial { get; set; } public String EmailAddress { get; set; } public String EmailAddressPartial { get; set; } public String Description { get; set; } public String DescriptionPartial { get; set; } public HashSet GroupIdsIncluded { get; } = new HashSet(); public StatusCat StatusCat { get; set; } = StatusCat.EnabledOnly; } //------------------------------------------------------------------- } //======================================================================= public static class SaSecurityItemList { public static SaSecurityItem GetSecItemById(this List secItems, SysId secItemId) { if (!SysId.IsValid(secItemId)) return null; foreach (SaSecurityItem secItem in secItems) { if (secItem.Id.Equals(secItemId)) return secItem; } return null; } } //======================================================================= }