using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.Core; using Sarand.Net.Standard.Base.Helpers; using Sarand.Net.Standard.Base.Utils; using Saxonite.Net.Core.Web.Admin.App; using Saxonite.Net.Core.Web.Base.App; using Saxonite.Net.Core.Web.Base.Controllers; using Saxonite.Net.Standard.Base.App; using Saxonite.Net.Standard.Base.Bus; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Threading.Tasks; using System.Web; namespace Saxonite.Net.Core.Web.Admin.Controllers { //======================================================================= public class SaxController_Admin : SaxController_Base { //------------------------------------------------------------------- // Data members. //------------------------------------------------------------------- // Protected construction. protected SaxController_Admin(SaxLocaliser localiser, ILogger logger) : base(localiser) { } //------------------------------------------------------------------- // Session data. //------------------------------------------------------------------- /// Admin session data protected SaxSessionData_Admin SessionData_Admin { get { return SessionData as SaxSessionData_Admin; } } protected override SaxSessionData DeserializeSessionData(String sessionDataJson) { return JsonConvert.DeserializeObject(sessionDataJson, SaxSessionDataJsonSerializerSettings); } //------------------------------------------------------------------- // User Session. //------------------------------------------------------------------- /// /// Sign the account in. /// - Record login time and clear invalid login data. /// - Open user session - store details against Sesion Data. /// - Set 'Keep logged in' cookie if required. /// protected bool SignUserIn(SaxUserAccount userAccount, List userRoles, bool keepLoggedIn, ref String errorMessage) { String fn = GetType().Name + ".SignUserIn"; // Validate User Account, just in case - should have been done before arriving here! if (userAccount == null) { SaLogger.Error(fn + " Unable to sign account in - null user account."); errorMessage = Localiser["security.msg.signin.invalid"]; return false; } if (!userAccount.IsStatusActive) { SaLogger.Error(fn + " Unable to sign account in - user account is not active. Email: " + userAccount.EmailAddress); errorMessage = Localiser["security.msg.signin.invalid"]; return false; } // 'Keep logged in' can be switched off by the application configuration. keepLoggedIn = App.AppConfig.Security.SignIn.KeepLoggedIn.Allowed && keepLoggedIn; // Log user in - Admin details stored against System Admin role. SaxUserRole userRole = userRoles.Find(item => item.UserRole == Sax.UserRole.SystemAdmin); if (userRole == null) { SaLogger.Warn(fn + " System Admin role not found for " + userAccount.EmailAddress); errorMessage = Localiser["security.msg.signin.invalid"]; return false; } userRole.KeepLoggedIn = keepLoggedIn; userRole.LastLoginAt = DateTime.Now; userRole.InvalidLoginCount = 0; userRole.LastInvalidLoginAt = DateTime.MinValue; if (!AccountMgr.UpdateUserRole(userRole, DbUpdateInfo)) { SaLogger.Warn(fn + " Failed to update Admin user role for " + userAccount.EmailAddress + ": " + AccountMgr.ErrorStateMessage); errorMessage = Localiser["security.msg.signin.invalid"]; return false; } SaLogger.Info(fn + " User session opened for " + userAccount.EmailAddress, true); // Create session. OpenUserSession(SessionContext, userAccount, userRoles); // Send the 'Keep Logged In' cookie if requested. if (userRole.KeepLoggedIn) SetKeepLoggedInCookie(); return true; } //------------------------------------------------------------------- /// Start a new user session on sign-in. protected bool OpenUserSession(SaxSessionContext sessionContext, SaxUserAccount userAccount, List userRoles) { // Validate user properties. if (!SysId.IsValid(userAccount.Id)) return false; if (StringUtil.IsEmpty(userAccount.EmailAddress)) return false; // Close previous session. CloseUserSession(); // Open session. return SetSessionData(new SaxSessionData_Admin(sessionContext, userAccount, userRoles)); } protected void CloseUserSession() { ClearSessionData(); } //------------------------------------------------------------------- // IMage utilities. //------------------------------------------------------------------- /// /// Construct URL to display Organisation logo. /// - Option to allow for 'Upload' image if active. /// protected String GetOrganisationLogoLink(SysId organisationId, bool uploadImage) { String imageLink = "Image/Organisation/Logo" + (uploadImage ? "/Upload" : "") + "?"; NameValueCollection parameters = new NameValueCollection(); parameters.Add(ImageController.ParamName_OrganisationId, SysId.ToString(organisationId)); return imageLink + ImageController.ParamName_OrganisationLogoEncrypted + "=" + EncryptSessionParams(parameters); } //------------------------------------------------------------------- // Keep Logged In. //------------------------------------------------------------------- /// Keep-Logged-In cookie. protected void SetKeepLoggedInCookie() { String keepLoggedInCookieValue = GenerateKeepLoggedInCookieValue(); int cookieExpiresDays = Math.Max(Sax.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Min, Math.Min(Sax.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Max, AppConfig.Security.SignIn.KeepLoggedIn.CookieExpiresDays)); SetResponseCookie(CookieName_KeepLoggedIn, keepLoggedInCookieValue, DateTime.Now.AddDays(cookieExpiresDays), true); SaLogger.Info(this.GetType().Name + ".SetKeepLoggedInCookie 'Keep Logged In' cookie set. Value: " + keepLoggedInCookieValue); } /// Set the 'Keep Logged In' response cookie to expired with no data. public void ClearKeepLoggedInCookie() { if (StringUtil.IsEmpty(GetRequestCookie(CookieName_KeepLoggedIn))) return; // Clear cookie by setting expiry date in the past. SetResponseCookie(CookieName_KeepLoggedIn, "", DateTime.Today.AddDays(-7), true); SaLogger.Info(GetType().Name + ".ClearKeepLoggedInCookie 'Keep Logged In' cookie cleared."); } //------------------------------------------------------------------- // 'Keep Logged In' cookie and encryption config. // NB - IV value is generated from the key and fixed so that it doesn't change when the application restarts // to ensure that cookie value can be decrypted even if application restarts in the meantime. // - Generating IV from same key results in different value each time. // - IV Must be regenerated if key is changed: uncomment 'Generated' variable and copy resulting value to IV variable. // NB public static String CookieName_KeepLoggedIn = "SXKLI"; public static String Base64Key_KeepLoggedIn = SacsEncryptionHelper.EncodePlainKey("<=Saxonite^{kEEp+1Ogg3d-IN}~32*>", SacsEncryptionHelper.KeyLength); public static String Base64IV_KeepLoggedIn_Generated = SacsEncryptionHelper.InitSymmetricEncryptionIV(Base64Key_KeepLoggedIn); public static String Base64IV_KeepLoggedIn = "YOX10FIPp1zM1pg5U6fW7A=="; //------------------------------------------------------------------- // Keep-logged-in cookie value. private static readonly String ParamName_KeepLoggedIn_UserAccountId = "uai"; private String GenerateKeepLoggedInCookieValue() { NameValueCollection keepLoggedInParams = new NameValueCollection(); keepLoggedInParams.Add(ParamName_KeepLoggedIn_UserAccountId, SysId.ToString(SessionData_Admin.User.UserAccountId)); return EncryptParameters(keepLoggedInParams, Base64IV_KeepLoggedIn, Base64Key_KeepLoggedIn); } //------------------------------------------------------------------- public class KeepLoggedInParams { public SysId UserAccountId { get; set; } /// Retrieve the list of valid parameters in printable format [Name: Value]. public List GetValidParams() { List values = new List(); if (SysId.IsValid(UserAccountId)) values.Add("UserAccountId: " + SysId.ToString(UserAccountId)); return values; } } public KeepLoggedInParams ExtractKeepLoggedInParams(String encryptedParams) { KeepLoggedInParams keepLoggedInParams = new KeepLoggedInParams(); if (!StringUtil.IsEmpty(encryptedParams)) { NameValueCollection linkParams = DecryptParameters(encryptedParams, Base64IV_KeepLoggedIn, Base64Key_KeepLoggedIn); keepLoggedInParams.UserAccountId = new SysId(StringUtil.ToInt(linkParams.Get(ParamName_KeepLoggedIn_UserAccountId))); } if (DebugMode) SaLogger.Info(GetType().Name + ".ExtractKeepLoggedInParams UserAccountId: " + SysId.ToString(keepLoggedInParams.UserAccountId)); return keepLoggedInParams; } //------------------------------------------------------------------- // Parameter encryption, eg. for images. //------------------------------------------------------------------- // Encryption key is the same for all users each time the application is started, // but a different IV value is generated for each session so that the links/parameters cannot be shared. public static String Base64Key_AdminApp { get; } = SacsEncryptionHelper.EncodePlainKey("Saxonite&Admin#Encryption-"+DateTime.Today.Format("ddMMyy"), SacsEncryptionHelper.KeyLength); protected String EncryptSessionParams(NameValueCollection parameters) { return HttpUtility.UrlEncode(EncryptParameters(parameters, SessionData_Admin.Base64IV_AdminApp, Base64Key_AdminApp)); } public static String EncryptSessionParams(SaxController_Admin controller, NameValueCollection parameters) { return controller.EncryptSessionParams(parameters); } protected NameValueCollection ExtractSessionParams(String encryptedParams) { return DecryptParameters(encryptedParams, SessionData_Admin.Base64IV_AdminApp, Base64Key_AdminApp); } //------------------------------------------------------------------- } //======================================================================= }