323 lines
14 KiB
C#
323 lines
14 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 Saxonite.Net.Standard.Base.Data;
|
|
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.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, List<SaxUserConfig> userConfigs, 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.
|
|
userAccount.KeepLoggedIn = 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.invalid"];
|
|
return false;
|
|
}
|
|
SaLogger.Info(fn + " User session opened for " + userAccount.EmailAddress, true);
|
|
|
|
// Create session.
|
|
OpenUserSession(SessionContext, userAccount, userRoles);
|
|
RefreshSessionProfileSports();
|
|
|
|
// Refresh User configuration.
|
|
foreach (SaxUserConfig userConfig in userConfigs)
|
|
{
|
|
switch (userConfig.ConfigType)
|
|
{
|
|
case Sax.UserConfigType.Online_Profile:
|
|
SessionData_Online.UserConfig.Profile = SaxConfigData.ProfileData.FromXml(userConfig.ConfigData);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Send the 'Keep Logged In' cookie if requested.
|
|
if (userAccount.KeepLoggedIn)
|
|
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 colParticipating = AccountMgr.GetUserSportColumnDfn(SaxUserSport.Column.SportParticipating);
|
|
ColumnDfn colPreparing = AccountMgr.GetUserSportColumnDfn(SaxUserSport.Column.SportPreparing);
|
|
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, colParticipating, 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.SportParticipating participating = (Sax.SportParticipating)values.GetLong(colParticipating.DataName);
|
|
Sax.SportPreparing preparing = (Sax.SportPreparing)values.GetLong(colPreparing.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();
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Image utilities.
|
|
//-------------------------------------------------------------------
|
|
/// <summary>
|
|
/// Construct URL to display Organisation logo.
|
|
/// <para>- Option to allow for 'Upload' image if active.</para>
|
|
/// </summary>
|
|
public 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 + "=" + EncryptOnlineParams(parameters);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Construct URL to display specific item image.
|
|
/// </summary>
|
|
public String GetItemImageLink(SysId itemImageId)
|
|
{
|
|
String imageLink = "Image/Item?";
|
|
NameValueCollection parameters = new NameValueCollection();
|
|
parameters.Add(ImageController.ParamName_ItemImageId, SysId.ToString(itemImageId));
|
|
return imageLink + ImageController.ParamName_ItemImageEncrypted + "=" + EncryptOnlineParams(parameters);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// 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, AppConfig.Security.SignIn.KeepLoggedIn.CookieExpiresDays));
|
|
SetResponseCookie(KeepLoggedIn.CookieName, 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(KeepLoggedIn.CookieName))) return;
|
|
// Clear cookie by setting expiry date in the past.
|
|
SetResponseCookie(KeepLoggedIn.CookieName, "", 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 class KeepLoggedIn
|
|
{
|
|
public static String CookieName = "SAXRM";
|
|
public static String Base64Key = SacsEncryptionHelper.EncodePlainKey("<=Saxonite^OnlIne{R$Member-m3}~>", SacsEncryptionHelper.KeyLength);
|
|
//private static String Base64IV_Generated = SacsEncryptionHelper.InitSymmetricEncryptionIV(KeepLoggedIn.Base64Key);
|
|
public static String Base64IV = "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, 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;
|
|
}
|
|
}
|
|
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(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_OnlineApp { get; } = SacsEncryptionHelper.EncodePlainKey("Saxonite#Online@Encrypt+" + DateTime.Today.Format("ddMMyyyy"), SacsEncryptionHelper.KeyLength);
|
|
protected String EncryptOnlineParams(NameValueCollection parameters)
|
|
{
|
|
return HttpUtility.UrlEncode(EncryptParameters(parameters, SessionData_Online.Base64IV_OnlineApp, Base64Key_OnlineApp));
|
|
}
|
|
protected NameValueCollection ExtractOnlineParams(String encryptedParams)
|
|
{
|
|
return DecryptParameters(encryptedParams, SessionData_Online.Base64IV_OnlineApp, Base64Key_OnlineApp);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Logging.
|
|
//-------------------------------------------------------------------
|
|
/// <summary>
|
|
/// Log suffix:
|
|
/// <para>- User's email address.</para>
|
|
/// <para>- Prefixed with space.</para>
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
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("", " ", ".");
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
}
|
|
//=======================================================================
|
|
}
|