266 lines
12 KiB
C#
266 lines
12 KiB
C#
using Microsoft.AspNetCore.Mvc;
|
|
using Newtonsoft.Json;
|
|
using NPOI.SS.Formula.Functions;
|
|
using Sarand.BusinessMgr.Net.Core.Web.App;
|
|
using Sarand.BusinessMgr.Web.Control.App;
|
|
using Sarand.BusinessMgr.Web.Control.Consoles;
|
|
using Sarand.BusinessMgr.Net.Core.Web.Controllers;
|
|
using Sarand.BusinessMgr.Net.Standard.Base.App;
|
|
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
|
|
using Sarand.BusinessMgr.Net.Standard.Base.Consoles;
|
|
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 System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.Web;
|
|
using Sarand.BusinessMgr.Net.Standard.Base.Core;
|
|
using static Sarand.BusinessMgr.Net.Standard.Base.App.Sbm.Config.Database;
|
|
using System.Net.Mail;
|
|
|
|
namespace Sarand.BusinessMgr.Web.Control.Controllers
|
|
{
|
|
//======================================================================
|
|
public class SbmController_Control : SbmController_Web
|
|
{
|
|
//-------------------------------------------------------------------
|
|
// Data members.
|
|
|
|
//-------------------------------------------------------------------
|
|
// Protected construction.
|
|
protected SbmController_Control(SbmLocaliser localiser, Microsoft.Extensions.Logging.ILogger<SbmController_Control> logger) : base(localiser)
|
|
{
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Session data.
|
|
//-------------------------------------------------------------------
|
|
/// <summary>Control session data</summary>
|
|
protected SbmSessionData_Control SessionData_Control { get { return SessionData as SbmSessionData_Control; } }
|
|
public override ISbmUserContext_Core UserContext_Core { get { return new SbmUserContext_Control(SessionData_Control); } }
|
|
protected override SbmSessionData_Web DeserializeSessionData(String sessionDataJson)
|
|
{
|
|
return JsonConvert.DeserializeObject<SbmSessionData_Control>(sessionDataJson, SbmSessionDataJsonSerializerSettings);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Globals.
|
|
//-------------------------------------------------------------------
|
|
public SbmEmailConsole_Control EmailConsole_Control { get { return Internals.EmailConsole as SbmEmailConsole_Control; } }
|
|
|
|
//-------------------------------------------------------------------
|
|
// 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 + ": " + AccountMgr.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 (StringUtil.IsEmpty(userAccount.EmailAddress)) 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 (StringUtil.IsEmpty(GetRequestCookie(KeepLoggedIn.CookieName))) return;
|
|
// Clear cookie by setting expiry date in the past.
|
|
SetResponseCookie(KeepLoggedIn.CookieName, "", DateTime.Today.AddDays(-7), true);
|
|
SaLogger.Info($"{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 (!StringUtil.IsEmpty(encryptedParams))
|
|
{
|
|
NameValueCollection linkParams = DecryptParameters(encryptedParams, KeepLoggedIn.Base64IV, KeepLoggedIn.Base64Key);
|
|
keepLoggedInParams.UserAccountId = new SysId(StringUtil.ToInt(linkParams.Get(ParamName_KeepLoggedIn_UserAccountId)));
|
|
}
|
|
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);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
}
|
|
//======================================================================
|
|
}
|