BusinessMgr

This commit is contained in:
Andy Howard
2025-01-09 16:31:16 +00:00
parent 4005870236
commit 4f91faae8b
30 changed files with 475 additions and 265 deletions
+2 -2
View File
@@ -135,7 +135,7 @@ namespace Sacs.Web.Components
// Serialise parameters as if in a URL query string - so that they can be deserialised in DecryptParameters() below. // Serialise parameters as if in a URL query string - so that they can be deserialised in DecryptParameters() below.
// Start with a random length parameter name at start of list to obscure the encryption. // Start with a random length parameter name at start of list to obscure the encryption.
string queryString = SacsRandomHelper.getRandomLengthNumber(4) + "&"; string queryString = SacsRandomHelper.GetRandomLengthNumber(4) + "&";
queryString += String.Join("&", paramKeys.Select(a => a + "=" + HttpUtility.UrlEncode(parametersToEncrypt[a]))); queryString += String.Join("&", paramKeys.Select(a => a + "=" + HttpUtility.UrlEncode(parametersToEncrypt[a])));
// Encrypt data. // Encrypt data.
@@ -286,7 +286,7 @@ namespace Sacs.Web.Components
ConfirmMessage = new ConfirmMessageData() { Title = title, Text = text, Actions = actions }; ConfirmMessage = new ConfirmMessageData() { Title = title, Text = text, Actions = actions };
return this; return this;
} }
public bool HasConfirmMessage { get { return ConfirmMessage != null; } } public bool? HasConfirmMessage { get { return ConfirmMessage != null ? true : null; } }
} }
//------------------------------------------------------------------- //-------------------------------------------------------------------
-1
View File
@@ -22,7 +22,6 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.20" /> <PackageReference Include="Microsoft.Maui.Controls" Version="8.0.20" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.20" /> <PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.20" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
+1 -1
View File
@@ -7,7 +7,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Xamarin.Essentials" Version="1.8.1" /> <PackageReference Include="Xamarin.Essentials" Version="1.8.1" />
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2622" /> <PackageReference Include="Xamarin.Forms" Version="5.0.0.2662" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -43,10 +43,15 @@ namespace Sarand.Net.Standard.Base.Bus
//------------------------------------------------------------------- //-------------------------------------------------------------------
// Properties. // Properties.
/// <summary>OK - No error state message.</summary>
public bool OK { get { return !HasErrorState(); } }
//------------------------------------------------------------------- //-------------------------------------------------------------------
// E r r o r S t a t e // Error State.
//------------------------------------------------------------------- //-------------------------------------------------------------------
private SacsErrorState errorState = null; private SacsErrorState errorState = null;
public bool HasErrorState() public bool HasErrorState()
{ {
return errorState != null; return errorState != null;
+7
View File
@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq; using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@@ -111,6 +112,12 @@ namespace Sarand.Net.Standard.Base.Bus
return (IsValid(sysId) ? sysId.id.ToString() : invalidValue); return (IsValid(sysId) ? sysId.id.ToString() : invalidValue);
} }
// From String.
public static SysId ToSysId(String value)
{
return new SysId(StringUtil.ToLong(value));
}
//------------------------------------------------------------------- //-------------------------------------------------------------------
/// <summary> /// <summary>
/// JSON Converter to serialise/deserialise SysId to/from simple integer value. /// JSON Converter to serialise/deserialise SysId to/from simple integer value.
@@ -1,9 +0,0 @@
namespace Sarand.Net.Standard.Base.Dbas
{
//=======================================================================
public interface IDbDataTable
{
}
//=======================================================================
}
+13 -1
View File
@@ -77,10 +77,22 @@ namespace Sarand.Net.Standard.Base.Dbas
// Database info - schema summary. // Database info - schema summary.
IDatabaseInfo CreateDbInfo(); IDatabaseInfo CreateDbInfo();
//-------------------------------------------------------------------
// Password utilities.
public enum PasswordHashMethod { MsSql }
/// <summary>Generate password hash using the specified method.</summary>
String EncodePassword_Hex(String password, PasswordHashMethod method = PasswordHashMethod.MsSql);
/// <summary>Compare password to hashed value using the specified method.</summary>
bool ComparePassword_Hex(String password, String passwordHashHex, ISacsDatabase.PasswordHashMethod method = ISacsDatabase.PasswordHashMethod.MsSql);
//------------------------------------------------------------------- //-------------------------------------------------------------------
// Query records. // Query records.
String GetSelectQueryPrefix(); String GetSelectQueryPrefix();
IDbDataTable ExecuteQuery (String query); /// <summary>
/// Execute query directly against database.
/// <para>- NB Check for null response and error state.</para>
/// </summary>
DbResultSet ExecuteQuery(String query);
int ExecuteNonQuery(String query); int ExecuteNonQuery(String query);
int ExecuteInsert(String tableName, List<StringPair> columnNameValues); int ExecuteInsert(String tableName, List<StringPair> columnNameValues);
int ExecuteUpdate(String tableName, List<StringPair> columnNameValues, String filter); int ExecuteUpdate(String tableName, List<StringPair> columnNameValues, String filter);
@@ -1,6 +1,7 @@
using Sarand.Net.Standard.Base.Utils; using Sarand.Net.Standard.Base.Utils;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
@@ -52,19 +53,27 @@ namespace Sarand.Net.Standard.Base.Helpers
} }
//------------------------------------------------------------------- //-------------------------------------------------------------------
public static String getRandomLengthNumber(ushort maxLength=4) public static String GetRandomLengthNumber(ushort maxLength = 4)
{ {
String fn = "getRandomLengthNumber";
String value = ""; String value = "";
int len = GetInt32(1, maxLength + 1); int len = GetInt32(1, maxLength + 1);
for (int i = 0; i < len; i++) for (int i = 0; i < len; i++)
value += GetInt32(0, 10); value += GetInt32(0, 10);
if (StringUtil.IsEmpty(value)) if (StringUtil.IsBlank(value))
throw new NotSupportedException("getRandomLengthNumber Generated blank value"); throw new NotSupportedException($"{fn} Generated blank value");
if (value.Length > maxLength) if (value.Length > maxLength)
throw new NotSupportedException("getRandomLengthNumber Generated excess length value"); throw new NotSupportedException($"{fn} Generated excess length value");
return value; return value;
} }
//-------------------------------------------------------------------
/// <summary>Returns filename in 8.3 format.</summary>
public static String GetRandomFileName()
{
return Path.GetRandomFileName();
}
//------------------------------------------------------------------- //-------------------------------------------------------------------
} }
//======================================================================= //=======================================================================
@@ -21,6 +21,12 @@ namespace Sarand.Net.Standard.Base.Security
Group = 3, Group = 3,
Right = 4, Right = 4,
} }
// Password storage methods - must be kept up to date with legacy application.
public enum PasswordMethod
{
Legacy = 0,
MsSql = 10,
}
public enum FilterAttrType public enum FilterAttrType
{ {
Code, // Partial code. Code, // Partial code.
@@ -29,14 +35,6 @@ namespace Sarand.Net.Standard.Base.Security
Group_Exclude, // Item doesn't belong to group. Group_Exclude, // Item doesn't belong to group.
}; };
} }
// Password configuration.
public class PasswordConfig
{
/// <summary>Minimum password length. < 1 = no limit.</summary>
public int MinimumLength { get; protected set; } = 6;
/// <summary>Maximum password length. < 1 = no limit.</summary>
public int MaximumLength { get; protected set; } = 6;
}
//======================================================================= //=======================================================================
public abstract class SaSecurityConsole : SaErrorStateItem public abstract class SaSecurityConsole : SaErrorStateItem
@@ -58,8 +56,8 @@ namespace Sarand.Net.Standard.Base.Security
//------------------------------------------------------------------- //-------------------------------------------------------------------
// Data members. // Data members.
protected static String GroupSuperCode = "SUPER"; public static String GroupCode_Super = "SUPER";
protected static String AdminUserCode = "admin"; public static String UserCode_Admin = "admin";
private static List<TypeCodeDesc> securityItemTypes = new List<TypeCodeDesc>(); private static List<TypeCodeDesc> securityItemTypes = new List<TypeCodeDesc>();
//------------------------------------------------------------------- //-------------------------------------------------------------------
@@ -116,6 +114,12 @@ namespace Sarand.Net.Standard.Base.Security
return userIdDefault; return userIdDefault;
} }
protected void ClearRegisteredSecurityRights()
{
RegisteredRights.Clear();
DataItemDfns.Clear();
}
//------------------------------------------------------------------- //-------------------------------------------------------------------
// L o g i n // L o g i n
//------------------------------------------------------------------- //-------------------------------------------------------------------
@@ -140,9 +144,28 @@ namespace Sarand.Net.Standard.Base.Security
/// <summary>Security Group modified event.</summary> /// <summary>Security Group modified event.</summary>
public abstract bool OnSecurityGroupModified(SysId userId); public abstract bool OnSecurityGroupModified(SysId userId);
//-------------------------------------------------------------------
// Password utilities.
//------------------------------------------------------------------- //-------------------------------------------------------------------
// Password configuration. // Password configuration.
public PasswordConfig PasswordConfig { get; protected set; } = new PasswordConfig(); public class SaPasswordConfig
{
/// <summary>Minimum password length. < 1 = no limit.</summary>
public int MinimumLength { get; protected set; } = 6;
/// <summary>Maximum password length. < 1 = no limit.</summary>
public int MaximumLength { get; protected set; } = 6;
}
public SaPasswordConfig PasswordConfig { get; protected set; } = new SaPasswordConfig();
/// <summary>Store the password against against the user.</summary>
public abstract bool SetPassword(SaSecurityItem securityItemUser, String password);
/// <summary>Does the password match that currently defined against the user.</summary>
public abstract bool PasswordMatches(SaSecurityItem securityItemUser, String password);
/// <summary>Is the password currently defined against the user blank.</summary>
public abstract bool PasswordIsBlank(SaSecurityItem securityItemUser);
/// <summary>Show form to change password for currently logged in user.</summary> /// <summary>Show form to change password for currently logged in user.</summary>
/// <returns>Flag to indicate password changed</returns> /// <returns>Flag to indicate password changed</returns>
public abstract bool ShowChangePasswordForm(DbUpdateInfo dbUpdateInfo); public abstract bool ShowChangePasswordForm(DbUpdateInfo dbUpdateInfo);
@@ -12,6 +12,11 @@ namespace Sarand.Net.Standard.Base.Security
//======================================================================= //=======================================================================
public abstract class SaSecurityItem : SacsHierarchyItem public abstract class SaSecurityItem : SacsHierarchyItem
{ {
//-------------------------------------------------------------------
// Definitions.
// Password Salt constants - must be kept up to date with legacy application.
private static String PasswordSalt_MsSql = "MsSql";
//------------------------------------------------------------------- //-------------------------------------------------------------------
// Columns. // Columns.
public enum Column public enum Column
@@ -359,68 +364,31 @@ namespace Sarand.Net.Standard.Base.Security
//------------------------------------------------------------------- //-------------------------------------------------------------------
// Password utilities. // Password utilities.
private static readonly Encoding Encoding_Ansi1252 = Encoding.GetEncoding(1252);
public bool IsPasswordBlank () /// <summary>Extract password storage method from data.</summary>
public SaSecurity.PasswordMethod GetPasswordMethod()
{ {
return EncryptPassword("").Equals(PasswordEncrypted); return SaSecurity.PasswordMethod.Legacy;
}
public bool PasswordMatches (String password)
{
return PasswordEncrypted.Equals(EncryptPassword(password));
}
public static String EncryptPassword (String password)
{
// We know this is not very water-tight, but it at least
// hides the contents of the password from prying eyes.
String toBeEncrypted = password;
// Password length goes in as first character:
toBeEncrypted = ByteToString((byte)(128 - password.Length)) + toBeEncrypted;
toBeEncrypted = string.Concat(toBeEncrypted.Reverse());
// Pad out to fixed length (minimum 16, multiples of 8):
int fixedLength = 16;
if (password.Length / 8 > 2) fixedLength = 8 * (int)(password.Length / 8);
while (toBeEncrypted.Length < fixedLength)
{
char charToAdd = (char)(password.Length > 0 ? password[(toBeEncrypted.Length % password.Length)] : 'A' + (toBeEncrypted.Length * 13) % 23);
if (toBeEncrypted.Length % 2 != 0)
toBeEncrypted += charToAdd;
else toBeEncrypted = charToAdd + toBeEncrypted;
} }
// Now perform a simple algorythm on each character to hide its true value: public void SetEncodedPassword(SaSecurity.PasswordMethod method, String encodedPassword)
byte[] encryptedChars = new byte[toBeEncrypted.Length];
for (int i = 0; i < toBeEncrypted.Length; i++)
{ {
int iData = toBeEncrypted[i]; if (method == SaSecurity.PasswordMethod.MsSql)
iData += 17 + i;
if (iData > 255) iData -= (255 + 32);
encryptedChars[i] = (byte)iData;
}
String result = Encoding_Ansi1252.GetString(encryptedChars);
#if DEBUG
// Password: mgbgt
// To encrypt: tbmggtgbgm{btggm
// Encrypted: 133, 116, 128, 123, 124, 138, 126, 122, 128, 135, 150, 126, 145, 133, 134, 141
//Logger.Info(fn + "\n\tPassword: " + password + "\n\tTo encrypt: " + toBeEncrypted + "\n\tEncrypted: " + encryptedChars.Select(i => i.ToString()).ToList().Concat(", "));
//Logger.Debug(password + ": " + result);
#endif
return result;
}
private byte[] PasswordStringToBytes (String value)
{ {
byte[] values = new byte[value.Length]; #pragma warning disable CS0618 // Deprecated property
for (int idx = 0; idx < value.Length; idx++) PasswordEncrypted = "";
{ #pragma warning restore CS0618 // Deprecated property
char c = value[idx]; PasswordSalt = PasswordSalt_MsSql;
UInt32 ui = Convert.ToUInt32(c); PasswordHash = encodedPassword;
SaLogger.Info(idx + ". " + ui.ToString("x8"));
values[idx] = (byte)(c);
} }
return values; else
}
private static String ByteToString (byte value)
{ {
return Encoding_Ansi1252.GetString(new[] { value }); #pragma warning disable CS0618 // Deprecated property
PasswordEncrypted = encodedPassword;
#pragma warning restore CS0618 // Deprecated property
PasswordSalt = "";
PasswordHash = "";
}
} }
//------------------------------------------------------------------- //-------------------------------------------------------------------
@@ -31,10 +31,19 @@ namespace Sarand.Net.Standard.Base.Security
// Database properties. // Database properties.
public abstract bool DatabaseIsValid { get; } public abstract bool DatabaseIsValid { get; }
public abstract bool CanUpdateDatabase { get; } public abstract bool CanUpdateDatabase { get; }
public abstract bool ColumnExists (SaSecurityDbDfn.ColumnIdx columnIdx);
//------------------------------------------------------------------- //-------------------------------------------------------------------
// Utilities. // Password utilities.
public abstract bool ColumnExists (SaSecurityDbDfn.ColumnIdx columnIdx);
/// <summary>Store the password against against the user.</summary>
public abstract bool SetPassword(SaSecurityItem securityItemUser, String password);
/// <summary>Does the password match that currently defined against the user.</summary>
public abstract bool PasswordMatches(SaSecurityItem securityItemUser, String password);
/// <summary>Is the password currently defined against the user blank.</summary>
public abstract bool PasswordIsBlank(SaSecurityItem securityItemUser);
//------------------------------------------------------------------- //-------------------------------------------------------------------
} }
@@ -10,40 +10,52 @@ namespace Sarand.Net.Standard.Base.Security
public class SaSecurityProfile public class SaSecurityProfile
{ {
//------------------------------------------------------------------- //-------------------------------------------------------------------
/// <summary>Security Group definition.</summary>
public class GroupData public class GroupData
{ {
public readonly List<SaSecurityProfile.RightData> rights = new List<RightData>(); private readonly List<SaSecurityProfile.RightData> rights = new List<RightData>();
public GroupData(SaSecurityItem secGroup, List<SaSecurityProfile.RightData> groupRights) public GroupData(SaSecurityItem secGroup, List<SaSecurityProfile.RightData> groupRights)
{ {
if (secGroup.ItemType != SaSecurity.ItemType.Group) throw new NotSupportedException("SaSecurityProfile.GroupData - Invalid security item type: " + secGroup.ItemType.ToString()); if (secGroup.ItemType != SaSecurity.ItemType.Group) throw new NotSupportedException("SaSecurityProfile.GroupData - Invalid security item type: " + secGroup.ItemType.ToString());
GroupId = secGroup.Id; Id = secGroup.Id;
GroupCode = secGroup.Code; Code = secGroup.Code;
GroupDesc = secGroup.Description; Desc = secGroup.Description;
rights.AddRange(groupRights); rights.AddRange(groupRights);
} }
protected GroupData() { } protected GroupData() { }
public SysId GroupId { get; protected set; } public SysId Id { get; }
public String GroupCode { get; protected set; } public String Code { get; }
public String GroupDesc { get; protected set; } public String Desc { get; }
// Security rights.
public List<SaSecurityProfile.RightData> Rights { get { return new List<RightData>(rights); } } public List<SaSecurityProfile.RightData> Rights { get { return new List<RightData>(rights); } }
/// <summary>
/// Retrieve the list of data items for which the user is explicitly granted the
/// specified security right, or where the right is explicitly denied if the flag is false.
/// </summary>
public List<SysId> GetDataItemsForRight(String rightCode, bool granted = true) { return rights.GetDataItemsForRight(rightCode, granted); }
/// <summary>Is the Security Right code granted access for the Data item (if specified).</summary>
public bool IsSecurityRightGranted(String rightCode, SysId dataItemId = null) { return rights.IsSecurityRightGranted(rightCode, dataItemId); }
/// <summary>Is the Security Right code denined access for the Data item (if specified).</summary>
public bool IsSecurityRightDenied(String rightCode, SysId dataItemId = null) { return rights.IsSecurityRightDenied(rightCode, dataItemId); }
} }
/// <summary>Security Right definition.</summary>
public class RightData public class RightData
{ {
public RightData(SaSecurityItem secRight, SaSecurityLink secRightLink) public RightData(SaSecurityItem secRight, SaSecurityLink secRightLink)
{ {
if (secRight.ItemType != SaSecurity.ItemType.Right) throw new NotSupportedException("SaSecurityProfile.RightData - Invalid security item type: " + secRight.ItemType.ToString()); if (secRight.ItemType != SaSecurity.ItemType.Right) throw new NotSupportedException("SaSecurityProfile.RightData - Invalid security item type: " + secRight.ItemType.ToString());
RightId = secRight.Id; Id = secRight.Id;
RightCode = secRight.Code; Code = secRight.Code;
RightDesc = secRight.Description; Desc = secRight.Description;
DataItemId = secRightLink.ItemIdContext; DataItemId = secRightLink.ItemIdContext;
Granted = (secRightLink.Status == SaStatus.Granted); Granted = (secRightLink.Status == SaStatus.Granted);
} }
protected RightData() { } protected RightData() { }
public SysId RightId { get; protected set; } public SysId Id { get; }
public String RightCode { get; protected set; } public String Code { get; }
public String RightDesc { get; protected set; } public String Desc { get; }
public SysId DataItemId { get; protected set; } public SysId DataItemId { get; }
public bool Granted { get; protected set; } public bool Granted { get; }
} }
//------------------------------------------------------------------- //-------------------------------------------------------------------
@@ -117,7 +129,7 @@ namespace Sarand.Net.Standard.Base.Security
// Utilities. // Utilities.
public bool IsMemberOfSecurityGroup(SysId secGroupId) public bool IsMemberOfSecurityGroup(SysId secGroupId)
{ {
return securityGroups.Exists(item => item.GroupId.Equals(secGroupId)); return securityGroups.Exists(item => item.Id.Equals(secGroupId));
} }
//------------------------------------------------------------------- //-------------------------------------------------------------------
@@ -158,20 +170,20 @@ namespace Sarand.Net.Standard.Base.Security
/// </summary> /// </summary>
public static List<SysId> GetDataItemsForRight(this List<SaSecurityProfile.GroupData> groups, String rightCode, bool granted = true) public static List<SysId> GetDataItemsForRight(this List<SaSecurityProfile.GroupData> groups, String rightCode, bool granted = true)
{ {
List<SysId> dataItemIds = new List<SysId>(); HashSet<SysId> dataItemIds = new HashSet<SysId>();
foreach (var securityGroup in groups) foreach (var securityGroup in groups)
dataItemIds.AddIds(securityGroup.rights.GetDataItemsForRight(rightCode,granted)); dataItemIds.AddIds(securityGroup.GetDataItemsForRight(rightCode, granted));
return dataItemIds; return new List<SysId>(dataItemIds);
} }
/// <summary>Is the Security Right code granted access for the Data item (if specified).</summary> /// <summary>Is the Security Right code granted access for the Data item (if specified).</summary>
public static bool IsSecurityRightGranted(this List<SaSecurityProfile.GroupData> groups, String rightCode, SysId dataItemId = null) public static bool IsSecurityRightGranted(this List<SaSecurityProfile.GroupData> groups, String rightCode, SysId dataItemId = null)
{ {
return groups.Exists(item => item.rights.IsSecurityRightGranted(rightCode, dataItemId)); return groups.Exists(group => group.IsSecurityRightGranted(rightCode, dataItemId));
} }
/// <summary>Is the Security Right code denined access for the Data item (if specified).</summary> /// <summary>Is the Security Right code denined access for the Data item (if specified).</summary>
public static bool IsSecurityRightDenied(this List<SaSecurityProfile.GroupData> groups, String rightCode, SysId dataItemId = null) public static bool IsSecurityRightDenied(this List<SaSecurityProfile.GroupData> groups, String rightCode, SysId dataItemId = null)
{ {
return groups.Exists(item => item.rights.IsSecurityRightDenied(rightCode, dataItemId)); return groups.Exists(group => group.IsSecurityRightDenied(rightCode, dataItemId));
} }
} }
@@ -184,15 +196,15 @@ namespace Sarand.Net.Standard.Base.Security
/// </summary> /// </summary>
public static List<SysId> GetDataItemsForRight(this List<SaSecurityProfile.RightData> rights, String rightCode, bool granted = true) public static List<SysId> GetDataItemsForRight(this List<SaSecurityProfile.RightData> rights, String rightCode, bool granted = true)
{ {
List<SysId> dataItemIds = new List<SysId>(); HashSet<SysId> dataItemIds = new HashSet<SysId>();
foreach (var securityRight in rights.FindAll(x => x.RightCode.EqualsCI(rightCode) && x.Granted == granted)) foreach (var securityRight in rights.FindAll(x => x.Code.EqualsCI(rightCode) && x.Granted == granted))
dataItemIds.AddId(securityRight.DataItemId); dataItemIds.AddId(securityRight.DataItemId);
return dataItemIds; return new List<SysId>(dataItemIds);
} }
/// <summary>Is the Security Right code granted access for the Data item (if specified).</summary> /// <summary>Is the Security Right code granted access for the Data item (if specified).</summary>
public static bool IsSecurityRightGranted(this List<SaSecurityProfile.RightData> rights, String rightCode, SysId dataItemId = null) public static bool IsSecurityRightGranted(this List<SaSecurityProfile.RightData> rights, String rightCode, SysId dataItemId = null)
{ {
foreach (var userSecurityRight in rights.FindAll(item => item.RightCode.EqualsCI(rightCode))) foreach (var userSecurityRight in rights.FindAll(item => item.Code.EqualsCI(rightCode)))
{ {
if (SysId.ToInt(dataItemId) != SysId.ToInt(userSecurityRight.DataItemId)) continue; if (SysId.ToInt(dataItemId) != SysId.ToInt(userSecurityRight.DataItemId)) continue;
return userSecurityRight.Granted; return userSecurityRight.Granted;
@@ -202,7 +214,7 @@ namespace Sarand.Net.Standard.Base.Security
/// <summary>Is the Security Right code denined access for the Data item (if specified).</summary> /// <summary>Is the Security Right code denined access for the Data item (if specified).</summary>
public static bool IsSecurityRightDenied(this List<SaSecurityProfile.RightData> rights, String rightCode, SysId dataItemId = null) public static bool IsSecurityRightDenied(this List<SaSecurityProfile.RightData> rights, String rightCode, SysId dataItemId = null)
{ {
foreach (var userSecurityRight in rights.FindAll(item => item.RightCode.EqualsCI(rightCode))) foreach (var userSecurityRight in rights.FindAll(item => item.Code.EqualsCI(rightCode)))
{ {
if (SysId.ToInt(dataItemId) != SysId.ToInt(userSecurityRight.DataItemId)) continue; if (SysId.ToInt(dataItemId) != SysId.ToInt(userSecurityRight.DataItemId)) continue;
return !userSecurityRight.Granted; return !userSecurityRight.Granted;
+7 -10
View File
@@ -474,7 +474,8 @@ namespace Sarand.Net.Standard.Base.Utils
} }
public static long ToLong(this string value, long defaultValue = 0) public static long ToLong(this string value, long defaultValue = 0)
{ {
if (String.IsNullOrEmpty(value)) return defaultValue; if (!String.IsNullOrEmpty(value))
{
try try
{ {
Double returnValue = defaultValue; Double returnValue = defaultValue;
@@ -482,6 +483,7 @@ namespace Sarand.Net.Standard.Base.Utils
return (Int64)returnValue; return (Int64)returnValue;
} }
catch (Exception) { } catch (Exception) { }
}
return defaultValue; return defaultValue;
} }
public static short ToShort(Object value, short defaultValue = 0) public static short ToShort(Object value, short defaultValue = 0)
@@ -494,20 +496,15 @@ namespace Sarand.Net.Standard.Base.Utils
} }
public static long ToLong(Object value, long defaultValue = 0) public static long ToLong(Object value, long defaultValue = 0)
{ {
if (value == null) return defaultValue; if (value != null)
{
try try
{ {
return ToLong(value.ToString()); return ToLong(value.ToString(), defaultValue);
} }
catch (Exception) { } catch (Exception) { }
return defaultValue;
} }
return defaultValue;
//---------------------------------------------------------------------------
// SysId.
public static SysId ToSysId(this String value)
{
return new SysId(StringUtil.ToLong(value));
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
+2 -2
View File
@@ -7,11 +7,11 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="CsvHelper" Version="19.0.0" /> <PackageReference Include="CsvHelper" Version="19.0.0" />
<PackageReference Include="MailKit" Version="4.8.0" /> <PackageReference Include="MailKit" Version="4.9.0" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" /> <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" /> <PackageReference Include="SixLabors.ImageSharp" Version="3.1.6" />
<PackageReference Include="ZstdNet" Version="1.4.5" /> <PackageReference Include="ZstdNet" Version="1.4.5" />
</ItemGroup> </ItemGroup>
+78 -1
View File
@@ -61,6 +61,83 @@ namespace Sarand.Net.Standard.Dbas.Base
public abstract int GetNextIdFromTable(string objectName, string idColumn = "Id"); public abstract int GetNextIdFromTable(string objectName, string idColumn = "Id");
public abstract bool SetMinNextId(String objectName, SysId id, DbUpdateInfo dbUpdateInfo, bool databaseTable = true, String idColumn = "Id", bool force = false); public abstract bool SetMinNextId(String objectName, SysId id, DbUpdateInfo dbUpdateInfo, bool databaseTable = true, String idColumn = "Id", bool force = false);
//-------------------------------------------------------------------
// Password utilities.
//-------------------------------------------------------------------
/// <summary>
/// Generate password hash using the specified method.
/// <para>- Returns hex notation as a string.</para>
/// </summary>
/// <param name="password"></param>
/// <returns>String of hex digits corresponding to encrypted password.</returns>
public String EncodePassword_Hex(String password, ISacsDatabase.PasswordHashMethod method = ISacsDatabase.PasswordHashMethod.MsSql)
{
ClearErrorState();
if (method == ISacsDatabase.PasswordHashMethod.MsSql)
{
if (DbmsType == DbmsType.MsSql)
{
try
{
String colAlias = "DataValue";
String query = $"Select {colAlias}=Convert(VarChar(2000),PwdEncrypt('{DbQuery.FormatForCondition(password)}'),2);";
using (DbResultSet dbResultSet = DbResultSet_DataReader.ExecuteQuery(this, query))
{
if (dbResultSet.NextRecord())
return dbResultSet.GetRecordValues().GetValue(colAlias).ToString();
else if (dbResultSet.HasErrorState())
SetErrorState(dbResultSet.ErrorStateMessage);
}
}
catch (Exception exc)
{
SaLogger.Error($"{SysUtil.Fn} {ExceptionUtil.GetLogMessage(exc, "hashing password")}", true);
SetErrorState(exc.Message);
}
}
else SaLogger.Warn($"{SysUtil.Fn} Unable to encode password by method 'SQL'. Database is '{DbmsType}'.");
}
else SaLogger.Warn($"{SysUtil.Fn} Unable to encode password. Unrecognised method: '{method}'.");
return "";
}
/// <summary>
/// Compare the password to the encrypted hash using the specified method.
/// </summary>
/// <param name="password"></param>
/// <param name="passwordHashHex"></param>
/// <returns>True if the password matches the encrypted hash value.</returns>
public bool ComparePassword_Hex(String password, String passwordHashHex, ISacsDatabase.PasswordHashMethod method = ISacsDatabase.PasswordHashMethod.MsSql)
{
ClearErrorState();
if (method == ISacsDatabase.PasswordHashMethod.MsSql)
{
if (DbmsType == DbmsType.MsSql)
{
try
{
String colAlias = "PwdCompare";
String query = $"Select {colAlias}=PwdCompare('{DbQuery.FormatForCondition(password)}', Convert(VarBinary(1000),'{DbQuery.FormatForCondition(passwordHashHex)}',2));";
using (DbResultSet dbResultSet = DbResultSet_DataReader.ExecuteQuery(this, query))
{
if (dbResultSet.NextRecord())
return StringUtil.ToBool(dbResultSet.GetRecordValues().GetValue(colAlias).ToString());
else if (dbResultSet.HasErrorState())
SetErrorState(dbResultSet.ErrorStateMessage);
}
}
catch (Exception exc)
{
SaLogger.Error($"{SysUtil.Fn} {ExceptionUtil.GetLogMessage(exc, "comparing password")}", true);
SetErrorState(exc.Message);
}
}
else SaLogger.Warn($"{SysUtil.Fn} Unable to compare password by method 'SQL'. Database is '{DbmsType}'.");
}
else SaLogger.Warn($"{SysUtil.Fn} Unable to compare password. Unrecognised method: '{method}'.");
return false;
}
//------------------------------------------------------------------- //-------------------------------------------------------------------
// O D B C D r i v e r s // O D B C D r i v e r s
//------------------------------------------------------------------- //-------------------------------------------------------------------
@@ -184,7 +261,7 @@ namespace Sarand.Net.Standard.Dbas.Base
} }
return ""; return "";
} }
public abstract IDbDataTable ExecuteQuery(String query); public abstract DbResultSet ExecuteQuery(String query);
public abstract int ExecuteNonQuery(String query); public abstract int ExecuteNonQuery(String query);
public abstract int ExecuteInsert(String tableName, List<StringPair> columnNameValues); public abstract int ExecuteInsert(String tableName, List<StringPair> columnNameValues);
public abstract int ExecuteUpdate(String tableName, List<StringPair> columnNameValues, String filter); public abstract int ExecuteUpdate(String tableName, List<StringPair> columnNameValues, String filter);
@@ -17,9 +17,9 @@ namespace Sarand.Net.Standard.Dbas.Base
// Database configuration // Database configuration
public class OdbcConfig public class OdbcConfig
{ {
public DbmsType dbms = DbmsType.General; public DbmsType Dbms = DbmsType.General;
public String connectionString; // Raw connection string. public String ConnectionString; // Raw connection string.
public String location, user, pword; // Application properties. public String Location, User, Password; // Connection properties.
/// <summary>Database name populated when connection string is generated if not done previously.</summary> /// <summary>Database name populated when connection string is generated if not done previously.</summary>
public String DatabaseName { get; set; } public String DatabaseName { get; set; }
} }
@@ -42,9 +42,9 @@ namespace Sarand.Net.Standard.Dbas.Base
//------------------------------------------------------------------- //-------------------------------------------------------------------
public static DbmsType GetDbmsType(OdbcConfig config) public static DbmsType GetDbmsType(OdbcConfig config)
{ {
if (config.dbms == DbmsType.MsSql || config.location.Left(6).EqualsCI("MsSql:")) if (config.Dbms == DbmsType.MsSql || config.Location.Left(6).EqualsCI("MsSql:"))
return DbmsType.MsSql; return DbmsType.MsSql;
return config.dbms; return config.Dbms;
} }
public static String GenerateConnectionString(OdbcConfig config, bool dbmsSpecificConnectionString = false) public static String GenerateConnectionString(OdbcConfig config, bool dbmsSpecificConnectionString = false)
{ {
@@ -58,7 +58,7 @@ namespace Sarand.Net.Standard.Dbas.Base
// MsSql:Server:Port\Instance.Database // MsSql:Server:Port\Instance.Database
dbms = DbmsType.MsSql; dbms = DbmsType.MsSql;
String sqlDriver = OdbcUtil.GetOdbcSqlDriverName(); String sqlDriver = OdbcUtil.GetOdbcSqlDriverName();
String sqlLocation = config.location.Left(6).EqualsCI("MsSql:") ? config.location.Substring(6) : config.location; String sqlLocation = config.Location.Left(6).EqualsCI("MsSql:") ? config.Location.Substring(6) : config.Location;
String sqlServer = "."; /* Assume local host */ String sqlServer = "."; /* Assume local host */
String sqlInstance = ""; String sqlInstance = "";
String sqlPort = ""; String sqlPort = "";
@@ -99,17 +99,17 @@ namespace Sarand.Net.Standard.Dbas.Base
// WsId=<WorkstationId>; // WsId=<WorkstationId>;
// Server=[protocol:]Server[,port][\instancename]; // Server=[protocol:]Server[,port][\instancename];
// Database=<Default database>; // Database=<Default database>;
// Trusted_Connection=<Yes|No>; // Trusted_Connection=<Yes|No>; [or Integrated Security=SSPI;]
// UseProcForPrepare=2 // UseProcForPrepare=2
connectionString = "Driver={" + sqlDriver + "};"; connectionString = "Driver={" + sqlDriver + "};";
connectionString += "Server=" + sqlServer + ";"; connectionString += "Server=" + sqlServer + ";";
connectionString += "Database=" + sqlDatabase + ";"; connectionString += "Database=" + sqlDatabase + ";";
if (!config.user.IsEmpty()) if (!config.User.IsEmpty())
{ {
connectionString += "UID=" + config.user + ";"; connectionString += "UID=" + config.User + ";";
connectionString += "PWD=" + config.pword + ";"; connectionString += "PWD=" + config.Password + ";";
} }
connectionString += "Trusted_Connection=" + (config.user.IsEmpty() ? "Yes" : "No") + ";"; connectionString += "Trusted_Connection=" + (config.User.IsEmpty() ? "Yes" : "No") + ";";
// Mar24 Added to avoid 'The certificate chain was issued by an authority that is not trusted'. // Mar24 Added to avoid 'The certificate chain was issued by an authority that is not trusted'.
connectionString += "TrustServerCertificate=Yes;"; connectionString += "TrustServerCertificate=Yes;";
// Multiple Active Result Sets. // Multiple Active Result Sets.
@@ -125,23 +125,23 @@ namespace Sarand.Net.Standard.Dbas.Base
connectionString = ""; connectionString = "";
connectionString += "Server=" + sqlServer + ";"; connectionString += "Server=" + sqlServer + ";";
connectionString += "Database=" + sqlDatabase + ";"; connectionString += "Database=" + sqlDatabase + ";";
if (!config.user.IsEmpty()) if (!config.User.IsEmpty())
{ {
connectionString += "User Id=" + config.user + ";"; connectionString += "User Id=" + config.User + ";";
connectionString += "Password=" + config.pword + ";"; connectionString += "Password=" + config.Password + ";";
} }
connectionString += "Trusted_Connection=" + (config.user.IsEmpty() ? "Yes" : "No") + ";"; connectionString += "Trusted_Connection=" + (config.User.IsEmpty() ? "Yes" : "No") + ";";
// Mar24 Added to avoid 'The certificate chain was issued by an authority that is not trusted'. // Mar24 Added to avoid 'The certificate chain was issued by an authority that is not trusted'.
connectionString += "TrustServerCertificate=Yes;"; connectionString += "TrustServerCertificate=Yes;";
} }
// Populate configuration values if blank. // Populate configuration values if blank.
if (config.dbms <= 0) if (config.Dbms <= 0)
config.dbms = dbms; config.Dbms = dbms;
if (StringUtil.IsEmpty(config.DatabaseName)) if (StringUtil.IsEmpty(config.DatabaseName))
config.DatabaseName = sqlDatabase; config.DatabaseName = sqlDatabase;
SaLogger.Info($"{SysUtil.Fn} Driver: " + sqlDriver + ". Server: " + sqlServer + ". Database: " + sqlDatabase + ". Connection: " + connectionString); //SaLogger.Debug($"{SysUtil.Fn} Driver: " + sqlDriver + ". Server: " + sqlServer + ". Database: " + sqlDatabase + ". Connection: " + connectionString);
} }
return connectionString; return connectionString;
} }
@@ -152,17 +152,17 @@ namespace Sarand.Net.Standard.Dbas.Base
public static SacsDatabase OpenOdbcDatabase(OdbcConfig config) public static SacsDatabase OpenOdbcDatabase(OdbcConfig config)
{ {
// Populate configuration. // Populate configuration.
config.dbms = GetDbmsType(config); config.Dbms = GetDbmsType(config);
// Connection strings. // Connection strings.
String connectionString = config.connectionString; String connectionString = config.ConnectionString;
if (StringUtil.IsEmpty(connectionString)) if (StringUtil.IsEmpty(connectionString))
connectionString = GenerateConnectionString(config); connectionString = GenerateConnectionString(config);
String sqlConnectionString = GenerateConnectionString(config, true); String sqlConnectionString = GenerateConnectionString(config, true);
// Open database. // Open database.
DbGlobalDbms.Type = config.dbms; DbGlobalDbms.Type = config.Dbms;
SacsDatabaseOdbc database = new SacsDatabaseOdbc(config.dbms, config.DatabaseName, connectionString); SacsDatabaseOdbc database = new SacsDatabaseOdbc(config.Dbms, config.DatabaseName, connectionString);
database.SelectLockMode = SelectLockMode.ReadUncommitted; database.SelectLockMode = SelectLockMode.ReadUncommitted;
database.SqlConnectionString = sqlConnectionString; database.SqlConnectionString = sqlConnectionString;
return database; return database;
@@ -29,7 +29,7 @@ namespace Sarand.Net.Standard.Dbas.CodeType
// Open database. // Open database.
// NB Must be ODBC connection - especially to retrieve index columns. // NB Must be ODBC connection - especially to retrieve index columns.
SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig(); SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig();
config.connectionString = connectionString; config.ConnectionString = connectionString;
SacsDatabase database = SacsDatabaseConsole.OpenOdbcDatabase(config); SacsDatabase database = SacsDatabaseConsole.OpenOdbcDatabase(config);
// Generate database definition. // Generate database definition.
-28
View File
@@ -1,28 +0,0 @@
using System.Data;
using Sarand.Net.Standard.Base.Dbas;
namespace Sarand.Net.Standard.Dbas.Data
{
//=======================================================================
/// <summary>
/// Wrapper for System.Data.DataTable
/// </summary>
public class DbDataTable : IDbDataTable
{
//-------------------------------------------------------------------
// Data members.
private DataTable dataTable;
//-------------------------------------------------------------------
// Construction.
public DbDataTable (DataTable dataTable)
{
this.dataTable = dataTable;
}
//-------------------------------------------------------------------
//-------------------------------------------------------------------
}
//=======================================================================
}
+3 -12
View File
@@ -171,29 +171,20 @@ namespace Sarand.Net.Standard.Dbas.Data
// Q u e r y D a t a b a s e // Q u e r y D a t a b a s e
//------------------------------------------------------------------- //-------------------------------------------------------------------
// Query records. // Query records.
public override IDbDataTable ExecuteQuery(String query) public override DbResultSet ExecuteQuery(String query)
{ {
ClearErrorState(); ClearErrorState();
DataTable dataTable = null;
Log(SysUtil.Fn, "Query: " + query, SaMessageLevel.Debug); Log(SysUtil.Fn, "Query: " + query, SaMessageLevel.Debug);
try try
{ {
using (DbConnection dbConnection = CreateConnection()) return DbResultSet_DataReader.ExecuteQuery(this, query);
using (var dbCommand = dbConnection.CreateCommand())
{
dbConnection.Open();
dbCommand.CommandType = System.Data.CommandType.Text;
dbCommand.CommandText = query;
dataTable = new DataTable();
dataTable.Load(dbCommand.ExecuteReader());
}
} }
catch (Exception exc) catch (Exception exc)
{ {
SaLogger.Error($"{SysUtil.Fn} {ExceptionUtil.GetLogMessage(exc)}\nQuery:\n\t{String.Join("\n\t", query.ReplaceAll("\n\r", "\n").Split("\n"))}", true); SaLogger.Error($"{SysUtil.Fn} {ExceptionUtil.GetLogMessage(exc)}\nQuery:\n\t{String.Join("\n\t", query.ReplaceAll("\n\r", "\n").Split("\n"))}", true);
SetErrorState(exc.Message); SetErrorState(exc.Message);
} }
return new DbDataTable(dataTable); return null;
} }
//------------------------------------------------------------------- //-------------------------------------------------------------------
/// <summary>Execute a non-query statement and return the row count.</summary> /// <summary>Execute a non-query statement and return the row count.</summary>
@@ -32,7 +32,7 @@ namespace Sarand.Net.Standard.Dbas.Interface
// Open database. // Open database.
// NB Must be ODBC connection - especially to retrieve index columns. // NB Must be ODBC connection - especially to retrieve index columns.
SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig(); SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig();
config.connectionString = connectionString; config.ConnectionString = connectionString;
SacsDatabase database = SacsDatabaseConsole.OpenOdbcDatabase(config); SacsDatabase database = SacsDatabaseConsole.OpenOdbcDatabase(config);
// Generate database definition. // Generate database definition.
@@ -31,7 +31,7 @@ namespace Sarand.Net.Standard.Dbas.ItemData
// Open database. // Open database.
// NB Must be ODBC connection - especially to retrieve index columns. // NB Must be ODBC connection - especially to retrieve index columns.
SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig(); SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig();
config.connectionString = connectionString; config.ConnectionString = connectionString;
SacsDatabase database = SacsDatabaseConsole.OpenOdbcDatabase(config); SacsDatabase database = SacsDatabaseConsole.OpenOdbcDatabase(config);
// Generate database definition. // Generate database definition.
@@ -35,7 +35,7 @@ namespace Sarand.Net.Standard.Dbas.ItemDocs
// Open database. // Open database.
// NB Must be ODBC connection - especially to retrieve index columns. // NB Must be ODBC connection - especially to retrieve index columns.
SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig(); SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig();
config.connectionString = connectionString; config.ConnectionString = connectionString;
SacsDatabase database = SacsDatabaseConsole.OpenOdbcDatabase(config); SacsDatabase database = SacsDatabaseConsole.OpenOdbcDatabase(config);
// Generate database definition. // Generate database definition.
@@ -31,7 +31,7 @@ namespace Sarand.Net.Standard.Dbas.ItemOptions
// Open database. // Open database.
// NB Must be ODBC connection - especially to retrieve index columns. // NB Must be ODBC connection - especially to retrieve index columns.
SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig(); SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig();
config.connectionString = connectionString; config.ConnectionString = connectionString;
SacsDatabase database = SacsDatabaseConsole.OpenOdbcDatabase(config); SacsDatabase database = SacsDatabaseConsole.OpenOdbcDatabase(config);
// Generate database definition. // Generate database definition.
@@ -31,7 +31,7 @@ namespace Sarand.Net.Standard.Dbas.ReferenceData
// Open database. // Open database.
// NB Must be ODBC connection - especially to retrieve index columns. // NB Must be ODBC connection - especially to retrieve index columns.
SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig(); SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig();
config.connectionString = connectionString; config.ConnectionString = connectionString;
SacsDatabase database = SacsDatabaseConsole.OpenOdbcDatabase(config); SacsDatabase database = SacsDatabaseConsole.OpenOdbcDatabase(config);
// Generate database definition. // Generate database definition.
@@ -73,6 +73,28 @@ namespace Sarand.Net.Standard.Dbas.Security
} }
//------------------------------------------------------------------- //-------------------------------------------------------------------
// Password utilities.
//-------------------------------------------------------------------
/// <summary>Store the password against against the user.</summary>
public override bool SetPassword(SaSecurityItem securityItemUser, String password)
{
return securityMgr.SetPassword(securityItemUser, password);
}
/// <summary>Does the password match that currently defined against the user.</summary>
public override bool PasswordMatches(SaSecurityItem securityItemUser, String password)
{
return securityMgr.PasswordMatches(securityItemUser, password);
}
/// <summary>Is the password currently defined against the user blank.</summary>
public override bool PasswordIsBlank(SaSecurityItem securityItemUser)
{
return securityMgr.PasswordIsBlank(securityItemUser);
}
/// <summary>Change Password form - desktop only.</summary>
public override bool ShowChangePasswordForm(DbUpdateInfo dbUpdateInfo) public override bool ShowChangePasswordForm(DbUpdateInfo dbUpdateInfo)
{ {
ClearErrorState(); ClearErrorState();
@@ -112,16 +134,16 @@ namespace Sarand.Net.Standard.Dbas.Security
if (!HasErrorState()) PopulateSuperGroup(dbUpdateInfo); if (!HasErrorState()) PopulateSuperGroup(dbUpdateInfo);
// Create the 'admin' user. // Create the 'admin' user.
if (!HasErrorState()) PopulateAdminUser(dbUpdateInfo); if (!HasErrorState()) PopulateAdminUser(dbUpdateInfo);
return !HasErrorState(); return OK;
} }
private bool PopulateSuperGroup(DbUpdateInfo dbUpdateInfo) private bool PopulateSuperGroup(DbUpdateInfo dbUpdateInfo)
{ {
ClearErrorState(); ClearErrorState();
SaSecurityItem superGroup = securityMgr.GetSecurityItemByTypeCode(SaSecurity.ItemType.Group, GroupSuperCode); SaSecurityItem superGroup = securityMgr.GetSecurityItemByTypeCode(SaSecurity.ItemType.Group, GroupCode_Super);
if (superGroup == null) if (superGroup == null)
{ {
superGroup = securityMgr.CreateSecurityItem(SaSecurity.ItemType.Group); superGroup = securityMgr.CreateSecurityItem(SaSecurity.ItemType.Group);
superGroup.Code = GroupSuperCode; superGroup.Code = GroupCode_Super;
superGroup.Description = "Superuser"; superGroup.Description = "Superuser";
if (!securityMgr.InsertSecurityItem(superGroup, dbUpdateInfo)) if (!securityMgr.InsertSecurityItem(superGroup, dbUpdateInfo))
return SetErrorState(securityMgr.ErrorStateMessage); return SetErrorState(securityMgr.ErrorStateMessage);
@@ -143,29 +165,29 @@ namespace Sarand.Net.Standard.Dbas.Security
} }
} }
} }
return !HasErrorState(); return OK;
} }
private bool PopulateAdminUser(DbUpdateInfo dbUpdateInfo) private bool PopulateAdminUser(DbUpdateInfo dbUpdateInfo)
{ {
ClearErrorState(); ClearErrorState();
SaSecurityItem user = null; SaSecurityItem user = null;
if (!securityMgr.SecurityItemExistsByTypeCode(SaSecurity.ItemType.User, AdminUserCode)) if (!securityMgr.SecurityItemExistsByTypeCode(SaSecurity.ItemType.User, UserCode_Admin))
{ {
// Create the admin user. // Create the admin user.
user = securityMgr.CreateSecurityItem(SaSecurity.ItemType.User); user = securityMgr.CreateSecurityItem(SaSecurity.ItemType.User);
user.Code = AdminUserCode; user.Code = UserCode_Admin;
user.Description = "<Admin>"; user.Description = "<Admin>";
user.Location = "<System>"; user.Location = "<System>";
user.PasswordEncrypted = SaSecurityItem.EncryptPassword(AdminUserCode); securityMgr.SetPassword(user, UserCode_Admin);
if (!securityMgr.InsertSecurityItem(user, dbUpdateInfo)) if (!securityMgr.InsertSecurityItem(user, dbUpdateInfo))
return SetErrorState(securityMgr.ErrorStateMessage); return SetErrorState(securityMgr.ErrorStateMessage);
} }
// Retrieve the user (even if just created). // Retrieve the user (even if just created).
user = securityMgr.GetSecurityItemByTypeCode(SaSecurity.ItemType.User, AdminUserCode); user = securityMgr.GetSecurityItemByTypeCode(SaSecurity.ItemType.User, UserCode_Admin);
if (user != null) if (user != null)
{ {
// Give this user superuser rights. // Give this user superuser rights.
SaSecurityItem superGroup = securityMgr.GetSecurityItemByTypeCode(SaSecurity.ItemType.Group, GroupSuperCode); SaSecurityItem superGroup = securityMgr.GetSecurityItemByTypeCode(SaSecurity.ItemType.Group, GroupCode_Super);
if (superGroup != null) if (superGroup != null)
{ {
if (!securityMgr.SecurityItemHasLinksWith(user.Id, superGroup.Id)) if (!securityMgr.SecurityItemHasLinksWith(user.Id, superGroup.Id))
@@ -176,7 +198,7 @@ namespace Sarand.Net.Standard.Dbas.Security
} }
} }
} }
return !HasErrorState(); return OK;
} }
//------------------------------------------------------------------- //-------------------------------------------------------------------
@@ -234,7 +256,7 @@ namespace Sarand.Net.Standard.Dbas.Security
} }
OnLogin(secItem != null ? secItem.Id : null); OnLogin(secItem != null ? secItem.Id : null);
return !HasErrorState(); return OK;
} }
//------------------------------------------------------------------- //-------------------------------------------------------------------
@@ -534,19 +556,35 @@ namespace Sarand.Net.Standard.Dbas.Security
if (!securityMgr.InsertSecurityItem(secItem, dbUpdateInfo)) if (!securityMgr.InsertSecurityItem(secItem, dbUpdateInfo))
return SetErrorState(securityMgr.ErrorStateMessage); return SetErrorState(securityMgr.ErrorStateMessage);
return !HasErrorState(); return OK;
} }
public override bool AssignUserToGroup(string userCode, string groupCode, DbUpdateInfo dbUpdateInfo) public override bool AssignUserToGroup(string userCode, string groupCode, DbUpdateInfo dbUpdateInfo)
{ {
ClearErrorState(); ClearErrorState();
throw new NotImplementedException(); // Validate environment.
if (securityMgr == null) return SetErrorState("security manager is not available");
if (!securityMgr.DatabaseIsValid) return SetErrorState("database has an invalid security profile");
// User security item.
SaSecurityItem securityItemUser = securityMgr.GetSecurityItemByTypeCode(SaSecurity.ItemType.User, userCode);
if (securityItemUser == null) return SetErrorState(("user does not exist"));
// Group security item.
SysId securityGroupId = GetIdForItemCode(SaSecurity.ItemType.Group, groupCode);
if (!SysId.IsValid(securityGroupId)) return SetErrorState(("invalid group code"));
if (!ItemsAreLinked(securityItemUser.Id, securityGroupId))
{
SaSecurityLink securityLinkUserGroup = securityMgr.CreateSecurityLink(securityItemUser.Id, securityGroupId, new SysId());
if (securityLinkUserGroup == null) return SetErrorState($"failed to create user-group ({securityMgr.ErrorStateMessage})");
if (!securityMgr.InsertSecurityLink(securityLinkUserGroup, dbUpdateInfo))
return SetErrorState($"failed to add user to group ({securityMgr.ErrorStateMessage})");
}
return OK;
} }
public override bool UpdateSecurityItem(SaSecurityItem securityItem, DbUpdateInfo dbUpdateInfo) public override bool UpdateSecurityItem(SaSecurityItem securityItem, DbUpdateInfo dbUpdateInfo)
{ {
ClearErrorState(); ClearErrorState();
if (!securityMgr.UpdateSecurityItem(securityItem, dbUpdateInfo)) if (!securityMgr.UpdateSecurityItem(securityItem, dbUpdateInfo))
return SetErrorState(securityMgr.ErrorStateMessage); return SetErrorState(securityMgr.ErrorStateMessage);
return !HasErrorState(); return OK;
} }
//------------------------------------------------------------------- //-------------------------------------------------------------------
@@ -31,7 +31,7 @@ namespace Sarand.Net.Standard.Dbas.Security
// Open database. // Open database.
// NB Must be ODBC connection - especially to retrieve index columns. // NB Must be ODBC connection - especially to retrieve index columns.
SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig(); SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig();
config.connectionString = connectionString; config.ConnectionString = connectionString;
SacsDatabase database = SacsDatabaseConsole.OpenOdbcDatabase(config); SacsDatabase database = SacsDatabaseConsole.OpenOdbcDatabase(config);
// Generate database definition. // Generate database definition.
@@ -278,7 +278,9 @@ namespace Sarand.Net.Standard.Dbas.Security
dbSecurityItem._Code = securityItem.Code; dbSecurityItem._Code = securityItem.Code;
dbSecurityItem._Description = securityItem.Description; dbSecurityItem._Description = securityItem.Description;
dbSecurityItem._Location = securityItem.Location; dbSecurityItem._Location = securityItem.Location;
#pragma warning disable CS0618 // Deprecated property
dbSecurityItem._EncPword = securityItem.PasswordEncrypted; dbSecurityItem._EncPword = securityItem.PasswordEncrypted;
#pragma warning restore CS0618 // Deprecated property
dbSecurityItem._PwordSalt = securityItem.PasswordSalt; dbSecurityItem._PwordSalt = securityItem.PasswordSalt;
dbSecurityItem._PwordHash = securityItem.PasswordHash; dbSecurityItem._PwordHash = securityItem.PasswordHash;
dbSecurityItem._LastLogin = securityItem.LastLogin; dbSecurityItem._LastLogin = securityItem.LastLogin;
@@ -304,7 +306,9 @@ namespace Sarand.Net.Standard.Dbas.Security
securityItem.EmailAddress = dbSecurityItem._EmailAddress; securityItem.EmailAddress = dbSecurityItem._EmailAddress;
securityItem.Description = dbSecurityItem._Description; securityItem.Description = dbSecurityItem._Description;
securityItem.Location = dbSecurityItem._Location; securityItem.Location = dbSecurityItem._Location;
#pragma warning disable CS0618 // Deprecated property
securityItem.PasswordEncrypted = dbSecurityItem._EncPword; securityItem.PasswordEncrypted = dbSecurityItem._EncPword;
#pragma warning restore CS0618 // Deprecated property
securityItem.PasswordSalt = dbSecurityItem._PwordSalt; securityItem.PasswordSalt = dbSecurityItem._PwordSalt;
securityItem.PasswordHash = dbSecurityItem._PwordHash; securityItem.PasswordHash = dbSecurityItem._PwordHash;
securityItem.LastLogin = dbSecurityItem._LastLogin; securityItem.LastLogin = dbSecurityItem._LastLogin;
@@ -1,6 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Dbas; using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Security; using Sarand.Net.Standard.Base.Security;
using Sarand.Net.Standard.Dbas.Data; using Sarand.Net.Standard.Dbas.Data;
@@ -31,8 +33,7 @@ namespace Sarand.Net.Standard.Dbas.Security
public override bool DatabaseIsValid { get { return (Database != null && Database.GetTableNames().Contains(TableName_SecItem) && Database.GetTableNames().Contains(TableName_SecLink)); } } public override bool DatabaseIsValid { get { return (Database != null && Database.GetTableNames().Contains(TableName_SecItem) && Database.GetTableNames().Contains(TableName_SecLink)); } }
public override bool CanUpdateDatabase { get { return DatabaseIsValid; } } public override bool CanUpdateDatabase { get { return DatabaseIsValid; } }
//------------------------------------------------------------------- // Database utilities.
// Utilities.
public override bool ColumnExists(SaSecurityDbDfn.ColumnIdx columnIdx) public override bool ColumnExists(SaSecurityDbDfn.ColumnIdx columnIdx)
{ {
String tableName = SaSecurityDbDfn.GetTableName(SaSecurityDbDfn.GetTableForColumn(columnIdx), false); String tableName = SaSecurityDbDfn.GetTableName(SaSecurityDbDfn.GetTableForColumn(columnIdx), false);
@@ -40,6 +41,101 @@ namespace Sarand.Net.Standard.Dbas.Security
return Database.ColumnExists(tableName, columnName); return Database.ColumnExists(tableName, columnName);
} }
//-------------------------------------------------------------------
// Password utilities.
public override bool PasswordIsBlank(SaSecurityItem securityItemUser)
{
if (securityItemUser == null) return false;
SaSecurity.PasswordMethod method = securityItemUser.GetPasswordMethod();
// MsSql method.
if (method == SaSecurity.PasswordMethod.MsSql)
{
//...
}
// Legacy.
#pragma warning disable CS0618 // Deprecated property
return EncryptPassword_Legacy("").Equals(securityItemUser.PasswordEncrypted);
#pragma warning restore CS0618 // Deprecated property
}
public override bool PasswordMatches(SaSecurityItem securityItemUser, String password)
{
if (securityItemUser == null) return false;
SaSecurity.PasswordMethod method = securityItemUser.GetPasswordMethod();
// MsSql method.
if (method == SaSecurity.PasswordMethod.MsSql)
{
//...
}
// Legacy.
#pragma warning disable CS0618 // Deprecated property
return (EncryptPassword_Legacy(password).Equals(securityItemUser.PasswordEncrypted));
#pragma warning restore CS0618 // Deprecated property
}
/// <summary>Store the password against against the user.</summary>
public override bool SetPassword(SaSecurityItem securityItemUser, String password)
{
return false;
}
internal static String EncryptPassword_Legacy(String password)
{
// We know this is not very water-tight, but it at least
// hides the contents of the password from prying eyes.
String toBeEncrypted = password;
// Password length goes in as first character:
toBeEncrypted = ByteToString((byte)(128 - password.Length)) + toBeEncrypted;
toBeEncrypted = string.Concat(toBeEncrypted.Reverse());
// Pad out to fixed length (minimum 16, multiples of 8):
int fixedLength = 16;
if (password.Length / 8 > 2) fixedLength = 8 * (int)(password.Length / 8);
while (toBeEncrypted.Length < fixedLength)
{
char charToAdd = (char)(password.Length > 0 ? password[(toBeEncrypted.Length % password.Length)] : 'A' + (toBeEncrypted.Length * 13) % 23);
if (toBeEncrypted.Length % 2 != 0)
toBeEncrypted += charToAdd;
else toBeEncrypted = charToAdd + toBeEncrypted;
}
// Now perform a simple algorythm on each character to hide its true value:
byte[] encryptedChars = new byte[toBeEncrypted.Length];
for (int i = 0; i < toBeEncrypted.Length; i++)
{
int iData = toBeEncrypted[i];
iData += 17 + i;
if (iData > 255) iData -= (255 + 32);
encryptedChars[i] = (byte)iData;
}
String result = Encoding_Ansi1252.GetString(encryptedChars);
#if DEBUG
// Password: mgbgt
// To encrypt: tbmggtgbgm{btggm
// Encrypted: 133, 116, 128, 123, 124, 138, 126, 122, 128, 135, 150, 126, 145, 133, 134, 141
//Logger.Info(fn + "\n\tPassword: " + password + "\n\tTo encrypt: " + toBeEncrypted + "\n\tEncrypted: " + encryptedChars.Select(i => i.ToString()).ToList().Concat(", "));
//Logger.Debug(password + ": " + result);
#endif
return result;
}
private static byte[] PasswordStringToBytes(String value)
{
byte[] values = new byte[value.Length];
for (int idx = 0; idx < value.Length; idx++)
{
char c = value[idx];
UInt32 ui = Convert.ToUInt32(c);
SaLogger.Info(idx + ". " + ui.ToString("x8"));
values[idx] = (byte)(c);
}
return values;
}
private static readonly Encoding Encoding_Ansi1252 = Encoding.GetEncoding(1252);
private static String ByteToString(byte value)
{
return Encoding_Ansi1252.GetString(new[] { value });
}
//------------------------------------------------------------------- //-------------------------------------------------------------------
} }
//======================================================================= //=======================================================================
@@ -31,7 +31,7 @@ namespace Sarand.Net.Standard.Dbas.UpdateLog
// Open database. // Open database.
// NB Must be ODBC connection - especially to retrieve index columns. // NB Must be ODBC connection - especially to retrieve index columns.
SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig(); SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig();
config.connectionString = connectionString; config.ConnectionString = connectionString;
SacsDatabase database = SacsDatabaseConsole.OpenOdbcDatabase(config); SacsDatabase database = SacsDatabaseConsole.OpenOdbcDatabase(config);
// Generate database definition. // Generate database definition.
+1 -1
View File
@@ -10,7 +10,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DocumentFormat.OpenXml" Version="3.1.1" /> <PackageReference Include="DocumentFormat.OpenXml" Version="3.2.0" />
<PackageReference Include="NPOI" Version="2.7.2" /> <PackageReference Include="NPOI" Version="2.7.2" />
</ItemGroup> </ItemGroup>