diff --git a/SaBusMgrCoreWeb/Web/Controllers/SbmController_Web.cs b/SaBusMgrCoreWeb/Web/Controllers/SbmController_Web.cs index 6e1e57a..16f9a7d 100644 --- a/SaBusMgrCoreWeb/Web/Controllers/SbmController_Web.cs +++ b/SaBusMgrCoreWeb/Web/Controllers/SbmController_Web.cs @@ -95,7 +95,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Controllers // Some actions (eg. score password) don't need to. if (StoreSessionData) { - String sessionDataJson = JsonConvert.SerializeObject(SessionData); + String sessionDataJson = JsonConvert.SerializeObject(SessionData, SbmController_Web.SessionDataJsonSerializerSettings); HttpContext.Session.Set(SessionStorage.SessionKey_SessionData, SessionStorage.sessionDataEncoding.GetBytes(sessionDataJson)); } } @@ -189,15 +189,21 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Controllers return base.CreateProperties(type, MemberSerialization.Fields); } }; + public static JsonSerializerSettings JsonSerialisationSettings = new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore + }; public static JsonSerializerSettings JsonDeserialisationSettings = new JsonSerializerSettings { ContractResolver = new SbmSessionDataContractResolver(), // Force all deserialised objects to be replaced - to avoid proxy lists not populating. - ObjectCreationHandling = ObjectCreationHandling.Replace + ObjectCreationHandling = ObjectCreationHandling.Replace, + NullValueHandling = NullValueHandling.Ignore }; } private readonly SessionStorage sessionStorage = new SessionStorage(); - public static JsonSerializerSettings SbmSessionDataJsonSerializerSettings { get { return SessionStorage.JsonDeserialisationSettings; } } + public static JsonSerializerSettings SessionDataJsonSerializerSettings { get { return SessionStorage.JsonSerialisationSettings; } } + public static JsonSerializerSettings SessionDataJsonDeserializerSettings { get { return SessionStorage.JsonDeserialisationSettings; } } //------------------------------------------------------------------- public SbmSessionData_Web SessionData diff --git a/SaBusMgrStandardBase/Base/App/Sbm.cs b/SaBusMgrStandardBase/Base/App/Sbm.cs index 0a70899..81fdaec 100644 --- a/SaBusMgrStandardBase/Base/App/Sbm.cs +++ b/SaBusMgrStandardBase/Base/App/Sbm.cs @@ -667,24 +667,24 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App /// public enum SecurityRight { - // Other. + /* Admin. */ Security_Admin, // Security administration. Database_Admin, // Database administration. ReferenceData_Admin, // Reference data administration - InterfaceConfig, // Interface configuration - // Contacts. + InterfaceConfig, // Interface configuration + /* Contacts. */ Affiliate_Create, // Create new contacts (companies and individuals). Affiliate_Update, // Update contacts. - // Associations. + /* Associations. */ Association_Create, // Create new Associations (customers and suppliers etc). Association_Update, // Update Associations. - // Commodity. + /* Commodity. */ Commodity_Create, // Create new products. Commodity_Update, // Update products. - StockTake, // Update stock take information. + StockTake, // Update stock take information. Asset_Create, // Create new assets. Asset_Update, // Update assets. - // Transaction. + /* Transaction. */ Transaction_Create, // Create new Transactions (quotes, orders etc). Transaction_View, // View Transactions. Transaction_Update, // Update Transactions. @@ -692,14 +692,14 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App Transaction_Remove, // Remove Transactions. Transaction_ByType, // View Transactions by type. Transaction_UpdateByTypeStatus, // Update Transactions by type and status. - InterfaceByCategory, // Interface configuration - // Financal. + InterfaceByCategory, // Interface configuration + /* Financal. */ Financial_Update, // View/update financial information. Financial_View, // View financial information. Financial_ViewByType, // View Financial information by type. Financial_UpdateByType, // Update Financial information by type. - TimesheetManager, // View other users' timesheets. - TaskManager, // Manage tasks. + TimesheetManager, // View other users' timesheets. + TaskManager, // Manage tasks. /* // Admin. Security_Manage, diff --git a/SaBusMgrStandardBase/Base/App/SbmAppConfig.cs b/SaBusMgrStandardBase/Base/App/SbmAppConfig.cs index da8dccc..cbabf03 100644 --- a/SaBusMgrStandardBase/Base/App/SbmAppConfig.cs +++ b/SaBusMgrStandardBase/Base/App/SbmAppConfig.cs @@ -24,7 +24,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App public static bool IsViewApplicable(SbmInternals internals, Sbm.View view) { SbmSessionConsole sessionConsole = internals.SessionConsole; - ISbmSecurityProfile_Core securityProfile = sessionConsole.UserContext.SecurityProfile_Core; + ISbmSecurityProfile_Core securityProfile = sessionConsole.UserContext.SecurityProfile; switch (view) { //----------------------------------------------------------- diff --git a/SaBusMgrStandardBase/Base/App/SbmInternals.cs b/SaBusMgrStandardBase/Base/App/SbmInternals.cs index 417800c..e322b39 100644 --- a/SaBusMgrStandardBase/Base/App/SbmInternals.cs +++ b/SaBusMgrStandardBase/Base/App/SbmInternals.cs @@ -49,7 +49,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App public SysId UserAffiliateId => new SysId(); public override DbUpdateInfo DbUpdateInfo => new DbUpdateInfo(UserSecurityItemId, UserDisplayName, SacsSystem.ComputerName); // User security. - public ISbmSecurityProfile_Core SecurityProfile_Core => throw new NotImplementedException(); + public ISbmSecurityProfile_Core SecurityProfile => throw new NotImplementedException(); // Organisation. public SysId OrganisationAffiliateId => new SysId(); public Sbm.AffiliateType OrganisationAffiliateType => Sbm.AffiliateType.Unknown; @@ -153,7 +153,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App public bool ReadOnly { get; } public ISbmUserOrganisationContext UserContext { get; } public SbmSessionContext SessionContext { get { return UserContext.SessionContext; } } - public ISbmSecurityProfile_Core SecurityProfile { get { return UserContext.SecurityProfile_Core; } } + public ISbmSecurityProfile_Core SecurityProfile { get { return UserContext.SecurityProfile; } } public SbmAccountsDfn AccountsDfn { get { if (accountsDfn == null) accountsDfn = new SbmAccountsDfn(UserContext); { return accountsDfn; } } } // Database. public ISacsDatabase BusMgrDatabase { get { return Application.GetBusMgrDatabase(SessionContext); } } diff --git a/SaBusMgrStandardBase/Base/App/SbmSecurityProfile.cs b/SaBusMgrStandardBase/Base/App/SbmSecurityProfile.cs index 5e7a76e..f868aa2 100644 --- a/SaBusMgrStandardBase/Base/App/SbmSecurityProfile.cs +++ b/SaBusMgrStandardBase/Base/App/SbmSecurityProfile.cs @@ -9,19 +9,24 @@ using Sarand.Net.Standard.Base.Security; using Sarand.Net.Standard.Base.Utils; using System; using System.Collections.Generic; +using System.Linq; +using static Sarand.BusinessMgr.Net.Standard.Base.App.Sbm; namespace Sarand.BusinessMgr.Net.Standard.Base.App { //======================================================================= - public class SbmSecurityProfile : SaErrorStateItem, ISbmSecurityProfile_Core + /// + /// Security Profile associated with Core BusinessMgr Session. + /// Once populated from database (Security tables etc) data needs to be self-sustaining via Serialisation.deserialistion + /// to ensure that no further database queries are required to reconsuct the data for each web request. + /// Hence the local data structures to store the defined Security Rights and User/Host profiles. + /// + public class SbmSecurityProfile : ISbmSecurityProfile_Core { //------------------------------------------------------------------- // Data members. private static readonly String userNameUnknown = "Unknown"; private bool includeHostSecurityProfile = false; - private SaSecurityProfile userSecurityProfile; - private SaSecurityProfile hostSecurityProfile; - private readonly List securityRights = new List(); //------------------------------------------------------------------- // Construction. @@ -40,15 +45,18 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App //------------------------------------------------------------------- // Properties. + // User. + public SysId UserId { get; protected set; } = new SysId(); + public String UserDisplayName { get; private set; } + public String UserLocation { get; private set; } + public DateTime UserLastLogin { get; private set; } + //------------------------------------------------------------------- // Refresh profile. public void RefreshProfile(SbmSessionConsole sessionConsole, SaSecurityConsole securityConsole, SysId userId, List securityRights) { - ClearErrorState(); - // Make a note of the security right. - this.securityRights.Clear(); - this.securityRights.AddRange(securityRights); + PopulateSecurityRights(securityRights); SysId previousUserId = UserId; @@ -58,19 +66,19 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App // Load new profile. if (securityConsole != null) { - userSecurityProfile = securityConsole.GetUserSecurityProfile(UserId); - if (hostSecurityProfile == null && includeHostSecurityProfile) - hostSecurityProfile = securityConsole.GetHostSecurityProfile(); + RefreshProfile(sessionConsole, securityConsole.GetUserSecurityProfile(UserId), includeHostSecurityProfile ? securityConsole.GetHostSecurityProfile() : null); } SaLogger.Debug($"{SysUtil.Fn} User: '{UserDisplayName}' [Id: {UserId}]" + (SysId.IsValid(previousUserId) ? ". Previous UserId: " + SysId.ToString(previousUserId) : "")); - RefreshProfileSettings(sessionConsole); - } protected void RefreshProfile(SbmSessionConsole sessionConsole, SaSecurityProfile userSecurityProfile, SaSecurityProfile hostSecurityProfile) { - this.userSecurityProfile = userSecurityProfile; - this.hostSecurityProfile = hostSecurityProfile; + UserDisplayName = (userSecurityProfile?.Description ?? userNameUnknown); + UserLocation = StringUtil.NullOnBlank(userSecurityProfile?.Location ?? ""); + UserLastLogin = (userSecurityProfile?.LastLogin ?? DateTime.MinValue); + UserProfile.Populate(userSecurityProfile); + if (hostSecurityProfile != null) + HostProfile = new SecurityProfileData(hostSecurityProfile); RefreshProfileSettings(sessionConsole); } private void RefreshProfileSettings(SbmSessionConsole sessionConsole) @@ -94,30 +102,143 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App } //------------------------------------------------------------------- - // User. - public SysId UserId { get; protected set; } = new SysId(); - public String UserDisplayName { get { return (userSecurityProfile != null ? userSecurityProfile.Description : userNameUnknown); } } - public String UserLocation { get { return (userSecurityProfile != null ? userSecurityProfile.Location : ""); } } - public DateTime UserLastLogin { get { return (userSecurityProfile != null ? userSecurityProfile.LastLogin : DateTime.MinValue); } } + // Security Rights. + public class SecurityRightData + { + public int Type { get; set; } + public String Code { get; set; } + } + [JsonProperty("SecurityRights")] + public List SecurityRights { get; private set; } = new List(); + private void PopulateSecurityRights(List rights) + { + SecurityRights = rights.Select(item => new SecurityRightData() { Type = item.Type, Code = item.Code }).ToList(); + } + + //------------------------------------------------------------------- + // Security Profiles. + // Profile properties are stored locally in such a way to allow serialisation/deserialisation + // and application of security rules without having to duplicate the complete Security Profile. + + public class SecurityProfileData + { + public SecurityProfileData() { } + public SecurityProfileData(SaSecurityProfile securityProfile) + { + Populate(securityProfile); + } + // Security. + /// Is the Security Right code granted access for the Data item (if specified). + public bool IsSecurityRightGranted(String rightCode, SysId dataItemId = null) + { + if (SystemAdmin ?? false) return true; + return ((Groups != null && Groups.Any(item => item.Rights.IsSecurityRightGranted(rightCode, dataItemId))) || (Rights != null && Rights.IsSecurityRightGranted(rightCode, dataItemId))); + } + /// Is the Security Right code denined access for the Data item (if specified). + public bool IsSecurityRightDenied(String rightCode, SysId dataItemId = null) + { + if (SystemAdmin ?? false) return false; + return ((Groups != null && Groups.Any(item => item.Rights.IsSecurityRightDenied(rightCode, dataItemId))) || (Rights != null && Rights.IsSecurityRightDenied(rightCode, dataItemId))); + } + // Population. + public void Populate(SaSecurityProfile securityProfile) + { + Clear(); + if (securityProfile.SystemAdmin) + SystemAdmin = true; + foreach (var group in securityProfile.Groups) + { + GroupData groupData = new GroupData() { Code = group.Code }; + groupData.Rights.Populate(group.Rights); + Groups.Add(groupData); + } + Rights.Populate(securityProfile.Rights); + } + public void Clear() + { + SystemAdmin = null; + Groups.Clear(); + Rights.Clear(); + } + // Properties. + public bool? SystemAdmin { get; private set; } + public class GroupData + { + public String Code { get; set; } + public RightsData Rights { get; set; } = new RightsData(); + } + public List Groups { get; private set; } = new List(); + public class RightData + { + public String Code { get; set; } + public SysId DataItemId { get; set; } + } + public class RightsData + { + // Construction. + public RightsData() { } + public RightsData(IEnumerable rights) + { + Populate(rights); + } + // Security. + public bool IsSecurityRightGranted(String rightCode, SysId dataItemId) + { + return Granted != null && Granted.Any(item => item.Code.EqualsCI(rightCode) && SysId.ToInt(dataItemId) == SysId.ToInt(item.DataItemId)); + } + public bool IsSecurityRightDenied(String rightCode, SysId dataItemId) + { + return Denied != null && Denied.Any(item => item.Code.EqualsCI(rightCode) && SysId.ToInt(dataItemId) == SysId.ToInt(item.DataItemId)); + } + // Population. + public void Populate(IEnumerable rights) + { + Clear(); + foreach (var right in rights) + { + RightData rightData = new RightData() { Code = right.Code, DataItemId = SysId.IsValid(right.DataItemId) ? right.DataItemId : null }; + if (right.Granted) + { + if (Granted == null) Granted = new HashSet(); + Granted.Add(rightData); + } + else + { + if (Denied == null) Denied = new HashSet(); + Denied.Add(rightData); + } + } + } + public void Clear() + { + Granted = null; + Denied = null; + } + public HashSet Granted { get; private set; } + public HashSet Denied { get; private set; } + } + public RightsData Rights { get; private set; } = new RightsData(); + } + // User and Host profiles. + public SecurityProfileData UserProfile { get; private set; } = new SecurityProfileData(); + public SecurityProfileData HostProfile { get; private set; } //------------------------------------------------------------------- // Utilities. - public bool IsUserMemberOfSecurityGroup(SysId secGroupId) + public String GetSecurityRightCode(Sbm.SecurityRight securityRight) { - if (userSecurityProfile != null) - return userSecurityProfile.IsMemberOfSecurityGroup(secGroupId); - return false; + return SecurityRights.FirstOrDefault(item => item.Type == (int)securityRight)?.Code ?? ""; } public bool IsSecurityRightGranted(Sbm.SecurityRight securityRight, SysId id = null) { - if (userSecurityProfile != null) - return userSecurityProfile.IsSecurityRightGranted(securityRights.GetCodeForType(securityRight)); + if (UserProfile != null) + return UserProfile.IsSecurityRightGranted(GetSecurityRightCode(securityRight), id); return false; } public bool IsSecurityRightDenied(Sbm.SecurityRight securityRight, SysId id = null) { - if (userSecurityProfile != null) - return userSecurityProfile.IsSecurityRightDenied(securityRights.GetCodeForType(securityRight), id); + if (UserProfile != null) + return UserProfile.IsSecurityRightDenied(GetSecurityRightCode(securityRight), id); return true; } @@ -126,59 +247,80 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App public class AdminProfile { internal AdminProfile() { } - public bool CanManageSecurity { get; internal set; } = false; - public bool CanManageDatabase { get; internal set; } = false; - public bool CanManageReferenceData { get; internal set; } = false; - public bool CanManageInterfaces { get; internal set; } = false; + public bool? ManageSecurity { get; internal set; } + public bool? ManageDatabase { get; internal set; } + public bool? ManageReferenceData { get; internal set; } + public bool? ManageInterfaces { get; internal set; } } public AdminProfile Admin { get; internal set; } private void RefreshAdminProfile(SbmSessionConsole sessionConsole) { - Admin.CanManageSecurity = UserHasSecurityRight(Sbm.SecurityRight.Security_Admin); - Admin.CanManageDatabase = UserHasSecurityRight(Sbm.SecurityRight.Database_Admin); - Admin.CanManageReferenceData = UserHasSecurityRight(Sbm.SecurityRight.ReferenceData_Admin); - Admin.CanManageInterfaces = UserHasSecurityRight(Sbm.SecurityRight.InterfaceConfig); + if (UserHasSecurityRight(Sbm.SecurityRight.Security_Admin)) + Admin.ManageSecurity = true; + if (UserHasSecurityRight(Sbm.SecurityRight.Database_Admin)) + Admin.ManageDatabase = true; + if (UserHasSecurityRight(Sbm.SecurityRight.ReferenceData_Admin)) + Admin.ManageReferenceData = true; + if (UserHasSecurityRight(Sbm.SecurityRight.InterfaceConfig)) + Admin.ManageInterfaces = true; } + // Admin. + public bool UserCanManageSecurity() { return Admin.ManageSecurity ?? false; } + public bool UserCanManageDatabase() { return Admin.ManageDatabase ?? false; } + public bool UserCanManageReferenceData() { return Admin.ManageReferenceData ?? false; } + public bool UserCanManageInterfaces() { return Admin.ManageInterfaces ?? false; } //------------------------------------------------------------------- // Security rights - Manage. public class ManageProfile { internal ManageProfile() { } - public bool CanManageTasks { get; internal set; } = false; - public bool CanManageTimesheets { get; internal set; } = false; + public bool? CanManageTasks { get; internal set; } + public bool? CanManageTimesheets { get; internal set; } } public ManageProfile Manage { get; internal set; } private void RefreshManageProfile(SbmSessionConsole sessionConsole) { - Manage.CanManageTasks = UserHasSecurityRight(Sbm.SecurityRight.TaskManager); - Manage.CanManageTimesheets = UserHasSecurityRight(Sbm.SecurityRight.TimesheetManager); + if (UserHasSecurityRight(Sbm.SecurityRight.TaskManager)) + Manage.CanManageTasks = true; + if (UserHasSecurityRight(Sbm.SecurityRight.TimesheetManager)) + Manage.CanManageTimesheets = true; } + // Manage. + public bool UserCanManageTasks() { return Manage.CanManageTasks ?? false; } + public bool UserCanManageTimesheets() { return Manage.CanManageTimesheets ?? false; } //------------------------------------------------------------------- // Security rights - Financials. public class FinancialProfile { internal FinancialProfile() { } - public bool ViewFinancials { get; internal set; } - public bool UpdateFinancials { get; internal set; } - public bool ViewCommoditySupplyFinancials { get; internal set; } - public bool UpdateCommoditySupplyFinancials { get; internal set; } - public bool ViewCommoditySalesFinancials { get; internal set; } - public bool UpdateCommoditySalesFinancials { get; internal set; } - public bool FinancialsAccessControl { get; internal set; } + public bool? ViewFinancials { get; internal set; } + public bool? UpdateFinancials { get; internal set; } + public bool? ViewCommoditySupplyFinancials { get; internal set; } + public bool? UpdateCommoditySupplyFinancials { get; internal set; } + public bool? ViewCommoditySalesFinancials { get; internal set; } + public bool? UpdateCommoditySalesFinancials { get; internal set; } + public bool? FinancialsAccessControl { get; internal set; } } public FinancialProfile Financial { get; internal set; } private void RefreshFinancialProfile(SbmSessionConsole sessionConsole) { - Financial.ViewFinancials = UserHasSecurityRight(Sbm.SecurityRight.Financial_View); - Financial.UpdateFinancials = UserHasSecurityRight(Sbm.SecurityRight.Financial_Update); - Financial.ViewCommoditySupplyFinancials = UserCanAccessFinancialViewOfType(Sbm.FinancialType.CommoditySupply); - Financial.UpdateCommoditySupplyFinancials = UserCanAccessFinancialUpdateOfType(Sbm.FinancialType.CommoditySupply); - Financial.ViewCommoditySalesFinancials = UserCanAccessFinancialViewOfType(Sbm.FinancialType.CommoditySales); - Financial.UpdateCommoditySalesFinancials = UserCanAccessFinancialUpdateOfType(Sbm.FinancialType.CommoditySales); + if (UserHasSecurityRight(Sbm.SecurityRight.Financial_View)) + Financial.ViewFinancials = true; + if (UserHasSecurityRight(Sbm.SecurityRight.Financial_Update)) + Financial.UpdateFinancials = true; + if (UserCanAccessFinancialViewOfType(Sbm.FinancialType.CommoditySupply)) + Financial.ViewCommoditySupplyFinancials = true; + if (UserCanAccessFinancialUpdateOfType(Sbm.FinancialType.CommoditySupply)) + Financial.UpdateCommoditySupplyFinancials = true; + if (UserCanAccessFinancialViewOfType(Sbm.FinancialType.CommoditySales)) + Financial.ViewCommoditySalesFinancials = true; + if (UserCanAccessFinancialUpdateOfType(Sbm.FinancialType.CommoditySales)) + Financial.UpdateCommoditySalesFinancials = true; // Config options. - Financial.FinancialsAccessControl = (sessionConsole != null && !sessionConsole.AllowOption(Sbm.ConfigOption.FinancialsAccessControl)); + if ((sessionConsole != null && !sessionConsole.AllowOption(Sbm.ConfigOption.FinancialsAccessControl))) + Financial.FinancialsAccessControl = true; } private bool UserCanAccessFinancialViewOfType(Sbm.FinancialType financialType) @@ -202,14 +344,14 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App /// Does the user have sufficient rights to view the specified financials. public bool UserCanViewFinancials(Sbm.FinancialType financialType = Sbm.FinancialType.Any) { - if (!Financial.FinancialsAccessControl) return true; - if (Financial.UpdateFinancials || Financial.ViewFinancials) + if (!Financial.FinancialsAccessControl ?? false) return true; + if ((Financial.UpdateFinancials ?? false) || (Financial.ViewFinancials ?? false)) { // If specific type of financial is requested, check that view access is not denied. if (financialType == Sbm.FinancialType.CommoditySupply) - return Financial.ViewCommoditySupplyFinancials; + return Financial.ViewCommoditySupplyFinancials ?? false; else if (financialType == Sbm.FinancialType.CommoditySales) - return Financial.ViewCommoditySalesFinancials; + return Financial.ViewCommoditySalesFinancials ?? false; else return true; } return false; @@ -218,14 +360,14 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App /// Does the user have sufficient rights to update the specified financials. public bool UserCanUpdateFinancials(Sbm.FinancialType financialType = Sbm.FinancialType.Any) { - if (!Financial.FinancialsAccessControl) return true; - if (Financial.UpdateFinancials) + if (!Financial.FinancialsAccessControl ?? false) return true; + if (Financial.UpdateFinancials ?? false) { // If specific type of financial is requested, check that update access is not denied. if (financialType == Sbm.FinancialType.CommoditySupply) - return Financial.UpdateCommoditySupplyFinancials; + return Financial.UpdateCommoditySupplyFinancials ?? false; else if (financialType == Sbm.FinancialType.CommoditySales) - return Financial.UpdateCommoditySalesFinancials; + return Financial.UpdateCommoditySalesFinancials ?? false; else return true; } return false; @@ -236,58 +378,38 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App public class ContactsProfile { internal ContactsProfile() { } - public bool Create { get; internal set; } = false; - public bool Update { get; internal set; } = false; + public bool? Create { get; internal set; } + public bool? Update { get; internal set; } } public ContactsProfile Contacts { get; internal set; } - public bool UserCanCreateContacts() { return Contacts.Create; } - public bool UserCanUpdateContacts() { return Contacts.Update; } private void RefreshContactsProfile(SbmSessionConsole sessionConsole) { - Contacts.Update = (UserHasSecurityRight(Sbm.SecurityRight.Affiliate_Update)); - Contacts.Create = (Contacts.Update || UserHasSecurityRight(Sbm.SecurityRight.Affiliate_Create)); + if (UserHasSecurityRight(Sbm.SecurityRight.Affiliate_Update)) + Contacts.Update = true; + if (Contacts.Update ?? false || UserHasSecurityRight(Sbm.SecurityRight.Affiliate_Create)) + Contacts.Create = true; } + // Contacts. + public bool UserCanCreateContacts() { return Contacts.Create ?? false; } + public bool UserCanUpdateContacts() { return Contacts.Update ?? false; } //------------------------------------------------------------------- // Security rights - Transactions. public class TransactionsProfile { internal TransactionsProfile() { } - public bool Create { get; internal set; } = false; - public bool Update { get; internal set; } = false; - public bool CanUpdateTransactionTypeStatusCat(SbmSecurityProfile securityProfile, Sbm.TransactionType transactionType, SbmTransaction.StatusCat transactionStatusCat) - { - SysId transactionTypeStatusCatId = SbmTransaction.GetTransactionTypeStatusId(transactionType, transactionStatusCat); - - // Check if update of this transaction type and status is denied: - if (securityProfile.IsSecurityRightDenied(Sbm.SecurityRight.Transaction_UpdateByTypeStatus, transactionTypeStatusCatId)) - return false; - - // Check if update of this transaction type and status is explicitly allowed: - if (securityProfile.IsSecurityRightGranted(Sbm.SecurityRight.Transaction_UpdateByTypeStatus, transactionTypeStatusCatId)) - return true; - - // Check if any other statuses for this type of transaction are allowed. - foreach (TypeCodeDesc statusCat in SbmTransaction.GetTransactionStatusCats(transactionType)) - { - SbmTransaction.StatusCat transactionStatusCatOther = (SbmTransaction.StatusCat)statusCat.Type; - // Ignore the transaction status category. - if (transactionStatusCatOther == transactionStatusCat) continue; - if (securityProfile.IsSecurityRightGranted(Sbm.SecurityRight.Transaction_UpdateByTypeStatus, SbmTransaction.GetTransactionTypeStatusId(transactionType, transactionStatusCatOther))) - // Explicit granting of right to another status for this transaction right - // implies that other statuses are implicitly denied. - return false; - } - return true; - } + public bool? Create { get; internal set; } + public bool? Update { get; internal set; } } public TransactionsProfile Transaction { get; internal set; } private void RefreshTransactionProfile(SbmSessionConsole sessionConsole) { - Transaction.Update = (UserHasSecurityRight(Sbm.SecurityRight.Transaction_Update)); - Transaction.Create = (Transaction.Update || UserHasSecurityRight(Sbm.SecurityRight.Transaction_View)); + if ((UserHasSecurityRight(Sbm.SecurityRight.Transaction_Update))) + Transaction.Update = true; + if ((Transaction.Update ?? false || UserHasSecurityRight(Sbm.SecurityRight.Transaction_View))) + Transaction.Create = true; } - + // Transactions. public bool UserCanAccessTransactionsOfType(Sbm.TransactionType transactionType) { if ((int)transactionType < 1) return false; @@ -296,29 +418,54 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App return false; return true; } + public bool CanUpdateTransactionTypeStatusCat(Sbm.TransactionType transactionType, SbmTransaction.StatusCat transactionStatusCat) + { + SysId transactionTypeStatusCatId = SbmTransaction.GetTransactionTypeStatusId(transactionType, transactionStatusCat); + + // Check if update of this transaction type and status is denied: + if (IsSecurityRightDenied(Sbm.SecurityRight.Transaction_UpdateByTypeStatus, transactionTypeStatusCatId)) + return false; + + // Check if update of this transaction type and status is explicitly allowed: + if (IsSecurityRightGranted(Sbm.SecurityRight.Transaction_UpdateByTypeStatus, transactionTypeStatusCatId)) + return true; + + // Check if any other statuses for this type of transaction are allowed. + foreach (TypeCodeDesc statusCat in SbmTransaction.GetTransactionStatusCats(transactionType)) + { + SbmTransaction.StatusCat transactionStatusCatOther = (SbmTransaction.StatusCat)statusCat.Type; + // Ignore the transaction status category. + if (transactionStatusCatOther == transactionStatusCat) continue; + if (IsSecurityRightGranted(Sbm.SecurityRight.Transaction_UpdateByTypeStatus, SbmTransaction.GetTransactionTypeStatusId(transactionType, transactionStatusCatOther))) + // Explicit granting of right to another status for this transaction right + // implies that other statuses are implicitly denied. + return false; + } + return true; + } //------------------------------------------------------------------- // Security rights - ItemType. public class ItemTypesProfile { internal ItemTypesProfile() { } - public bool Create { get; internal set; } - public bool Update { get; internal set; } - public bool Remove { get; internal set; } + public bool? Create { get; internal set; } + public bool? Update { get; internal set; } + public bool? Remove { get; internal set; } // Config options. - public bool Processes { get; internal set; } - public bool Contacts { get; internal set; } - public bool Commodities { get; internal set; } - public bool Portfolios { get; internal set; } - public bool PriceSets { get; internal set; } - public bool CommodityCustomers { get; internal set; } - public bool CommoditySuppliers { get; internal set; } - public bool Transactions { get; internal set; } - public bool Assets { get; internal set; } - public bool Vehicles { get; internal set; } - public bool StockLocations { get; internal set; } - public bool StockContainers { get; internal set; } - public bool StockControl { get; internal set; } + public bool? Processes { get; internal set; } + public bool? Contacts { get; internal set; } + public bool? Commodities { get; internal set; } + public bool? Portfolios { get; internal set; } + public bool? PriceSets { get; internal set; } + public bool? CommodityCustomers { get; internal set; } + public bool? CommoditySuppliers { get; internal set; } + public bool? Transactions { get; internal set; } + public bool? Assets { get; internal set; } + public bool? Vehicles { get; internal set; } + public bool? StockLocations { get; internal set; } + public bool? StockContainers { get; internal set; } + public bool? StockControl { get; internal set; } } public ItemTypesProfile ItemType { get; } = new ItemTypesProfile(); private void RefreshItemTypeProfile(SbmSessionConsole sessionConsole) @@ -337,56 +484,14 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App ItemType.StockContainers = sessionConsole.GetStockControlConfig().AllowStockContainers(); ItemType.StockControl = sessionConsole.GetStockControlConfig().AllowStockControl(); } - - public bool ItemTypeIsAllowed(Sbm.ItemType itemType) + // Item types. + public bool UserCanViewItemType(Sbm.ItemType itemType) { - switch (itemType) - { - //case Sbm.ItemType.SecItem: - //case Sbm.ItemType.Currency: - //case Sbm.ItemType.Region: - //case Sbm.ItemType.Language: - //case Sbm.ItemType.TaxCode: - //case Sbm.ItemType.UnitMeasure: - case Sbm.ItemType.Process: return ItemType.Processes; - case Sbm.ItemType.Affiliate: - case Sbm.ItemType.Address: return ItemType.Contacts; - case Sbm.ItemType.Commodity: return ItemType.Commodities; - case Sbm.ItemType.PortfolioItem: return ItemType.Portfolios; - case Sbm.ItemType.Location: return ItemType.StockLocations; - case Sbm.ItemType.Container: - case Sbm.ItemType.ContainerItem: return ItemType.StockContainers; - case Sbm.ItemType.Movement: - case Sbm.ItemType.StockLevel: return ItemType.StockControl; - case Sbm.ItemType.PriceSet: - case Sbm.ItemType.Price: return ItemType.PriceSets; - case Sbm.ItemType.CommodityCustomer: return ItemType.CommodityCustomers; - case Sbm.ItemType.CommoditySupplier: return ItemType.CommoditySuppliers; - //case Sbm.ItemType.Association: - case Sbm.ItemType.Transaction: - case Sbm.ItemType.Requirement: - case Sbm.ItemType.Constituent: return ItemType.Transactions; - //case Sbm.ItemType.SystemAccount: - //case Sbm.ItemType.SystemUser: - //case Sbm.ItemType.SystemAccountUser: - case Sbm.ItemType.AssetCategory: - case Sbm.ItemType.AssetCategoryProcess: - case Sbm.ItemType.Asset: - case Sbm.ItemType.AssetProcess: return ItemType.Assets; - case Sbm.ItemType.VehicleClass: - case Sbm.ItemType.Vehicle: - case Sbm.ItemType.VehicleEvent: - case Sbm.ItemType.VehicleFeature: return ItemType.Vehicles; - case Sbm.ItemType.CommodityProcess: return ItemType.Processes; - case Sbm.ItemType.RequirementProcess: return ItemType.Processes; - //case Sbm.ItemType.Interface: - } return true; } public bool UserCanCreateItemType(Sbm.ItemType itemType) { - if (userSecurityProfile?.SystemAdmin ?? false) return true; - if (!ItemTypeIsAllowed(itemType)) return false; + if (UserProfile?.SystemAdmin ?? false) return true; switch (itemType) { case Sbm.ItemType.SecItem: return IsSecurityRightGranted(Sbm.SecurityRight.Security_Admin); @@ -432,7 +537,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App } public bool UserCanUpdateItemType(Sbm.ItemType itemType) { - if (userSecurityProfile?.SystemAdmin ?? false) return true; + if (UserProfile?.SystemAdmin ?? false) return true; switch (itemType) { case Sbm.ItemType.SecItem: return IsSecurityRightGranted(Sbm.SecurityRight.Security_Admin); @@ -493,17 +598,75 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App // Security rights. public bool UserHasSecurityRight(Sbm.SecurityRight securityRight, SysId dataItemId = null) { - String rightCode = securityRights.GetCodeForType(securityRight); + String rightCode = GetSecurityRightCode(securityRight); if (StringUtil.IsEmpty(rightCode)) return false; // Check if right is denied by either user or host profiles. - if (userSecurityProfile != null && userSecurityProfile.IsSecurityRightDenied(rightCode)) return false; - if (hostSecurityProfile != null && hostSecurityProfile.IsSecurityRightDenied(rightCode)) return false; + if (UserProfile?.IsSecurityRightDenied(rightCode) ?? false) return false; + if (HostProfile?.IsSecurityRightDenied(rightCode) ?? false) return false; // Check if right is granted by either user or host profiles. - if (userSecurityProfile != null && userSecurityProfile.IsSecurityRightGranted(rightCode)) return true; - if (hostSecurityProfile != null && hostSecurityProfile.IsSecurityRightGranted(rightCode)) return true; + if (UserProfile?.IsSecurityRightGranted(rightCode) ?? false) return true; + if (HostProfile?.IsSecurityRightGranted(rightCode) ?? false) return true; return false; } + //------------------------------------------------------------------- + public class SecurityProfileException : Exception + { + public SecurityProfileException(string message) : base(message) { } + } + /// Verify that the specified profiles matches this one, eg. after deserialisation. + public bool CompareProfiles(ISbmSecurityProfile_Core securityProfile_Core) + { + // Profile. + if (securityProfile_Core == null) + throw new SecurityProfileException($"{SysUtil.Fn} Supplied profile is null."); + SbmSecurityProfile securityProfile = securityProfile_Core as SbmSecurityProfile; + if (securityProfile == null) + throw new SecurityProfileException($"{SysUtil.Fn} Supplied profile does not match type."); + + // Security rights. + if (SecurityRights.Count != securityProfile.SecurityRights.Count) + throw new SecurityProfileException($"{SysUtil.Fn} Supplied profile has different number of Security Rights. Expected: {SecurityRights.Count}. Found: {securityProfile.SecurityRights}."); + + // User profile. + if (UserProfile != null && securityProfile.UserProfile == null) + throw new SecurityProfileException($"{SysUtil.Fn} Supplied profile is missing a User Profile."); + if (UserProfile == null && securityProfile.UserProfile != null) + throw new SecurityProfileException($"{SysUtil.Fn} Supplied profile includes a User Profile."); + + // User profile - Groups. + if (UserProfile.Groups?.Count != securityProfile.UserProfile.Groups?.Count) + throw new SecurityProfileException($"{SysUtil.Fn} Supplied profile has different number of User Groups. Expected: {UserProfile.Groups?.Count}. Found: {securityProfile.UserProfile.Groups?.Count}."); + + // User profile - Granted Rights. + if (UserProfile.Rights?.Granted?.Count != securityProfile.UserProfile.Rights?.Granted?.Count) + throw new SecurityProfileException($"{SysUtil.Fn} Supplied profile has different number of Granted User Rights. Expected: {UserProfile.Rights?.Granted?.Count}. Found: {securityProfile.UserProfile.Rights?.Granted?.Count}."); + + // User profile - Denied Rights. + if (UserProfile.Rights?.Denied?.Count != securityProfile.UserProfile.Rights?.Denied?.Count) + throw new SecurityProfileException($"{SysUtil.Fn} Supplied profile has different number of Denied User Rights. Expected: {UserProfile.Rights?.Denied?.Count}. Found: {securityProfile.UserProfile.Rights?.Denied?.Count}."); + + // Host profile. + if (HostProfile != null && securityProfile.HostProfile == null) + throw new SecurityProfileException($"{SysUtil.Fn} Supplied profile is missing a Host Profile."); + if (HostProfile == null && securityProfile.HostProfile != null) + throw new SecurityProfileException($"{SysUtil.Fn} Supplied profile includes a Host Profile."); + + // Host profile - Groups. + if (HostProfile?.Groups?.Count != securityProfile.HostProfile?.Groups?.Count) + throw new SecurityProfileException($"{SysUtil.Fn} Supplied profile has different number of Host Groups. Expected: {HostProfile?.Groups?.Count}. Found: {securityProfile.HostProfile?.Groups?.Count}."); + + // Host profile - Granted Rights. + if (HostProfile?.Rights?.Granted?.Count != securityProfile.HostProfile?.Rights?.Granted?.Count) + throw new SecurityProfileException($"{SysUtil.Fn} Supplied profile has different number of Granted Host Rights. Expected: {HostProfile?.Rights?.Granted?.Count}. Found: {securityProfile.HostProfile?.Rights?.Granted?.Count}."); + + // Host profile - Denied Rights. + if (HostProfile?.Rights?.Denied?.Count != securityProfile.HostProfile?.Rights?.Denied?.Count) + throw new SecurityProfileException($"{SysUtil.Fn} Supplied profile has different number of Denied Host Rights. Expected: {HostProfile?.Rights?.Denied?.Count}. Found: {securityProfile.HostProfile?.Rights?.Denied?.Count}."); + + return true; + } + //------------------------------------------------------------------- } //======================================================================= diff --git a/SaBusMgrStandardBase/Base/Core/ISbmSecurityProfile_Core.cs b/SaBusMgrStandardBase/Base/Core/ISbmSecurityProfile_Core.cs index 19f5ef5..15c7fd1 100644 --- a/SaBusMgrStandardBase/Base/Core/ISbmSecurityProfile_Core.cs +++ b/SaBusMgrStandardBase/Base/Core/ISbmSecurityProfile_Core.cs @@ -1,4 +1,5 @@ using Sarand.BusinessMgr.Net.Standard.Base.App; +using Sarand.BusinessMgr.Net.Standard.Base.Bus; using Sarand.BusinessMgr.Net.Standard.Base.Consoles; using Sarand.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.Core; @@ -20,19 +21,30 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.Core DateTime UserLastLogin { get; } //------------------------------------------------------------------- - // Security - Contacts. - bool UserCanCreateContacts(); - bool UserCanUpdateContacts(); + // Admin. + bool UserCanManageSecurity(); + bool UserCanManageDatabase(); + bool UserCanManageReferenceData(); + bool UserCanManageInterfaces(); - // Security - Financials. + // Manage. + bool UserCanManageTasks(); + bool UserCanManageTimesheets(); + + // Financials. bool UserCanViewFinancials(Sbm.FinancialType financialType = Sbm.FinancialType.Any); bool UserCanUpdateFinancials(Sbm.FinancialType financialType = Sbm.FinancialType.Any); - // Security - Transactions. - bool UserCanAccessTransactionsOfType(Sbm.TransactionType transactionType); + // Contacts. + bool UserCanCreateContacts(); + bool UserCanUpdateContacts(); - // Security Item types. - bool ItemTypeIsAllowed(Sbm.ItemType itemType); + // Transactions. + bool UserCanAccessTransactionsOfType(Sbm.TransactionType transactionType); + bool CanUpdateTransactionTypeStatusCat(Sbm.TransactionType transactionType, SbmTransaction.StatusCat transactionStatusCat); + + // Item types. + bool UserCanViewItemType(Sbm.ItemType itemType); bool UserCanCreateItemType(Sbm.ItemType itemType); bool UserCanUpdateItemType(Sbm.ItemType itemType); bool UserCanRemoveItemType(Sbm.ItemType itemType); @@ -44,6 +56,13 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.Core /// Refresh profile. void RefreshProfile(SbmSessionConsole sessionConsole, SaSecurityConsole securityConsole, SysId userId, List securityRights); + /// + /// Verify that the specified profiles matches this one, eg. after deserialisation. + /// - Only to be used for unit tests etc. + /// - Expected to throw Exception for any discrepancies found. + /// + bool CompareProfiles(ISbmSecurityProfile_Core securityProfile); + //------------------------------------------------------------------- } //======================================================================= diff --git a/SaBusMgrStandardBase/Base/Core/ISbmUserOrganisationContext.cs b/SaBusMgrStandardBase/Base/Core/ISbmUserOrganisationContext.cs index 7f6aae2..2583eaa 100644 --- a/SaBusMgrStandardBase/Base/Core/ISbmUserOrganisationContext.cs +++ b/SaBusMgrStandardBase/Base/Core/ISbmUserOrganisationContext.cs @@ -26,7 +26,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.Core //------------------------------------------------------------------- /// Security profile - what the user is allowed to do based on security roles and groups. - ISbmSecurityProfile_Core SecurityProfile_Core { get; } + ISbmSecurityProfile_Core SecurityProfile { get; } //------------------------------------------------------------------- } diff --git a/SaBusMgrStandardBase/Base/Mgr/SbmCoreMgr.cs b/SaBusMgrStandardBase/Base/Mgr/SbmCoreMgr.cs index c7c47bb..940fd6d 100644 --- a/SaBusMgrStandardBase/Base/Mgr/SbmCoreMgr.cs +++ b/SaBusMgrStandardBase/Base/Mgr/SbmCoreMgr.cs @@ -190,8 +190,8 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.Mgr protected SbmAccountsDfn AccountsDfn { get { return Internals.AccountsDfn; } } protected DbUpdateInfo DbUpdateInfo { get { return Internals.DbUpdateInfo; } } // User security profile. - public bool UserCanViewCommoditySalesFinancials() { return UserContext.SecurityProfile_Core.UserCanViewFinancials(Sbm.FinancialType.CommoditySales); } - public bool UserCanViewCommoditySupplyFinancials() { return UserContext.SecurityProfile_Core.UserCanViewFinancials(Sbm.FinancialType.CommoditySupply); } + public bool UserCanViewCommoditySalesFinancials() { return UserContext.SecurityProfile.UserCanViewFinancials(Sbm.FinancialType.CommoditySales); } + public bool UserCanViewCommoditySupplyFinancials() { return UserContext.SecurityProfile.UserCanViewFinancials(Sbm.FinancialType.CommoditySupply); } // Managers. protected SbmAccountMgr AccountMgr { get { return Internals.AccountMgr; } } protected SbmCommodityMgr CommodityMgr { get { return Internals.CommodityMgr; } } diff --git a/SaBusMgrTest_Control/Web/Control/App/SbmApplication_SessionData_Tester.cs b/SaBusMgrTest_Control/Web/Control/App/SbmApplication_SessionData_Tester.cs index 5d374ca..467e8c4 100644 --- a/SaBusMgrTest_Control/Web/Control/App/SbmApplication_SessionData_Tester.cs +++ b/SaBusMgrTest_Control/Web/Control/App/SbmApplication_SessionData_Tester.cs @@ -67,11 +67,11 @@ namespace Sarand.BusinessMgr.Test.Web.Control.App // Serialise data. - string sessionDataJson = JsonConvert.SerializeObject(sessionData); + string sessionDataJson = JsonConvert.SerializeObject(sessionData, SbmController_Web.SessionDataJsonSerializerSettings); Assert.IsNotNull(sessionDataJson); // Deserialise data. - SbmSessionData_Control? sessionDataDeserialized = JsonConvert.DeserializeObject(sessionDataJson, SbmController_Web.SbmSessionDataJsonSerializerSettings); + SbmSessionData_Control? sessionDataDeserialized = JsonConvert.DeserializeObject(sessionDataJson, SbmController_Web.SessionDataJsonDeserializerSettings); Assert.IsNotNull(sessionDataDeserialized); // Validate deserialised data. diff --git a/SaBusMgrTest_Portal/Web/Portal/App/SbmApplication_SessionData_Tester.cs b/SaBusMgrTest_Portal/Web/Portal/App/SbmApplication_SessionData_Tester.cs index f11d7a2..ed9cf31 100644 --- a/SaBusMgrTest_Portal/Web/Portal/App/SbmApplication_SessionData_Tester.cs +++ b/SaBusMgrTest_Portal/Web/Portal/App/SbmApplication_SessionData_Tester.cs @@ -41,11 +41,11 @@ namespace Sarand.BusinessMgr.Test.Web.Portal.App string repositoryContext = "Mimo"; SbmSessionContext sessionContext = CreateCoreInstanceConsole(repositoryContext); Assert.IsNotNull(sessionContext); - SbmInternals internals = new SbmInternals(GetSessionData(sessionContext, repositoryContext)); - disposables.Add(internals); ISacsDatabase busMgrDatabase = AppPortal.GetBusMgrDatabase(sessionContext); // Internals. + SbmInternals internals = new SbmInternals(GetSessionData(sessionContext, repositoryContext)); + disposables.Add(internals); SaSecurityConsole securityConsole = internals.SecurityConsole; Assert.IsNotNull(securityConsole); securityConsole.RegisterSecurityRights(); @@ -88,9 +88,9 @@ namespace Sarand.BusinessMgr.Test.Web.Portal.App Assert.AreEqual(securityItemUser.EmailAddress, sessionData.User.EmailAddress); // Security profile. - sessionData.SecurityProfile_Core.RefreshProfile(sessionConsole, securityConsole, securityItemUser.Id, securityConsole.GetRegisteredRights()); - Assert.IsTrue(sessionData.SecurityProfile_Core.UserCanCreateContacts()); - Assert.IsTrue(sessionData.SecurityProfile_Core.UserCanViewFinancials()); + sessionData.SecurityProfile.RefreshProfile(sessionConsole, securityConsole, securityItemUser.Id, securityConsole.GetRegisteredRights()); + Assert.IsTrue(sessionData.SecurityProfile.UserCanCreateContacts()); + Assert.IsTrue(sessionData.SecurityProfile.UserCanViewFinancials()); // User details. Assert.AreEqual(affiliateUser.Id, sessionData.User.AffiliateId); @@ -107,11 +107,11 @@ namespace Sarand.BusinessMgr.Test.Web.Portal.App // Serialise data. - string sessionDataJson = JsonConvert.SerializeObject(sessionData); + string sessionDataJson = JsonConvert.SerializeObject(sessionData, SbmController_Web.SessionDataJsonSerializerSettings); Assert.IsNotNull(sessionDataJson); // Deserialise data. - SbmSessionData_Portal sessionDataDeserialized = JsonConvert.DeserializeObject(sessionDataJson, SbmController_Web.SbmSessionDataJsonSerializerSettings); + SbmSessionData_Portal sessionDataDeserialized = JsonConvert.DeserializeObject(sessionDataJson, SbmController_Web.SessionDataJsonDeserializerSettings); Assert.IsNotNull(sessionDataDeserialized); string sessionDataJsonDeserialized = JsonConvert.SerializeObject(sessionDataDeserialized); @@ -131,12 +131,11 @@ namespace Sarand.BusinessMgr.Test.Web.Portal.App Assert.AreEqual(securityItemUser.EmailAddress, sessionDataDeserialized.User.EmailAddress); // Security profile. - sessionData.SecurityProfile_Core.RefreshProfile(sessionConsole, securityConsole, securityItemUser.Id, securityConsole.GetRegisteredRights()); - Assert.IsNotNull(sessionDataDeserialized.SecurityProfile_Core); - Assert.AreEqual(sessionData.SecurityProfile_Core.UserCanCreateContacts(), sessionDataDeserialized.SecurityProfile_Core.UserCanCreateContacts()); - Assert.IsTrue(sessionDataDeserialized.SecurityProfile_Core.UserCanCreateContacts()); - Assert.AreEqual(sessionData.SecurityProfile_Core.UserCanViewFinancials(), sessionDataDeserialized.SecurityProfile_Core.UserCanViewFinancials()); - Assert.IsTrue(sessionDataDeserialized.SecurityProfile_Core.UserCanViewFinancials()); + Assert.IsNotNull(sessionDataDeserialized.SecurityProfile); + Assert.AreEqual(sessionData.SecurityProfile.UserCanCreateContacts(), sessionDataDeserialized.SecurityProfile.UserCanCreateContacts()); + Assert.IsTrue(sessionDataDeserialized.SecurityProfile.UserCanCreateContacts()); + Assert.AreEqual(sessionData.SecurityProfile.UserCanViewFinancials(), sessionDataDeserialized.SecurityProfile.UserCanViewFinancials()); + Assert.IsTrue(sessionDataDeserialized.SecurityProfile.UserCanViewFinancials()); // User details. Assert.AreEqual(affiliateUser.Id, sessionDataDeserialized.User.AffiliateId); @@ -155,6 +154,141 @@ namespace Sarand.BusinessMgr.Test.Web.Portal.App } } + + //------------------------------------------------------------------- + [TestMethod] + public void Test_Application_SessionData_SecurityProfile() + { + // Record disposable items as they are created so all can be disposed when needed. + using (DisposableCollection disposables = new DisposableCollection()) + { + //----------------------------------------------------------- + // Initialisation. + + // Session configuration. + string repositoryContext = "Mimo"; + SbmSessionContext sessionContext = CreateCoreInstanceConsole(repositoryContext); + Assert.IsNotNull(sessionContext); + ISacsDatabase busMgrDatabase = AppPortal.GetBusMgrDatabase(sessionContext); + + // User Session. + SbmSessionData_Portal sessionData = new SbmSessionData_Portal(sessionContext, repositoryContext, null, null, null, null); + Assert.IsNotNull(sessionData); + + // Internals. + SbmInternals internals = new SbmInternals(GetSessionData(sessionContext, repositoryContext)); + disposables.Add(internals); + SaSecurityConsole securityConsole = internals.SecurityConsole; + Assert.IsNotNull(securityConsole); + securityConsole.RegisterSecurityRights(); + SbmAccountMgr accountMgr = internals.AccountMgr; + Assert.IsNotNull(accountMgr); + + // Session Console. + SbmSessionConsole sessionConsole = SbmSessionConsole.CreateConsole(internals.UserContext); + + //----------------------------------------------------------- + // Quering. + + // Security item. + SaSecurityItem securityItemUser = securityConsole.GetUserByEmail("andy@sarand.com"); + Assert.IsNotNull(securityItemUser); + + // User account. + SbmAffiliate affiliateUser = accountMgr.GetAffiliateByUserId(securityItemUser.Id); + Assert.IsNotNull(affiliateUser); + + // Organisations. + List supervisedOrganisations = accountMgr.GetAffiliatesSupervisedBy(affiliateUser.Id); + Assert.IsNotNull(supervisedOrganisations); + Assert.IsTrue(supervisedOrganisations.Count > 0); + SysId affiliateIdOrganisation = supervisedOrganisations[0].Id; + SbmAssociation associationOrganisation = accountMgr.GetAssociationByOwnerTypeTarget(affiliateIdOrganisation, Sbm.AssociationType.Internal, affiliateIdOrganisation); + Assert.IsNotNull(associationOrganisation); + SbmAffiliate affiliateOrganisation = accountMgr.GetAffiliateById(affiliateIdOrganisation); + Assert.IsNotNull(affiliateOrganisation); + + //----------------------------------------------------------- + // Population. + + // Populate User Session. + sessionData.Refresh(securityItemUser, affiliateUser, associationOrganisation, affiliateOrganisation); + Assert.AreEqual(securityItemUser.Id, sessionData.UserSecurityItemId); + Assert.AreEqual(affiliateUser.Id, sessionData.UserAffiliateId); + + // Validate user session. + Assert.IsNotNull(sessionData.User); + + // Security item. + Assert.AreEqual(securityItemUser.Id, sessionData.User.SecurityItemId); + Assert.AreEqual(securityItemUser.EmailAddress, sessionData.User.EmailAddress); + + // Security profile. + sessionData.SecurityProfile.RefreshProfile(sessionConsole, securityConsole, securityItemUser.Id, securityConsole.GetRegisteredRights()); + Assert.IsTrue(sessionData.SecurityProfile.UserCanManageDatabase()); + Assert.IsTrue(sessionData.SecurityProfile.UserCanManageInterfaces()); + Assert.IsTrue(sessionData.SecurityProfile.UserCanCreateContacts()); + Assert.IsTrue(sessionData.SecurityProfile.UserCanViewFinancials()); + Assert.IsTrue(sessionData.SecurityProfile.UserCanUpdateItemType(Sbm.ItemType.Asset)); + + //----------------------------------------------------------- + // Storing and Retrieving. + + // Serialise data. + string sessionDataJson = JsonConvert.SerializeObject(sessionData, SbmController_Web.SessionDataJsonSerializerSettings); + Assert.IsNotNull(sessionDataJson); + + // Deserialise data. + SbmSessionData_Portal sessionDataDeserialized = JsonConvert.DeserializeObject(sessionDataJson, SbmController_Web.SessionDataJsonDeserializerSettings); + Assert.IsNotNull(sessionDataDeserialized); + string sessionDataJsonDeserialized = JsonConvert.SerializeObject(sessionDataDeserialized); + + //----------------------------------------------------------- + // Verifing. + + // User. + Assert.AreEqual(securityItemUser.Id, sessionDataDeserialized.UserSecurityItemId); + Assert.AreEqual(affiliateUser.Id, sessionDataDeserialized.UserAffiliateId); + + // Security profile. + Assert.IsNotNull(sessionDataDeserialized.SecurityProfile); + Assert.IsTrue(sessionData.SecurityProfile.CompareProfiles(sessionDataDeserialized.SecurityProfile)); + + // User. + Assert.AreEqual(sessionData.SecurityProfile.UserId, sessionDataDeserialized.SecurityProfile.UserId); + Assert.AreEqual(sessionData.SecurityProfile.UserDisplayName, sessionDataDeserialized.SecurityProfile.UserDisplayName); + Assert.AreEqual(sessionData.SecurityProfile.UserLocation, sessionDataDeserialized.SecurityProfile.UserLocation); + Assert.AreEqual(sessionData.SecurityProfile.UserLastLogin, sessionDataDeserialized.SecurityProfile.UserLastLogin); + + // Security groups. + + // Admin. + Assert.AreEqual(sessionData.SecurityProfile.UserCanManageSecurity(), sessionDataDeserialized.SecurityProfile.UserCanManageSecurity()); + Assert.AreEqual(sessionData.SecurityProfile.UserCanManageDatabase(), sessionDataDeserialized.SecurityProfile.UserCanManageDatabase()); + Assert.AreEqual(sessionData.SecurityProfile.UserCanManageReferenceData(), sessionDataDeserialized.SecurityProfile.UserCanManageReferenceData()); + Assert.AreEqual(sessionData.SecurityProfile.UserCanManageInterfaces(), sessionDataDeserialized.SecurityProfile.UserCanManageInterfaces()); + + // Manage. + Assert.AreEqual(sessionData.SecurityProfile.UserCanManageTasks(), sessionDataDeserialized.SecurityProfile.UserCanManageTasks()); + Assert.AreEqual(sessionData.SecurityProfile.UserCanManageTimesheets(), sessionDataDeserialized.SecurityProfile.UserCanManageTimesheets()); + + // Hard-coded rules. + + // Create Contacts. + Assert.AreEqual(sessionData.SecurityProfile.UserCanCreateContacts(), sessionDataDeserialized.SecurityProfile.UserCanCreateContacts()); + + // View Financials. + Assert.AreEqual(sessionData.SecurityProfile.UserCanViewFinancials(), sessionDataDeserialized.SecurityProfile.UserCanViewFinancials()); + + // Rules determined by security groups and rights. + + // Item types. + Assert.AreEqual(sessionData.SecurityProfile.UserCanCreateItemType(Sbm.ItemType.Asset), sessionDataDeserialized.SecurityProfile.UserCanCreateItemType(Sbm.ItemType.Asset)); + Assert.AreEqual(sessionData.SecurityProfile.UserCanUpdateItemType(Sbm.ItemType.Asset), sessionDataDeserialized.SecurityProfile.UserCanUpdateItemType(Sbm.ItemType.Asset)); + + //----------------------------------------------------------- + } + } } //======================================================================= } diff --git a/SaBusinessMgrCoreWebControl/Control/Controllers/SbmController_Control.cs b/SaBusinessMgrCoreWebControl/Control/Controllers/SbmController_Control.cs index fa56036..80a9771 100644 --- a/SaBusinessMgrCoreWebControl/Control/Controllers/SbmController_Control.cs +++ b/SaBusinessMgrCoreWebControl/Control/Controllers/SbmController_Control.cs @@ -59,7 +59,7 @@ namespace Sarand.BusinessMgr.Web.Control.Controllers public override ISbmUserOrganisationContext UserContext_Core => throw new NotImplementedException(); protected override SbmSessionData_Web DeserializeSessionData(String sessionDataJson) { - return JsonConvert.DeserializeObject(sessionDataJson, SbmSessionDataJsonSerializerSettings); + return JsonConvert.DeserializeObject(sessionDataJson, SessionDataJsonDeserializerSettings); } //------------------------------------------------------------------- diff --git a/SaBusinessMgrCoreWebPortal/Portal/App/SbmSessionData_Portal.cs b/SaBusinessMgrCoreWebPortal/Portal/App/SbmSessionData_Portal.cs index 0bf8ceb..bd37ab5 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/App/SbmSessionData_Portal.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/App/SbmSessionData_Portal.cs @@ -31,7 +31,7 @@ namespace Sarand.BusinessMgr.Web.Portal.App private SbmSessionData_Portal(long sessionIdx, SbmSessionContext sessionContext) : base(sessionIdx, sessionContext) { - securityProfile = new SbmSecurityProfile(false); + securityProfile_Internal = new SbmSecurityProfile(false); Organisation = new OrganisationData_Portal(null, null); User = new UserData_Portal(sessionContext, null, null); } @@ -42,7 +42,7 @@ namespace Sarand.BusinessMgr.Web.Portal.App public SbmSessionData_Portal(SbmSessionContext sessionContext) : base(0, sessionContext) { - securityProfile = new SbmSecurityProfile(false); + securityProfile_Internal = new SbmSecurityProfile(false); Organisation = new OrganisationData_Portal(null, null); User = new UserData_Portal(sessionContext, null, null); } @@ -55,7 +55,7 @@ namespace Sarand.BusinessMgr.Web.Portal.App base(sessionContext) { RepositoryContext = repositoryContext; - securityProfile = new SbmSecurityProfile(false); + securityProfile_Internal = new SbmSecurityProfile(false); Organisation = new OrganisationData_Portal(associationOrganisation, affiliateOrganisation); User = new UserData_Portal(sessionContext, securityItemUser, affiliateUser); } @@ -117,10 +117,11 @@ namespace Sarand.BusinessMgr.Web.Portal.App //------------------------------------------------------------------- // Security Profile. + [JsonProperty("SecurityProfile")] - private readonly SbmSecurityProfile securityProfile; + private readonly SbmSecurityProfile securityProfile_Internal; [JsonIgnore] - public ISbmSecurityProfile_Core SecurityProfile_Core { get { return securityProfile; } } + public ISbmSecurityProfile_Core SecurityProfile { get { return securityProfile_Internal; } } //------------------------------------------------------------------- // Organisation. @@ -173,12 +174,13 @@ namespace Sarand.BusinessMgr.Web.Portal.App { base.Refresh(); // Check that user matches the session. - if (!SysId.IsValid(securityItemUser?.Id) || (SysId.IsValid(SecurityItemId) && !SecurityItemId.Equals(securityItemUser.Id))) + // Can only refresh existing User or populate a new one (with no details previously recorded). + if (SysId.IsValid(securityItemUser?.Id) && SysId.IsValid(SecurityItemId) && SysId.ToLong(SecurityItemId) != SysId.ToLong(securityItemUser.Id)) { SaLogger.Warn($"{SysUtil.Fn} Invalid - User Security Item doesn't match session. Expected Id: {SecurityItemId}. Found: {securityItemUser?.Id}."); return false; } - if (!SysId.IsValid(affiliateUser?.Id) || (SysId.IsValid(AffiliateId) && !AffiliateId.Equals(affiliateUser.Id))) + if (SysId.IsValid(affiliateUser?.Id) && SysId.IsValid(AffiliateId) && SysId.ToLong(AffiliateId) != SysId.ToLong(affiliateUser.Id)) { SaLogger.Warn($"{SysUtil.Fn} Invalid - User Affiliate doesn't match session. Expected Id: {AffiliateId}. Found: {affiliateUser?.Id}."); return false; diff --git a/SaBusinessMgrCoreWebPortal/Portal/Controllers/SbmController_Portal.cs b/SaBusinessMgrCoreWebPortal/Portal/Controllers/SbmController_Portal.cs index 059c142..24b9d12 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Controllers/SbmController_Portal.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Controllers/SbmController_Portal.cs @@ -109,7 +109,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers /// protected override SbmSessionData_Web DeserializeSessionData(String sessionDataJson) { - return JsonConvert.DeserializeObject(sessionDataJson, SbmSessionDataJsonSerializerSettings); + return JsonConvert.DeserializeObject(sessionDataJson, SessionDataJsonDeserializerSettings); } //------------------------------------------------------------------- @@ -180,7 +180,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers } // Configure security profile. - sessionDataForUser.SecurityProfile_Core.RefreshProfile(internalsForUser.SessionConsole, internalsForUser.SecurityConsole, securityItemUser.Id, internalsForUser.SecurityConsole.GetRegisteredRights()); + sessionDataForUser.SecurityProfile.RefreshProfile(internalsForUser.SessionConsole, internalsForUser.SecurityConsole, securityItemUser.Id, internalsForUser.SecurityConsole.GetRegisteredRights()); // 'Keep logged in' can be switched off by the application configuration. keepLoggedIn = App.AppConfig.Security.SignIn.KeepLoggedIn.Allowed && keepLoggedIn; diff --git a/SaBusinessMgrCoreWebPortal/Portal/Models/Assets/Model_AssetCategory-Config.cs b/SaBusinessMgrCoreWebPortal/Portal/Models/Assets/Model_AssetCategory-Config.cs index eb3aa09..e3d1863 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Models/Assets/Model_AssetCategory-Config.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Models/Assets/Model_AssetCategory-Config.cs @@ -95,8 +95,8 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Assets ViewMode pageMode = GetFilterMode(); bool modeIsSearch = pageMode == ViewMode.Search; bool modeIsMaintain = pageMode == ViewMode.Maintain; - bool userCanCreateAssetCategories = SessionData_Portal.SecurityProfile_Core.UserCanCreateItemType(Sbm.ItemType.AssetCategory); - bool userCanUpdateAssetCategories = SessionData_Portal.SecurityProfile_Core.UserCanUpdateItemType(Sbm.ItemType.AssetCategory); + bool userCanCreateAssetCategories = SessionData_Portal.SecurityProfile.UserCanCreateItemType(Sbm.ItemType.AssetCategory); + bool userCanUpdateAssetCategories = SessionData_Portal.SecurityProfile.UserCanUpdateItemType(Sbm.ItemType.AssetCategory); // Actions. @@ -187,8 +187,8 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Assets public void PopulateItemConfig(ItemConfigData config, ItemId assetCategoryId, Sbm.AssetType assetType) { // Session configuration. - bool userCanCreateAssetCategories = SessionData_Portal.SecurityProfile_Core.UserCanCreateItemType(Sbm.ItemType.AssetCategory); - bool userCanUpdateAssetCategories = SessionData_Portal.SecurityProfile_Core.UserCanUpdateItemType(Sbm.ItemType.AssetCategory); + bool userCanCreateAssetCategories = SessionData_Portal.SecurityProfile.UserCanCreateItemType(Sbm.ItemType.AssetCategory); + bool userCanUpdateAssetCategories = SessionData_Portal.SecurityProfile.UserCanUpdateItemType(Sbm.ItemType.AssetCategory); // Update dialog title. config.Title = Localiser["caption.vehicle.update"]; diff --git a/SaBusinessMgrCoreWebPortal/Portal/Models/SbmModel_Portal.cs b/SaBusinessMgrCoreWebPortal/Portal/Models/SbmModel_Portal.cs index 02a9341..2773162 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Models/SbmModel_Portal.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Models/SbmModel_Portal.cs @@ -33,13 +33,13 @@ namespace Sarand.BusinessMgr.Web.Portal.Models //------------------------------------------------------------------- // Security profile. - public bool UserCanViewFinancials() { return SessionData_Portal.SecurityProfile_Core.UserCanViewFinancials(); } - public bool UserCanViewCommodityFinancials_Supply() { return SessionData_Portal.SecurityProfile_Core.UserCanViewFinancials(FinancialType.CommoditySupply); } - public bool UserCanViewCommodityFinancials_Sales() { return SessionData_Portal.SecurityProfile_Core.UserCanViewFinancials(FinancialType.CommoditySales); } - public bool UserCanUpdateFinancials() { return SessionData_Portal.SecurityProfile_Core.UserCanUpdateFinancials(); } - public bool UserCanStockTake() { return SessionData_Portal.SecurityProfile_Core.UserHasSecurityRight(SecurityRight.StockTake); } - public bool UserCanCreateItem(Sbm.ItemType itemType) { return SessionData_Portal.SecurityProfile_Core.UserCanCreateItemType(itemType); } - public bool UserCanUpdateItem(Sbm.ItemType itemType) { return SessionData_Portal.SecurityProfile_Core.UserCanUpdateItemType(itemType); } + public bool UserCanViewFinancials() { return SessionData_Portal.SecurityProfile.UserCanViewFinancials(); } + public bool UserCanViewCommodityFinancials_Supply() { return SessionData_Portal.SecurityProfile.UserCanViewFinancials(FinancialType.CommoditySupply); } + public bool UserCanViewCommodityFinancials_Sales() { return SessionData_Portal.SecurityProfile.UserCanViewFinancials(FinancialType.CommoditySales); } + public bool UserCanUpdateFinancials() { return SessionData_Portal.SecurityProfile.UserCanUpdateFinancials(); } + public bool UserCanStockTake() { return SessionData_Portal.SecurityProfile.UserHasSecurityRight(SecurityRight.StockTake); } + public bool UserCanCreateItem(Sbm.ItemType itemType) { return SessionData_Portal.SecurityProfile.UserCanCreateItemType(itemType); } + public bool UserCanUpdateItem(Sbm.ItemType itemType) { return SessionData_Portal.SecurityProfile.UserCanUpdateItemType(itemType); } //------------------------------------------------------------------- // Page menu options. diff --git a/SaBusinessMgrCoreWebPortal/Portal/Models/Vehicles/Model_Vehicle-Config.cs b/SaBusinessMgrCoreWebPortal/Portal/Models/Vehicles/Model_Vehicle-Config.cs index 92d1b87..1f44e64 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Models/Vehicles/Model_Vehicle-Config.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Models/Vehicles/Model_Vehicle-Config.cs @@ -103,8 +103,8 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Vehicles ViewMode pageMode = GetFilterMode(); bool modeIsSearch = pageMode == ViewMode.Search; bool modeIsMaintain = pageMode == ViewMode.Maintain; - bool userCanCreateVehicles = SessionData_Portal.SecurityProfile_Core.UserCanCreateItemType(Sbm.ItemType.Vehicle); - bool userCanUpdateVehicles = SessionData_Portal.SecurityProfile_Core.UserCanUpdateItemType(Sbm.ItemType.Vehicle); + bool userCanCreateVehicles = SessionData_Portal.SecurityProfile.UserCanCreateItemType(Sbm.ItemType.Vehicle); + bool userCanUpdateVehicles = SessionData_Portal.SecurityProfile.UserCanUpdateItemType(Sbm.ItemType.Vehicle); // Actions. @@ -196,8 +196,8 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Vehicles public void PopulateItemConfig(ItemConfigData config, ItemId vehicleItemId) { // Session configuration. - bool userCanCreateVehicles = SessionData_Portal.SecurityProfile_Core.UserCanCreateItemType(Sbm.ItemType.Vehicle); - bool userCanUpdateVehicles = SessionData_Portal.SecurityProfile_Core.UserCanUpdateItemType(Sbm.ItemType.Vehicle); + bool userCanCreateVehicles = SessionData_Portal.SecurityProfile.UserCanCreateItemType(Sbm.ItemType.Vehicle); + bool userCanUpdateVehicles = SessionData_Portal.SecurityProfile.UserCanUpdateItemType(Sbm.ItemType.Vehicle); // Update dialog title. config.Title = Localiser["caption.vehicle.update"];