using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Routing; using Microsoft.Net.Http.Headers; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using Sacs.Web.Components; using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.Core; using Sarand.Net.Standard.Base.Dbas; using Sarand.Net.Standard.Base.Email; using Sarand.Net.Standard.Base.Helpers; using Sarand.Net.Standard.Base.Utils; using Saxonite.Net.Core.Web.Base.App; using Saxonite.Net.Core.Web.Base.Consoles; using Saxonite.Net.Core.Web.Base.Models; using Saxonite.Net.Standard.Base.App; using Saxonite.Net.Standard.Base.Bus; using Saxonite.Net.Standard.Base.Consoles; using Saxonite.Net.Standard.Base.Mgr; using Saxonite.Net.Standard.Base.Mgr.Reference; using Saxonite.Net.Standard.Base.Mgr.Support; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Globalization; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Web; namespace Saxonite.Net.Core.Web.Base.Controllers { //======================================================================= public abstract class SaxController_Base : SaController { //------------------------------------------------------------------- // Data members. private SaxInternals internals; //------------------------------------------------------------------- // Protected construction. protected SaxController_Base(SaxLocaliser localiser) { Localiser = localiser; // Initialise console (debug) logging listener. //if (Directory.Exists(App.LoggingRootFolder) && SaxSystem.DebugMode) // SaxConsoleListener.ConfigureDebugListener(App.LoggingRootFolder); if (DebugMode) SaLogger.Trace(GetType().Name + ".CTR"); } protected override void Dispose(bool disposing) { if (disposing) { } base.Dispose(disposing); } //------------------------------------------------------------------- // Properties. protected SaxApplication_Web App { get { return SaxApplication_Web.WebApp; } } public SaxAppConfig AppConfig { get { return App.AppConfig; } } public SaxSessionContext SessionContext { get { return App.SessionContext; } } public SaxLocaliser Localiser { get; } //------------------------------------------------------------------- // Globals. protected SaxConsoleHelper ConsoleHelper { get { return SaxGlobals.ConsoleHelper; } } //------------------------------------------------------------------- // Business components. public SaxInternals Internals { get { if (internals == null) internals = new SaxInternals(SessionContext); return internals; } } // Consoles. public SaxDataCacheConsole DataCacheConsole { get { return Internals.DataCacheConsole; } } public SaxSessionDataConsole SessionDataConsole { get { return Internals.SessionDataConsole; } } public SaxActivityConsole ActivityConsole { get { return Internals.ActivityConsole; } } // Managers. public SaxReferenceMgr ReferenceMgr { get { return Internals.ReferenceMgr; } } public SaxAccountMgr AccountMgr { get { return Internals.AccountMgr; } } public SaxActivityMgr ActivityMgr { get { return Internals.ActivityMgr; } } public SaxItemDataMgr ItemDataMgr { get { return Internals.ItemDataMgr; } } public SaxReferenceGolfMgr ReferenceGolfMgr { get { return Internals.ReferenceGolfMgr; } } public SaxSupportMgr SupportMgr { get { return Internals.SupportMgr; } } // System codes. public List DataItemTypeCodes { get { return Internals.DataItemTypeCodes; } } public List SportSpecialityLabelCodes { get { return Internals.SportSpecialityLabelCodes; } } public List SportContactTypeCodes { get { return Internals.SportContactTypeCodes; } } public List SportParticipatingCodes { get { return Internals.SportParticipatingCodes; } } public List SportPreparingCodes { get { return Internals.SportPreparingCodes; } } public List SportOfficiatingCodes { get { return Internals.SportOfficiatingCodes; } } public List RegionTypeCodes { get { return Internals.RegionTypeCodes; } } public List EligibilityTypeCodes { get { return Internals.EligibilityTypeCodes; } } public List UserRoleCodes { get { return Internals.UserRoleCodes; } } public List OrganisationTypeCodes { get { return Internals.OrganisationTypeCodes; } } public List OrganisationCategoryCodes { get { return Internals.OrganisationCategoryCodes; } } public List OrganisationRoleCodes { get { return Internals.OrganisationRoleCodes; } } public List AssociationTypeCodes { get { return Internals.AssociationTypeCodes; } } public List AddressTypeCodes { get { return Internals.AddressTypeCodes; } } public List ContactMethodCodes { get { return Internals.ContactMethodCodes; } } public List EventTypeCodes { get { return Internals.EventTypeCodes; } } public List OpportunityTypeCodes { get { return Internals.OpportunityTypeCodes; } } public List ActivityReactionCodes { get { return Internals.ActivityReactionCodes; } } // Support. public List UserReportTypeCodes { get { return Internals.UserReportTypeCodes; } } // Email. public SaEmailConfig EmailConfig { get { return App.EmailConfig; } } public SaxEmailConsole EmailConsole { get { return Internals.EmailConsole; } } //------------------------------------------------------------------- // Action execution events. //------------------------------------------------------------------- public override void OnActionExecuting(ActionExecutingContext filterContext) { //if (DebugMode) SaLogger.Debug(GetType().Name + ".OnActionExecuting" + (ViewData.Model != null ? " Model: " + ViewData.Model : "")); // Refreh model properties. SaxModel_Base baseModel = ViewData.Model as SaxModel_Base; RefreshModel(baseModel); base.OnActionExecuting(filterContext); } public override void OnActionExecuted(ActionExecutedContext filterContext) { //if (DebugMode) SaLogger.Debug(GetType().Name + ".OnActionExecuted" + (ViewData.Model != null ? " Model: " + ViewData.Model : "")); // Refreh model properties. SaxModel_Base baseModel = ViewData.Model as SaxModel_Base; RefreshModel(baseModel); // Store session data if present - as potentially modfied. if (SessionData != null) { // Most actions will store the session data in case it's been modified. // Some actions (eg. score password) don't need to. if (StoreSessionData) { String sessionDataJson = JsonConvert.SerializeObject(SessionData); HttpContext.Session.Set(SessionStorage.SessionKey_SessionData, SessionStorage.sessionDataEncoding.GetBytes(sessionDataJson)); } } else HttpContext.Session.Remove(SessionStorage.SessionKey_SessionData); // Response headers. if (baseModel != null && baseModel.ProhibitCache) { if (Response.Headers.ContainsKey(HeaderNames.CacheControl)) Response.Headers.Remove(HeaderNames.CacheControl); Response.Headers.Add(HeaderNames.CacheControl, "no-cache, no-store, must-revalidate"); Response.Headers.Add(HeaderNames.Pragma, "no-cache"); Response.Headers.Add(HeaderNames.Expires, "0"); } base.OnActionExecuted(filterContext); } //------------------------------------------------------------------- // Model. //------------------------------------------------------------------- protected void RefreshModel(SaxModel_Base model) { if (model == null) return; // Session data. model.Controller = this; model.SessionData = SessionData; // Redirect locations. model.RedirectLocation.Base = GetRedirectLocation(RedirectLocation.WebAppBase); model.RedirectLocation.Home = GetRedirectLocation(RedirectLocation.WebAppHome); model.RedirectLocation.Home_SessionExpired = GetRedirectLocation(RedirectLocation.WebAppHome_SessionExpired); } //------------------------------------------------------------------- // Parameters. //------------------------------------------------------------------- // Online Request Password encryption parameters (key & IV) are defined in // the base Controller so that they can be shared with the Adnin application. // NB IV value is generated from the key and fixed so that it doesn't change when the application restarts. // IV Must be regenerated if key is changed. protected static String Base64Key_RequestPassword_Online { get; } = SacsEncryptionHelper.EncodePlainKey("{~Saxonite@@Request^Password=}", SacsEncryptionHelper.KeyLength); //private static String Base64IV_RequestPassword_Generated_Online { get; } = SacsEncryptionHelper.InitSymmetricEncryptionIV(Base64Key_RequestPassword_Online); protected static String Base64IV_RequestPassword_Online { get; } = "1VAWYTW6d72n/6HDNhbqow=="; //------------------------------------------------------------------- // Session. //------------------------------------------------------------------- private bool UserActiveRecorded { get; set; } = false; public bool ValidUserSession { get { if (SessionData == null || !SessionData.ValidUserSession) return false; SaxSessionDataConsole sessionDataConsole = SessionDataConsole; if (sessionDataConsole != null && !UserActiveRecorded) { // Record the user as active once per request. sessionDataConsole.RecordUserActive(SessionUserAccountId, CreatedAt); UserActiveRecorded = true; } return true; } } public SysId SessionUserAccountId { get { return SessionData?.User?.UserAccountId ?? null; } } public DbUpdateInfo DbUpdateInfo { get { return SessionData?.DbUpdateInfo ?? new DbUpdateInfo(null, "", SaxSystem.ComputerName); } } // Referrer. public String Referer { get { return Request.Headers["Referer"].ToString(); } } public bool ValidReferer { get { return !StringUtil.IsEmpty(Referer); } } /// Store session data, eg. to start a new user session on sign-in. protected bool SetSessionData(SaxSessionData sessionData) { if (sessionStorage.SessionData != null) return false; sessionStorage.SessionData = sessionData; return true; } protected void ClearSessionData() { if (HttpContext.Session.IsAvailable) HttpContext.Session.Clear(); sessionStorage.SessionData = null; } //------------------------------------------------------------------- // Session storage. public class SessionStorage { public SaxSessionData SessionData { get; set; } public static String SessionKey_SessionData { get; } = "SaxSessionData"; public static Encoding sessionDataEncoding { get; } = Encoding.UTF8; //JSON Serialisation. public class SaxSessionDataContractResolver : DefaultContractResolver { // To allow private properties to be deserialised. // NB Only applies to top level properties in the serialised object. protected override List GetSerializableMembers(Type objectType) { var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; MemberInfo[] fields = objectType.GetFields(flags); return fields .Concat(objectType.GetProperties(flags).Where(propInfo => propInfo.CanWrite)) .ToList(); } protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) { return base.CreateProperties(type, MemberSerialization.Fields); } }; public static JsonSerializerSettings JsonDeserialisationSettings = new JsonSerializerSettings { ContractResolver = new SaxSessionDataContractResolver(), // Force all deserialised objects to be replaced - to avoid proxy lists not populating. ObjectCreationHandling = ObjectCreationHandling.Replace }; } private readonly SessionStorage sessionStorage = new SessionStorage(); protected JsonSerializerSettings SaxSessionDataJsonSerializerSettings { get { return SessionStorage.JsonDeserialisationSettings; } } //------------------------------------------------------------------- public SaxSessionData SessionData { get { try { // Retrieve session data if not already in controller. if (sessionStorage.SessionData == null) { byte[] sessionDataBytes = null; HttpContext.Session.TryGetValue(SessionStorage.SessionKey_SessionData, out sessionDataBytes); if (sessionDataBytes != null) { String sessionDataJson = SessionStorage.sessionDataEncoding.GetString(sessionDataBytes); sessionStorage.SessionData = DeserializeSessionData(sessionDataJson); } } return sessionStorage.SessionData; } catch (Exception exc) { SaLogger.Warn(GetType().Name + ".SessionData " + exc.GetType().Name + " - " + exc.Message + exc.GetStackEntries().Concat("", "\n\t")); } return null; } } protected abstract SaxSessionData DeserializeSessionData(String sessionDataJson); //------------------------------------------------------------------- } //======================================================================= }