3.0.0.0 Initial deployment.
This commit is contained in:
@@ -0,0 +1,289 @@
|
||||
using Newtonsoft.Json;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.App;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.Components;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.App;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Consoles;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Core;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Mgr.Control;
|
||||
using Sarand.BusinessMgr.Web.Control.App;
|
||||
using Sarand.BusinessMgr.Web.Control.Consoles;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Bus;
|
||||
using Sarand.Net.Standard.Base.Core;
|
||||
using Sarand.Net.Standard.Base.Email;
|
||||
using Sarand.Net.Standard.Base.Helpers;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Web;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Components
|
||||
{
|
||||
//======================================================================
|
||||
public class SbmController_Control : SbmController_Web
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
private SbmInternals_Control internals_Control;
|
||||
private SbmEmailConsole_Control emailConsole;
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Protected construction.
|
||||
protected SbmController_Control(SbmLocaliser localiser, Microsoft.Extensions.Logging.ILogger<SbmController_Control> logger) : base(localiser)
|
||||
{
|
||||
}
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (internals_Control != null) internals_Control.Dispose(); internals_Control = null;
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Properties.
|
||||
public SbmInternals_Control Internals_Control { get { if (internals_Control == null) { internals_Control = new SbmInternals_Control(); } return internals_Control; } }
|
||||
protected SbmcAccountMgr ControlAccountMgr { get { return Internals_Control.ControlAccountMgr; } }
|
||||
protected SbmcInstanceMgr ControlInstanceMgr { get { return Internals_Control.ControlInstanceMgr; } }
|
||||
protected SbmcItemMgr ControlItemMgr { get { return Internals_Control.ControlItemMgr; } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Session data.
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>Control session data</summary>
|
||||
protected SbmApplication_Control App_Control { get { return SbmApplication_Control.Instance; } }
|
||||
protected SbmSessionData_Control SessionData_Control { get { return SessionData as SbmSessionData_Control; } }
|
||||
protected override SbmSessionData_Web DeserializeSessionData(string sessionDataJson)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<SbmSessionData_Control>(sessionDataJson, SessionDataJsonDeserializerSettings);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Email console.
|
||||
//-------------------------------------------------------------------
|
||||
public SaEmailConfig EmailConfig { get { return App_Control.EmailConfig; } }
|
||||
public SbmEmailConsole_Control EmailConsole_Control
|
||||
{
|
||||
get
|
||||
{
|
||||
if (emailConsole == null)
|
||||
emailConsole = SbmApplication_Control.CreateEmailConsole_Control(App_Control.EmailConfig);
|
||||
return emailConsole;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// User Session.
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Sign the account in.
|
||||
/// <para>- Record login time and clear invalid login data.</para>
|
||||
/// <para>- Open user session - store details against Sesion Data.</para>
|
||||
/// <para>- Set 'Keep logged in' cookie if required.</para>
|
||||
/// </summary>
|
||||
protected bool SignUserIn(SbmcUserAccount userAccount, bool keepLoggedIn, ref string errorMessage)
|
||||
{
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
// Validate User Account, just in case - should have been done before arriving here!
|
||||
if (userAccount == null)
|
||||
{
|
||||
SaLogger.Error($"{SysUtil.Fn} Unable to sign account in - null user account.");
|
||||
errorMessage = Localiser["security.msg.signin.invalid"];
|
||||
return false;
|
||||
}
|
||||
if (!userAccount.IsStatusActive)
|
||||
{
|
||||
SaLogger.Error($"{SysUtil.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;
|
||||
|
||||
userAccount.KeepLoggedIn = keepLoggedIn;
|
||||
userAccount.LastLoginAt = DateTime.Now;
|
||||
userAccount.InvalidLoginCount = 0;
|
||||
userAccount.LastInvalidLoginAt = null;
|
||||
if (!ControlAccountMgr.UpdateUserAccount(userAccount, DbUpdateInfo))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to update User Account for " + userAccount.EmailAddress + ": " + ControlAccountMgr.ErrorStateMessage);
|
||||
errorMessage = Localiser["security.msg.signin.invalid"];
|
||||
return false;
|
||||
}
|
||||
SaLogger.Info($"{SysUtil.Fn} User session opened for " + userAccount.EmailAddress, true);
|
||||
|
||||
// Create session.
|
||||
OpenUserSession(SessionContext, userAccount);
|
||||
|
||||
// Send the 'Keep Logged In' cookie if requested.
|
||||
if (userAccount.KeepLoggedIn)
|
||||
SetKeepLoggedInCookie();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>Start a new user session on sign-in.</summary>
|
||||
protected bool OpenUserSession(SbmSessionContext sessionContext, SbmcUserAccount userAccount)
|
||||
{
|
||||
// Validate user properties.
|
||||
if (!SysId.IsValid(userAccount.Id)) return false;
|
||||
if (userAccount.EmailAddress.IsEmpty()) return false;
|
||||
|
||||
// Close previous session.
|
||||
CloseUserSession();
|
||||
|
||||
// Open session.
|
||||
return SetSessionData(new SbmSessionData_Control(sessionContext, userAccount));
|
||||
}
|
||||
protected void CloseUserSession()
|
||||
{
|
||||
ClearSessionData();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Keep Logged In.
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Check for 'Keep Logged In' cookie and auto-login user if details are valid.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected bool AutoLoginUser()
|
||||
{
|
||||
// NB Only allowed if there isn't a current user session.
|
||||
if (ValidUserSession) return true;
|
||||
if (!AppConfig.Security.SignIn.KeepLoggedIn.Allowed) return false;
|
||||
|
||||
// Extract cookie details.
|
||||
string encryptedParams = GetRequestCookie(KeepLoggedIn.CookieName);
|
||||
KeepLoggedInParams keepLoggedInParams = ExtractKeepLoggedInParams(encryptedParams);
|
||||
string logSuffix = keepLoggedInParams.GetValidParams().Concat("", " ", ".");
|
||||
if (keepLoggedInParams.IsValid())
|
||||
{
|
||||
using (SbmcUserAccount userAccount = ControlAccountMgr.GetUserAccountById(keepLoggedInParams.UserAccountId))
|
||||
{
|
||||
if (userAccount != null)
|
||||
{
|
||||
if (userAccount.IsStatusActive)
|
||||
{
|
||||
string errorMessage = "";
|
||||
if (SignUserIn(userAccount, true, ref errorMessage))
|
||||
{
|
||||
// Successful login.
|
||||
SaLogger.Info($"{SysUtil.Fn} Keep Logged In cookie found." + logSuffix);
|
||||
return true;
|
||||
}
|
||||
else SaLogger.Warn($"{SysUtil.Fn} Keep Logged In cookie found but failed to sign user in: " + errorMessage + "." + logSuffix);
|
||||
}
|
||||
else SaLogger.Warn($"{SysUtil.Fn} Keep Logged In cookie found but UserAccount is not active." + logSuffix);
|
||||
}
|
||||
else SaLogger.Warn($"{SysUtil.Fn} Keep Logged In cookie found but UserAccount not found." + logSuffix);
|
||||
}
|
||||
}
|
||||
ClearKeepLoggedInCookie();
|
||||
return false;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>Keep-Logged-In cookie.</summary>
|
||||
protected void SetKeepLoggedInCookie()
|
||||
{
|
||||
string keepLoggedInCookieValue = GenerateKeepLoggedInCookieValue();
|
||||
int cookieExpiresDays = Math.Max(Sbm.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Min, Math.Min(Sbm.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Max, AppConfig.Security.SignIn.KeepLoggedIn.CookieExpiresDays));
|
||||
SetResponseCookie(KeepLoggedIn.CookieName, keepLoggedInCookieValue, DateTime.Now.AddDays(cookieExpiresDays), true);
|
||||
SaLogger.Debug($"{SysUtil.Fn} 'Keep Logged In' cookie set. Value: " + keepLoggedInCookieValue);
|
||||
}
|
||||
|
||||
/// <summary>Set the 'Keep Logged In' response cookie to expired with no data.</summary>
|
||||
public void ClearKeepLoggedInCookie()
|
||||
{
|
||||
if (GetRequestCookie(KeepLoggedIn.CookieName).IsEmpty()) return;
|
||||
// Clear cookie by setting expiry date in the past.
|
||||
SetResponseCookie(KeepLoggedIn.CookieName, "", DateTime.Today.AddDays(-7), true);
|
||||
SaLogger.Debug($"{SysUtil.Fn} '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.
|
||||
public static class KeepLoggedIn
|
||||
{
|
||||
public static string CookieName = "BCKLI";
|
||||
public static string Base64Key = SacsEncryptionHelper.EncodePlainKey("$BusinessMgr+KeEp-1OgG3d=1n}*32?", SacsEncryptionHelper.KeyLength);
|
||||
public static string Base64IV = "fpHMZchDkrJzgjVMrjpdHg==";
|
||||
//public static String Base64IV_Generated = SacsEncryptionHelper.InitSymmetricEncryptionIV(KeepLoggedIn.Base64Key);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// 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_Control.User.UserAccountId));
|
||||
return EncryptParameters(keepLoggedInParams, KeepLoggedIn.Base64IV, KeepLoggedIn.Base64Key);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
public class KeepLoggedInParams
|
||||
{
|
||||
public SysId UserAccountId { get; set; }
|
||||
/// <summary>Retrieve the list of valid parameters in printable format [Name: Value].</summary>
|
||||
public List<string> GetValidParams()
|
||||
{
|
||||
List<string> values = new List<string>();
|
||||
if (SysId.IsValid(UserAccountId))
|
||||
values.Add("UserAccountId: " + SysId.ToString(UserAccountId));
|
||||
return values;
|
||||
}
|
||||
/// <summary>Verifiy that the account details are populated.</summary>
|
||||
public bool IsValid()
|
||||
{
|
||||
return
|
||||
SysId.IsValid(UserAccountId);
|
||||
}
|
||||
}
|
||||
public KeepLoggedInParams ExtractKeepLoggedInParams(string encryptedParams)
|
||||
{
|
||||
KeepLoggedInParams keepLoggedInParams = new KeepLoggedInParams();
|
||||
if (!encryptedParams.IsEmpty())
|
||||
{
|
||||
NameValueCollection linkParams = DecryptParameters(encryptedParams, KeepLoggedIn.Base64IV, KeepLoggedIn.Base64Key);
|
||||
keepLoggedInParams.UserAccountId = new SysId(linkParams.Get(ParamName_KeepLoggedIn_UserAccountId).ToInt());
|
||||
}
|
||||
if (DebugMode) SaLogger.Info($"{SysUtil.Fn} 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_ControlApp { get; } = SacsEncryptionHelper.EncodePlainKey("BusinessMgr=Control&Encryption-" + DateTime.Today.Format("ddMMyy"), SacsEncryptionHelper.KeyLength);
|
||||
protected string EncryptSessionParams(NameValueCollection parameters)
|
||||
{
|
||||
return HttpUtility.UrlEncode(EncryptParameters(parameters, SessionData_Control.Base64IV_ControlApp, Base64Key_ControlApp));
|
||||
}
|
||||
public static string EncryptSessionParams(SbmController_Control controller, NameValueCollection parameters)
|
||||
{
|
||||
return controller.EncryptSessionParams(parameters);
|
||||
}
|
||||
protected NameValueCollection ExtractSessionParams(string encryptedParams)
|
||||
{
|
||||
return DecryptParameters(encryptedParams, SessionData_Control.Base64IV_ControlApp, Base64Key_ControlApp);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//======================================================================
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.App;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.Components;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.App;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Core;
|
||||
using Sarand.BusinessMgr.Web.Control.App;
|
||||
using Sarand.Net.Standard.Base.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Components
|
||||
{
|
||||
//=======================================================================
|
||||
public class SbmModel_Control : SbmModel_Base
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Protected construction.
|
||||
protected SbmModel_Control(SbmLocaliser localiser) :
|
||||
base(localiser)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Properties.
|
||||
protected SbmApplication_Control App_Control { get { return SbmApplication_Control.Instance; } }
|
||||
protected SbmSessionData_Control SessionData_Control { get { return SessionData_Web as SbmSessionData_Control; } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
public IEnumerable<SelectListItem> GetInstanceContextsForSelect()
|
||||
{
|
||||
List<SelectListItem> items = new List<SelectListItem>();
|
||||
SbmcRepository.Params prmRepository = new SbmcRepository.Params();
|
||||
prmRepository.StatusCat = SbmcRepository.StatusCat.NotDeleted;
|
||||
foreach (SbmcRepository repository in ControlInstanceMgr.GetRepositoryList(prmRepository))
|
||||
items.Add(new SelectListItem(repository.Context, repository.Context));
|
||||
return items;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Form control value max length - BusinessMgr tables.
|
||||
/// <para>In many cases the database columns allow plenty of extra space for the value, in case they need to be encrypted.</para>
|
||||
/// <para>Hence, several cases where the value entered in the control isn't allowed the full database length.</para>
|
||||
/// </summary>
|
||||
public int GetControlMaxLength(SbmDbDfn_Control.ColumnIdx column)
|
||||
{
|
||||
int maxLength = (int)SbmDbDfn_Control.GetColTextLen(column);
|
||||
switch (column)
|
||||
{
|
||||
case SbmDbDfn_Control.ColumnIdx.UserAccount_EmailAddress:
|
||||
return Math.Min(maxLength, 100);
|
||||
case SbmDbDfn_Control.ColumnIdx.UserAccount_FirstName:
|
||||
case SbmDbDfn_Control.ColumnIdx.UserAccount_LastName:
|
||||
return Math.Min(maxLength, 40);
|
||||
case SbmDbDfn_Control.ColumnIdx.UserAccount_PhoneMain:
|
||||
case SbmDbDfn_Control.ColumnIdx.UserAccount_PhoneMobile:
|
||||
case SbmDbDfn_Control.ColumnIdx.UserAccount_PhoneAlt:
|
||||
return Math.Min(maxLength, 20);
|
||||
case SbmDbDfn_Control.ColumnIdx.ItemParam_Value:
|
||||
if (maxLength == 0 || maxLength > 2048) return 2048;
|
||||
break;
|
||||
}
|
||||
return maxLength;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
Reference in New Issue
Block a user