diff --git a/Constants/EnvironmentConstants.cs b/Constants/EnvironmentConstants.cs index ebdf2669..88ac1042 100644 --- a/Constants/EnvironmentConstants.cs +++ b/Constants/EnvironmentConstants.cs @@ -116,5 +116,7 @@ public static class EnvironmentConstants public const string MOVEON_AZURE_PNS_CON = "MOVEON_AZURE_PNS_CON"; public const string MOVEON_AZURE_PNS_HUBNAME = "MOVEON_AZURE_PNS_HUBNAME"; public const string EB_API_SECRET = "EB_API_SECRET"; + + public const string EB_AES_ENC_KEY = "EB_AES_ENC_KEY"; } } diff --git a/Constants/ObjectConstants.cs b/Constants/ObjectConstants.cs index 8a7c4601..e2f2bf4b 100644 --- a/Constants/ObjectConstants.cs +++ b/Constants/ObjectConstants.cs @@ -53,5 +53,8 @@ public class ObjectConstants public const string MATERIALIZED_VIEW = "MaterializedView"; public const string POS_FORM = "PosForm"; public const string PRINT_LAYOUT = "PrintLayout"; + + public const int WEB_FORM_PUBLIC_STATUS_CODE = 1; + public const int WEB_FORM_PRIVATE_STATUS_CODE = 2; } } diff --git a/Constants/RoutingConstants.cs b/Constants/RoutingConstants.cs index 4bea5207..a1ea3476 100644 --- a/Constants/RoutingConstants.cs +++ b/Constants/RoutingConstants.cs @@ -74,5 +74,23 @@ public static class RoutingConstants public const string RESET_PASSWORD_PAGE = "/ResetPasswordIn"; public const string SUPPORT_MAIL_ID = "mailto:support@expressbase.com"; + + public const string WEBAUTHID = "web_authid"; + + public const string INTERNAL_SOLUTION_ID = "InternalSolutionId"; + public const string EXTERNAL_SOLUTION_ID = "ExternalSolutionId"; + public const string CONTEXT_BEARER_TOKEN = "ContextBearerToken"; + public const string CONTEXT_REFRESH_TOKEN = "ContextRefreshToken"; + public const string AUTH_ID = "AuthId"; + public const string USER = "User"; + public const string CONSOLE = "Console"; + public const string DEV_CONSOLE_HOST = "DevConsoleHost"; + public const string USER_CONSOLE_HOST = "UserConsoleHost"; + public const string BASE_HOST = "BaseHost"; + public const string HOST = "Host"; + public const string SUB_DOMAIN = "SubDomain"; + public const string SCHEME = "Scheme"; + public const string DOMAIN = "Domain"; + } } diff --git a/Constants/TokenConstants.cs b/Constants/TokenConstants.cs index 1f5892c1..081596e9 100644 --- a/Constants/TokenConstants.cs +++ b/Constants/TokenConstants.cs @@ -31,5 +31,11 @@ public static class TokenConstants public const string SUB_FORMAT = "{0}:{1}:{2}"; public const string ANONYM_EMAIL = "anonymous@anonym.com"; public const string SSE_SUBSCRIP_ID = "SSE_SubscriptionId"; + + public const string ANONYMOUS_USER_V2_SESSION_TAG = "anonymous-authentication-v2"; + public const string PUBLIC_FORM_V2_ANONYMOUS_USER_EMAIL = "anonymous@anonym.com"; + public const int PUBLIC_FORM_V2_ANONYMOUS_USER_APP_ID = 0; + + public const string SESSION_TAG = "session_tag"; } } diff --git a/Helpers/AesEncryptionHelper.cs b/Helpers/AesEncryptionHelper.cs new file mode 100644 index 00000000..51e7b5fc --- /dev/null +++ b/Helpers/AesEncryptionHelper.cs @@ -0,0 +1,208 @@ +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using System.Text; +using Newtonsoft.Json; + +namespace ExpressBase.Common.Helpers +{ + + public static class AesEncryptionHelper + { + private const int IvLength = 16; // AES block size + private const int RequiredKeyLength = 32; // 256-bit key + + public static string GenerateKey() + { + using (var rng = new RNGCryptoServiceProvider()) + { + var key = new byte[RequiredKeyLength]; + rng.GetBytes(key); + return Convert.ToBase64String(key); + } + } + + + public static string EncryptString(string base64CurrentKey, string plainText) + { + if (plainText == null) throw new ArgumentNullException(nameof(plainText)); + var key = DecodeAndValidateKey(base64CurrentKey); + + var plainBytes = Encoding.UTF8.GetBytes(plainText); + + var iv = new byte[IvLength]; + using (var rng = new RNGCryptoServiceProvider()) + { + rng.GetBytes(iv); + } + + byte[] cipherBytes; + using (var aes = Aes.Create()) + { + aes.KeySize = 256; + aes.BlockSize = 128; + aes.Mode = CipherMode.CBC; + aes.Padding = PaddingMode.PKCS7; + aes.Key = key; + aes.IV = iv; + + using (var encryptor = aes.CreateEncryptor()) + { + cipherBytes = encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length); + } + } + + var mac = ComputeHmac(key, iv, cipherBytes); + + var payload = new Payload + { + iv = Convert.ToBase64String(iv), + ct = Convert.ToBase64String(cipherBytes), + mac = Convert.ToBase64String(mac) + }; + + var json = JsonConvert.SerializeObject(payload); + return Convert.ToBase64String(Encoding.UTF8.GetBytes(json)); + } + + + public static string DecryptString(string base64CurrentKey, IEnumerable base64PreviousKeys, string base64Payload) + { + if (string.IsNullOrWhiteSpace(base64Payload)) throw new ArgumentNullException(nameof(base64Payload)); + var currentKey = DecodeAndValidateKey(base64CurrentKey); + + var previousKeys = new List(); + if (base64PreviousKeys != null) + { + foreach (var k in base64PreviousKeys) + { + if (string.IsNullOrWhiteSpace(k)) continue; + previousKeys.Add(DecodeAndValidateKey(k)); + } + } + + string json; + try + { + var bytes = Convert.FromBase64String(base64Payload); + json = Encoding.UTF8.GetString(bytes); + } + catch (FormatException) + { + throw new CryptographicException("Payload is not valid base64."); + } + + var payload = JsonConvert.DeserializeObject(json); + if (payload == null || string.IsNullOrEmpty(payload.iv) || string.IsNullOrEmpty(payload.ct) || string.IsNullOrEmpty(payload.mac)) + throw new CryptographicException("Invalid payload structure."); + + var iv = Convert.FromBase64String(payload.iv); + var ct = Convert.FromBase64String(payload.ct); + var mac = Convert.FromBase64String(payload.mac); + + if (TryValidateAndDecrypt(currentKey, iv, ct, mac, out var plain)) + return Encoding.UTF8.GetString(plain); + + foreach (var pk in previousKeys) + { + if (TryValidateAndDecrypt(pk, iv, ct, mac, out plain)) + return Encoding.UTF8.GetString(plain); + } + + throw new CryptographicException("Unable to decrypt payload with any known key (invalid MAC or corrupted data)."); + } + + + public static string DecryptString(string base64CurrentKey, string base64Payload) + { + return DecryptString(base64CurrentKey, null, base64Payload); + } + + // ----------------- Internal helpers ----------------- + + private static bool TryValidateAndDecrypt(byte[] key, byte[] iv, byte[] ciphertext, byte[] mac, out byte[] plain) + { + plain = null; + try + { + var expectedMac = ComputeHmac(key, iv, ciphertext); + if (!FixedTimeEquals(expectedMac, mac)) + return false; + + using (var aes = Aes.Create()) + { + aes.KeySize = 256; + aes.BlockSize = 128; + aes.Mode = CipherMode.CBC; + aes.Padding = PaddingMode.PKCS7; + aes.Key = key; + aes.IV = iv; + + using (var decryptor = aes.CreateDecryptor()) + { + plain = decryptor.TransformFinalBlock(ciphertext, 0, ciphertext.Length); + return true; + } + } + } + catch + { + return false; + } + } + + private static byte[] ComputeHmac(byte[] key, byte[] iv, byte[] ciphertext) + { + byte[] hmacKey; + using (var h = new HMACSHA256(key)) + { + hmacKey = h.ComputeHash(Encoding.UTF8.GetBytes("LaravelCompatibleHMACKey")); + } + + using (var hmac = new HMACSHA256(hmacKey)) + { + var input = new byte[iv.Length + ciphertext.Length]; + Buffer.BlockCopy(iv, 0, input, 0, iv.Length); + Buffer.BlockCopy(ciphertext, 0, input, iv.Length, ciphertext.Length); + return hmac.ComputeHash(input); + } + } + + private static bool FixedTimeEquals(byte[] a, byte[] b) + { + if (a == null || b == null || a.Length != b.Length) return false; + var result = 0; + for (var i = 0; i < a.Length; i++) + result |= a[i] ^ b[i]; + return result == 0; + } + + private static byte[] DecodeAndValidateKey(string base64Key) + { + if (string.IsNullOrWhiteSpace(base64Key)) + throw new ArgumentException("Encryption key must be provided (base64)."); + + byte[] key; + try + { + key = Convert.FromBase64String(base64Key); + } + catch (FormatException ex) + { + throw new ArgumentException("Encryption key must be a valid base64 string.", ex); + } + + if (key.Length != RequiredKeyLength) + throw new ArgumentException($"Encryption key must be {RequiredKeyLength} bytes (base64-encoded). Use GenerateKey() to produce one."); + + return key; + } + + private class Payload + { + [JsonProperty("iv")] public string iv { get; set; } + [JsonProperty("ct")] public string ct { get; set; } + [JsonProperty("mac")] public string mac { get; set; } + } + } +} diff --git a/Helpers/DebugHelper.cs b/Helpers/DebugHelper.cs new file mode 100644 index 00000000..fbbfa081 --- /dev/null +++ b/Helpers/DebugHelper.cs @@ -0,0 +1,216 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using Newtonsoft.Json; + +/// +/// DebugHelper provides easy logging for objects, exceptions, and raw messages. +/// Automatically adds caller info (file, line, method). +/// You can optionally pass a "label" to distinguish logs (it will appear in the banner). +/// +public static class DebugHelper +{ + /// + /// Prints all public instance properties of an object (via reflection), + /// or the whole object as JSON if printAsJson = true. + /// + public static string PrintObject( + object obj, + bool toError = false, + bool printAsJson = false, + string label = null, + [CallerMemberName] string memberName = "", + [CallerFilePath] string filePath = "", + [CallerLineNumber] int lineNumber = 0) + { + string header = FormatHeader(memberName, filePath, lineNumber); + + if (obj == null) + { + string nil = $"{header} null"; + WriteOutput(nil, toError, label); + return nil; + } + + string output; + + if (printAsJson) + { + try + { + string json = JsonConvert.SerializeObject(obj, Formatting.Indented, + new JsonSerializerSettings + { + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + NullValueHandling = NullValueHandling.Ignore + }); + output = $"{header}\n{json}"; + } + catch (Exception ex) + { + output = $"{header}\n"; + } + } + else + { + var sb = new System.Text.StringBuilder(); + sb.AppendLine(header); + var type = obj.GetType(); + sb.AppendLine($"Type: {type.FullName}"); + + var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(p => p.GetIndexParameters().Length == 0) + .OrderBy(p => p.Name); + + foreach (var p in props) + { + object val; + try + { + val = p.GetValue(obj); + } + catch (Exception ex) + { + val = $""; + } + sb.AppendLine($" {p.Name} = {FormatValue(val)}"); + } + + output = sb.ToString(); + } + + WriteOutput(output, toError, label); + return output; + } + + /// + /// Pretty-prints exception info (with inner exceptions). + /// + public static string PrintException( + Exception ex, + bool toError = true, + string label = null, + [CallerMemberName] string memberName = "", + [CallerFilePath] string filePath = "", + [CallerLineNumber] int lineNumber = 0) + { + string header = FormatHeader(memberName, filePath, lineNumber); + if (ex == null) + { + string nil = $"{header} Exception is null"; + WriteOutput(nil, toError, label); + return nil; + } + + var sb = new System.Text.StringBuilder(); + sb.AppendLine(header); + AppendException(sb, ex, 0); + + string output = sb.ToString(); + WriteOutput(output, toError, label); + return output; + } + + /// + /// Shorthand log with caller info. + /// + public static void Log( + object message, + bool toError = false, + string label = null, + [CallerMemberName] string memberName = "", + [CallerFilePath] string filePath = "", + [CallerLineNumber] int lineNumber = 0) + { + string header = FormatHeader(memberName, filePath, lineNumber); + string text = $"{header} {message ?? "null"}"; + WriteOutput(text, toError, label); + } + + /// + /// Shorthand log without caller info (raw message only). + /// + public static void LogRaw( + object message, + bool toError = false, + string label = null) + { + WriteOutput(message?.ToString() ?? "null", toError, label); + } + + // ===== Helpers ===== + + private static string FormatValue(object val) + { + if (val == null) return "null"; + if (val is string) return $"\"{val}\""; + if (val.GetType().IsPrimitive) return val.ToString(); + + try + { + return JsonConvert.SerializeObject(val, Formatting.None, + new JsonSerializerSettings + { + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + NullValueHandling = NullValueHandling.Ignore + }); + } + catch + { + return val.ToString(); + } + } + + private static void AppendException(System.Text.StringBuilder sb, Exception ex, int depth) + { + string indent = new string(' ', depth * 2); + sb.AppendLine($"{indent}=== Exception ==="); + sb.AppendLine($"{indent}Type: {ex.GetType().FullName}"); + sb.AppendLine($"{indent}Message: {ex.Message}"); + sb.AppendLine($"{indent}StackTrace: {ex.StackTrace ?? ""}"); + + if (ex.InnerException != null) + { + sb.AppendLine($"{indent}--- Inner Exception ---"); + AppendException(sb, ex.InnerException, depth + 1); + } + } + + private static string FormatHeader(string memberName, string filePath, int lineNumber) + { + string time = DateTime.UtcNow.ToString("o"); + string file = string.IsNullOrEmpty(filePath) ? "" : System.IO.Path.GetFileName(filePath); + return $"[{time}] [{memberName} at {file}:{lineNumber}]"; + } + + /// + /// Writes output to console with Begin/End banners. + /// If "label" is provided, it appears in the banners. + /// + private static void WriteOutput(string text, bool toError, string label = null) + { + var sb = new System.Text.StringBuilder(); + + // Build begin/end lines dynamically + string begin = label == null + ? "----------BEGIN DEBUG--------------" + : $"----------BEGIN DEBUG ({label})--------------"; + + string end = label == null + ? "----------END DEBUG--------------" + : $"----------END DEBUG ({label})--------------"; + + sb.AppendLine(begin); + sb.AppendLine(); + sb.AppendLine(text.TrimEnd()); + sb.AppendLine(); + sb.AppendLine(end); + sb.AppendLine(); // spacing between multiple logs + + string wrapped = sb.ToString(); + + if (toError) Console.Error.WriteLine(wrapped); + else Console.Out.WriteLine(wrapped); + } +} diff --git a/Helpers/QueryStringEncDecHelper.cs b/Helpers/QueryStringEncDecHelper.cs new file mode 100644 index 00000000..6e523111 --- /dev/null +++ b/Helpers/QueryStringEncDecHelper.cs @@ -0,0 +1,47 @@ +using Newtonsoft.Json; +using System; + +namespace ExpressBase.Common.Helpers +{ + + public static class QueryStringEncDecHelper + { + + public static string EncryptString( + T obj, + string base64CurrentKey, + JsonSerializerSettings jsonSettings = null) + { + if (obj == null) throw new ArgumentNullException(nameof(obj)); + if (string.IsNullOrWhiteSpace(base64CurrentKey)) throw new ArgumentNullException(nameof(base64CurrentKey)); + + var json = JsonConvert.SerializeObject(obj, jsonSettings ?? DefaultJsonSettings()); + var cipher = AesEncryptionHelper.EncryptString(base64CurrentKey, json); + return Uri.EscapeDataString(cipher); + } + + public static T DecryptEncryptedString( + string encryptedOrEscaped, + string base64CurrentKey, + JsonSerializerSettings jsonSettings = null) + { + if (string.IsNullOrWhiteSpace(encryptedOrEscaped)) throw new ArgumentNullException(nameof(encryptedOrEscaped)); + if (string.IsNullOrWhiteSpace(base64CurrentKey)) throw new ArgumentNullException(nameof(base64CurrentKey)); + + var unescaped = Uri.UnescapeDataString(encryptedOrEscaped); + try + { + var json = AesEncryptionHelper.DecryptString(base64CurrentKey, unescaped); + return JsonConvert.DeserializeObject(json, jsonSettings ?? DefaultJsonSettings()); + } + catch + { + var json = AesEncryptionHelper.DecryptString(base64CurrentKey, encryptedOrEscaped); + return JsonConvert.DeserializeObject(json, jsonSettings ?? DefaultJsonSettings()); + } + } + + private static JsonSerializerSettings DefaultJsonSettings() => + new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; + } +} diff --git a/Helpers/RedisCacheHelper.cs b/Helpers/RedisCacheHelper.cs new file mode 100644 index 00000000..9b4be0b2 --- /dev/null +++ b/Helpers/RedisCacheHelper.cs @@ -0,0 +1,119 @@ +using ServiceStack.Redis; +using System; + +namespace ExpressBase.Common.Helpers +{ + public static class RedisCacheHelper + { + + private static void CheckManager(PooledRedisClientManager manager) + { + if (manager == null) throw new ArgumentNullException(nameof(manager)); + } + + + public static void Set(PooledRedisClientManager manager, string key, T value, TimeSpan? expiry = null) + { + ValidateKey(key); + CheckManager(manager); + + using (var cache = manager.GetCacheClient()) + { + if (expiry.HasValue) cache.Set(key, value, expiry.Value); + else cache.Set(key, value); + } + } + + public static T Get(PooledRedisClientManager manager, string key) + { + ValidateKey(key); + CheckManager(manager); + + using (var cache = manager.GetReadOnlyCacheClient()) + { + return cache.Get(key); + } + } + + public static T GetOrSet(PooledRedisClientManager manager, string key, Func valueFactory, TimeSpan? expiry = null) + { + if (valueFactory == null) throw new ArgumentNullException(nameof(valueFactory)); + ValidateKey(key); + CheckManager(manager); + + using (var client = manager.GetClient()) + { + if (client.ContainsKey(key)) + { + using (var ro = manager.GetReadOnlyCacheClient()) + return ro.Get(key); + } + } + + + var newValue = valueFactory(); + + using (var cache = manager.GetCacheClient()) + { + var added = expiry.HasValue + ? cache.Add(key, newValue, expiry.Value) + : cache.Add(key, newValue); + + return added ? newValue : cache.Get(key); + } + } + + public static bool Remove(PooledRedisClientManager manager, string key) + { + ValidateKey(key); + CheckManager(manager); + + using (var cache = manager.GetCacheClient()) + { + return cache.Remove(key); + } + } + + public static bool Exists(PooledRedisClientManager manager, string key) + { + ValidateKey(key); + CheckManager(manager); + + using (var client = manager.GetClient()) + { + return client.ContainsKey(key); + } + } + + + public static string GetRaw(PooledRedisClientManager manager, string key) + { + ValidateKey(key); + CheckManager(manager); + + using (var client = manager.GetClient()) + { + return client.GetValue(key); + } + } + + public static void SetRaw(PooledRedisClientManager manager, string key, string rawValue, TimeSpan? expiry = null) + { + ValidateKey(key); + CheckManager(manager); + + using (var client = manager.GetClient()) + { + var val = rawValue ?? string.Empty; + if (expiry.HasValue) client.SetValue(key, val, expiry.Value); + else client.SetValue(key, val); + } + } + + private static void ValidateKey(string key) + { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentException("Key must not be null or empty.", nameof(key)); + } + } +} diff --git a/Helpers/StringHelper.cs b/Helpers/StringHelper.cs new file mode 100644 index 00000000..7961b4de --- /dev/null +++ b/Helpers/StringHelper.cs @@ -0,0 +1,15 @@ +using System; + +namespace ExpressBase.Common.Helpers +{ + public static class StringHelper + { + /// + /// Returns true if the string is neither null, empty, nor whitespace. + /// + public static bool HasValue(string input) + { + return !string.IsNullOrEmpty(input) && !string.IsNullOrWhiteSpace(input); + } + } +} diff --git a/Security/User.cs b/Security/User.cs index 880658f0..5696a2dc 100644 --- a/Security/User.cs +++ b/Security/User.cs @@ -190,6 +190,123 @@ public int GeFirstLocationIdFromPermission() return -1; } + public static User Init( + int userId, + string email, + string fullName, + List roles, + List roleIds, + List permissions, + Preferences preference, + int signInLogId, + string sourceIp, + List userGroupIds, + int userType, + string phoneNumber, + bool IsForcePWReset + ) + { + return new User + { + UserId = userId, + Email = email, + FullName = fullName, + Roles = roles, + RoleIds = roleIds, + Permissions = permissions, + Preference = preference, + SignInLogId = signInLogId, + SourceIp = sourceIp, + UserGroupIds = userGroupIds, + UserType = userType, + PhoneNumber = phoneNumber, + IsForcePWReset = IsForcePWReset + }; + } + + //TODO: change the method name + public static User GetOrCreateAnonymousUserForV2PublicForm( + IDatabase df, + string emailId, + string context = "uc", // which console is set to user console + int appid = 0, + string userIp = null + ) + { + + List paramlist = new List(); + string parameters = ""; + + parameters += "in_emailid => :emailId, "; + paramlist.Add(df.GetNewParameter("emailId", EbDbTypes.String, emailId)); + + parameters += "in_user_ip => :user_ip, "; + paramlist.Add(df.GetNewParameter("user_ip", EbDbTypes.String, userIp)); + + paramlist.Add(df.GetNewParameter("appid", EbDbTypes.Int32, appid)); + paramlist.Add(df.GetNewParameter(RoutingConstants.WC, EbDbTypes.String, context)); + + + string sql = df.EB_AUTHENTICATE_ANONYMOUS.Replace( + "@params", + (df.Vendor == DatabaseVendors.PGSQL) ? parameters.Replace("=>", ":=") : parameters + ); + + var dt = df.DoQuery(sql, paramlist.ToArray()); + + if (dt.Rows.Count > 0) + { + var row = dt.Rows[0]; + + List roleIds = row["out_public_ids"] != DBNull.Value && row["out_public_ids"] is string roleIdsStr && !string.IsNullOrWhiteSpace(roleIdsStr) + ? roleIdsStr.Split(',').Select(int.Parse).ToList() + : new List(); + + List permissions = new List(); + + foreach (int roleId in roleIds) + { + permissions.Add("000" /*appid*/ + "-" + "00"/*type*/ + "-" + roleId.ToString().PadLeft(5, '0') + "-" + "00" /*operation*/ + ":-1"); + } + + + return Init( + userId: row["out_userid"] != DBNull.Value ? Convert.ToInt32(row["out_userid"]) : 0, + email: row["out_email"]?.ToString(), + fullName: row["out_fullname"]?.ToString(), + + roles: row["out_roles_a"] != DBNull.Value && row["out_roles_a"] is string rolesStr && !string.IsNullOrWhiteSpace(rolesStr) + ? rolesStr.Split(',').ToList() + : new List(), + + roleIds: roleIds, + + permissions: permissions, + + preference: row["out_preferencesjson"] != DBNull.Value && row["out_preferencesjson"] is string prefJson && !string.IsNullOrWhiteSpace(prefJson) + ? JsonConvert.DeserializeObject(prefJson) + : new Preferences { Locale = "en-IN", TimeZone = "(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi", DefaultLocation = -1 }, //TODO: add to solution settings + + signInLogId: row["out_signin_id"] != DBNull.Value ? Convert.ToInt32(row["out_signin_id"]) : 0, + + sourceIp: row["out_source_ip"]?.ToString(), + + userGroupIds: row["out_usergroup_a"] != DBNull.Value && row["out_usergroup_a"] is string groupStr && !string.IsNullOrWhiteSpace(groupStr) + ? groupStr.Split(',').Select(int.Parse).ToList() + : new List(), + + phoneNumber: row["out_phone"]?.ToString(), + + IsForcePWReset: row["out_forcepwreset"] != DBNull.Value && bool.TryParse(row["out_forcepwreset"].ToString(), out bool pwReset) && pwReset, + + userType: row["out_user_type"] != DBNull.Value ? Convert.ToInt32(row["out_user_type"]) : 0 + ); + } + + return null; + } + + public List GetLocationsByObject(string RefId) { //Sample refid - Only for reference diff --git a/ServiceStack/Auth0/CustomUserSession.cs b/ServiceStack/Auth0/CustomUserSession.cs index d4ceb307..0ec1054a 100644 --- a/ServiceStack/Auth0/CustomUserSession.cs +++ b/ServiceStack/Auth0/CustomUserSession.cs @@ -36,6 +36,9 @@ public class CustomUserSession : AuthUserSession [DataMember(Order = 7)] public string SourceIp { get; set; } + [DataMember(Order = 8)] + public string SessionTag { get; set; } + public override bool IsAuthorized(string provider) { return true; diff --git a/ServiceStack/Auth0/MyJwtAuthProvider.cs b/ServiceStack/Auth0/MyJwtAuthProvider.cs index 77bcbd0c..a404cfd5 100644 --- a/ServiceStack/Auth0/MyJwtAuthProvider.cs +++ b/ServiceStack/Auth0/MyJwtAuthProvider.cs @@ -123,6 +123,7 @@ public override JsonObject CreateJwtPayload( jwtPayload[TokenConstants.UID] = csession.Uid.ToString(); jwtPayload[RoutingConstants.WC] = csession.WhichConsole; jwtPayload[TokenConstants.IP] = csession.SourceIp; + jwtPayload[TokenConstants.SESSION_TAG] = csession.SessionTag ?? null; return jwtPayload; } diff --git a/ServiceStack/ReqNRes/FileService_Artifacts.cs b/ServiceStack/ReqNRes/FileService_Artifacts.cs index 06f8d977..6e8098f4 100644 --- a/ServiceStack/ReqNRes/FileService_Artifacts.cs +++ b/ServiceStack/ReqNRes/FileService_Artifacts.cs @@ -702,6 +702,7 @@ public class DownloadLogoExtRequest : EbServiceStackNoAuthRequest, IReturn { [DataMember(Order = 1)]