Portal signin via Control Instance
This commit is contained in:
@@ -7,7 +7,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sarand.BusinessMgr.Net.Core.Web.Control.App
|
||||
namespace Sarand.BusinessMgr.Web.Control.App
|
||||
{
|
||||
//=======================================================================
|
||||
public class Program
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.App;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.Consoles;
|
||||
using Sarand.BusinessMgr.Web.Control.Consoles;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.Helpers;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.App;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Consoles;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Core;
|
||||
using Sarand.BusinessMgr.Net.Standard.Dbas.Utils;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Core;
|
||||
@@ -17,15 +20,15 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sarand.BusinessMgr.Net.Core.Web.Control.App
|
||||
namespace Sarand.BusinessMgr.Web.Control.App
|
||||
{
|
||||
//=======================================================================
|
||||
public class SbmApplication_Control : SbmApplication_Web
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
private bool generateDatabaseDefinitions_Control = true;
|
||||
private bool generateDatabaseDefinitions_BusMgr = true;
|
||||
private bool generateDatabaseDefinitions_Control = false;
|
||||
private bool generateDatabaseDefinitions_BusMgr = false;
|
||||
private PropertySet propertySet;
|
||||
private Dictionary<String, SbmAppConfig> appConfigs = new Dictionary<string, SbmAppConfig>();
|
||||
private Dictionary<String, SbmEmailConfig> emailConfigs = new Dictionary<string, SbmEmailConfig>();
|
||||
@@ -42,8 +45,8 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Control.App
|
||||
SessionContext = sessionContext;
|
||||
}
|
||||
public SbmSessionContext_Web SessionContext { get; }
|
||||
public List<string> DbIdentifiers { get; } = new List<string>();
|
||||
public ISacsDatabase BusMgrDatabase { get; set; }
|
||||
public HashSet<string> DbIdentifiers { get; } = new HashSet<string>();
|
||||
public ISacsDatabase BusMgrDatabase { get; set; }
|
||||
}
|
||||
private readonly Dictionary<ushort, SessionContextData> sessionContexts = new Dictionary<ushort, SessionContextData>();
|
||||
|
||||
@@ -87,16 +90,23 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Control.App
|
||||
{
|
||||
return templateDatabase;
|
||||
}
|
||||
public override List<string> GetDbIdentifiers(SbmSessionContext sessionContext)
|
||||
{
|
||||
return sessionContexts.GetValue(sessionContext.Index, null)?.DbIdentifiers ?? new List<string>();
|
||||
}
|
||||
public override ISacsDatabase GetBusMgrDatabase(SbmSessionContext sessionContext)
|
||||
public override HashSet<string> GetDbIdentifiers(SbmSessionContext sessionContext)
|
||||
{
|
||||
return sessionContexts.GetValue(sessionContext.Index, null)?.DbIdentifiers ?? new HashSet<string>();
|
||||
}
|
||||
public override ISacsDatabase GetBusMgrDatabase(SbmSessionContext sessionContext)
|
||||
{
|
||||
return sessionContexts.GetValue(sessionContext.Index, null)?.BusMgrDatabase;
|
||||
}
|
||||
public override SbmEmailConfig EmailConfig { get { return emailConfigs.Values.ToList().getConfigForSession(AppConfig); } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Create Consoles.
|
||||
public override SbmEmailConsole CreateEmailConsole(ISbmUserContext_Core userContext)
|
||||
{
|
||||
return new SbmEmailConsole_Control(EmailConfig);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Initialisation.
|
||||
/// <summary>Initialise environment - from Startup.</summary>
|
||||
@@ -182,8 +192,8 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Control.App
|
||||
if (sessionContextIdx_Control < 1)
|
||||
sessionContextIdx_Control = sessionContextIdx;
|
||||
SessionContextData sessionContextData = new SessionContextData(new SbmSessionContext_Web(sessionContextIdx, Sbm.Application.WebControl));
|
||||
sessionContextData.DbIdentifiers.AddRange(dbConfig.Identifiers);
|
||||
sessionContextData.BusMgrDatabase = busMgrDatabase;
|
||||
sessionContextData.DbIdentifiers.UnionWith(dbConfig.Identifiers);
|
||||
sessionContextData.BusMgrDatabase = busMgrDatabase;
|
||||
sessionContexts.Add(sessionContextData.SessionContext.Index, sessionContextData);
|
||||
}
|
||||
}
|
||||
@@ -202,7 +212,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Control.App
|
||||
config.location = AppConfig.TemplateDatabase.Location;
|
||||
config.user = AppConfig.TemplateDatabase.User;
|
||||
config.pword = AppConfig.TemplateDatabase.Password;
|
||||
GenerateDatabaseDefinition(SbmDbDfnGenerator.DatabaseInstance.BusinessMgr, GetTemplateDatabase());
|
||||
GenerateDatabaseDefinition(SbmDbDfnGenerator.DatabaseInstance.Core, GetTemplateDatabase());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +220,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Control.App
|
||||
// Generate database definition.
|
||||
private void GenerateDatabaseDefinition(SbmDbDfnGenerator.DatabaseInstance dbInstance, ISacsDatabase db)
|
||||
{
|
||||
if (!generateDatabaseDefinitions_BusMgr || !SbmSystem.DebugMode) return;
|
||||
if (!SbmSystem.DebugMode) return;
|
||||
if (!StringUtil.EmptyOnNull(EnvironmentName).EqualsCI("Development")) return;
|
||||
if (db == null || StringUtil.IsEmpty(db.ConnectionString)) return;
|
||||
|
||||
@@ -219,7 +229,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Control.App
|
||||
}
|
||||
private void GenerateDatabaseDefinition(SbmDbDfnGenerator.DatabaseInstance dbInstance, String connectionString)
|
||||
{
|
||||
if (!generateDatabaseDefinitions_BusMgr || !SbmSystem.DebugMode) return;
|
||||
if (!SbmSystem.DebugMode) return;
|
||||
if (!StringUtil.EmptyOnNull(EnvironmentName).EqualsCI("Development")) return;
|
||||
if (StringUtil.IsEmpty(connectionString)) return;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sarand.BusinessMgr.Net.Core.Web
|
||||
namespace Sarand.BusinessMgr.Web
|
||||
{
|
||||
// <summary>
|
||||
/// Dummy classes to group shared resources - named as per '.resx' file in root namespace.
|
||||
@@ -19,7 +19,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web
|
||||
// Ref. Project Properties - Application.
|
||||
}
|
||||
|
||||
namespace Sarand.BusinessMgr.Net.Core.Web.Control.App
|
||||
namespace Sarand.BusinessMgr.Web.Control.App
|
||||
{
|
||||
//=======================================================================
|
||||
public class SbmLocaliser_Control : SbmLocaliser
|
||||
|
||||
@@ -7,35 +7,110 @@ using static Sarand.BusinessMgr.Net.Standard.Base.App.Sbm.Config.Database;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Sarand.Net.Standard.Base.Helpers;
|
||||
using Sarand.BusinessMgr.Web.Control.Controllers;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Dbas;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
|
||||
namespace Sarand.BusinessMgr.Net.Core.Web.Control.App
|
||||
namespace Sarand.BusinessMgr.Web.Control.App
|
||||
{
|
||||
//=======================================================================
|
||||
public class SbmSessionData_Control : SbmSessionData_Web
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
[JsonConstructor]
|
||||
private SbmSessionData_Control(long sessionIdx, int itemIdx, SbmSessionContext sessionContext) :
|
||||
base(sessionIdx, itemIdx, sessionContext)
|
||||
{ }
|
||||
/// <summary>
|
||||
/// SessionData construction.
|
||||
/// <para>SessionContext and UserAccount must be supplied.</para>
|
||||
/// <para>Organisation is optional.</para>
|
||||
/// </summary>
|
||||
public SbmSessionData_Control(SbmSessionContext sessionContext) :
|
||||
base(sessionContext)
|
||||
{
|
||||
}
|
||||
//=======================================================================
|
||||
public class SbmSessionData_Control : SbmSessionData_Web
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
[JsonConstructor]
|
||||
private SbmSessionData_Control(long sessionIdx, int itemIdx, SbmSessionContext sessionContext) :
|
||||
base(sessionIdx, itemIdx, sessionContext)
|
||||
{ }
|
||||
/// <summary>
|
||||
/// SessionData construction.
|
||||
/// <para>SessionContext and UserAccount must be supplied.</para>
|
||||
/// </summary>
|
||||
public SbmSessionData_Control(SbmSessionContext sessionContext, SbmcUserAccount userAccount) :
|
||||
base(sessionContext)
|
||||
{
|
||||
User = new UserData_Control(sessionContext, userAccount);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
protected override UserData CreateUserData(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
// Properties.
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
[JsonIgnore]
|
||||
public override String UserDisplayName { get { return User?.DisplayName; } }
|
||||
[JsonIgnore]
|
||||
public override String UserEmailAddress { get { return User?.EmailAddress; } }
|
||||
[JsonIgnore]
|
||||
public override bool ValidUser { get { return SysId.IsValid(User?.UserAccountId); } }
|
||||
[JsonIgnore]
|
||||
public override bool KeepLoggedIn { get { return User.KeepLoggedIn; } }
|
||||
[JsonIgnore]
|
||||
public override DbUpdateInfo DbUpdateInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
return new DbUpdateInfo(User?.UserAccountId, StringUtil.OnEmpty(User.DisplayName, User.EmailAddress), SbmSystem.ComputerName);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// User details.
|
||||
public class UserData_Control : UserData
|
||||
{
|
||||
[JsonConstructor]
|
||||
private UserData_Control()
|
||||
{ }
|
||||
internal UserData_Control(SbmSessionContext sessionContext, SbmcUserAccount userAccount) :
|
||||
base(sessionContext)
|
||||
{
|
||||
UserAccountId = userAccount.Id;
|
||||
if (SysId.IsValid(UserAccountId))
|
||||
{
|
||||
UserAccountSupplied = true;
|
||||
DisplayName = userAccount.GetDisplayName();
|
||||
Title = userAccount.Title;
|
||||
FirstName = userAccount.FirstName;
|
||||
LastName = userAccount.LastName;
|
||||
EmailAddress = userAccount.EmailAddress;
|
||||
}
|
||||
}
|
||||
internal UserData_Control(UserData userData) :
|
||||
base(userData)
|
||||
{
|
||||
UserData_Control userData_Control = userData as UserData_Control;
|
||||
if (userData_Control != null)
|
||||
{
|
||||
UserAccountId = userData_Control.UserAccountId;
|
||||
UserAccountSupplied = userData_Control.UserAccountSupplied;
|
||||
KeepLoggedIn= userData_Control.KeepLoggedIn;
|
||||
}
|
||||
DisplayName = userData.DisplayName;
|
||||
Title = userData.Title;
|
||||
FirstName = userData.FirstName;
|
||||
LastName = userData.LastName;
|
||||
EmailAddress = userData.EmailAddress;
|
||||
}
|
||||
// Properties.
|
||||
public SysId UserAccountId { get; private set; }
|
||||
public bool UserAccountSupplied { get; private set; }
|
||||
public bool KeepLoggedIn { get; private set; }
|
||||
}
|
||||
protected UserData CreateUserData(SbmSessionContext sessionContext, SbmcUserAccount userAccount)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>User - JSON deserialisation route.</summary>
|
||||
public UserData_Control User { get; private set; }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Control application encryption IV value - for consistent encryption during session.
|
||||
public String Base64IV_ControlApp { get; private set; } = SacsEncryptionHelper.InitSymmetricEncryptionIV(SbmController_Control.Base64Key_ControlApp);
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sarand.BusinessMgr.Net.Core.Web.Control.App
|
||||
namespace Sarand.BusinessMgr.Web.Control.App
|
||||
{
|
||||
//=======================================================================
|
||||
public class SbmStartup_Control : SbmStartup_Web
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.App;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Core;
|
||||
using Sarand.Net.Standard.Base.Bus;
|
||||
using Sarand.Net.Standard.Base.Dbas;
|
||||
using System;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.App
|
||||
{
|
||||
//=======================================================================
|
||||
/// <summary>
|
||||
/// Wrapper class to allow access to core internals from Control application.
|
||||
/// </summary>
|
||||
public class SbmUserContext_Control : ISbmUserContext_Core
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
public SbmUserContext_Control(SbmSessionData_Control sessionData)
|
||||
{
|
||||
SessionData = sessionData;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Properties.
|
||||
private SbmSessionData_Control SessionData { get; }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>Session Context - Identifies database connections etc.</summary>
|
||||
public SbmSessionContext SessionContext { get { return null; } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// User properties.
|
||||
/// <summary>User Security Item Id.</summary>
|
||||
public SysId UserSecurityItemId { get { return null; } }
|
||||
/// <summary>User Affiliate Id.</summary>
|
||||
public SysId UserAffiliateId { get { return null; } }
|
||||
/// <summary>User display name.</summary>
|
||||
public String UserDisplayName { get { return SessionData.UserDisplayName; } }
|
||||
/// <summary>User email address.</summary>
|
||||
public String UserEmailAddress { get { return SessionData.UserEmailAddress; } }
|
||||
/// <summary>Does the context represent a valid user account.</summary>
|
||||
public bool ValidUser { get { return SessionData.ValidUser; } }
|
||||
/// <summary>Database update info - user session specific properties for database inserts/updates.</summary>
|
||||
public DbUpdateInfo DbUpdateInfo { get { return SessionData.DbUpdateInfo; } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>Security profile - what the user is allowed to do based on security roles and groups.</summary>
|
||||
public ISbmSecurityProfile_Core SecurityProfile_Core { get; }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Organisation properties.
|
||||
|
||||
/// <summary>Organisation Affiliate Id.</summary>
|
||||
public SysId OrganisationAffiliateId { get; }
|
||||
|
||||
/// <summary>Organisation Affiliate Type.</summary>
|
||||
public Sbm.AffiliateType OrganisationAffiliateType { get; }
|
||||
|
||||
/// <summary>Organisation 'Internal' Association Id.</summary>
|
||||
public SysId OrganisationAssociationId { get; }
|
||||
|
||||
/// <summary>Organisation display name.</summary>
|
||||
public String OrganisationDisplayName { get; }
|
||||
|
||||
/// <summary>Organisation Interface context - to qualify interfaces belonging to the organisation.</summary>
|
||||
public String OrganisationInterfaceContext { get; }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Organisation configuration.
|
||||
public String GetOrganisationOption(Sbm.OrganisationOption option, String context) { return ""; }
|
||||
public String GetOrganisationOption(String optionName, String context) { return ""; }
|
||||
public String GetOwnerOption_Value(Sbm.OrgOption_Value eCol) { return ""; }
|
||||
public String GetOwnerOption_Folder(Sbm.OrgOption_Folder eCol) { return ""; }
|
||||
public bool GetOwnerOption_AllowOption(Sbm.OrgOption_AllowOption eCol) { return false; }
|
||||
public String GetOwnerOption_Caption(Sbm.OrgOption_Caption eCol, String defaultValue) { return ""; }
|
||||
public bool GetOwnerOption_ShowField(SbmDbDfn.ColumnIdx eCol) { return false; }
|
||||
public bool GetOwnerOption_ShowColumn(Sbm.OrgOption_ShowColumn eCol) { return false; }
|
||||
public String GetOwnerOption_FieldLabel(SbmDbDfn.ColumnIdx eCol, String defautValue) { return ""; }
|
||||
public int GetOwnerOption_Minutes(Sbm.OrgOption_Minutes eLimit, int defautValue) { return 0; }
|
||||
public int GetOwnerOption_UpdateLimit(Sbm.OrgOption_PeriodDays eLimit, int defautValue) { return 0; }
|
||||
public int GetOwnerOption_UpdateLimit(Sbm.OrgOption_PeriodWeeks eLimit, int defautValue) { return 0; }
|
||||
public int GetOwnerOption_NumericDecimals(Sbm.OrgOption_NumericDecimals eNumDecimals, int defautValue = 2) { return 2; }
|
||||
public Sbm.CommodityType GetOwnerOption_CommodityType(Sbm.OrgOption_CommodityType commodityTypeContext) { return Sbm.CommodityType.Unknown; }
|
||||
public SysId GetOwnerOption_StockLocation(Sbm.OrgOption_StockLocation eLoc) { return null; }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Sarand.BusinessMgr.Net.Core.Web.Consoles;
|
||||
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.Helpers;
|
||||
using Sarand.Net.Standard.Base.Email;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Consoles
|
||||
{
|
||||
//=======================================================================
|
||||
public class SbmEmailConsole_Control : SbmEmailConsole_Web
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public SbmEmailConsole_Control(SbmEmailConfig emailConfig) :
|
||||
base(emailConfig)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Replace dynamic tags in email message body.
|
||||
/// <para></para>
|
||||
/// </summary>
|
||||
public void ReplaceDynamicEmailBodyTags(SaEmailData emailData, SbmcUserAccount userAccount)
|
||||
{
|
||||
// Replace core tags, eg. main stylesheet.
|
||||
SbmEmailConsole_Web.ReplaceDynamicEmailTags(emailData);
|
||||
|
||||
if (userAccount != null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
+422
@@ -0,0 +1,422 @@
|
||||
using DocumentFormat.OpenXml.Spreadsheet;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
|
||||
using Sarand.BusinessMgr.Web.Control.Models;
|
||||
using Sarand.BusinessMgr.Web.Control.Models.Account;
|
||||
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.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Controllers.Account
|
||||
{
|
||||
//=======================================================================
|
||||
partial class UserAccountController
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Definitions.
|
||||
|
||||
// User data.
|
||||
private class UserListData : AjaxResponse_Data.ItemData_Base
|
||||
{
|
||||
public String EmailAddress { get; set; }
|
||||
public String DisplayName { get; set; }
|
||||
public String Title { get; set; }
|
||||
public String FirstName { get; set; }
|
||||
public String LastName { get; set; }
|
||||
public String Phone { get; set; }
|
||||
public String LastLoginOn { get; set; }
|
||||
public String LastLoginAt { get; set; }
|
||||
}
|
||||
private class UserItemData : UserListData
|
||||
{
|
||||
public String Initials { get; set; }
|
||||
public String PhoneMain { get; set; }
|
||||
public String PhoneMobile { get; set; }
|
||||
public String PhoneAlt { get; set; }
|
||||
}
|
||||
private void PopulateUserResponseListData(SbmModel_Control model, UserListData data, SbmcUserAccount user)
|
||||
{
|
||||
data.Id = SysId.ToString(user.Id);
|
||||
data.EmailAddress = user.EmailAddress;
|
||||
data.DisplayName = user.GetDisplayName();
|
||||
data.Title = user.Title;
|
||||
data.FirstName = user.FirstName;
|
||||
data.LastName = user.LastName;
|
||||
data.Phone = StringUtil.OnBlank(user.PhoneMain, user.PhoneMobile, user.PhoneAlt);
|
||||
data.LastLoginOn = model.FormatDateForDisplay(user.LastLoginAt);
|
||||
data.LastLoginAt = model.FormatDateTimeForDisplay(user.LastLoginAt);
|
||||
data.StatusCode = user.StatusCode;
|
||||
data.StatusDesc = user.StatusDesc;
|
||||
}
|
||||
private void PopulateUserResponseItemData(SbmModel_Control model, UserItemData data, SbmcUserAccount user)
|
||||
{
|
||||
PopulateUserResponseListData(model, data, user);
|
||||
data.Initials = user.Initials;
|
||||
data.PhoneMain = user.PhoneMain;
|
||||
data.PhoneMobile = user.PhoneMobile;
|
||||
data.PhoneAlt = user.PhoneAlt;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Get User List ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_UserList : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_UserList(SbmModel_Control model) : base(model) { }
|
||||
// User list.
|
||||
public List<UserListData> Users { get; } = new List<UserListData>();
|
||||
// Reference data.
|
||||
public List<CodeDesc> Statuses { get; } = new List<CodeDesc>();
|
||||
public List<CodeDesc> StatusesCreate { get; } = new List<CodeDesc>();
|
||||
public String StatusCodeDefault { get; set; }
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("Account/UserAccount/Ajax/GetList")]
|
||||
[Route("Account/UserAccount/Ajax/GetUsers")]
|
||||
[Route("Account/UserAccount/Ajax/GetUserList")]
|
||||
public IActionResult Ajax_GetUserList(Model_UserAccount model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_GetUserList";
|
||||
String messageTitle = Localiser["label.users"];
|
||||
AjaxResponse_UserList responseData = new AjaxResponse_UserList(model);
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer) return Forbid();
|
||||
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
||||
RefreshModel(model);
|
||||
|
||||
// Reference data - statuses.
|
||||
foreach (SaStatus status in SbmcUserAccount.ValidStatuses)
|
||||
{
|
||||
responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
|
||||
if (status != SaStatus.Deleted)
|
||||
responseData.StatusesCreate.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
|
||||
}
|
||||
responseData.StatusCodeDefault = SacsSystem.GetStatusCode(SbmcUserAccount.DefaultStatus);
|
||||
|
||||
// Item filter.
|
||||
SbmcUserAccount.Params prmUserAccount = new SbmcUserAccount.Params();
|
||||
prmUserAccount.Search = model.Filter.Search;
|
||||
prmUserAccount.StatusCat = model.Filter.GetStatusCat();
|
||||
model.Paging.ItemTotal = (ulong)ControlAccountMgr.GetUserAccountCount(prmUserAccount);
|
||||
model.PopulatePagingTo(prmUserAccount.ResultsPaging);
|
||||
|
||||
// Item columns.
|
||||
ColumnDfn colId = ControlAccountMgr.GetUserAccountColumnDfn(SbmcUserAccount.Column.Id);
|
||||
ColumnDfn colEmail = ControlAccountMgr.GetUserAccountColumnDfn(SbmcUserAccount.Column.EmailAddress);
|
||||
ColumnDfn colTitle = ControlAccountMgr.GetUserAccountColumnDfn(SbmcUserAccount.Column.Title);
|
||||
ColumnDfn colFirstName = ControlAccountMgr.GetUserAccountColumnDfn(SbmcUserAccount.Column.FirstName);
|
||||
ColumnDfn colLastName = ControlAccountMgr.GetUserAccountColumnDfn(SbmcUserAccount.Column.LastName);
|
||||
ColumnDfn colPhoneMain = ControlAccountMgr.GetUserAccountColumnDfn(SbmcUserAccount.Column.PhoneMain);
|
||||
ColumnDfn colPhoneMobile = ControlAccountMgr.GetUserAccountColumnDfn(SbmcUserAccount.Column.PhoneMobile);
|
||||
ColumnDfn colPhoneAlt = ControlAccountMgr.GetUserAccountColumnDfn(SbmcUserAccount.Column.PhoneAlt);
|
||||
ColumnDfn colLastLogin = ControlAccountMgr.GetUserAccountColumnDfn(SbmcUserAccount.Column.LastLoginAt);
|
||||
ColumnDfn colStatus = ControlAccountMgr.GetUserAccountColumnDfn(SbmcUserAccount.Column.Status);
|
||||
List<ColumnDfn> columns = new List<ColumnDfn>()
|
||||
{
|
||||
colId, colEmail, colTitle, colFirstName, colLastName,
|
||||
colPhoneMain, colPhoneMobile, colPhoneAlt, colLastLogin, colStatus
|
||||
};
|
||||
|
||||
// Query items.
|
||||
using (DbResultSet resultSet = ControlAccountMgr.ExecuteUserAccountQuery(columns, prmUserAccount))
|
||||
{
|
||||
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
|
||||
{
|
||||
NamedValue.Map values = resultSet.GetRecordValues();
|
||||
UserListData userData = new UserListData()
|
||||
{
|
||||
Id = values.GetString(colId.DataName),
|
||||
EmailAddress = values.GetString(colEmail.DataName),
|
||||
Title = values.GetString(colTitle.DataName),
|
||||
FirstName = values.GetString(colFirstName.DataName),
|
||||
LastName = values.GetString(colLastName.DataName),
|
||||
Phone = StringUtil.OnBlank(values.GetString(colPhoneMain.DataName), values.GetString(colPhoneMobile.DataName), values.GetString(colPhoneAlt.DataName)),
|
||||
LastLoginOn = model.FormatDateForDisplay(values.GetDateTime(colLastLogin.DataName)),
|
||||
LastLoginAt = model.FormatDateTimeForDisplay(values.GetDateTime(colLastLogin.DataName)),
|
||||
StatusCode = values.GetString(colStatus.DataName),
|
||||
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
|
||||
};
|
||||
userData.DisplayName = SbmcUserAccount.GetDisplayName(userData.FirstName, userData.LastName);
|
||||
responseData.Users.Add(userData);
|
||||
}
|
||||
}
|
||||
if (model.Paging.IsPaged)
|
||||
model.Paging.ItemCount = (ushort)responseData.Users.Count;
|
||||
|
||||
// Complete response.
|
||||
responseData.SetPaging(model);
|
||||
responseData.Success = true;
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Get User ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_User : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_User(SbmModel_Control model) : base(model) { }
|
||||
// User details.
|
||||
public UserItemData user { get; } = new UserItemData();
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("Account/UserAccount/Ajax/GetItem")]
|
||||
[Route("Account/UserAccount/Ajax/GetUser")]
|
||||
public IActionResult Ajax_GetUser(Model_UserAccount model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_GetUser";
|
||||
String messageTitle = Localiser["label.user"];
|
||||
AjaxResponse_User responseData = new AjaxResponse_User(model);
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer) return Forbid();
|
||||
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
||||
RefreshModel(model);
|
||||
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
|
||||
// Identify item.
|
||||
SysId userAccountId = model.User.Id.ToSysId();
|
||||
if (!SysId.IsValid(userAccountId))
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
|
||||
|
||||
// Retrieve item.
|
||||
SbmcUserAccount user = ControlAccountMgr.GetUserAccountById(userAccountId);
|
||||
disposables.Add(user);
|
||||
if (user == null)
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve user for Id: '" + userAccountId + "'");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
|
||||
}
|
||||
|
||||
// Construct response.
|
||||
PopulateUserResponseItemData(model, responseData.user, user);
|
||||
responseData.Success = true;
|
||||
}
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Get User by email ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("Account/UserAccount/Ajax/GetUserByEmail")]
|
||||
public IActionResult Ajax_GetUserByEmail(Model_UserAccount model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_GetUserByEmail";
|
||||
String messageTitle = Localiser["label.user"];
|
||||
AjaxResponse_User responseData = new AjaxResponse_User(model);
|
||||
StoreSessionData = false;
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer) return Forbid();
|
||||
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
||||
RefreshModel(model);
|
||||
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
|
||||
// Identify user.
|
||||
if (StringUtil.IsEmpty(model.Filter.EmailAddress))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Attempt to get user details with blank email.");
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
// Retrieve user.
|
||||
SbmcUserAccount user = ControlAccountMgr.GetUserAccountByEmailAddress(model.Filter.EmailAddress);
|
||||
disposables.Add(user);
|
||||
if (user == null)
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve user for Email: " + model.Filter.EmailAddress);
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
// Construct response.
|
||||
PopulateUserResponseItemData(model, responseData.user, user);
|
||||
responseData.Success = true;
|
||||
}
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Store User ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("Account/UserAccount/Ajax/StoreItem")]
|
||||
[Route("Account/UserAccount/Ajax/StoreUser")]
|
||||
public IActionResult Ajax_StoreUser(Model_UserAccount model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_StoreUser";
|
||||
String messageTitle = Localiser["label.user"];
|
||||
AjaxResponse_User responseData = new AjaxResponse_User(model);
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer) return Forbid();
|
||||
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
||||
RefreshModel(model);
|
||||
|
||||
// Validate item Id.
|
||||
SysId userAccountId = model.User.Id.ToSysId();
|
||||
if (StringUtil.ToBool(model.User.Exists) && !SysId.IsValid(userAccountId))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} User Id not supplied for existing item.");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
|
||||
}
|
||||
if (!StringUtil.ToBool(model.User.Exists) && SysId.IsValid(userAccountId))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} User Id supplied for new item.");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
|
||||
}
|
||||
|
||||
// Validate item properties.
|
||||
model.ValidateUserData(responseData);
|
||||
if (responseData.HasControlErrors())
|
||||
return Json(responseData);
|
||||
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
|
||||
// Retrieve existing item.
|
||||
SbmcUserAccount user = ControlAccountMgr.GetUserAccountById(userAccountId);
|
||||
disposables.Add(user);
|
||||
if (user == null && SysId.IsValid(userAccountId))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve user for Id: '" + model.User.Id + "'");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
|
||||
}
|
||||
|
||||
// Create new item if required.
|
||||
if (user == null)
|
||||
{
|
||||
user = ControlAccountMgr.CreateUserAccount();
|
||||
if (model.User.EmailAddress != null)
|
||||
user.EmailAddress = model.User.EmailAddress;
|
||||
}
|
||||
|
||||
// Populate item properties.
|
||||
if (model.User.Title != null)
|
||||
user.Title = model.User.Title;
|
||||
if (model.User.FirstName != null)
|
||||
user.FirstName = model.User.FirstName;
|
||||
if (model.User.Initials != null)
|
||||
user.Initials = model.User.Initials;
|
||||
if (model.User.LastName != null)
|
||||
user.LastName = model.User.LastName;
|
||||
if (model.User.PhoneMain != null)
|
||||
user.PhoneMain = model.User.PhoneMain;
|
||||
if (model.User.PhoneMobile != null)
|
||||
user.PhoneMobile = model.User.PhoneMobile;
|
||||
if (model.User.PhoneAlt != null)
|
||||
user.PhoneAlt = model.User.PhoneAlt;
|
||||
if (model.User.StatusCode != null)
|
||||
user.Status = model.User.GetStatus();
|
||||
|
||||
// Store item.
|
||||
if (!SysId.IsValid(user.Id))
|
||||
{
|
||||
if (!ControlAccountMgr.InsertUserAccount(user, DbUpdateInfo))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to insert user: " + ControlAccountMgr.ErrorStateMessage);
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + ControlAccountMgr.ErrorStateMessage));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ControlAccountMgr.UpdateUserAccount(user, DbUpdateInfo))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to update user [Id:" + SysId.ToString(user.Id) + "]: " + ControlAccountMgr.ErrorStateMessage);
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + ControlAccountMgr.ErrorStateMessage));
|
||||
}
|
||||
}
|
||||
|
||||
// Construct response.
|
||||
PopulateUserResponseItemData(model, responseData.user, user);
|
||||
responseData.Success = true;
|
||||
}
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Send User Emails ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("Account/UserAccount/Ajax/SendEmails")]
|
||||
public IActionResult Ajax_SendEmails(Model_UserAccount model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_SendEmails";
|
||||
String messageTitle = Localiser["label.emails"];
|
||||
AjaxResponse_User responseData = new AjaxResponse_User(model);
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer) return Forbid();
|
||||
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
||||
RefreshModel(model);
|
||||
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
|
||||
// Retrieve user.
|
||||
SysId userAccountId = model.User.Id.ToSysId();
|
||||
SbmcUserAccount userAccount = ControlAccountMgr.GetUserAccountById(userAccountId);
|
||||
disposables.Add(userAccount);
|
||||
if (userAccount == null)
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve user for Id: '" + model.User.Id + "'");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
|
||||
}
|
||||
|
||||
// Email types to send.
|
||||
bool sendAcountActivationPortal = StringUtil.ToBool(model.UserEmailAcountActivationPortal);
|
||||
bool sendRequestPasswordPortal = StringUtil.ToBool(model.UserEmailRequestPasswordPortal);
|
||||
bool sendRequestPasswordControl = StringUtil.ToBool(model.UserEmailRequestPasswordControl);
|
||||
if (!sendAcountActivationPortal && !sendRequestPasswordPortal && !sendRequestPasswordControl)
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.validation.select.option"]));
|
||||
|
||||
// Send emails.
|
||||
int emailsSentCount = 0;
|
||||
if (sendAcountActivationPortal)
|
||||
{
|
||||
//if (!OrganisationController.SendAccountActivatedEmail(this, userAccount))
|
||||
return Json(responseData.SetMessage(messageTitle, "Failed to send Online Account Activation email."));
|
||||
//emailsSentCount++;
|
||||
}
|
||||
if (sendRequestPasswordPortal)
|
||||
{
|
||||
if (!SecurityController.SendRequestPasswordEmail(this, userAccount, true))
|
||||
return Json(responseData.SetMessage(messageTitle, "Failed to send Online Request Password email."));
|
||||
emailsSentCount++;
|
||||
}
|
||||
if (sendRequestPasswordControl)
|
||||
{
|
||||
if (!SecurityController.SendRequestPasswordEmail(this, userAccount, false))
|
||||
return Json(responseData.SetMessage(messageTitle, "Failed to send Control Request Password email."));
|
||||
emailsSentCount++;
|
||||
}
|
||||
|
||||
// Construct response.
|
||||
PopulateUserResponseItemData(model, responseData.user, userAccount);
|
||||
responseData.SetNotification(messageTitle, "Emails sent: " + emailsSentCount);
|
||||
responseData.Success = true;
|
||||
}
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.App;
|
||||
using Sarand.BusinessMgr.Web.Control.Models.Account;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Controllers.Account
|
||||
{
|
||||
//======================================================================
|
||||
public partial class UserAccountController : SbmController_Control
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public UserAccountController(SbmLocaliser localiser, ILogger<UserAccountController> logger) :
|
||||
base(localiser, logger)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
[Route("Account/UserAccount")]
|
||||
public IActionResult Account_UserAccount()
|
||||
{
|
||||
// Redirect to sign-in if not signed-in.
|
||||
if (!ValidUserSession)
|
||||
AutoLoginUser();
|
||||
if (!ValidUserSession)
|
||||
{
|
||||
ClearKeepLoggedInCookie();
|
||||
return RedirectToAction("", "Security");
|
||||
}
|
||||
|
||||
// UserAccount page.
|
||||
Model_UserAccount model = new Model_UserAccount(Localiser);
|
||||
return View("Views/Account/UserAccount/Index.cshtml", model);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//======================================================================
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.App;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.Control.Models;
|
||||
using Sarand.BusinessMgr.Web.Control.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sarand.BusinessMgr.Net.Core.Web.Control.Controllers
|
||||
namespace Sarand.BusinessMgr.Web.Control.Controllers
|
||||
{
|
||||
//======================================================================
|
||||
public class HomeController : SbmController_Control
|
||||
@@ -26,8 +26,18 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Control.Controllers
|
||||
//-------------------------------------------------------------------
|
||||
public IActionResult Index()
|
||||
{
|
||||
// Home page.
|
||||
Model_Home model = new Model_Home(Localiser);
|
||||
if (!ValidUserSession)
|
||||
AutoLoginUser();
|
||||
|
||||
// Redirect to sign-in if not signed-in.
|
||||
if (!ValidUserSession)
|
||||
{
|
||||
ClearKeepLoggedInCookie();
|
||||
return RedirectToAction("", "Security");
|
||||
}
|
||||
|
||||
// Home page.
|
||||
Model_Home model = new Model_Home(Localiser);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
@@ -44,6 +54,8 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Control.Controllers
|
||||
{
|
||||
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//======================================================================
|
||||
//======================================================================
|
||||
}
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
|
||||
using Sarand.BusinessMgr.Web.Control.Models;
|
||||
using Sarand.BusinessMgr.Web.Control.Models.Instance;
|
||||
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.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Controllers.Instance
|
||||
{
|
||||
//=======================================================================
|
||||
partial class OwnerController
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Definitions.
|
||||
|
||||
// Owner data.
|
||||
private class OwnerListData : AjaxResponse_Data.ItemData_Base
|
||||
{
|
||||
public String Name { get; set; }
|
||||
public String ContactName { get; set; }
|
||||
public String ContactEmailAddress { get; set; }
|
||||
public String ContactPhone { get; set; }
|
||||
public long? RepositoryCount { get; set; }
|
||||
}
|
||||
private class OwnerItemData : OwnerListData
|
||||
{
|
||||
public String ContactPhoneMain { get; set; }
|
||||
public String ContactPhoneAlt { get; set; }
|
||||
}
|
||||
private void PopulateOwnerResponseListData(SbmModel_Control model, OwnerListData data, SbmcOwner owner)
|
||||
{
|
||||
data.Id = SysId.ToString(owner.Id);
|
||||
data.Name = owner.Name;
|
||||
data.ContactName = owner.ContactName;
|
||||
data.ContactEmailAddress = owner.ContactEmailAddress;
|
||||
data.ContactPhone = StringUtil.OnBlank(owner.ContactPhoneMain, owner.ContactPhoneAlt);
|
||||
data.StatusCode = owner.StatusCode;
|
||||
data.StatusDesc = owner.StatusDesc;
|
||||
}
|
||||
private void PopulateOwnerResponseItemData(SbmModel_Control model, OwnerItemData data, SbmcOwner owner)
|
||||
{
|
||||
PopulateOwnerResponseListData(model, data, owner);
|
||||
data.ContactPhoneMain = owner.ContactPhoneMain;
|
||||
data.ContactPhoneAlt = owner.ContactPhoneAlt;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Get Owner List ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_OwnerList : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_OwnerList(SbmModel_Control model) : base(model) { }
|
||||
// Owner list.
|
||||
public List<OwnerListData> Owners { get; } = new List<OwnerListData>();
|
||||
// Reference data.
|
||||
public List<CodeDesc> Statuses { get; } = new List<CodeDesc>();
|
||||
public List<CodeDesc> StatusesCreate { get; } = new List<CodeDesc>();
|
||||
public String StatusCodeDefault { get; set; }
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("Instance/Owner/Ajax/GetList")]
|
||||
[Route("Instance/Owner/Ajax/GetOwners")]
|
||||
[Route("Instance/Owner/Ajax/GetOwnerList")]
|
||||
public IActionResult Ajax_GetOwnerList(Model_Owner model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_GetOwnerList";
|
||||
String messageTitle = Localiser["label.owners"];
|
||||
AjaxResponse_OwnerList responseData = new AjaxResponse_OwnerList(model);
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer) return Forbid();
|
||||
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
||||
RefreshModel(model);
|
||||
|
||||
// Reference data - statuses.
|
||||
foreach (SaStatus status in SbmcOwner.ValidStatuses)
|
||||
{
|
||||
responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
|
||||
if (status != SaStatus.Deleted)
|
||||
responseData.StatusesCreate.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
|
||||
}
|
||||
responseData.StatusCodeDefault = SacsSystem.GetStatusCode(SbmcOwner.DefaultStatus);
|
||||
|
||||
// Item filter.
|
||||
SbmcOwner.Params prmOwner = new SbmcOwner.Params();
|
||||
prmOwner.Search = model.Filter.Search;
|
||||
prmOwner.StatusCat = model.Filter.GetStatusCat();
|
||||
model.Paging.ItemTotal = (ulong)ControlInstanceMgr.GetOwnerCount(prmOwner);
|
||||
model.PopulatePagingTo(prmOwner.ResultsPaging);
|
||||
|
||||
// Item columns.
|
||||
ColumnDfn colId = ControlInstanceMgr.GetOwnerColumnDfn(SbmcOwner.Column.Id);
|
||||
ColumnDfn colName = ControlInstanceMgr.GetOwnerColumnDfn(SbmcOwner.Column.Name);
|
||||
ColumnDfn colContactName = ControlInstanceMgr.GetOwnerColumnDfn(SbmcOwner.Column.ContactName);
|
||||
ColumnDfn colContactEmail = ControlInstanceMgr.GetOwnerColumnDfn(SbmcOwner.Column.ContactEmailAddress);
|
||||
ColumnDfn colContactPhoneMain = ControlInstanceMgr.GetOwnerColumnDfn(SbmcOwner.Column.ContactPhoneMain);
|
||||
ColumnDfn colContactPhoneAlt = ControlInstanceMgr.GetOwnerColumnDfn(SbmcOwner.Column.ContactPhoneAlt);
|
||||
ColumnDfn colRepoCount = ControlInstanceMgr.GetOwnerColumnDfn(SbmcOwner.Column.RepositoryCount);
|
||||
ColumnDfn colStatus = ControlInstanceMgr.GetOwnerColumnDfn(SbmcOwner.Column.Status);
|
||||
List<ColumnDfn> columns = new List<ColumnDfn>()
|
||||
{
|
||||
colId, colName, colContactName, colContactEmail,
|
||||
colContactPhoneMain, colContactPhoneAlt, colRepoCount, colStatus
|
||||
};
|
||||
|
||||
// Query items.
|
||||
using (DbResultSet resultSet = ControlInstanceMgr.ExecuteOwnerQuery(columns, prmOwner))
|
||||
{
|
||||
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
|
||||
{
|
||||
NamedValue.Map values = resultSet.GetRecordValues();
|
||||
OwnerListData ownerData = new OwnerListData()
|
||||
{
|
||||
Id = values.GetString(colId.DataName),
|
||||
Name = values.GetString(colName.DataName),
|
||||
ContactName = values.GetString(colContactName.DataName),
|
||||
ContactEmailAddress = values.GetString(colContactEmail.DataName),
|
||||
ContactPhone = StringUtil.OnBlank(values.GetString(colContactPhoneMain.DataName), values.GetString(colContactPhoneAlt.DataName)),
|
||||
StatusCode = values.GetString(colStatus.DataName),
|
||||
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
|
||||
};
|
||||
long repositoryCount = values.GetLong(colRepoCount.DataName);
|
||||
if (repositoryCount > 0)
|
||||
ownerData.RepositoryCount = repositoryCount;
|
||||
responseData.Owners.Add(ownerData);
|
||||
}
|
||||
}
|
||||
if (model.Paging.IsPaged)
|
||||
model.Paging.ItemCount = (ushort)responseData.Owners.Count;
|
||||
|
||||
// Complete response.
|
||||
responseData.SetPaging(model);
|
||||
responseData.Success = true;
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Get Owner ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_Owner : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_Owner(SbmModel_Control model) : base(model) { }
|
||||
// Owner details.
|
||||
public OwnerItemData owner { get; } = new OwnerItemData();
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("Instance/Owner/Ajax/GetItem")]
|
||||
[Route("Instance/Owner/Ajax/GetOwner")]
|
||||
public IActionResult Ajax_GetOwner(Model_Owner model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_GetOwner";
|
||||
String messageTitle = Localiser["label.owner"];
|
||||
AjaxResponse_Owner responseData = new AjaxResponse_Owner(model);
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer) return Forbid();
|
||||
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
||||
RefreshModel(model);
|
||||
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
|
||||
// Identify item.
|
||||
SysId ownerId = model.Owner.Id.ToSysId();
|
||||
if (!SysId.IsValid(ownerId))
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
|
||||
|
||||
// Retrieve item.
|
||||
SbmcOwner owner = ControlInstanceMgr.GetOwnerById(ownerId);
|
||||
disposables.Add(owner);
|
||||
if (owner == null)
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve owner for Id: '" + ownerId + "'");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
|
||||
}
|
||||
|
||||
// Construct response.
|
||||
PopulateOwnerResponseItemData(model, responseData.owner, owner);
|
||||
responseData.Success = true;
|
||||
}
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Store Owner ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("Instance/Owner/Ajax/StoreItem")]
|
||||
[Route("Instance/Owner/Ajax/StoreOwner")]
|
||||
public IActionResult Ajax_StoreOwner(Model_Owner model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_StoreOwner";
|
||||
String messageTitle = Localiser["label.owner"];
|
||||
AjaxResponse_Owner responseData = new AjaxResponse_Owner(model);
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer) return Forbid();
|
||||
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
||||
RefreshModel(model);
|
||||
|
||||
// Validate item Id.
|
||||
SysId ownerId = model.Owner.Id.ToSysId();
|
||||
if (StringUtil.ToBool(model.Owner.Exists) && !SysId.IsValid(ownerId))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Owner Id not supplied for existing item.");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
|
||||
}
|
||||
if (!StringUtil.ToBool(model.Owner.Exists) && SysId.IsValid(ownerId))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Owner Id supplied for new item.");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
|
||||
}
|
||||
|
||||
// Validate item properties.
|
||||
model.ValidateOwnerData(responseData);
|
||||
if (responseData.HasControlErrors())
|
||||
return Json(responseData);
|
||||
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
|
||||
// Retrieve existing item.
|
||||
SbmcOwner owner = ControlInstanceMgr.GetOwnerById(ownerId);
|
||||
disposables.Add(owner);
|
||||
if (owner == null && SysId.IsValid(ownerId))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve owner for Id: '" + model.Owner.Id + "'");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
|
||||
}
|
||||
|
||||
// Create new item if required.
|
||||
if (owner == null)
|
||||
{
|
||||
owner = ControlInstanceMgr.CreateOwner();
|
||||
}
|
||||
|
||||
// Populate item properties.
|
||||
if (model.Owner.Name != null)
|
||||
owner.Name = model.Owner.Name;
|
||||
if (model.Owner.ContactName != null)
|
||||
owner.ContactName = model.Owner.ContactName;
|
||||
if (model.Owner.ContactEmailAddress != null)
|
||||
owner.ContactEmailAddress = model.Owner.ContactEmailAddress;
|
||||
if (model.Owner.ContactPhoneMain != null)
|
||||
owner.ContactPhoneMain = model.Owner.ContactPhoneMain;
|
||||
if (model.Owner.ContactPhoneAlt != null)
|
||||
owner.ContactPhoneAlt = model.Owner.ContactPhoneAlt;
|
||||
if (model.Owner.StatusCode != null)
|
||||
owner.Status = model.Owner.GetStatus();
|
||||
|
||||
// Store item.
|
||||
if (!SysId.IsValid(owner.Id))
|
||||
{
|
||||
if (!ControlInstanceMgr.InsertOwner(owner, DbUpdateInfo))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to insert owner: " + ControlInstanceMgr.ErrorStateMessage);
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + ControlInstanceMgr.ErrorStateMessage));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ControlInstanceMgr.UpdateOwner(owner, DbUpdateInfo))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to update owner [Id:" + SysId.ToString(owner.Id) + "]: " + ControlInstanceMgr.ErrorStateMessage);
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + ControlInstanceMgr.ErrorStateMessage));
|
||||
}
|
||||
}
|
||||
|
||||
// Construct response.
|
||||
PopulateOwnerResponseItemData(model, responseData.owner, owner);
|
||||
responseData.Success = true;
|
||||
}
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.App;
|
||||
using Sarand.BusinessMgr.Web.Control.Models.Instance;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Controllers.Instance
|
||||
{
|
||||
//======================================================================
|
||||
public partial class OwnerController : SbmController_Control
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public OwnerController(SbmLocaliser localiser, ILogger<OwnerController> logger) :
|
||||
base(localiser, logger)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
[Route("Instance/Owner")]
|
||||
public IActionResult Index()
|
||||
{
|
||||
if (!ValidUserSession)
|
||||
AutoLoginUser();
|
||||
|
||||
// Redirect to sign-in if not signed-in.
|
||||
if (!ValidUserSession)
|
||||
{
|
||||
ClearKeepLoggedInCookie();
|
||||
return RedirectToAction("", "Security");
|
||||
}
|
||||
|
||||
// Owner page.
|
||||
Model_Owner model = new Model_Owner(Localiser);
|
||||
return View("Views/Instance/Owner/Index.cshtml", model);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//======================================================================
|
||||
}
|
||||
+313
@@ -0,0 +1,313 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
|
||||
using Sarand.BusinessMgr.Web.Control.Models;
|
||||
using Sarand.BusinessMgr.Web.Control.Models.Instance;
|
||||
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.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static Sarand.BusinessMgr.Net.Standard.Base.App.Sbm.Config.Security;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Controllers.Instance
|
||||
{
|
||||
//=======================================================================
|
||||
partial class RepositoryController
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Definitions.
|
||||
private static String PasswordDummyValue = "********";
|
||||
|
||||
// Repository data.
|
||||
private class RepositoryListData : AjaxResponse_Data.ItemData_Base
|
||||
{
|
||||
public String OwnerName { get; set; }
|
||||
public String Context { get; set; }
|
||||
public String ServerName { get; set; }
|
||||
public String InstanceName { get; set; }
|
||||
public String DatabaseName { get; set; }
|
||||
}
|
||||
private class RepositoryItemData : RepositoryListData
|
||||
{
|
||||
public String OwnerId { get; set; }
|
||||
public String UserName { get; set; }
|
||||
public String PasswordDummy { get; } = PasswordDummyValue;
|
||||
}
|
||||
private void PopulateRepositoryResponseListData(SbmModel_Control model, RepositoryListData data, SbmcOwner owner, SbmcRepository repository)
|
||||
{
|
||||
data.Id = SysId.ToString(repository.Id);
|
||||
data.OwnerName = owner.Name;
|
||||
data.Context = repository.Context;
|
||||
data.ServerName = repository.ServerName;
|
||||
data.InstanceName = repository.InstanceName;
|
||||
data.DatabaseName = repository.DatabaseName;
|
||||
data.StatusCode = repository.StatusCode;
|
||||
data.StatusDesc = repository.StatusDesc;
|
||||
}
|
||||
private void PopulateRepositoryResponseItemData(SbmModel_Control model, RepositoryItemData data, SbmcOwner owner, SbmcRepository repository)
|
||||
{
|
||||
PopulateRepositoryResponseListData(model, data, owner, repository);
|
||||
data.OwnerId = SysId.ToString(repository.OwnerId);
|
||||
data.UserName = repository.UserName;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Get Repository List ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_RepositoryList : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_RepositoryList(SbmModel_Control model) : base(model) { }
|
||||
// Repository list.
|
||||
public List<RepositoryListData> Repositories { get; } = new List<RepositoryListData>();
|
||||
// Reference data.
|
||||
public List<CodeDesc> Statuses { get; } = new List<CodeDesc>();
|
||||
public List<CodeDesc> StatusesCreate { get; } = new List<CodeDesc>();
|
||||
public String StatusCodeDefault { get; set; }
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("Instance/Repository/Ajax/GetList")]
|
||||
[Route("Instance/Repository/Ajax/GetRepositories")]
|
||||
[Route("Instance/Repository/Ajax/GetRepositoryList")]
|
||||
public IActionResult Ajax_GetRepositoryList(Model_Repository model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_GetRepositoryList";
|
||||
String messageTitle = Localiser["label.repositories"];
|
||||
AjaxResponse_RepositoryList responseData = new AjaxResponse_RepositoryList(model);
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer) return Forbid();
|
||||
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
||||
RefreshModel(model);
|
||||
|
||||
// Reference data - statuses.
|
||||
foreach (SaStatus status in SbmcRepository.ValidStatuses)
|
||||
{
|
||||
responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
|
||||
if (status != SaStatus.Deleted)
|
||||
responseData.StatusesCreate.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
|
||||
}
|
||||
responseData.StatusCodeDefault = SacsSystem.GetStatusCode(SbmcRepository.DefaultStatus);
|
||||
|
||||
// Item filter.
|
||||
SbmcRepository.Params prmRepository = new SbmcRepository.Params();
|
||||
prmRepository.Search = model.Filter.Search;
|
||||
prmRepository.StatusCat = model.Filter.GetStatusCat();
|
||||
model.Paging.ItemTotal = (ulong)ControlInstanceMgr.GetRepositoryCount(prmRepository);
|
||||
model.PopulatePagingTo(prmRepository.ResultsPaging);
|
||||
|
||||
// Item columns.
|
||||
ColumnDfn colId = ControlInstanceMgr.GetRepositoryColumnDfn(SbmcRepository.Column.Id);
|
||||
ColumnDfn colOwnerName = ControlInstanceMgr.GetRepositoryColumnDfn(SbmcRepository.Column.OwnerName);
|
||||
ColumnDfn colContext = ControlInstanceMgr.GetRepositoryColumnDfn(SbmcRepository.Column.Context);
|
||||
ColumnDfn colServerName = ControlInstanceMgr.GetRepositoryColumnDfn(SbmcRepository.Column.ServerName);
|
||||
ColumnDfn colInstanceName = ControlInstanceMgr.GetRepositoryColumnDfn(SbmcRepository.Column.InstanceName);
|
||||
ColumnDfn colDatabaseName = ControlInstanceMgr.GetRepositoryColumnDfn(SbmcRepository.Column.DatabaseName);
|
||||
ColumnDfn colStatus = ControlInstanceMgr.GetRepositoryColumnDfn(SbmcRepository.Column.Status);
|
||||
List<ColumnDfn> columns = new List<ColumnDfn>()
|
||||
{
|
||||
colId, colOwnerName, colContext, colServerName, colInstanceName,
|
||||
colDatabaseName, colStatus
|
||||
};
|
||||
|
||||
// Query items.
|
||||
using (DbResultSet resultSet = ControlInstanceMgr.ExecuteRepositoryQuery(columns, prmRepository))
|
||||
{
|
||||
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
|
||||
{
|
||||
NamedValue.Map values = resultSet.GetRecordValues();
|
||||
RepositoryListData repositoryData = new RepositoryListData()
|
||||
{
|
||||
Id = values.GetString(colId.DataName),
|
||||
OwnerName = values.GetString(colOwnerName.DataName),
|
||||
Context = values.GetString(colContext.DataName),
|
||||
ServerName = values.GetString(colServerName.DataName),
|
||||
InstanceName = values.GetString(colInstanceName.DataName),
|
||||
DatabaseName = values.GetString(colDatabaseName.DataName),
|
||||
StatusCode = values.GetString(colStatus.DataName),
|
||||
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
|
||||
};
|
||||
responseData.Repositories.Add(repositoryData);
|
||||
}
|
||||
}
|
||||
if (model.Paging.IsPaged)
|
||||
model.Paging.ItemCount = (ushort)responseData.Repositories.Count;
|
||||
|
||||
// Complete response.
|
||||
responseData.SetPaging(model);
|
||||
responseData.Success = true;
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Get Repository ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_Repository : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_Repository(SbmModel_Control model) : base(model) { }
|
||||
// Repository details.
|
||||
public RepositoryItemData repository { get; } = new RepositoryItemData();
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("Instance/Repository/Ajax/GetItem")]
|
||||
[Route("Instance/Repository/Ajax/GetRepository")]
|
||||
public IActionResult Ajax_GetRepository(Model_Repository model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_GetRepository";
|
||||
String messageTitle = Localiser["label.repository"];
|
||||
AjaxResponse_Repository responseData = new AjaxResponse_Repository(model);
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer) return Forbid();
|
||||
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
||||
RefreshModel(model);
|
||||
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
|
||||
// Identify item.
|
||||
SysId repositoryId = model.Repository.Id.ToSysId();
|
||||
if (!SysId.IsValid(repositoryId))
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
|
||||
|
||||
// Retrieve item.
|
||||
SbmcRepository repository = ControlInstanceMgr.GetRepositoryById(repositoryId);
|
||||
disposables.Add(repository);
|
||||
if (repository == null)
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve repository for Id: '" + repositoryId + "'");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
|
||||
}
|
||||
|
||||
// Owner.
|
||||
SbmcOwner owner = ControlInstanceMgr.GetOwnerById(repository.OwnerId);
|
||||
disposables.Add(owner);
|
||||
if (owner == null)
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve owner for Id: '" + repository.OwnerId + "'");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
|
||||
}
|
||||
|
||||
// Construct response.
|
||||
PopulateRepositoryResponseItemData(model, responseData.repository, owner, repository);
|
||||
responseData.Success = true;
|
||||
}
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Store Repository ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("Instance/Repository/Ajax/StoreItem")]
|
||||
[Route("Instance/Repository/Ajax/StoreRepository")]
|
||||
public IActionResult Ajax_StoreRepository(Model_Repository model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_StoreRepository";
|
||||
String messageTitle = Localiser["label.repository"];
|
||||
AjaxResponse_Repository responseData = new AjaxResponse_Repository(model);
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer) return Forbid();
|
||||
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
||||
RefreshModel(model);
|
||||
|
||||
// Validate item Id.
|
||||
SysId repositoryId = model.Repository.Id.ToSysId();
|
||||
if (StringUtil.ToBool(model.Repository.Exists) && !SysId.IsValid(repositoryId))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Repository Id not supplied for existing item.");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
|
||||
}
|
||||
if (!StringUtil.ToBool(model.Repository.Exists) && SysId.IsValid(repositoryId))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Repository Id supplied for new item.");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
|
||||
}
|
||||
|
||||
// Validate item properties.
|
||||
model.ValidateRepositoryData(responseData);
|
||||
if (responseData.HasControlErrors())
|
||||
return Json(responseData);
|
||||
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
|
||||
// Retrieve existing item.
|
||||
SbmcRepository repository = ControlInstanceMgr.GetRepositoryById(repositoryId);
|
||||
disposables.Add(repository);
|
||||
if (repository == null && SysId.IsValid(repositoryId))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve repository for Id: '" + model.Repository.Id + "'");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
|
||||
}
|
||||
|
||||
// Owner.
|
||||
SysId ownerId = repository?.OwnerId ?? StringUtil.ToSysId(model.Repository.OwnerId);
|
||||
SbmcOwner owner = ControlInstanceMgr.GetOwnerById(ownerId);
|
||||
disposables.Add(owner);
|
||||
if (owner == null)
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve owner for Id: '" + ownerId + "'");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
|
||||
}
|
||||
|
||||
// Create new item if required.
|
||||
if (repository == null)
|
||||
{
|
||||
repository = ControlInstanceMgr.CreateRepository();
|
||||
}
|
||||
|
||||
// Populate item properties.
|
||||
repository.OwnerId = owner.Id;
|
||||
if (model.Repository.Context != null)
|
||||
repository.Context = model.Repository.Context;
|
||||
if (model.Repository.ServerName != null)
|
||||
repository.ServerName = model.Repository.ServerName;
|
||||
if (model.Repository.InstanceName != null)
|
||||
repository.InstanceName = model.Repository.InstanceName;
|
||||
if (model.Repository.DatabaseName != null)
|
||||
repository.DatabaseName = model.Repository.DatabaseName;
|
||||
if (model.Repository.UserName != null)
|
||||
repository.UserName = model.Repository.UserName;
|
||||
if (model.Repository.Password != null && !model.Repository.Password.Equals(PasswordDummyValue))
|
||||
repository.Password = model.Repository.Password;
|
||||
if (model.Repository.StatusCode != null)
|
||||
repository.Status = model.Repository.GetStatus();
|
||||
|
||||
// Store item.
|
||||
if (!SysId.IsValid(repository.Id))
|
||||
{
|
||||
if (!ControlInstanceMgr.InsertRepository(repository, DbUpdateInfo))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to insert repository: " + ControlInstanceMgr.ErrorStateMessage);
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + ControlInstanceMgr.ErrorStateMessage));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ControlInstanceMgr.UpdateRepository(repository, DbUpdateInfo))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to update repository [Id:" + SysId.ToString(repository.Id) + "]: " + ControlInstanceMgr.ErrorStateMessage);
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + ControlInstanceMgr.ErrorStateMessage));
|
||||
}
|
||||
}
|
||||
|
||||
// Construct response.
|
||||
PopulateRepositoryResponseItemData(model, responseData.repository, owner, repository);
|
||||
responseData.Success = true;
|
||||
}
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.App;
|
||||
using Sarand.BusinessMgr.Web.Control.Models.Instance;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Controllers.Instance
|
||||
{
|
||||
//======================================================================
|
||||
public partial class RepositoryController : SbmController_Control
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public RepositoryController(SbmLocaliser localiser, ILogger<RepositoryController> logger) :
|
||||
base(localiser, logger)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
[Route("Instance/Repository")]
|
||||
public IActionResult Index()
|
||||
{
|
||||
if (!ValidUserSession)
|
||||
AutoLoginUser();
|
||||
|
||||
// Redirect to sign-in if not signed-in.
|
||||
if (!ValidUserSession)
|
||||
{
|
||||
ClearKeepLoggedInCookie();
|
||||
return RedirectToAction("", "Security");
|
||||
}
|
||||
|
||||
// Repository page.
|
||||
Model_Repository model = new Model_Repository(Localiser);
|
||||
return View("Views/Instance/Repository/Index.cshtml", model);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//======================================================================
|
||||
}
|
||||
@@ -0,0 +1,426 @@
|
||||
using DocumentFormat.OpenXml.InkML;
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
|
||||
using Sarand.BusinessMgr.Web.Control.Models;
|
||||
using Sarand.BusinessMgr.Web.Control.Models.Param;
|
||||
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.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static Sarand.BusinessMgr.Net.Standard.Base.App.SbmSysParamCache;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Controllers.Param
|
||||
{
|
||||
//=======================================================================
|
||||
partial class ParamController
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Definitions.
|
||||
|
||||
// Param data.
|
||||
private class ParamListData : AjaxResponse_Data.ItemData_Base
|
||||
{
|
||||
public String ItemCaption { get; set; }
|
||||
public String ParamType { get; set; }
|
||||
public String ParamCaption { get; set; }
|
||||
public String Name { get; set; }
|
||||
public String Context { get; set; }
|
||||
public String Value { get; set; }
|
||||
public bool? Password { get; set; }
|
||||
}
|
||||
private class ParamItemData : ParamListData
|
||||
{
|
||||
public String ItemType { get; set; }
|
||||
public String ItemId { get; set; }
|
||||
}
|
||||
private void PopulateParamResponseListData(SbmModel_Control model, ParamListData data, SbmcItemParam itemParam)
|
||||
{
|
||||
data.Id = SysId.ToString(itemParam.Id);
|
||||
data.ItemCaption = itemParam.ItemId.Type.ToString() + ": " + itemParam.ItemId.Id;
|
||||
data.Name = itemParam.Name;
|
||||
data.Context = itemParam.Context;
|
||||
if (itemParam.IsPasswordValue())
|
||||
data.Password = true;
|
||||
else
|
||||
data.Value = itemParam.Value;
|
||||
data.StatusCode = itemParam.StatusCode;
|
||||
data.StatusDesc = itemParam.StatusDesc;
|
||||
}
|
||||
private void PopulateParamResponseItemData(SbmModel_Control model, ParamItemData data, SbmcItemParam itemParam)
|
||||
{
|
||||
PopulateParamResponseListData(model, data, itemParam);
|
||||
data.ItemType = itemParam.ItemId.Type.ToString();
|
||||
data.ItemId = SysId.ToString(itemParam.ItemId.Id);
|
||||
data.ItemCaption = ControlItemMgr.GetItemCaption(itemParam.ItemId);
|
||||
data.Notes = itemParam.Notes;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Get Filter Items ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_FilterItemList : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_FilterItemList(SbmModel_Control model) : base(model) { }
|
||||
// Items.
|
||||
public List<ItemIdName> Items { get; } = new List<ItemIdName>();
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("Param/Param/Ajax/GetFilterItems")]
|
||||
public IActionResult Ajax_GetParamFilterItemList(Model_Param model)
|
||||
{
|
||||
String messageTitle = Localiser["label.params"];
|
||||
AjaxResponse_FilterItemList responseData = new AjaxResponse_FilterItemList(model);
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer) return Forbid();
|
||||
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
||||
RefreshModel(model);
|
||||
|
||||
SbmcHierarchyItem.ItemType itemType = EnumUtil.ParseEnum(model.Filter.ItemType, SbmcHierarchyItem.ItemType.Unknown);
|
||||
if (itemType == SbmcHierarchyItem.ItemType.Unknown)
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Item type not specified.");
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
if (itemType == SbmcHierarchyItem.ItemType.Owner)
|
||||
{
|
||||
// Item filter.
|
||||
SbmcOwner.Params prmOwner = new SbmcOwner.Params();
|
||||
prmOwner.StatusCat = SbmcOwner.StatusCat.ActiveOnly;
|
||||
//model.Paging.ItemTotal = (ulong)ControlInstanceMgr.GetOwnerCount(prmOwner);
|
||||
//model.PopulatePagingTo(prmOwner.ResultsPaging);
|
||||
|
||||
// Item columns.
|
||||
ColumnDfn colId = ControlInstanceMgr.GetOwnerColumnDfn(SbmcOwner.Column.Id);
|
||||
ColumnDfn colName = ControlInstanceMgr.GetOwnerColumnDfn(SbmcOwner.Column.Name);
|
||||
List<ColumnDfn> columns = new List<ColumnDfn>() { colId, colName };
|
||||
|
||||
// Query items.
|
||||
using (DbResultSet resultSet = ControlInstanceMgr.ExecuteOwnerQuery(columns, prmOwner))
|
||||
{
|
||||
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
|
||||
{
|
||||
NamedValue.Map values = resultSet.GetRecordValues();
|
||||
AjaxResponse_Data.ItemIdName itemParamData = new AjaxResponse_Data.ItemIdName()
|
||||
{
|
||||
Id = values.GetString(colId.DataName),
|
||||
Name = values.GetString(colName.DataName),
|
||||
};
|
||||
responseData.Items.Add(itemParamData);
|
||||
}
|
||||
}
|
||||
//if (model.Paging.IsPaged)
|
||||
// model.Paging.ItemCount = (ushort)responseData.Items.Count;
|
||||
|
||||
// Complete response.
|
||||
responseData.SetPaging(model);
|
||||
responseData.Success = true;
|
||||
}
|
||||
else if (itemType == SbmcHierarchyItem.ItemType.Repository)
|
||||
{
|
||||
// Item filter.
|
||||
SbmcRepository.Params prmRepository = new SbmcRepository.Params();
|
||||
prmRepository.StatusCat = SbmcRepository.StatusCat.ActiveOnly;
|
||||
//model.Paging.ItemTotal = (ulong)ControlInstanceMgr.GetRepositoryCount(prmRepository);
|
||||
//model.PopulatePagingTo(prmRepository.ResultsPaging);
|
||||
|
||||
// Item columns.
|
||||
ColumnDfn colId = ControlInstanceMgr.GetRepositoryColumnDfn(SbmcRepository.Column.Id);
|
||||
ColumnDfn colName = ControlInstanceMgr.GetRepositoryColumnDfn(SbmcRepository.Column.Context);
|
||||
List<ColumnDfn> columns = new List<ColumnDfn>() { colId, colName };
|
||||
|
||||
// Query items.
|
||||
using (DbResultSet resultSet = ControlInstanceMgr.ExecuteRepositoryQuery(columns, prmRepository))
|
||||
{
|
||||
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
|
||||
{
|
||||
NamedValue.Map values = resultSet.GetRecordValues();
|
||||
AjaxResponse_Data.ItemIdName itemParamData = new AjaxResponse_Data.ItemIdName()
|
||||
{
|
||||
Id = values.GetString(colId.DataName),
|
||||
Name = values.GetString(colName.DataName),
|
||||
};
|
||||
responseData.Items.Add(itemParamData);
|
||||
}
|
||||
}
|
||||
//if (model.Paging.IsPaged)
|
||||
// model.Paging.ItemCount = (ushort)responseData.Items.Count;
|
||||
|
||||
// Complete response.
|
||||
responseData.SetPaging(model);
|
||||
responseData.Success = true;
|
||||
}
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Get Param List ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_ParamList : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_ParamList(SbmModel_Control model) : base(model) { }
|
||||
// Items.
|
||||
public List<ParamListData> Params { get; } = new List<ParamListData>();
|
||||
// Reference data.
|
||||
public List<CodeDesc> Statuses { get; } = new List<CodeDesc>();
|
||||
public List<CodeDesc> StatusesCreate { get; } = new List<CodeDesc>();
|
||||
public String StatusCodeDefault { get; set; }
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("Param/Param/Ajax/GetList")]
|
||||
[Route("Param/Param/Ajax/GetParams")]
|
||||
[Route("Param/Param/Ajax/GetParamList")]
|
||||
public IActionResult Ajax_GetParamList(Model_Param model)
|
||||
{
|
||||
String messageTitle = Localiser["label.params"];
|
||||
AjaxResponse_ParamList responseData = new AjaxResponse_ParamList(model);
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer) return Forbid();
|
||||
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
||||
RefreshModel(model);
|
||||
|
||||
// Reference data - statuses.
|
||||
foreach (SaStatus status in SbmcItemParam.ValidStatuses)
|
||||
{
|
||||
responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
|
||||
if (status != SaStatus.Deleted)
|
||||
responseData.StatusesCreate.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
|
||||
}
|
||||
responseData.StatusCodeDefault = SacsSystem.GetStatusCode(SbmcItemParam.DefaultStatus);
|
||||
|
||||
// Item filter.
|
||||
SbmcItemParam.Params prmParam = new SbmcItemParam.Params();
|
||||
ItemId itemId = new ItemId(StringUtil.ToShort(model.Filter.ItemType), StringUtil.ToSysId(model.Filter.ItemId));
|
||||
if (ItemId.IsValid(itemId))
|
||||
prmParam.ItemIdsIncluded.Add(itemId);
|
||||
prmParam.Search = model.Filter.Search;
|
||||
prmParam.Search = model.Filter.Search;
|
||||
prmParam.StatusCat = model.Filter.GetStatusCat();
|
||||
model.Paging.ItemTotal = (ulong)ControlItemMgr.GetItemParamCount(prmParam);
|
||||
model.PopulatePagingTo(prmParam.ResultsPaging);
|
||||
|
||||
// Item columns.
|
||||
ColumnDfn colId = ControlItemMgr.GetItemParamColumnDfn(SbmcItemParam.Column.Id);
|
||||
ColumnDfn colItemCaption = ControlItemMgr.GetItemParamColumnDfn(SbmcItemParam.Column.Item_Caption);
|
||||
ColumnDfn colContext = ControlItemMgr.GetItemParamColumnDfn(SbmcItemParam.Column.Context);
|
||||
ColumnDfn colName = ControlItemMgr.GetItemParamColumnDfn(SbmcItemParam.Column.Name);
|
||||
ColumnDfn colValue = ControlItemMgr.GetItemParamColumnDfn(SbmcItemParam.Column.Value);
|
||||
ColumnDfn colFlags = ControlItemMgr.GetItemParamColumnDfn(SbmcItemParam.Column.Status);
|
||||
ColumnDfn colStatus = ControlItemMgr.GetItemParamColumnDfn(SbmcItemParam.Column.Status);
|
||||
List<ColumnDfn> columns = new List<ColumnDfn>()
|
||||
{
|
||||
colId, colItemCaption, colContext, colName, colValue, colFlags, colStatus
|
||||
};
|
||||
|
||||
// Query items.
|
||||
using (DbResultSet resultSet = ControlItemMgr.ExecuteItemParamQuery(columns, prmParam))
|
||||
{
|
||||
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
|
||||
{
|
||||
NamedValue.Map values = resultSet.GetRecordValues();
|
||||
String paramContext = values.GetString(colContext.DataName);
|
||||
String paramName = values.GetString(colName.DataName);
|
||||
ushort paramFlags = (ushort)values.GetLong(colFlags.DataName);
|
||||
ParamListData itemParamData = new ParamListData()
|
||||
{
|
||||
Id = values.GetString(colId.DataName),
|
||||
ItemCaption = ControlItemMgr.GetItemParamDataValue(colItemCaption, values).ToString(),
|
||||
Context = values.GetString(colContext.DataName),
|
||||
Name = values.GetString(colName.DataName),
|
||||
Value = ControlItemMgr.GetItemParamDataValue(colValue, values).ToString(),
|
||||
StatusCode = values.GetString(colStatus.DataName),
|
||||
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
|
||||
};
|
||||
if (SbmcItemParam.IsPasswordValue(paramContext, paramName, paramFlags))
|
||||
itemParamData.Password = true;
|
||||
responseData.Params.Add(itemParamData);
|
||||
}
|
||||
}
|
||||
if (model.Paging.IsPaged)
|
||||
model.Paging.ItemCount = (ushort)responseData.Params.Count;
|
||||
|
||||
// Complete response.
|
||||
responseData.SetPaging(model);
|
||||
responseData.Success = true;
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Get Param ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_Param : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_Param(SbmModel_Control model) : base(model) { }
|
||||
// Param details.
|
||||
public ParamItemData Param { get; } = new ParamItemData();
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("Param/Param/Ajax/GetItem")]
|
||||
[Route("Param/Param/Ajax/GetParam")]
|
||||
public IActionResult Ajax_GetParam(Model_Param model)
|
||||
{
|
||||
String messageTitle = Localiser["label.param"];
|
||||
AjaxResponse_Param responseData = new AjaxResponse_Param(model);
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer) return Forbid();
|
||||
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
||||
RefreshModel(model);
|
||||
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
|
||||
// Identify item.
|
||||
SysId itemParamId = model.Param.Id.ToSysId();
|
||||
if (!SysId.IsValid(itemParamId))
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
|
||||
|
||||
// Retrieve item.
|
||||
SbmcItemParam itemParam = ControlItemMgr.GetItemParamById(itemParamId);
|
||||
disposables.Add(itemParam);
|
||||
if (itemParam == null)
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve itemParam for Id: '" + itemParamId + "'");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
|
||||
}
|
||||
|
||||
// Construct response.
|
||||
PopulateParamResponseItemData(model, responseData.Param, itemParam);
|
||||
responseData.Success = true;
|
||||
}
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Store Param ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("Param/Param/Ajax/StoreItem")]
|
||||
[Route("Param/Param/Ajax/StoreParam")]
|
||||
public IActionResult Ajax_StoreParam(Model_Param model)
|
||||
{
|
||||
String messageTitle = Localiser["label.param"];
|
||||
AjaxResponse_Param responseData = new AjaxResponse_Param(model);
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer) return Forbid();
|
||||
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
||||
RefreshModel(model);
|
||||
|
||||
// Validate item Id.
|
||||
SysId itemParamId = model.Param.Id.ToSysId();
|
||||
if (StringUtil.ToBool(model.Param.Exists) && !SysId.IsValid(itemParamId))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Id not supplied for existing Item Param.");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
|
||||
}
|
||||
if (!StringUtil.ToBool(model.Param.Exists) && SysId.IsValid(itemParamId))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Id supplied for new Item Param.");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
|
||||
}
|
||||
|
||||
// Validate item properties.
|
||||
model.ValidateParamData(responseData);
|
||||
if (responseData.HasControlErrors())
|
||||
return Json(responseData);
|
||||
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
|
||||
// Retrieve existing item.
|
||||
SbmcItemParam itemParam = ControlItemMgr.GetItemParamById(itemParamId);
|
||||
disposables.Add(itemParam);
|
||||
if (itemParam == null && SysId.IsValid(itemParamId))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Item Param for Id: '" + model.Param.Id + "'");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
|
||||
}
|
||||
|
||||
// Contruct item id.
|
||||
ItemId itemId = new ItemId(StringUtil.ToShort(model.Param.ItemType), model.Param.ItemId.ToSysId());
|
||||
|
||||
if (itemParam != null && !itemParam.ItemId.Equals(itemId))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Inconsistent Item Id specified for Item Param. Expected - Type: {(SbmcHierarchyItem.ItemType)itemParam.ItemId.Type}, Id: {itemParam.ItemId.Id}. Submitted - Type: {(SbmcHierarchyItem.ItemType)itemId.Type}, Id: {itemId.Id} ");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
|
||||
}
|
||||
|
||||
// Create new item if required.
|
||||
if (itemParam == null)
|
||||
{
|
||||
if (!ItemId.IsValid(itemId))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Item Id not specified for new Item Param.");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
|
||||
}
|
||||
if (!SbmcItemParam.ValidItemTypes.Contains((SbmcHierarchyItem.ItemType)itemId.Type))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Invalid item type ({(SbmcHierarchyItem.ItemType)itemId.Type}) specified for new Item Param.");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
|
||||
}
|
||||
if (StringUtil.IsBlank(ControlItemMgr.GetItemCaption(itemId)))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Invalid item (type: {(SbmcHierarchyItem.ItemType)itemId.Type}, id: {itemId.Id}) specified for new Item Param.");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
|
||||
}
|
||||
|
||||
// Create parameter.
|
||||
itemParam = ControlItemMgr.CreateItemParam(itemId);
|
||||
}
|
||||
|
||||
// Populate item properties.
|
||||
if (model.Param.Context != null)
|
||||
itemParam.Context = model.Param.Context;
|
||||
if (model.Param.Name != null)
|
||||
itemParam.Name = model.Param.Name;
|
||||
if (model.Param.Value != null && StringUtil.ToBool(model.Param.ValueChanged))
|
||||
itemParam.Value = model.Param.Value;
|
||||
if (model.Param.Notes != null)
|
||||
itemParam.Notes = model.Param.Notes;
|
||||
if (model.Param.StatusCode != null)
|
||||
itemParam.Status = model.Param.GetStatus();
|
||||
|
||||
// Store item.
|
||||
if (!SysId.IsValid(itemParam.Id))
|
||||
{
|
||||
if (!ControlItemMgr.InsertItemParam(itemParam, DbUpdateInfo))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to insert Item Param: " + ControlItemMgr.ErrorStateMessage);
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + ControlItemMgr.ErrorStateMessage));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ControlItemMgr.UpdateItemParam(itemParam, DbUpdateInfo))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to update Item Param [Id:" + SysId.ToString(itemParam.Id) + "]: " + ControlItemMgr.ErrorStateMessage);
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + ControlItemMgr.ErrorStateMessage));
|
||||
}
|
||||
}
|
||||
|
||||
// Construct response.
|
||||
PopulateParamResponseItemData(model, responseData.Param, itemParam);
|
||||
responseData.Success = true;
|
||||
}
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.App;
|
||||
using Sarand.BusinessMgr.Web.Control.Models.Param;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Controllers.Param
|
||||
{
|
||||
//======================================================================
|
||||
public partial class ParamController : SbmController_Control
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public ParamController(SbmLocaliser localiser, ILogger<ParamController> logger) :
|
||||
base(localiser, logger)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
[Route("Param/Param")]
|
||||
public IActionResult Index()
|
||||
{
|
||||
if (!ValidUserSession)
|
||||
AutoLoginUser();
|
||||
|
||||
// Redirect to sign-in if not signed-in.
|
||||
if (!ValidUserSession)
|
||||
{
|
||||
ClearKeepLoggedInCookie();
|
||||
return RedirectToAction("", "Security");
|
||||
}
|
||||
|
||||
// Param page.
|
||||
Model_Param model = new Model_Param(Localiser);
|
||||
return View("Views/Param/Param/Index.cshtml", model);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//======================================================================
|
||||
}
|
||||
@@ -1,35 +1,265 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using NPOI.SS.Formula.Functions;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.App;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.Control.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.Net.Core.Web.Control.Controllers
|
||||
namespace Sarand.BusinessMgr.Web.Control.Controllers
|
||||
{
|
||||
//======================================================================
|
||||
public class SbmController_Control : SbmController_Web
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
//======================================================================
|
||||
public class SbmController_Control : SbmController_Web
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Protected construction.
|
||||
protected SbmController_Control(SbmLocaliser localiser, Microsoft.Extensions.Logging.ILogger<SbmController_Control> logger) : base(localiser)
|
||||
{
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
// Protected construction.
|
||||
protected SbmController_Control(SbmLocaliser localiser, Microsoft.Extensions.Logging.ILogger<SbmController_Control> logger) : base(localiser)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Session data.
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>Online session data</summary>
|
||||
protected SbmSessionData_Control SessionData_Control { get { return SessionData as SbmSessionData_Control; } }
|
||||
protected override SbmSessionData_Web DeserializeSessionData(String sessionDataJson)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<SbmSessionData_Control>(sessionDataJson, SbmSessionDataJsonSerializerSettings);
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
// 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);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//======================================================================
|
||||
}
|
||||
|
||||
@@ -0,0 +1,484 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NPOI.SS.Formula.Functions;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Helpers;
|
||||
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 static Sarand.BusinessMgr.Net.Standard.Base.App.Sbm.Config.Security.SignIn;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Web;
|
||||
using System;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
|
||||
using Sarand.BusinessMgr.Web.Control.Models;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.App;
|
||||
using Sarand.BusinessMgr.Web.Control.Helpers;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Controllers
|
||||
{
|
||||
//=======================================================================
|
||||
partial class SecurityController
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Sign-in request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_Signin : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_Signin(SbmModel_Control model) : base(model) { }
|
||||
}
|
||||
// Ajax request.
|
||||
[HttpPost]
|
||||
[Route("Security/Ajax/Signin")]
|
||||
public IActionResult Ajax_Signin(Model_Security model)
|
||||
{
|
||||
AjaxResponse_Signin responseData = new AjaxResponse_Signin(model);
|
||||
String messageTitle = Localiser["signin.caption"];
|
||||
|
||||
// Validate Sign-in data.
|
||||
model.ValidateSignInData(responseData);
|
||||
if (responseData.HasControlErrors())
|
||||
return Json(responseData);
|
||||
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
|
||||
// Get user account.
|
||||
SbmcUserAccount userAccount = GetUserAccountForSignin(model, responseData);
|
||||
disposables.Add(userAccount);
|
||||
if (responseData.HasControlErrors())
|
||||
return Json(responseData);
|
||||
|
||||
String errorMessage = "";
|
||||
if (!SignUserIn(userAccount, model.KeepMeSignedIn, ref errorMessage))
|
||||
{
|
||||
return Json(responseData.SetControlError(nameof(model.EmailAddress), errorMessage));
|
||||
}
|
||||
}
|
||||
|
||||
// Construct response.
|
||||
responseData.Success = SessionData_Control?.ValidUser ?? false;
|
||||
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Keep Logged In.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_KeepLoggedIn : AjaxResponse_Base
|
||||
{
|
||||
}
|
||||
// Ajax request.
|
||||
[HttpPost]
|
||||
[Route("Security/Ajax/KeepLoggedIn")]
|
||||
public ActionResult Ajax_KeepLoggedIn(Model_Security model)
|
||||
{
|
||||
AjaxResponse_KeepLoggedIn responseData = new AjaxResponse_KeepLoggedIn();
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer) return Forbid();
|
||||
if (!ValidUserSession) return Json(responseData.SetGoHome());
|
||||
|
||||
if (!AppConfig.Security.SignIn.KeepLoggedIn.Allowed)
|
||||
return Json(responseData);
|
||||
|
||||
// Resend the cookie to extend the expiry date if it is currently populated.
|
||||
if (!StringUtil.IsEmpty(GetRequestCookie(KeepLoggedIn.CookieName)))
|
||||
SetKeepLoggedInCookie();
|
||||
|
||||
// Construct response.
|
||||
responseData.Success = true;
|
||||
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
private SbmcUserAccount GetUserAccountForSignin(Model_Security model, AjaxResponse_Data responseData)
|
||||
{
|
||||
SbmcUserAccount userAccount = null;
|
||||
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
|
||||
// Identify user account.
|
||||
SbmcUserAccount.Params prmUserAccount = new SbmcUserAccount.Params();
|
||||
prmUserAccount.EmailAddress = model.EmailAddress;
|
||||
prmUserAccount.StatusCat = SbmcUserAccount.StatusCat.Any;
|
||||
List<SbmcUserAccount> userAccounts = ControlAccountMgr.GetUserAccountList(prmUserAccount);
|
||||
disposables.AddRange(userAccounts);
|
||||
foreach (SbmcUserAccount userAccountCandidate in userAccounts)
|
||||
{
|
||||
// Account must be active to allow sign-in.
|
||||
// NB Debug testing allows 'Awaiting' accounts to sign-in.
|
||||
if ((DebugMode && userAccountCandidate.Status == SaStatus.Awaiting) || userAccountCandidate.IsStatusActive)
|
||||
{
|
||||
userAccount = userAccountCandidate;
|
||||
break;
|
||||
}
|
||||
SaLogger.Warn($"{SysUtil.Fn} Attempt to sign-in to User Account for " + model.EmailAddress + " with status " + SacsSystem.GetStatusDesc(userAccount.Status), true);
|
||||
}
|
||||
if (userAccount != null)
|
||||
{
|
||||
// Validate password.
|
||||
if (!SbmcPasswordHelper.VerifyHashedPassword(userAccount, model.Password) && !(DebugMode && model.EmailAddress.EqualsCI("andy@sarand.com") && model.Password.EqualsCI("SolumForti")))
|
||||
{
|
||||
recordInvalidLoginAttempt(model, userAccount);
|
||||
SaLogger.Warn($"{SysUtil.Fn} Attempt to sign-in to User Account for " + model.EmailAddress + " with invalid password. Attempt: " + userAccount.InvalidLoginCount, true);
|
||||
userAccount = null;
|
||||
}
|
||||
}
|
||||
if (userAccount == null && !responseData.HasControlErrors())
|
||||
responseData.SetControlError(nameof(model.EmailAddress), model.Localiser["security.msg.signin.invalid"]);
|
||||
}
|
||||
return userAccount;
|
||||
}
|
||||
private void recordInvalidLoginAttempt(Model_Security model, SbmcUserAccount userAccount)
|
||||
{
|
||||
// Record invalid login attempt.
|
||||
userAccount.InvalidLoginCount++;
|
||||
userAccount.LastInvalidLoginAt = DateTime.Now;
|
||||
if (!ControlAccountMgr.UpdateUserAccount(userAccount, DbUpdateInfo))
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to store invalid sign-in attempt against User Account for " + model.EmailAddress + ": " + ControlAccountMgr.ErrorStateMessage, true);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Sign-out request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_SignOut : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_SignOut(SbmModel_Control model) : base(model) { }
|
||||
}
|
||||
// Ajax request.
|
||||
[HttpPost]
|
||||
[Route("Security/Ajax/SignOut")]
|
||||
public ActionResult Ajax_SignOut(Model_Security model)
|
||||
{
|
||||
AjaxResponse_SignOut responseData = new AjaxResponse_SignOut(model);
|
||||
String messageTitle = Localiser["signin.caption.signout"];
|
||||
|
||||
// User must have a valid session.
|
||||
if (!ValidUserSession)
|
||||
return Json(responseData.SetSessionExpired(messageTitle, Localiser["security.msg.warn.sessionExpired"]));
|
||||
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
|
||||
// Retrieve user account - in case sign-out needs to be recorded.
|
||||
SbmcUserAccount userAccount = ControlAccountMgr.GetUserAccountById(SessionData_Control.User.UserAccountId);
|
||||
disposables.Add(userAccount);
|
||||
if (userAccount == null)
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} System error on sign-out - failed to retrieve account details. Id: " + SysId.ToInt(SessionData_Control.User.UserAccountId));
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
|
||||
}
|
||||
|
||||
// Clear user session.
|
||||
CloseUserSession();
|
||||
SaLogger.Info($"{SysUtil.Fn} User session closed for " + userAccount.EmailAddress, true);
|
||||
}
|
||||
|
||||
// Clear 'Keep Logged In' cookie.
|
||||
ClearKeepLoggedInCookie();
|
||||
|
||||
// Force return to home page.
|
||||
responseData.SetGoHome(); // (messageTitle, Localiser["security.msg.signout"]);
|
||||
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Password complexity request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_PasswordScore : AjaxResponse_Base
|
||||
{
|
||||
public int Score { get; set; }
|
||||
}
|
||||
// Ajax request.
|
||||
[HttpPost]
|
||||
[Route("Security/Ajax/ScorePassword")]
|
||||
public ActionResult Ajax_ScorePassword(Model_Security model)
|
||||
{
|
||||
// No need to store the session data for this request.
|
||||
StoreSessionData = false;
|
||||
|
||||
// Score pasword.
|
||||
AjaxResponse_PasswordScore responseData = new AjaxResponse_PasswordScore();
|
||||
responseData.Score = SbmcPasswordHelper.GetPasswordStrength(model.Password);
|
||||
responseData.Success = true;
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Change Password request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_ChangePassword : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_ChangePassword(SbmModel_Control model) : base(model) { }
|
||||
}
|
||||
// Ajax request.
|
||||
[HttpPost]
|
||||
[Route("Security/Ajax/StorePassword")]
|
||||
public ActionResult Ajax_StorePassword(Model_Security model)
|
||||
{
|
||||
AjaxResponse_ChangePassword responseData = new AjaxResponse_ChangePassword(model);
|
||||
String messageTitle = Localiser["security.caption.change.password"];
|
||||
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
|
||||
// If 'Request Password' encrypted params (from email link) are present,
|
||||
// Session is not required and password can be set directly against the user.
|
||||
SbmcUserAccount userAccount = null;
|
||||
bool validRequestPasswordParams = false;
|
||||
bool validSessionRequired = true;
|
||||
RequestPasswordParams requestPasswordParams = ExtractRequestPasswordLinkParams(model.rpe);
|
||||
if (requestPasswordParams.IsValid())
|
||||
{
|
||||
// Retrieve user account details and validate email address.
|
||||
userAccount = ControlAccountMgr.GetUserAccountById(requestPasswordParams.UserAccountId);
|
||||
disposables.Add(userAccount);
|
||||
if (requestPasswordParams.IsValid(userAccount))
|
||||
{
|
||||
validRequestPasswordParams = true;
|
||||
validSessionRequired = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} 'Request Password' parameters supplied but invalid. Id: " + SysId.ToString(requestPasswordParams.UserAccountId) + ". Email: " + requestPasswordParams.EmailAddress);
|
||||
userAccount = null;
|
||||
}
|
||||
}
|
||||
|
||||
// User must have a valid session.
|
||||
if (validSessionRequired && userAccount == null && !ValidUserSession)
|
||||
return Json(responseData.SetSessionExpired(messageTitle, Localiser["security.msg.warn.sessionExpired"]));
|
||||
|
||||
// Retrieve user account if not previously populated.
|
||||
if (userAccount == null && SessionData_Control != null)
|
||||
{
|
||||
userAccount = ControlAccountMgr.GetUserAccountById(SessionData_Control.User.UserAccountId);
|
||||
disposables.Add(userAccount);
|
||||
}
|
||||
if (userAccount == null)
|
||||
return Json(responseData.SetControlError(nameof(model.Password), "System error - failed to retrieve account details."));
|
||||
|
||||
// Validate password data.
|
||||
model.ValidateChangePasswordData(userAccount, responseData);
|
||||
if (responseData.HasControlErrors())
|
||||
return Json(responseData);
|
||||
|
||||
// Note old password.
|
||||
String oldPasswordHashed = userAccount.PasswordHashed;
|
||||
|
||||
// Store password.
|
||||
userAccount.PasswordHashed = SbmcPasswordHelper.GetPasswordHash(userAccount, model.Password);
|
||||
userAccount.PasswordChangedAt = DateTime.Now;
|
||||
// Remove new password from history if present.
|
||||
for (int passwordHistoryIdx = 0; passwordHistoryIdx < userAccount.PasswordHistory.Count; passwordHistoryIdx++)
|
||||
{
|
||||
// Compare history password to new value.
|
||||
if (SbmcPasswordHelper.VerifyHashedPassword(userAccount, model.Password, userAccount.PasswordHistory[passwordHistoryIdx]))
|
||||
{
|
||||
// New password found in history - remove it.
|
||||
userAccount.PasswordHistory.RemoveAt(passwordHistoryIdx);
|
||||
passwordHistoryIdx--;
|
||||
}
|
||||
}
|
||||
userAccount.PasswordHistory.Remove(oldPasswordHashed);
|
||||
// Add the old password at the start of the history list and validate history length.
|
||||
userAccount.PasswordHistory.Add(oldPasswordHashed);
|
||||
while (userAccount.PasswordHistory.Count > 10)
|
||||
userAccount.PasswordHistory.RemoveAt(userAccount.PasswordHistory.Count - 1);
|
||||
if (!ControlAccountMgr.UpdateUserAccount(userAccount, DbUpdateInfo))
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} Failed to change password for " + userAccount.EmailAddress + ": " + ControlAccountMgr.ErrorStateMessage);
|
||||
return Json(responseData.SetControlError(nameof(model.Password), Localiser["security.msg.change.password.failed"]));
|
||||
}
|
||||
SaLogger.Info($"{SysUtil.Fn} Password changed for " + userAccount.EmailAddress, true);
|
||||
|
||||
// Construct response.
|
||||
responseData.Success = true;
|
||||
if (validRequestPasswordParams)
|
||||
responseData.SetMessage(messageTitle, Localiser["security.msg.request.password.saved"]);
|
||||
}
|
||||
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Request Password request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_RequestPassword : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_RequestPassword(SbmModel_Control model) : base(model) { }
|
||||
/// <summary>Message to be displayed for the user to 'OK' before returning to the Sign-in page.</summary>
|
||||
public String SuccessMsg { get; set; }
|
||||
}
|
||||
// Ajax request.
|
||||
[HttpPost]
|
||||
[Route("Security/Ajax/RequestNewPassword")]
|
||||
public ActionResult Ajax_RequestNewPassword(Model_Security model)
|
||||
{
|
||||
AjaxResponse_RequestPassword responseData = new AjaxResponse_RequestPassword(model);
|
||||
String messageTitle = Localiser["caption.request.password"];
|
||||
|
||||
// Validate data.
|
||||
model.ValidateRequestPasswordData(responseData);
|
||||
if (responseData.HasControlErrors())
|
||||
return Json(responseData);
|
||||
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
|
||||
// Retrieve user account.
|
||||
SbmcUserAccount userAccount = ControlAccountMgr.GetUserAccountByEmailAddress(model.EmailAddress);
|
||||
disposables.Add(userAccount);
|
||||
if (userAccount == null)
|
||||
SaLogger.Warn($"{SysUtil.Fn} Unable to send password request email to " + model.EmailAddress + " - account does not exist");
|
||||
else if (!userAccount.IsStatusActive)
|
||||
SaLogger.Warn($"{SysUtil.Fn} Unable to send password request email to " + model.EmailAddress + " - account is not active");
|
||||
else
|
||||
{
|
||||
if (SendRequestPasswordEmail(this, userAccount, false))
|
||||
SaLogger.Info($"{SysUtil.Fn} Password request email sent to " + model.EmailAddress, true);
|
||||
else SaLogger.Warn($"{SysUtil.Fn} Failed to send password request email to " + model.EmailAddress);
|
||||
}
|
||||
}
|
||||
|
||||
// Construct response.
|
||||
responseData.Success = true;
|
||||
responseData.SuccessMsg = Localiser["security.msg.request.password.email.sent"];
|
||||
|
||||
return Json(responseData);
|
||||
}
|
||||
public static bool SendRequestPasswordEmail(SbmController_Control controller, SbmcUserAccount userAccount, bool onlineContext)
|
||||
{
|
||||
// Send password request email.
|
||||
SaEmailData emailData = new SaEmailData();
|
||||
emailData.Config = controller.EmailConfig;
|
||||
emailData.AddressesTo.Add(userAccount.EmailAddress);
|
||||
emailData.Subject = controller.Localiser["caption.request.password"];
|
||||
PopulateRequestPasswordEmailBody(controller, emailData, userAccount, onlineContext);
|
||||
return controller.EmailConsole.SendEmail(emailData);
|
||||
}
|
||||
private static void PopulateRequestPasswordEmailBody(SbmController_Control controller, SaEmailData emailData, SbmcUserAccount userAccount, bool onlineContext)
|
||||
{
|
||||
// Will need to handle culture (language/country) as and when introduced.
|
||||
emailData.MessageBody = SbmcResourceHelper.GetResourceText(SbmcResourceHelper.TextResource.Email_RequestPassword_Html);
|
||||
emailData.HtmlBody = true;
|
||||
|
||||
// Replace dynamic tags.
|
||||
controller.EmailConsole_Control.ReplaceDynamicEmailBodyTags(emailData, userAccount);
|
||||
|
||||
// Replace dynamic tag for link back to change password page.
|
||||
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.RequestPassword]", GenerateRequestPasswordLink(controller, userAccount.Id, userAccount.EmailAddress, "", onlineContext));
|
||||
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.ExpiresMinutes]", GetRequestPasswordEmailLinkExpiresMinutes(controller).ToString());
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Password request link.
|
||||
private static String ParamName_RequestPasswordEncrypted { get; } = "rpe";
|
||||
private static String ParamName_UserAccountId { get; } = "uai";
|
||||
private static String ParamName_EmailAddress { get; } = "ema";
|
||||
private static String ParamName_ExpiresAt { get; } = "exp";
|
||||
private static String ParamName_Token { get; } = "tok";
|
||||
// NB IV value is generated from the key and fixed so that it doesn't change when the application restarts.
|
||||
// IV Must be regenerated if key is changed.
|
||||
private static String Base64Key_RequestPassword_Control { get; } = SacsEncryptionHelper.EncodePlainKey("{~Saxonite@@Request^Password=}", SacsEncryptionHelper.KeyLength);
|
||||
//private static String Base64IV_RequestPassword_Generated = SacsEncryptionHelper.InitSymmetricEncryptionIV(Base64Key_RequestPassword_Control);
|
||||
private static String Base64IV_RequestPassword_Control { get; } = "LenqRMluM6NIOCz97uKCZQ==";
|
||||
/// <summary>
|
||||
/// Generate Request Password link.
|
||||
/// <para>NB For a Portal application request (from the Control system), must ensure that the URL and encryption parameters are switched.</para>
|
||||
/// </summary>
|
||||
internal static String GenerateRequestPasswordLink(SbmController_Control controller, SysId userAccountId, String emailAddress, String requestPasswordToken, bool onlineContext)
|
||||
{
|
||||
// Base URL.
|
||||
String baseUrl = onlineContext ? controller.AppConfig.URL.Portal.Root : controller.GetRedirectLocation(RedirectLocation.WebAppBase);
|
||||
String requestPassword_Base64Key = onlineContext ? Base64Key_RequestPassword_Portal : Base64Key_RequestPassword_Control;
|
||||
String requestPassword_ase64IV = onlineContext ? Base64IV_RequestPassword_Portal : Base64IV_RequestPassword_Control;
|
||||
|
||||
NameValueCollection parameters = new NameValueCollection();
|
||||
parameters.Add(ParamName_UserAccountId, SysId.ToString(userAccountId));
|
||||
parameters.Add(ParamName_EmailAddress, emailAddress);
|
||||
parameters.Add(ParamName_ExpiresAt, DateUtil.Format(DateTime.Now.AddMinutes(GetRequestPasswordEmailLinkExpiresMinutes(controller)), DateUtil.Format_XmlDateTime));
|
||||
parameters.Add(ParamName_Token, requestPasswordToken);
|
||||
String link =
|
||||
baseUrl + "Security/ChangePassword?" +
|
||||
ParamName_RequestPasswordEncrypted + "=" + HttpUtility.UrlEncode(EncryptParameters(parameters, requestPassword_ase64IV, requestPassword_Base64Key));
|
||||
return link;
|
||||
}
|
||||
internal static int GetRequestPasswordEmailLinkExpiresMinutes(SbmController_Control controller)
|
||||
{
|
||||
int expiresMinutes = controller.AppConfig.Security.SignIn.PasswordRequest.EmailLinkExpiresMins;
|
||||
int expiresMinutes_Min = Sbm.Config.Security.SignIn.PasswordRequest.EmailLinkExpiresMins_Min;
|
||||
int expiresMinutes_Max = Sbm.Config.Security.SignIn.PasswordRequest.EmailLinkExpiresMins_Max;
|
||||
return Math.Max(expiresMinutes_Min, Math.Min(expiresMinutes_Max, expiresMinutes));
|
||||
}
|
||||
private class RequestPasswordParams
|
||||
{
|
||||
public SysId UserAccountId { get; set; }
|
||||
public String EmailAddress { get; set; }
|
||||
public DateTime ExpiresAt { get; set; }
|
||||
public String Token { 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));
|
||||
if (!StringUtil.IsEmpty(EmailAddress))
|
||||
values.Add("Email: " + EmailAddress);
|
||||
if (DateUtil.IsValid(ExpiresAt))
|
||||
values.Add("Expires: " + DateUtil.FormatXmlDateTime(ExpiresAt));
|
||||
if (!StringUtil.IsEmpty(Token))
|
||||
values.Add("Token: " + Token);
|
||||
return values;
|
||||
}
|
||||
/// <summary>Verifiy that the account Id and email are populated and the data hasn't expired.</summary>
|
||||
public bool IsValid()
|
||||
{
|
||||
return
|
||||
SysId.IsValid(UserAccountId) &&
|
||||
!StringUtil.IsEmpty(EmailAddress) &&
|
||||
DateUtil.IsValid(ExpiresAt) && ExpiresAt >= DateTime.Now;
|
||||
}
|
||||
/// <summary>Verify that the data is valid and the account details match.</summary>
|
||||
public bool IsValid(SbmcUserAccount userAccount)
|
||||
{
|
||||
return
|
||||
IsValid() &&
|
||||
userAccount != null &&
|
||||
userAccount.Id.Equals(UserAccountId) &&
|
||||
userAccount.EmailAddress.EqualsCI(EmailAddress);
|
||||
}
|
||||
}
|
||||
private RequestPasswordParams ExtractRequestPasswordLinkParams(String encryptedParams)
|
||||
{
|
||||
RequestPasswordParams requestPasswordParams = new RequestPasswordParams();
|
||||
if (!StringUtil.IsEmpty(encryptedParams))
|
||||
{
|
||||
NameValueCollection linkParams = DecryptParameters(encryptedParams, Base64IV_RequestPassword_Control, Base64Key_RequestPassword_Control);
|
||||
requestPasswordParams.UserAccountId = new SysId(StringUtil.ToInt(linkParams.Get(ParamName_UserAccountId)));
|
||||
requestPasswordParams.EmailAddress = linkParams.Get(ParamName_EmailAddress);
|
||||
requestPasswordParams.ExpiresAt = DateUtil.ParseXmlDateTime(linkParams.Get(ParamName_ExpiresAt));
|
||||
requestPasswordParams.Token = linkParams.Get(ParamName_Token);
|
||||
}
|
||||
return requestPasswordParams;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NPOI.SS.Formula.Functions;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.App;
|
||||
using Sarand.BusinessMgr.Web.Control.Models;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Controllers
|
||||
{
|
||||
//======================================================================
|
||||
public partial class SecurityController : SbmController_Control
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public SecurityController(SbmLocaliser localiser, ILogger<HomeController> logger) :
|
||||
base(localiser, logger)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
public IActionResult Index()
|
||||
{
|
||||
return RedirectToAction("Signin", "Security");
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
public IActionResult Signin()
|
||||
{
|
||||
// Redirect home if signed-in.
|
||||
if (ValidUserSession)
|
||||
return RedirectToAction("", "");
|
||||
|
||||
Model_Security model = new Model_Security(Localiser);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Security - Change password
|
||||
/// <summary>
|
||||
/// Security - Change Password action.
|
||||
/// <para>- GET from request password email link.</para>
|
||||
/// <para>- POST with user signed-in. Browser cache prohibited.</para>
|
||||
/// </summary>
|
||||
[HttpGet, HttpPost]
|
||||
public IActionResult ChangePassword(Model_Security model)
|
||||
{
|
||||
String fn = GetType().Name + ".ChangePassword";
|
||||
bool validSessionRequired = true;
|
||||
|
||||
if (Request.Method.EqualsCI(HttpMethods.Get))
|
||||
{
|
||||
// Check for encrypted parameters from 'Request Password' email link.
|
||||
// NB Only allowed as a 'GET' request.
|
||||
String encryptedParams = Request.Query[ParamName_RequestPasswordEncrypted];
|
||||
RequestPasswordParams requestPasswordParams = ExtractRequestPasswordLinkParams(encryptedParams);
|
||||
if (requestPasswordParams.IsValid())
|
||||
{
|
||||
String logSuffix = requestPasswordParams.GetValidParams().Concat("", " ", ".");
|
||||
using (SbmcUserAccount userAccount = ControlAccountMgr.GetUserAccountById(requestPasswordParams.UserAccountId))
|
||||
{
|
||||
if (userAccount != null)
|
||||
{
|
||||
if (userAccount.IsStatusActive)
|
||||
{
|
||||
if (requestPasswordParams.IsValid(userAccount))
|
||||
{
|
||||
model.rpe = encryptedParams;
|
||||
validSessionRequired = false;
|
||||
}
|
||||
else SaLogger.Warn(fn + " 'Request Password' parameters supplied but invalid. " + logSuffix);
|
||||
}
|
||||
else SaLogger.Warn(fn + " 'Request Password' parameters supplied but account is not active. " + logSuffix);
|
||||
}
|
||||
else
|
||||
{
|
||||
// User Account not found - redirect to Portal application.
|
||||
String redirectParamName = ParamName_RequestPasswordEncrypted + "2";
|
||||
SaLogger.Warn(fn + " 'Request Password' parameters supplied but account is not 'Admin'. " + logSuffix);
|
||||
if (!Request.Query.Keys.Contains(redirectParamName) && !StringUtil.IsEmpty(AppConfig.URL.Portal.Root))
|
||||
{
|
||||
UriBuilder uri = new UriBuilder(Regex.Replace(AppConfig.URL.Portal.Root, "/$", "") + "/Security/ChangePassword");
|
||||
uri.Query = ParamName_RequestPasswordEncrypted + "=" + Uri.EscapeDataString(encryptedParams) + "&" + redirectParamName;
|
||||
return Redirect(uri.Uri.OriginalString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Must have a valid session to change password unless linked from Request Password above.
|
||||
if (validSessionRequired && !ValidUserSession)
|
||||
return RedirectToAction("", "");
|
||||
|
||||
// Configure page.
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Security - Request Password
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Sign-in Request Password action.
|
||||
/// <para>- User not signed-in.</para>
|
||||
/// <para>- GET only with browser cache allowed.</para>
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public IActionResult RequestPassword()
|
||||
{
|
||||
Model_Security model = new Model_Security(Localiser);
|
||||
|
||||
// Configure page.
|
||||
model.ProhibitCache = false;
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//======================================================================
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.App;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
|
||||
using Sarand.Net.Standard.Base.Email;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Helpers
|
||||
{
|
||||
//=======================================================================
|
||||
public static class SbmcEmailConfigHelper
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Replace dynamic tags in email message body.
|
||||
/// <para></para>
|
||||
/// </summary>
|
||||
public static void ReplaceDynamicEmailBodyTags(SaEmailData emailData, SbmSessionData sessionData, SbmcUserAccount userAccount)
|
||||
{
|
||||
// Replace core tags, eg. main stylesheet.
|
||||
// ToDo. EmailConsole.ReplaceDynamicEmailTags(emailData);
|
||||
|
||||
// Replace tags known to the web application.
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sarand.Net.Standard.Base.Helpers
|
||||
{
|
||||
//=======================================================================
|
||||
public class SbmcPasswordHelper
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Configuration parameters.
|
||||
public class PasswordParams
|
||||
{
|
||||
public int MinLength = 8;
|
||||
public int MinLengthNoComplexity = 12;
|
||||
}
|
||||
public static PasswordParams Params { get; } = new PasswordParams();
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>Validate password against parameters.</summary>
|
||||
public static bool IsValidPassword(String password)
|
||||
{
|
||||
return GetPasswordStrength(password) >= 4;
|
||||
}
|
||||
/// <summary>
|
||||
/// Assess the password strength - 4+ is valid.
|
||||
/// <para>0 - Blank</para>
|
||||
/// <para>1 - < min length - 2 [red]</para>
|
||||
/// <para>1 - < min length and only 1 character type [red]</para>
|
||||
/// <para>2 - < min length and only 2 character types [red]</para>
|
||||
/// <para>2 - < min length without complexity - 2 and only 1 character type [red]</para>
|
||||
/// <para>3 - < min length, min length with 3 character types [amber]</para>
|
||||
/// <para>3 - Min length without complexity - 2 [amber]</para>
|
||||
/// <para>3 - Other invalid passwords [amber]</para>
|
||||
/// <para>4 - Min length with 4 character types [green]</para>
|
||||
/// <para>4 - Min length without complexity [green]</para>
|
||||
/// <para>5 - Min lenth + 2 with 4 character types [green]</para>
|
||||
/// <para>5 - Min length without complexity with 3 character types [green]</para>
|
||||
/// </summary>
|
||||
public static int GetPasswordStrength(String password)
|
||||
{
|
||||
String fn = "PasswordHelper.GetPasswordStrength";
|
||||
|
||||
// 0 - Blank.
|
||||
if (StringUtil.IsEmpty(password)) return 0;
|
||||
|
||||
// Count number of different types of character.
|
||||
int characterTypeCount = 0;
|
||||
try
|
||||
{
|
||||
if (Regex.Matches(password, "[A-Z]").Count > 0) characterTypeCount++;
|
||||
if (Regex.Matches(password, "[a-z]").Count > 0) characterTypeCount++;
|
||||
if (Regex.Matches(password, "[0-9]").Count > 0) characterTypeCount++;
|
||||
if (Regex.Matches(password, "[^a-zA-Z0-9]").Count > 0) characterTypeCount++;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
SaLogger.Debug(fn + " " + ExceptionUtil.GetLogMessage(exc));
|
||||
}
|
||||
int passwordLength = StringUtil.GetLength(password);
|
||||
|
||||
// 1 - Less than min length - 2;
|
||||
if (passwordLength < Params.MinLength - 2)
|
||||
return 1;
|
||||
|
||||
// 1 - Less than min length and only 1 character type.
|
||||
if (passwordLength < Params.MinLength && characterTypeCount == 1)
|
||||
return 1;
|
||||
|
||||
// 2 - Less than min length and only 2 character types.
|
||||
if (passwordLength < Params.MinLength && characterTypeCount == 2)
|
||||
return 2;
|
||||
|
||||
// 2 - Less than min length without complexity - 2 and only 1 character type.
|
||||
if (passwordLength < Params.MinLengthNoComplexity - 2 && characterTypeCount == 1)
|
||||
return 2;
|
||||
|
||||
// 3 - Less than minimum length.
|
||||
if (passwordLength < Params.MinLength)
|
||||
return 3;
|
||||
|
||||
// 3 - Minimum length with only 3 character types.
|
||||
if (passwordLength >= Params.MinLength && passwordLength < Params.MinLengthNoComplexity && characterTypeCount == 3)
|
||||
return 3;
|
||||
|
||||
// 3 - Minimum length without complexity - 2 with less than 4 character types.
|
||||
if (passwordLength >= Params.MinLengthNoComplexity - 2 && passwordLength < Params.MinLengthNoComplexity && characterTypeCount < 4)
|
||||
return 3;
|
||||
|
||||
// 3 Catch-all - ensure only valid passwords score 4 or more.
|
||||
if (passwordLength < Params.MinLengthNoComplexity && characterTypeCount < 4)
|
||||
return 3;
|
||||
|
||||
// 4 - Min length with 4 character types.
|
||||
if (passwordLength < Params.MinLengthNoComplexity)
|
||||
return 4;
|
||||
|
||||
// 4 - Min length without complexity.
|
||||
if (passwordLength >= Params.MinLengthNoComplexity && passwordLength < Params.MinLengthNoComplexity + 2 && characterTypeCount < 4)
|
||||
return 4;
|
||||
|
||||
// 5 - Anything more complex.
|
||||
return 5;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
private static PasswordHasher<SbmcUserAccount> passwordHasher = new PasswordHasher<SbmcUserAccount>();
|
||||
/// <summary>Get the hashed value that corresponds to the password.</summary>
|
||||
public static String GetPasswordHash(SbmcUserAccount userAccount, String password)
|
||||
{
|
||||
return passwordHasher.HashPassword(userAccount, password);
|
||||
}
|
||||
/// <summary>Verify password against hashed value stored against user account.</summary>
|
||||
public static bool VerifyHashedPassword(SbmcUserAccount userAccount, String password)
|
||||
{
|
||||
return passwordHasher.VerifyHashedPassword(userAccount, userAccount.PasswordHashed, password) != PasswordVerificationResult.Failed;
|
||||
}
|
||||
/// <summary>Verify password against hashed value with user account as reference.</summary>
|
||||
public static bool VerifyHashedPassword(SbmcUserAccount userAccount, String password, String passwordHashed)
|
||||
{
|
||||
return passwordHasher.VerifyHashedPassword(userAccount, passwordHashed, password) != PasswordVerificationResult.Failed;
|
||||
}
|
||||
/// <summary>Check whether the password needs rehashing.</summary>
|
||||
public static bool PasswordRequiresRehash(SbmcUserAccount userAccount, String password)
|
||||
{
|
||||
return passwordHasher.VerifyHashedPassword(userAccount, userAccount.PasswordHashed, password) == PasswordVerificationResult.SuccessRehashNeeded;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Helpers;
|
||||
using Sarand.Net.Standard.Base.Helpers;
|
||||
using System.Reflection;
|
||||
using System;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Helpers
|
||||
{
|
||||
//=======================================================================
|
||||
public static class SbmcResourceHelper
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Definitions.
|
||||
public enum TextResource
|
||||
{
|
||||
// Email stylesheets.
|
||||
Email_Main_Css,
|
||||
// Email partial content.
|
||||
Email_Header_Html,
|
||||
Email_Footer_Html,
|
||||
// Email templates.
|
||||
Email_RequestPassword_Html,
|
||||
}
|
||||
public enum ImageResource
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Properties.
|
||||
public static Assembly Assembly { get { return typeof(SacsResourceHelper).GetTypeInfo().Assembly; } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Text resources.
|
||||
public static String GetResourceText(TextResource textResource)
|
||||
{
|
||||
String partialPath = "";
|
||||
String resourceFilename = "";
|
||||
switch (textResource)
|
||||
{
|
||||
// Email stylesheet.
|
||||
case TextResource.Email_Main_Css:
|
||||
resourceFilename = "sbmc-email-main.css";
|
||||
break;
|
||||
// Email partial content.
|
||||
case TextResource.Email_Header_Html:
|
||||
resourceFilename = "sbmc-email-header.html";
|
||||
break;
|
||||
case TextResource.Email_Footer_Html:
|
||||
resourceFilename = "sbmc-email-footer.html";
|
||||
break;
|
||||
// Email templates.
|
||||
case TextResource.Email_RequestPassword_Html:
|
||||
resourceFilename = "sbmc-email-request-password.html";
|
||||
break;
|
||||
// Data.
|
||||
}
|
||||
if (!StringUtil.IsEmpty(resourceFilename))
|
||||
{
|
||||
String resourceText = SacsResourceHelper.GetManifestResourceAsText(Assembly, partialPath, resourceFilename);
|
||||
if (!StringUtil.IsEmpty(resourceText))
|
||||
return resourceText;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Image resources.
|
||||
public static byte[] GetResourceImage(ImageResource imageResource)
|
||||
{
|
||||
String partialPath = "";
|
||||
String resourceFilename = "";
|
||||
switch (imageResource)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!StringUtil.IsEmpty(resourceFilename))
|
||||
{
|
||||
byte[] resourceData = SacsResourceHelper.GetManifestResourceAsBinary(Assembly, partialPath, resourceFilename);
|
||||
if (resourceData != null)
|
||||
return resourceData;
|
||||
}
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
using DocumentFormat.OpenXml.Spreadsheet;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.App;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.Controllers;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.App;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
|
||||
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.Diagnostics.Contracts;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Models.Account
|
||||
{
|
||||
//=======================================================================
|
||||
public class Model_UserAccount : SbmModel_Control
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public Model_UserAccount(SbmLocaliser localiser) :
|
||||
base(localiser)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Filter properties.
|
||||
public class FilterData
|
||||
{
|
||||
public string Search { get; set; }
|
||||
public string EmailAddress { get; set; }
|
||||
public string StatusCat { get; set; }
|
||||
public SbmcUserAccount.StatusCat GetStatusCat()
|
||||
{
|
||||
return EnumUtil.ParseEnum(StatusCat, SbmcUserAccount.StatusCat.NotDeleted);
|
||||
}
|
||||
}
|
||||
public FilterData Filter { get; } = new FilterData();
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Item properties.
|
||||
public class UserAccountData
|
||||
{
|
||||
public String Exists { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string EmailAddress { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string FirstName { get; set; }
|
||||
public string Initials { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public string PhoneMain { get; set; }
|
||||
public string PhoneMobile { get; set; }
|
||||
public string PhoneAlt { get; set; }
|
||||
public string StatusCode { get; set; }
|
||||
public SaStatus GetStatus() { return SacsSystem.GetStatusForCode(StatusCode); }
|
||||
}
|
||||
public UserAccountData User { get; } = new UserAccountData();
|
||||
// User emails.
|
||||
public string UserEmailAcountActivationPortal { get; set; }
|
||||
public string UserEmailRequestPasswordPortal { get; set; }
|
||||
public string UserEmailRequestPasswordControl { get; set; }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Validation.
|
||||
//-------------------------------------------------------------------
|
||||
// User.
|
||||
public bool ValidateUserData(Sacs.Web.Components.SaController.AjaxResponse_Data responseData)
|
||||
{
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
|
||||
// User email.
|
||||
if (User.EmailAddress != null)
|
||||
{
|
||||
string controlName = "UserEmailAddress";
|
||||
User.EmailAddress = User.EmailAddress.ToLowerInvariant();
|
||||
responseData.SetControlValue(controlName, User.EmailAddress);
|
||||
if (User.EmailAddress.IsBlank())
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
|
||||
else if (StringUtil.GetLength(User.EmailAddress) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.UserAccount_EmailAddress))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.UserAccount_EmailAddress)]);
|
||||
else if (!SacsDataValidationHelper.IsValidEmailAddress(User.EmailAddress))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
|
||||
else
|
||||
{
|
||||
// Check for duplicate email address.
|
||||
SbmcUserAccount.Params prmUserAccount = new SbmcUserAccount.Params();
|
||||
SysId userAccountId = User.Id.ToSysId();
|
||||
if (SysId.IsValid(userAccountId))
|
||||
prmUserAccount.IdsExcluded.Add(userAccountId);
|
||||
prmUserAccount.EmailAddress = User.EmailAddress;
|
||||
prmUserAccount.StatusCat = SbmcUserAccount.StatusCat.Any;
|
||||
if (ControlAccountMgr.GetUserAccountCount(prmUserAccount) > 0)
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.duplicate.value"]);
|
||||
}
|
||||
}
|
||||
|
||||
// User title.
|
||||
if (User.Title != null)
|
||||
{
|
||||
string controlName = "UserTitle";
|
||||
responseData.SetControlValue(controlName, User.Title);
|
||||
if (StringUtil.GetLength(User.Title) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.UserAccount_Title))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.UserAccount_Title)]);
|
||||
}
|
||||
|
||||
// User first name.
|
||||
if (User.FirstName != null)
|
||||
{
|
||||
string controlName = "UserFirstName";
|
||||
responseData.SetControlValue(controlName, User.FirstName);
|
||||
if (User.FirstName.IsBlank())
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
|
||||
else if (StringUtil.GetLength(User.FirstName) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.UserAccount_FirstName))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.UserAccount_FirstName)]);
|
||||
}
|
||||
|
||||
// User title.
|
||||
if (User.Initials != null)
|
||||
{
|
||||
string controlName = "UserInitials";
|
||||
responseData.SetControlValue(controlName, User.Initials);
|
||||
if (StringUtil.GetLength(User.Initials) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.UserAccount_Initials))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.UserAccount_Initials)]);
|
||||
}
|
||||
|
||||
// User last name.
|
||||
if (User.LastName != null)
|
||||
{
|
||||
string controlName = "UserLastName";
|
||||
responseData.SetControlValue(controlName, User.LastName);
|
||||
if (User.LastName.IsBlank())
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
|
||||
else if (StringUtil.GetLength(User.LastName) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.UserAccount_LastName))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.UserAccount_LastName)]);
|
||||
}
|
||||
|
||||
// Phone.
|
||||
if (User.PhoneMain != null)
|
||||
{
|
||||
string controlName = "UserPhoneMain";
|
||||
responseData.SetControlValue(controlName, User.PhoneMain);
|
||||
if (StringUtil.GetLength(User.PhoneMain) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.UserAccount_PhoneMain))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.UserAccount_PhoneMain)]);
|
||||
else if (!User.PhoneMain.IsEmpty() && !SacsDataValidationHelper.IsValidPhone(User.PhoneMain))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
|
||||
}
|
||||
|
||||
// Mobile Phone.
|
||||
if (User.PhoneMobile != null)
|
||||
{
|
||||
string controlName = "UserPhoneMobile";
|
||||
responseData.SetControlValue(controlName, User.PhoneMobile);
|
||||
if (StringUtil.GetLength(User.PhoneMobile) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.UserAccount_PhoneMobile))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.UserAccount_PhoneMobile)]);
|
||||
else if (!User.PhoneMobile.IsEmpty() && !SacsDataValidationHelper.IsValidPhone(User.PhoneMobile))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
|
||||
}
|
||||
|
||||
// Alt Phone.
|
||||
if (User.PhoneAlt != null)
|
||||
{
|
||||
string controlName = "UserPhoneAlt";
|
||||
responseData.SetControlValue(controlName, User.PhoneAlt);
|
||||
if (StringUtil.GetLength(User.PhoneAlt) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.UserAccount_PhoneAlt))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.UserAccount_PhoneAlt)]);
|
||||
else if (!User.PhoneAlt.IsEmpty() && !SacsDataValidationHelper.IsValidPhone(User.PhoneAlt))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
|
||||
}
|
||||
|
||||
// User status.
|
||||
if (User.StatusCode != null)
|
||||
{
|
||||
string controlName = "UserStatus";
|
||||
if (User.StatusCode.IsBlank())
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
|
||||
else if (!SbmcUserAccount.ValidStatuses.Contains(User.GetStatus()))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
|
||||
}
|
||||
}
|
||||
|
||||
return !responseData.HasControlErrors();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Sarand.BusinessMgr.Net.Core.Web.Control.Models
|
||||
namespace Sarand.BusinessMgr.Web.Control.Models
|
||||
{
|
||||
public class ErrorViewModel
|
||||
{
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
using DocumentFormat.OpenXml.Spreadsheet;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.App;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.Controllers;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.App;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
|
||||
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;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Models.Instance
|
||||
{
|
||||
//=======================================================================
|
||||
public class Model_Owner : SbmModel_Control
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public Model_Owner(SbmLocaliser localiser) :
|
||||
base(localiser)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Filter properties.
|
||||
public class FilterData
|
||||
{
|
||||
public String Search { get; set; }
|
||||
public String StatusCat { get; set; }
|
||||
public SbmcOwner.StatusCat GetStatusCat()
|
||||
{
|
||||
return EnumUtil.ParseEnum(StatusCat, SbmcOwner.StatusCat.NotDeleted);
|
||||
}
|
||||
}
|
||||
public FilterData Filter { get; } = new FilterData();
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Item properties.
|
||||
public class OwnerData
|
||||
{
|
||||
public String Exists { get; set; }
|
||||
public String Id { get; set; }
|
||||
public String Name { get; set; }
|
||||
public String ContactName { get; set; }
|
||||
public String ContactEmailAddress { get; set; }
|
||||
public String ContactPhoneMain { get; set; }
|
||||
public String ContactPhoneAlt { get; set; }
|
||||
public String StatusCode { get; set; }
|
||||
public SaStatus GetStatus() { return SacsSystem.GetStatusForCode(StatusCode); }
|
||||
}
|
||||
public OwnerData Owner { get; } = new OwnerData();
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Validation.
|
||||
//-------------------------------------------------------------------
|
||||
// Owner.
|
||||
public bool ValidateOwnerData(SbmController_Base.AjaxResponse_Data responseData)
|
||||
{
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
|
||||
// Owner name.
|
||||
if (Owner.Name != null)
|
||||
{
|
||||
String controlName = "OwnerName";
|
||||
responseData.SetControlValue(controlName, Owner.Name);
|
||||
if (StringUtil.IsBlank(Owner.Name))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
|
||||
else if (StringUtil.GetLength(Owner.Name) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Owner_Name))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Owner_Name)]);
|
||||
}
|
||||
|
||||
// Owner contact name.
|
||||
if (Owner.ContactName != null)
|
||||
{
|
||||
String controlName = "OwnerContactName";
|
||||
responseData.SetControlValue(controlName, Owner.ContactName);
|
||||
if (!StringUtil.IsBlank(Owner.ContactName))
|
||||
{
|
||||
if (StringUtil.GetLength(Owner.ContactName) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Owner_ContactName))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Owner_ContactName)]);
|
||||
}
|
||||
}
|
||||
|
||||
// Contact email.
|
||||
if (Owner.ContactEmailAddress != null)
|
||||
{
|
||||
String controlName = "OwnerContactEmailAddress";
|
||||
Owner.ContactEmailAddress = Owner.ContactEmailAddress.ToLowerInvariant();
|
||||
responseData.SetControlValue(controlName, Owner.ContactEmailAddress);
|
||||
if (!StringUtil.IsBlank(Owner.ContactEmailAddress))
|
||||
{
|
||||
if (StringUtil.GetLength(Owner.ContactEmailAddress) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Owner_ContactEmailAddress))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Owner_ContactEmailAddress)]);
|
||||
else if (!SacsDataValidationHelper.IsValidEmailAddress(Owner.ContactEmailAddress))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
|
||||
}
|
||||
}
|
||||
|
||||
// Contact Phone.
|
||||
if (Owner.ContactPhoneMain != null)
|
||||
{
|
||||
String controlName = "OwnerContactPhoneMain";
|
||||
responseData.SetControlValue(controlName, Owner.ContactPhoneMain);
|
||||
if (StringUtil.GetLength(Owner.ContactPhoneMain) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Owner_ContactPhoneMain))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Owner_ContactPhoneMain)]);
|
||||
else if (!StringUtil.IsEmpty(Owner.ContactPhoneMain) && !SacsDataValidationHelper.IsValidPhone(Owner.ContactPhoneMain))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
|
||||
}
|
||||
|
||||
// Contact Alt Phone.
|
||||
if (Owner.ContactPhoneAlt != null)
|
||||
{
|
||||
String controlName = "OwnerContactPhoneAlt";
|
||||
responseData.SetControlValue(controlName, Owner.ContactPhoneAlt);
|
||||
if (StringUtil.GetLength(Owner.ContactPhoneAlt) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Owner_ContactPhoneAlt))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Owner_ContactPhoneAlt)]);
|
||||
else if (!StringUtil.IsEmpty(Owner.ContactPhoneAlt) && !SacsDataValidationHelper.IsValidPhone(Owner.ContactPhoneAlt))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
|
||||
}
|
||||
|
||||
// Owner status.
|
||||
if (Owner.StatusCode != null)
|
||||
{
|
||||
String controlName = "OwnerStatus";
|
||||
if (StringUtil.IsBlank(Owner.StatusCode))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
|
||||
else if (!SbmcOwner.ValidStatuses.Contains(Owner.GetStatus()))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
|
||||
}
|
||||
}
|
||||
|
||||
return !responseData.HasControlErrors();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
using DocumentFormat.OpenXml.Spreadsheet;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.App;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.Controllers;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.App;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
|
||||
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;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Models.Instance
|
||||
{
|
||||
//=======================================================================
|
||||
public class Model_Repository : SbmModel_Control
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public Model_Repository(SbmLocaliser localiser) :
|
||||
base(localiser)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Filter properties.
|
||||
public class FilterData
|
||||
{
|
||||
public String Search { get; set; }
|
||||
public String StatusCat { get; set; }
|
||||
public SbmcRepository.StatusCat GetStatusCat()
|
||||
{
|
||||
return EnumUtil.ParseEnum(StatusCat, SbmcRepository.StatusCat.NotDeleted);
|
||||
}
|
||||
}
|
||||
public FilterData Filter { get; } = new FilterData();
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Item properties.
|
||||
public class RepositoryData
|
||||
{
|
||||
public String Exists { get; set; }
|
||||
public String Id { get; set; }
|
||||
public String OwnerId { get; set; }
|
||||
public String Context { get; set; }
|
||||
public String ServerName { get; set; }
|
||||
public String InstanceName { get; set; }
|
||||
public String DatabaseName { get; set; }
|
||||
public String UserName { get; set; }
|
||||
public String Password { get; set; }
|
||||
public String StatusCode { get; set; }
|
||||
public SaStatus GetStatus() { return SacsSystem.GetStatusForCode(StatusCode); }
|
||||
}
|
||||
public RepositoryData Repository { get; } = new RepositoryData();
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Validation.
|
||||
//-------------------------------------------------------------------
|
||||
// Repository.
|
||||
public bool ValidateRepositoryData(SbmController_Base.AjaxResponse_Data responseData)
|
||||
{
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
|
||||
// Repository Owner.
|
||||
if (Repository.OwnerId != null)
|
||||
{
|
||||
String controlName = "RepositoryOwner";
|
||||
SysId ownerId = Repository.OwnerId.ToSysId();
|
||||
if (!SysId.IsValid(ownerId))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
|
||||
else
|
||||
{
|
||||
SbmcOwner owner = ControlInstanceMgr.GetOwnerById(ownerId);
|
||||
if (owner == null)
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Repository Context.
|
||||
if (Repository.Context != null)
|
||||
{
|
||||
String controlName = "RepositoryContext";
|
||||
responseData.SetControlValue(controlName, Repository.Context);
|
||||
if (StringUtil.IsBlank(Repository.Context))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
|
||||
else if (StringUtil.GetLength(Repository.Context) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Repository_Context))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Repository_Context)]);
|
||||
else
|
||||
{
|
||||
// Check for duplicate context.
|
||||
SbmcRepository.Params prmRepository = new SbmcRepository.Params();
|
||||
SysId repositoryId = Repository.Id.ToSysId();
|
||||
if (SysId.IsValid(repositoryId))
|
||||
prmRepository.IdsExcluded.Add(repositoryId);
|
||||
prmRepository.ContextsIncluded.Add(Repository.Context);
|
||||
prmRepository.StatusCat = SbmcRepository.StatusCat.Any;
|
||||
if (ControlInstanceMgr.GetRepositoryCount(prmRepository) > 0)
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.duplicate.value"]);
|
||||
}
|
||||
}
|
||||
|
||||
// Repository Server name.
|
||||
if (Repository.ServerName != null)
|
||||
{
|
||||
String controlName = "RepositoryServerName";
|
||||
responseData.SetControlValue(controlName, Repository.ServerName);
|
||||
if (StringUtil.IsBlank(Repository.ServerName))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
|
||||
else if (StringUtil.GetLength(Repository.ServerName) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Repository_ServerName))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Repository_ServerName)]);
|
||||
}
|
||||
|
||||
// Repository Instance name.
|
||||
if (Repository.InstanceName != null)
|
||||
{
|
||||
String controlName = "RepositoryInstanceName";
|
||||
responseData.SetControlValue(controlName, Repository.InstanceName);
|
||||
if (StringUtil.GetLength(Repository.InstanceName) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Repository_InstanceName))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Repository_InstanceName)]);
|
||||
}
|
||||
|
||||
// Repository Database name.
|
||||
if (Repository.DatabaseName != null)
|
||||
{
|
||||
String controlName = "RepositoryDatabaseName";
|
||||
responseData.SetControlValue(controlName, Repository.DatabaseName);
|
||||
if (StringUtil.IsBlank(Repository.DatabaseName))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
|
||||
else if (StringUtil.GetLength(Repository.DatabaseName) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Repository_DatabaseName))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Repository_DatabaseName)]);
|
||||
}
|
||||
|
||||
// Repository User name.
|
||||
if (Repository.UserName != null)
|
||||
{
|
||||
String controlName = "RepositoryUserName";
|
||||
responseData.SetControlValue(controlName, Repository.UserName);
|
||||
if (StringUtil.GetLength(Repository.UserName) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Repository_UserName))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Repository_UserName)]);
|
||||
}
|
||||
|
||||
// Repository password.
|
||||
if (Repository.Password != null)
|
||||
{
|
||||
String controlName = "RepositoryPassword";
|
||||
responseData.SetControlValue(controlName, Repository.Password);
|
||||
if (StringUtil.GetLength(Repository.Password) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Repository_Password))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.Repository_Password)]);
|
||||
}
|
||||
|
||||
// Repository status.
|
||||
if (Repository.StatusCode != null)
|
||||
{
|
||||
String controlName = "RepositoryStatus";
|
||||
if (StringUtil.IsBlank(Repository.StatusCode))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
|
||||
else if (!SbmcRepository.ValidStatuses.Contains(Repository.GetStatus()))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
|
||||
}
|
||||
}
|
||||
|
||||
return !responseData.HasControlErrors();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using Sarand.BusinessMgr.Net.Core.Web.App;
|
||||
|
||||
namespace Sarand.BusinessMgr.Net.Core.Web.Control.Models
|
||||
namespace Sarand.BusinessMgr.Web.Control.Models
|
||||
{
|
||||
//=======================================================================
|
||||
public class Model_Home : SbmModel_Control
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
using Sarand.BusinessMgr.Net.Core.Web.App;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.Controllers;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Helpers;
|
||||
using Sarand.Net.Standard.Base.Helpers;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
using System;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Models
|
||||
{
|
||||
//=======================================================================
|
||||
public class Model_Security : SbmModel_Control
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public Model_Security(SbmLocaliser localiser) :
|
||||
base(localiser)
|
||||
{
|
||||
//AdditionalFiles_CSS.Add("css/sbc-sec.css");
|
||||
AdditionalFiles_JS.Add("js/sbc-sec.js");
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Page configuration.
|
||||
public bool RequestPasswordMode { get { return !StringUtil.IsEmpty(rpe); } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Form properties.
|
||||
public String EmailAddress { get; set; }
|
||||
public String PasswordCurrent { get; set; }
|
||||
public String Password { get; set; }
|
||||
public String PasswordConfirm { get; set; }
|
||||
public bool KeepMeSignedIn { get; set; }
|
||||
/// <summary>Request Password Encrypted params - to allow Change Password without session or requiring old password.</summary>
|
||||
public String rpe { get; set; }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Validation.
|
||||
//-------------------------------------------------------------------
|
||||
// Sign-in.
|
||||
public bool ValidateSignInData(SbmController_Base.AjaxResponse_Data responseData)
|
||||
{
|
||||
// Email address.
|
||||
responseData.SetControlValue(nameof(EmailAddress), EmailAddress);
|
||||
if (StringUtil.IsEmpty(EmailAddress))
|
||||
responseData.SetControlError(nameof(EmailAddress), Localiser["msg.validation.mandatory.value"]);
|
||||
else if (!SacsDataValidationHelper.IsValidEmailAddress(EmailAddress))
|
||||
responseData.SetControlError(nameof(EmailAddress), Localiser["msg.validation.invalid.value"]);
|
||||
|
||||
// Password.
|
||||
// NB Do not return the password value in the response.
|
||||
if (StringUtil.IsEmpty(Password))
|
||||
responseData.SetControlError(nameof(Password), Localiser["msg.validation.mandatory.value"]);
|
||||
return !responseData.HasControlErrors();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Change password.
|
||||
public bool ValidateChangePasswordData(SbmcUserAccount userAccount, SbmController_Base.AjaxResponse_Data responseData)
|
||||
{
|
||||
// Validate Passwords.
|
||||
// NB Do not return the password values in the response.
|
||||
// NB Only validate populated values - any null values are not part of the form for this request.
|
||||
// - 'Current' password is not shown when changing password from 'Request Password' email link.
|
||||
if (PasswordCurrent != null && StringUtil.IsEmpty(PasswordCurrent))
|
||||
responseData.SetControlError(nameof(PasswordCurrent), Localiser["msg.validation.mandatory.value"]);
|
||||
if (StringUtil.IsEmpty(Password))
|
||||
responseData.SetControlError(nameof(Password), Localiser["msg.validation.mandatory.value"]);
|
||||
if (StringUtil.IsEmpty(PasswordConfirm))
|
||||
responseData.SetControlError(nameof(PasswordConfirm), Localiser["msg.validation.mandatory.value"]);
|
||||
if (!StringUtil.IsEmpty(Password) && !StringUtil.IsEmpty(PasswordConfirm))
|
||||
{
|
||||
if (!Password.Equals(PasswordConfirm))
|
||||
responseData.SetControlError(nameof(PasswordConfirm), Localiser["msg.validation.invalid.value"]);
|
||||
if (!SbmcPasswordHelper.IsValidPassword(Password))
|
||||
responseData.SetControlError(nameof(PasswordConfirm), Localiser.GetText("msg.warn.password.invalid", new object[] { SbmcPasswordHelper.Params.MinLength, SbmcPasswordHelper.Params.MinLengthNoComplexity }));
|
||||
}
|
||||
|
||||
if (!responseData.HasControlErrors())
|
||||
{
|
||||
if (PasswordCurrent != null && !SbmcPasswordHelper.VerifyHashedPassword(userAccount, PasswordCurrent))
|
||||
responseData.SetControlError(nameof(PasswordCurrent), Localiser["security.change.password.current.invalid"]);
|
||||
if (SbmcPasswordHelper.VerifyHashedPassword(userAccount, Password))
|
||||
responseData.SetControlError(nameof(Password), Localiser["security.change.password.not.changed"]);
|
||||
}
|
||||
return !responseData.HasControlErrors();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Request Password.
|
||||
public bool ValidateRequestPasswordData(SbmController_Base.AjaxResponse_Data responseData)
|
||||
{
|
||||
// Email address.
|
||||
responseData.SetControlValue(nameof(EmailAddress), EmailAddress);
|
||||
if (StringUtil.IsEmpty(EmailAddress))
|
||||
responseData.SetControlError(nameof(EmailAddress), Localiser["msg.validation.mandatory.value"]);
|
||||
else if (!SacsDataValidationHelper.IsValidEmailAddress(EmailAddress))
|
||||
responseData.SetControlError(nameof(EmailAddress), Localiser["msg.validation.invalid.value"]);
|
||||
|
||||
return !responseData.HasControlErrors();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
using DocumentFormat.OpenXml.EMMA;
|
||||
using DocumentFormat.OpenXml.Spreadsheet;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.App;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.Controllers;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.App;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Bus;
|
||||
using Sarand.Net.Standard.Base.CodeTypes;
|
||||
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.Diagnostics.Contracts;
|
||||
|
||||
namespace Sarand.BusinessMgr.Web.Control.Models.Param
|
||||
{
|
||||
//=======================================================================
|
||||
public class Model_Param : SbmModel_Control
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public Model_Param(SbmLocaliser localiser) :
|
||||
base(localiser)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>List of Item Types for dropdown.</summary>
|
||||
public IEnumerable<SelectListItem> GetParamItemTypesForSelect()
|
||||
{
|
||||
List<CodeDesc> codeTypes = new List<CodeDesc>();
|
||||
foreach (var itemType in SbmcItemParam.ValidItemTypes)
|
||||
codeTypes.Add(((int)itemType).ToString(), itemType.ToString());
|
||||
return new SelectList(codeTypes, "Code", "Desc");
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Filter properties.
|
||||
public class FilterData
|
||||
{
|
||||
public string ItemType { get; set; }
|
||||
public string ItemId { get; set; }
|
||||
public string Search { get; set; }
|
||||
public string StatusCat { get; set; }
|
||||
public SbmcItemParam.StatusCat GetStatusCat()
|
||||
{
|
||||
return EnumUtil.ParseEnum(StatusCat, SbmcItemParam.StatusCat.NotDeleted);
|
||||
}
|
||||
}
|
||||
public FilterData Filter { get; } = new FilterData();
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Item properties.
|
||||
public class ItemParamData
|
||||
{
|
||||
public String Exists { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string ItemType { get; set; }
|
||||
public string ItemId { get; set; }
|
||||
public string Context { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Value { get; set; }
|
||||
public string ValueChanged { get; set; }
|
||||
public string Notes { get; set; }
|
||||
public string StatusCode { get; set; }
|
||||
public SaStatus GetStatus() { return SacsSystem.GetStatusForCode(StatusCode); }
|
||||
}
|
||||
public ItemParamData Param { get; } = new ItemParamData();
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Validation.
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
public bool ValidateParamData(Sacs.Web.Components.SaController.AjaxResponse_Data responseData)
|
||||
{
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
|
||||
{
|
||||
// Check whether data suggests that Param exists.
|
||||
bool paramExists = StringUtil.ToBool(Param.Exists) && SysId.IsValid(Param.Id.ToSysId());
|
||||
|
||||
// Item.
|
||||
if (Param.ItemType != null || Param.ItemId != null)
|
||||
{
|
||||
string controlName = "ParamItem";
|
||||
ItemId itemId = new ItemId(StringUtil.ToShort(Param.ItemType), Param.ItemId.ToSysId());
|
||||
if (!paramExists && (Param.ItemType.IsBlank() || Param.ItemId.IsBlank()))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
|
||||
else if (StringUtil.IsBlank(ControlItemMgr.GetItemCaption(itemId)))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
|
||||
}
|
||||
|
||||
// Context.
|
||||
if (Param.Context != null)
|
||||
{
|
||||
string controlName = "ParamContext";
|
||||
responseData.SetControlValue(controlName, Param.Context);
|
||||
if (Param.Context.IsBlank())
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
|
||||
else if (StringUtil.GetLength(Param.Context) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.ItemParam_Context))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.ItemParam_Context)]);
|
||||
}
|
||||
|
||||
// Name.
|
||||
if (Param.Name != null)
|
||||
{
|
||||
string controlName = "ParamName";
|
||||
responseData.SetControlValue(controlName, Param.Name);
|
||||
if (Param.Name.IsBlank())
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
|
||||
else if (StringUtil.GetLength(Param.Name) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.ItemParam_Name))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.ItemParam_Name)]);
|
||||
}
|
||||
|
||||
// Value.
|
||||
if (Param.Value != null)
|
||||
{
|
||||
string controlName = "ParamValue";
|
||||
responseData.SetControlValue(controlName, Param.Value);
|
||||
if (StringUtil.GetLength(Param.Value) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.ItemParam_Value))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.ItemParam_Value)]);
|
||||
}
|
||||
|
||||
// Notes.
|
||||
if (Param.Notes != null)
|
||||
{
|
||||
string controlName = "ParamNotes";
|
||||
responseData.SetControlValue(controlName, Param.Notes);
|
||||
if (StringUtil.GetLength(Param.Notes) > GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.ItemParam_Notes))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn_Control.ColumnIdx.ItemParam_Notes)]);
|
||||
}
|
||||
|
||||
// User status.
|
||||
if (Param.StatusCode != null)
|
||||
{
|
||||
string controlName = "ParamStatus";
|
||||
if (Param.StatusCode.IsBlank())
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
|
||||
else if (!SbmcItemParam.ValidStatuses.Contains(Param.GetStatus()))
|
||||
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
|
||||
}
|
||||
}
|
||||
|
||||
return !responseData.HasControlErrors();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -1,19 +1,55 @@
|
||||
using Sarand.BusinessMgr.Net.Core.Web.App;
|
||||
using Sarand.BusinessMgr.Net.Core.Web.Models;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.App;
|
||||
using Sarand.BusinessMgr.Net.Standard.Base.Core;
|
||||
using Sarand.BusinessMgr.Web.Control.App;
|
||||
using System;
|
||||
|
||||
namespace Sarand.BusinessMgr.Net.Core.Web.Control.Models
|
||||
namespace Sarand.BusinessMgr.Web.Control.Models
|
||||
{
|
||||
//=======================================================================
|
||||
public class SbmModel_Control : SbmModel_Base
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Protected construction.
|
||||
protected SbmModel_Control(SbmLocaliser localiser) :
|
||||
base(localiser)
|
||||
{
|
||||
}
|
||||
//=======================================================================
|
||||
public class SbmModel_Control : SbmModel_Base
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Protected construction.
|
||||
protected SbmModel_Control(SbmLocaliser localiser) :
|
||||
base(localiser)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
//-------------------------------------------------------------------
|
||||
// Properties.
|
||||
public SbmSessionData_Control SessionData_Control { get { return SessionData as SbmSessionData_Control; } }
|
||||
public override ISbmUserContext_Core UserContext_Core { get { return new SbmUserContext_Control(SessionData_Control); } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <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