Files
Saxonite/SaxoniteCoreWebOnline/Online/Controllers/SaxController_Online.cs
T
2021-06-15 15:16:50 +01:00

257 lines
12 KiB
C#

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.Dbas;
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.Controllers;
using Saxonite.Net.Core.Web.Online.App;
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;
namespace Saxonite.Net.Core.Web.Online.Controllers
{
//=======================================================================
public class SaxController_Online : SaxController_Base
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Protected construction.
protected SaxController_Online(SaxLocaliser localiser, Microsoft.Extensions.Logging.ILogger<HomeController> logger) : base(localiser)
{
}
//-------------------------------------------------------------------
// Session data.
//-------------------------------------------------------------------
/// <summary>Online session data</summary>
protected SaxSessionData_Online SessionData_Online { get { return SessionData as SaxSessionData_Online; } }
protected override SaxSessionData DeserializeSessionData(String sessionDataJson)
{
return JsonConvert.DeserializeObject<SaxSessionData_Online>(sessionDataJson, SaxSessionDataJsonSerializerSettings);
}
// Security profile.
public bool UserIsJournalist { get { return SessionData != null && SessionData.User != null && SessionData.User.IsJournalist; } }
//-------------------------------------------------------------------
// 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(SaxUserAccount userAccount, List<SaxUserRole> 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.failed"];
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.failed"];
return false;
}
// 'Keep logged in' can be switched off by the application configuration.
keepLoggedIn = App.SessionConfig.Security.SignIn.KeepLoggedIn.Allowed && keepLoggedIn;
// Log user in.
userAccount.KeepLoggedIn_Online = keepLoggedIn;
userAccount.LastLoginAt = DateTime.Now;
userAccount.InvalidLoginCount = 0;
userAccount.LastInvalidLoginAt = DateTime.MinValue;
if (!AccountMgr.UpdateUserAccount(userAccount, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to update account for " + userAccount.EmailAddress + ": " + AccountMgr.ErrorStateMessage);
errorMessage = Localiser["security.msg.signin.failed"];
return false;
}
SaLogger.Info(fn + " User session opened for " + userAccount.EmailAddress, true);
// Create session.
OpenUserSession(SessionContext, userAccount, userRoles);
RefreshSessionProfileSports();
// Send the 'Keep Logged In' cookie if requested.
if (userAccount.KeepLoggedIn_Online)
SetKeepLoggedInCookie();
return true;
}
//-------------------------------------------------------------------
/// <summary>Populate the user's profile contact details to the session.</summary>
protected bool RefreshSessionProfileContact(SaxUserAccount userAccount)
{
String fn = GetType().Name + ".RefreshSessionProfileContact";
SaxSessionData_Online sessionData = SessionData_Online;
SysId userAccountId = sessionData?.User.UserAccountId ?? null;
if (!SysId.IsValid(userAccountId)) return false;
if (!SysId.IsValid(userAccount?.Id ?? null)) return false;
if (!userAccountId.Equals(userAccount.Id)) return false;
sessionData.RefreshUser(userAccount);
return true;
}
//-------------------------------------------------------------------
/// <summary>Populate the user's profile sports to the session.</summary>
protected bool RefreshSessionProfileSports()
{
String fn = GetType().Name + ".RefreshSessionProfileSports";
SaxSessionData_Online sessionData = SessionData_Online;
SysId userAccountId = sessionData?.User.UserAccountId ?? null;
if (!SysId.IsValid(userAccountId)) return false;
sessionData.Profile.ClearSports();
// Read discrete values to avoid loading the entire UserSport records which can be substantial.
ColumnDfn colSportId = AccountMgr.GetUserSportColumnDfn(SaxUserSport.Column.SportId);
ColumnDfn colSportName = AccountMgr.GetUserSportColumnDfn(SaxUserSport.Column.Sport_Name);
ColumnDfn colParticipation = AccountMgr.GetUserSportColumnDfn(SaxUserSport.Column.SportParticipation);
ColumnDfn colOfficiating = AccountMgr.GetUserSportColumnDfn(SaxUserSport.Column.SportOfficiating);
ColumnDfn colStatus = AccountMgr.GetUserSportColumnDfn(SaxUserSport.Column.Status);
SaxUserSport.Params prmUserSport = new SaxUserSport.Params();
prmUserSport.UserAccountIdsIncluded.Add(userAccountId);
prmUserSport.StatusCat = SaxUserSport.StatusCat.ActiveOnly;
using (DbResultSet resultSet = AccountMgr.ExecuteUserSportQuery(new List<ColumnDfn>() { colSportId, colSportName, colParticipation, colOfficiating, colStatus }, prmUserSport))
{
while (resultSet != null && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
SysId sportId = new SysId(values.GetLong(colSportId.DataName));
String sportName = values.GetString(colSportName.DataName);
Sax.SportParticipation participation = (Sax.SportParticipation)values.GetLong(colParticipation.DataName);
Sax.SportOfficiating officiating = (Sax.SportOfficiating)values.GetLong(colOfficiating.DataName);
SaStatus status = SacsSystem.GetStatusForCode(values.GetString(colStatus.DataName));
sessionData.Profile.AddSport(sportId, status);
}
}
SaLogger.Debug(fn+" Profile Sports: " + sessionData.Profile.SportCount);
return true;
}
//-------------------------------------------------------------------
/// <summary>Start a new user session on sign-in.</summary>
protected bool OpenUserSession(SaxSessionContext sessionContext, SaxUserAccount userAccount, List<SaxUserRole> 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_Online(sessionContext, userAccount, userRoles));
}
protected void CloseUserSession()
{
ClearSessionData();
}
//-------------------------------------------------------------------
// Keep Logged In.
//-------------------------------------------------------------------
/// <summary>Keep-Logged-In cookie.</summary>
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, SessionConfig.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);
}
/// <summary>Set the 'Keep Logged In' response cookie to expired with no data.</summary>
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 - 'Keep Logged In' is termed 'Remember Me' in the Online application.
public static String CookieName_KeepLoggedIn = "SAXRM";
public static String Base64Key_KeepLoggedIn = SacsEncryptionHelper.EncodePlainKey("<=Saxonite^OnlIne{R$Member-m3}~>", SacsEncryptionHelper.KeyLength);
//public static String Base64IV_KeepLoggedIn_Generated = SacsEncryptionHelper.InitSymmetricEncryptionIV(Base64Key_KeepLoggedIn);
public static String Base64IV_KeepLoggedIn = "zZiDncOwG4H+PpChrd4DvA==";
//-------------------------------------------------------------------
// 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_Online.User.UserAccountId));
return EncryptParameters(keepLoggedInParams, Base64IV_KeepLoggedIn, Base64Key_KeepLoggedIn);
}
//-------------------------------------------------------------------
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;
}
}
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;
}
//-------------------------------------------------------------------
// Logging.
protected String getLogSuffix()
{
List<String> logSuffix = new List<string>();
SaxSessionData_Online sessionData = SessionData_Online;
if (sessionData != null)
{
if (!StringUtil.IsEmpty(sessionData.User.EmailAddress))
logSuffix.Add("User: " + sessionData.User.EmailAddress);
}
return logSuffix.Concat("", " ", ".");
}
//-------------------------------------------------------------------
}
//=======================================================================
}