This commit is contained in:
2021-07-23 14:01:49 +01:00
parent 10907d3aff
commit 0d7be69e1e
109 changed files with 3161 additions and 943 deletions
@@ -32,7 +32,7 @@ namespace Saxonite.Net.Core.Web.Admin.App
// Data members.
private bool generateDatabaseDefinitions = false;
private bool populateRegions = false;
private Dictionary<String, SaxSessionConfig> sessionConfigs = new Dictionary<string, SaxSessionConfig>();
private Dictionary<String, SaxAppConfig> appConfigs = new Dictionary<string, SaxAppConfig>();
private Dictionary<String, SaxEmailConfig> emailConfigs = new Dictionary<string, SaxEmailConfig>();
// Session contexts.
private class SessionContextData
@@ -75,8 +75,8 @@ namespace Saxonite.Net.Core.Web.Admin.App
}
public override String SessionCookieName { get { return Sax.Config.Admin.Session.Cookie.Name; } }
// Configuration.
public override SaxSessionConfig SessionConfig { get { return sessionConfigs.Values.ToList().getConfigForApplication(); } }
public override SaxEmailConfig EmailConfig { get { return emailConfigs.Values.ToList().getConfigForSession(SessionConfig); } }
public override SaxAppConfig AppConfig { get { return appConfigs.Values.ToList().getConfigForApplication(); } }
public override SaxEmailConfig EmailConfig { get { return emailConfigs.Values.ToList().getConfigForSession(AppConfig); } }
//-------------------------------------------------------------------
// Initialisation.
@@ -107,15 +107,15 @@ namespace Saxonite.Net.Core.Web.Admin.App
SaxConfiguration = builder.Build();
// Application configuration.
sessionConfigs = SaxSessionConfigHelper.ExtractSessionConfigs(SaxConfiguration.GetSection("SessionConfigs"));
appConfigs = SaxAppConfigHelper.ExtractAppConfigs(SaxConfiguration.GetSection("AppConfigs"));
emailConfigs = SaxEmailConfigHelper.ExtractEmailConfigs(SaxConfiguration.GetSection("EmailConfigs"));
LoggingRootFolder = SessionConfig.Logging.RootFolder;
LoggingRootFolder = AppConfig.Logging.RootFolder;
// Application instance.
if (SessionConfig.Instance.EqualsCI("Dev")) instanceType = Sax.InstanceType.Dev;
else if (SessionConfig.Instance.ContainsCI("Q")) instanceType = Sax.InstanceType.QC;
else if (SessionConfig.Instance.ContainsCI("UAT")) instanceType = Sax.InstanceType.UAT;
else if (SessionConfig.Instance.EqualsCI("PreProd")) instanceType = Sax.InstanceType.UAT;
if (AppConfig.Instance.EqualsCI("Dev")) instanceType = Sax.InstanceType.Dev;
else if (AppConfig.Instance.ContainsCI("Q")) instanceType = Sax.InstanceType.QC;
else if (AppConfig.Instance.ContainsCI("UAT")) instanceType = Sax.InstanceType.UAT;
else if (AppConfig.Instance.EqualsCI("PreProd")) instanceType = Sax.InstanceType.UAT;
// Initialise hard-coded logging path.
if (StringUtil.IsEmpty(LoggingRootFolder))
@@ -129,9 +129,9 @@ namespace Saxonite.Net.Core.Web.Admin.App
// Configure database connection.
SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig();
config.location = SessionConfig.CoreDatabase.Location;
config.user = SessionConfig.CoreDatabase.User;
config.pword = SessionConfig.CoreDatabase.Password;
config.location = AppConfig.CoreDatabase.Location;
config.user = AppConfig.CoreDatabase.User;
config.pword = AppConfig.CoreDatabase.Password;
SacsDatabase coreDatabase = SacsDatabaseConsole.OpenOdbcDatabase(config);
if (coreDatabase.HasErrorState())
SaLogger.Warn(fn + " Database initialisation error: " + coreDatabase.ErrorStateMessage);
@@ -38,7 +38,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers
// Check for 'Keep Logged In' cookie.
// NB Only allowed if there isn't a current user session.
if (!ValidUserSession && SessionConfig.Security.SignIn.KeepLoggedIn.Allowed)
if (!ValidUserSession && AppConfig.Security.SignIn.KeepLoggedIn.Allowed)
{
String encryptedParams = GetRequestCookie(CookieName_KeepLoggedIn);
KeepLoggedInParams keepLoggedInParams = ExtractKeepLoggedInParams(encryptedParams);
@@ -45,7 +45,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
if (StringUtil.IsEmpty(SessionConfig.URL.Online.Root))
if (StringUtil.IsEmpty(AppConfig.URL.Online.Root))
return Json(responseData.SetMessage(messageTitle, "Root online URL not specified."));
// Identify item.
@@ -74,13 +74,28 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers
return Json(responseData);
}
//-------------------------------------------------------------------
private class OrganisationMemberData : SacsDisposable
{
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (UserOrganisation != null) UserOrganisation.Dispose(); UserOrganisation = null;
if (UserAccount != null) UserAccount.Dispose(); UserAccount = null;
}
}
public SaxUserOrganisation UserOrganisation { get; set; }
public SaxUserAccount UserAccount { get; set; }
}
//-------------------------------------------------------------------
// Activate Organisation ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/Activate")]
[Route("Organisation/Ajax/ActivateItem")]
public IActionResult Ajax_Activate(Model_Organisation model)
public IActionResult Ajax_Activate(Model_OrganisationActivation model)
{
String fn = GetType().Name + ".Ajax_Activate";
String messageTitle = Localiser["organisation.caption.organisation"];
@@ -90,65 +105,115 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Validate item properties.
model.ValidateOrganisationData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Retrieve existing item.
SysId organisationId = model.OrganisationId.ToSysId();
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
if (organisation == null && SysId.IsValid(organisationId))
using (SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId))
{
SaLogger.Warn(fn + " Failed to retrieve organisation for Id: '" + model.OrganisationId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Activate organisation.
organisation.Status = Sarand.Net.Standard.Base.Core.SaStatus.Active;
if (!AccountMgr.UpdateOrganisation(organisation, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to update organisation [Id:" + SysId.ToString(organisation.Id) + "]: " + AccountMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
}
// Activate awaiting members and send welcome email to each.
SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params();
prmUserOrganisation.OrganisationIdsIncluded.Add(organisationId);
prmUserOrganisation.UserAccountStatusCat = SaxUserAccount.StatusCat.AwaitingOnly;
prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.ActiveOnly;
List<SaxUserOrganisation> userOrganisations = new List<SaxUserOrganisation>();
foreach (SaxUserOrganisation userOrganisation in userOrganisations)
{
using (SaxUserAccount userAccount = AccountMgr.GetUserAccountById(userOrganisation.UserAccountId))
if (organisation == null && SysId.IsValid(organisationId))
{
if (userAccount == null) continue;
userAccount.Status = SaStatus.Active;
if (AccountMgr.UpdateUserAccount(userAccount, DbUpdateInfo))
SaLogger.Warn(fn + " Failed to retrieve organisation for. : '" + model.OrganisationId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Retrieve users.
using (DisposableCollection<OrganisationMemberData> organisationMembersToActivate = new DisposableCollection<OrganisationMemberData>())
{
foreach (String userOrganisationId in model.OrganisationMemberIds)
{
// Send welcome email.
SaEmailData emailData = new SaEmailData();
emailData.Config = EmailConfig;
emailData.AddressesTo.Add(userAccount.EmailAddress);
emailData.Subject = Localiser["security.caption.request.password"];
PopulateAccountActivatedEmailBody(emailData, userAccount);
if (EmailConsole.SendEmail(emailData))
SaLogger.Info(fn + " Password request email sent to " + userAccount.EmailAddress, true);
else SaLogger.Warn(fn + " Failed to send password request email to " + userAccount.EmailAddress);
if (userOrganisationId == null) continue;
OrganisationMemberData organisationMember = new OrganisationMemberData();
organisationMember.UserOrganisation = AccountMgr.GetUserOrganisationById(userOrganisationId.ToSysId());
organisationMember.UserAccount = AccountMgr.GetUserAccountById(organisationMember.UserOrganisation?.UserAccountId ?? null);
if (organisationMember.UserOrganisation == null)
{
SaLogger.Warn(fn + " Failed to retrieve user organisation. Id: '" + userOrganisationId + "'");
organisationMember.Dispose();
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
if (organisationMember.UserAccount == null)
{
SaLogger.Warn(fn + " Failed to retrieve user account . Id: '" + organisationMember.UserOrganisation.UserAccountId + "'");
organisationMember.Dispose();
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
if (organisationMember.UserAccount.Status != SaStatus.Awaiting)
{
SaLogger.Warn(fn + " User organisation is not awaiting. Id: '" + userOrganisationId + "'. Status: " + organisationMember.UserAccount.StatusDesc);
organisationMember.Dispose();
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
// Check if this user is to be activated.
bool activateUser = false;
String controlName_Activate = "MemberActivate_" + userOrganisationId;
foreach (String key in Request.Form.Keys)
{
if (key.EqualsCI(controlName_Activate))
{
activateUser = true;
break;
}
}
if (activateUser)
organisationMembersToActivate.Add(organisationMember);
else organisationMember.Dispose();
organisationMember = null;
}
else
if (organisationMembersToActivate.IsEmpty())
{
// No users to be activated.
// Check that organisation already has active members.
if (AccountMgr.GetOrganisationMemberCount(organisation.Id) < 1)
{
SaLogger.Warn(fn + " Unable to activate Organisation - no active members. Id: '" + SysId.ToString(organisationId));
return Json(responseData.SetMessage(messageTitle, Localiser["organisation.activate.msg.no.active.members"]));
}
}
// Activate organisation.
organisation.Status = Sarand.Net.Standard.Base.Core.SaStatus.Active;
if (!AccountMgr.UpdateOrganisation(organisation, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to update organisation [Id:" + SysId.ToString(organisation.Id) + "]: " + AccountMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
}
// Activate awaiting members and send welcome email to each.
foreach (OrganisationMemberData organisationMember in organisationMembersToActivate)
{
// Ensure that User Organisation has 'Member Admin' role.
if (organisationMember.UserOrganisation == null) continue;
organisationMember.UserOrganisation.AddOrganisationRole(Sax.OrganisationRole.MemberAdmin);
if (AccountMgr.UpdateUserOrganisation(organisationMember.UserOrganisation, DbUpdateInfo))
{
if (organisationMember.UserAccount == null) continue;
organisationMember.UserAccount.Status = SaStatus.Active;
if (AccountMgr.UpdateUserAccount(organisationMember.UserAccount, DbUpdateInfo))
{
// Send activation email.
SaEmailData emailData = new SaEmailData();
emailData.Config = EmailConfig;
emailData.AddressesTo.Add(organisationMember.UserAccount.EmailAddress);
emailData.Subject = Localiser["user.activate.email.subject"];
PopulateAccountActivatedEmailBody(emailData, organisationMember.UserAccount);
if (EmailConsole.SendEmail(emailData))
SaLogger.Info(fn + " User Activation email sent to " + organisationMember.UserAccount.EmailAddress, true);
else SaLogger.Warn(fn + " Failed to send User Activation email to " + organisationMember.UserAccount.EmailAddress);
}
else
SaLogger.Warn(fn + " Failed to update user account [Id:" + SysId.ToString(organisationMember.UserAccount.Id) + "]: " + AccountMgr.ErrorStateMessage);
}
else
SaLogger.Warn(fn + " Failed to update user organisation [Id:" + SysId.ToString(organisationMember.UserOrganisation.Id) + "]: " + AccountMgr.ErrorStateMessage);
}
// Construct response.
PopulateOrganisationResponseData(organisation, responseData.organisation);
}
}
// Construct response.
PopulateOrganisationResponseData(organisation, responseData.organisation);
// Complete response.
responseData.Success = true;
return Json(responseData);
}
@@ -163,7 +228,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers
EmailConsole.ReplaceDynamicEmailBodyTags(emailData, null, userAccount);
// Replace dynamic tag for link back to change password page.
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.RequestPassword]", SecurityController.GenerateRequestPasswordLink(this, userAccount.Id, userAccount.EmailAddress, "", SessionConfig.URL.Online.Root));
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.RequestPassword]", SecurityController.GenerateRequestPasswordLink(this, userAccount.Id, userAccount.EmailAddress, "", AppConfig.URL.Online.Root));
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.ExpiresMinutes]", SecurityController.GetRequestPasswordEmailLinkExpiresMinutes(this).ToString());
}
@@ -21,9 +21,8 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//-------------------------------------------------------------------
// Organisation data.
private class OrganisationData_List
private class OrganisationData_List : AjaxResponse_Data.ItemData_Base
{
public String Id { get; set; }
public String Name { get; set; }
public String TypeCode { get; set; }
public String TypeDesc { get; set; }
@@ -38,12 +37,6 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers
public String RegionNameCountry { get; set; }
// Address.
public String AddressSummary { get; set; }
// Common properties.
public String StatusCode { get; set; }
public String StatusDesc { get; set; }
public Boolean Awaiting { get { return StatusCode.EqualsCI(SacsSystem.GetStatusCode(SaStatus.Awaiting)); } }
public Boolean Active { get { return StatusCode.EqualsCI(SacsSystem.GetStatusCode(SaStatus.Active)); } }
public Boolean Deleted { get { return StatusCode.EqualsCI(SacsSystem.GetStatusCode(SaStatus.Deleted)); } }
}
private class OrganisationData_Full : OrganisationData_List
{
@@ -46,9 +46,9 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers
data.OrganisationId = SysId.ToString(organisation.Id);
data.OrganisationName = organisation.Name;
// Exclude 'Member' role.
foreach (Sax.OrganisationRole organisationRole in userOrganisation.OrganisationRoles.FindAll(item => item != Sax.OrganisationRole.Member))
foreach (Sax.OrganisationRole organisationRole in userOrganisation.GetOrganisationRoles().ToList().FindAll(item => item != Sax.OrganisationRole.Member))
data.RoleCodes.Add(OrganisationRoleCodes.GetCodeForType(organisationRole));
data.RoleCount = userOrganisation.OrganisationRoles.Count - 1;
data.RoleCount = userOrganisation.OrganisationRoleCount - 1;
data.StatusCode = userOrganisation.StatusCode;
data.StatusDesc = userOrganisation.StatusDesc;
}
@@ -113,7 +113,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers
responseData.Success = true;
return Json(responseData);
}
private void GetOrganisationMembers (SaxModel_Admin model, SysId organisationId, List<MemberData_List> members)
private void GetOrganisationMembers(SaxModel_Admin model, SysId organisationId, List<MemberData_List> members)
{
// Item filter.
SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params();
@@ -312,8 +312,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers
// Populate item properties.
if (model.MemberRoleCodes != null)
userOrganisation.OrganisationRoles = SaxUserOrganisation.GetOrganisationRolesFromCodes(model.GetMemberRoleCodes());
if (!userOrganisation.OrganisationRoles.Contains(Sax.OrganisationRole.Member))
userOrganisation.OrganisationRoles.Add(Sax.OrganisationRole.Member);
userOrganisation.AddOrganisationRole(Sax.OrganisationRole.Member);
if (model.MemberStatusCode != null)
userOrganisation.Status = model.MemberStatus;
@@ -57,18 +57,18 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers
if (userAccount == null)
{
SaLogger.Error(fn + " Unable to sign account in - null user account.");
errorMessage = Localiser["security.msg.signin.failed"];
errorMessage = Localiser["security.msg.signin.invalid"];
return false;
}
if (!userAccount.IsStatusActive)
{
SaLogger.Error(fn + " Unable to sign account in - user account is not active. Email: " + userAccount.EmailAddress);
errorMessage = Localiser["security.msg.signin.failed"];
errorMessage = Localiser["security.msg.signin.invalid"];
return false;
}
// 'Keep logged in' can be switched off by the application configuration.
keepLoggedIn = App.SessionConfig.Security.SignIn.KeepLoggedIn.Allowed && keepLoggedIn;
keepLoggedIn = App.AppConfig.Security.SignIn.KeepLoggedIn.Allowed && keepLoggedIn;
// Log user in.
userAccount.KeepLoggedIn_Admin = keepLoggedIn;
@@ -78,7 +78,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers
if (!AccountMgr.UpdateUserAccount(userAccount, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to update account for " + userAccount.EmailAddress + ": " + AccountMgr.ErrorStateMessage);
errorMessage = Localiser["security.msg.signin.failed"];
errorMessage = Localiser["security.msg.signin.invalid"];
return false;
}
SaLogger.Info(fn + " User session opened for " + userAccount.EmailAddress, true);
@@ -119,7 +119,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers
protected void SetKeepLoggedInCookie()
{
String keepLoggedInCookieValue = GenerateKeepLoggedInCookieValue();
int cookieExpiresDays = Math.Max(Sax.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Min, Math.Min(Sax.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Max, SessionConfig.Security.SignIn.KeepLoggedIn.CookieExpiresDays));
int cookieExpiresDays = Math.Max(Sax.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Min, Math.Min(Sax.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Max, AppConfig.Security.SignIn.KeepLoggedIn.CookieExpiresDays));
SetResponseCookie(CookieName_KeepLoggedIn, keepLoggedInCookieValue, DateTime.Now.AddDays(cookieExpiresDays), true);
SaLogger.Info(this.GetType().Name + ".SetKeepLoggedInCookie 'Keep Logged In' cookie set. Value: " + keepLoggedInCookieValue);
}
@@ -79,7 +79,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers
if (!ValidUserSession)
return Json(responseData.SetGoHome());
if (!SessionConfig.Security.SignIn.KeepLoggedIn.Allowed)
if (!AppConfig.Security.SignIn.KeepLoggedIn.Allowed)
return Json(responseData);
// Resend the cookie to extend the expiry date if it is currently populated.
@@ -394,7 +394,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers
}
internal static int GetRequestPasswordEmailLinkExpiresMinutes(SaxController_Admin controller)
{
int expiresMinutes = controller.SessionConfig.Security.SignIn.PasswordRequest.EmailLinkExpiresMins;
int expiresMinutes = controller.AppConfig.Security.SignIn.PasswordRequest.EmailLinkExpiresMins;
int expiresMinutes_Min = Sax.Config.Security.SignIn.PasswordRequest.EmailLinkExpiresMins_Min;
int expiresMinutes_Max = Sax.Config.Security.SignIn.PasswordRequest.EmailLinkExpiresMins_Max;
return Math.Max(expiresMinutes_Min, Math.Min(expiresMinutes_Max, expiresMinutes));
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
@@ -16,6 +17,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
@@ -88,7 +90,19 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers
}
else SaLogger.Warn(fn + " 'Request Password' parameters supplied but invalid. " + logSuffix);
}
else SaLogger.Warn(fn + " 'Request Password' parameters supplied but account is not 'Admin'. " + logSuffix);
else
{
// Account is active but not 'Admin'.
// Redirect to Online application if URL is available and not already redirected ('rpe2' parameter).
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.Online.Root))
{
UriBuilder uri = new UriBuilder(Regex.Replace(AppConfig.URL.Online.Root,"/$","") + "/Security/ChangePassword");
uri.Query = ParamName_RequestPasswordEncrypted + "=" + Uri.EscapeDataString(encryptedParams) + "&" + redirectParamName;
return Redirect(uri.Uri.OriginalString);
}
}
}
else SaLogger.Warn(fn + " 'Request Password' parameters supplied but account is not active. " + logSuffix);
}
@@ -0,0 +1,44 @@
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Base.App;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using Saxonite.Net.Core.Web.Base.Models;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Models
{
//=======================================================================
/// <summary>
/// Model for Organisation Activation.
/// <para>NB Only used for Ajax activation request.</para>
/// </summary>
public class Model_OrganisationActivation : SaxModel_Admin
{
//-------------------------------------------------------------------
// Construction.
public Model_OrganisationActivation(SaxLocaliser localiser) :
base(localiser)
{
}
//-------------------------------------------------------------------
// Organisation properties.
public String OrganisationId { get; set; }
/// <summary>
/// List of User Organisation Ids that were offered for activation.
/// </summary>
public List<String> OrganisationMemberIds { get; set; }
//-------------------------------------------------------------------
}
//=======================================================================
}