setup login for web

This commit is contained in:
dibakor 2026-08-09 18:20:12 +06:00
parent 83f92ea6c8
commit cf1a8e74a6
15 changed files with 820 additions and 432 deletions

View File

@ -1,4 +1,6 @@
using System;
using DocumentFormat.OpenXml.Office2010.ExcelAc;
using System;
using System.Collections.Generic;
namespace OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems
{
@ -34,6 +36,10 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems
public string? DesignationName { get; set; }
public EnumLoginStatus LoginStatus { get; set; }
public DateTime? NextLoginTime { get; set; }
public int NotificationCount { get; set; }
public List<string> ModuleIds { get; set; } = new List<string>();
public List<int> TeamSpaceIds { get; set; } = new List<int>();
}
public class LoginHistory

View File

@ -27,45 +27,27 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Responses.Systems
public DateTime? Expires { get; set; }
public DateTime SystemDate { get; set; }
public string LoginTime { get; set; }
public int LogId { get; set; }
public DateTime? LogoutTime { get; private set; }
public string MenuLayout { get; set; }
public string ThemeName { get; set; }
public string SchemeName { get; set; }
public bool DbOnStartup { get; set; }
public bool ViewOwnTaskOnly { get; private set; }
public int? EmployeeId { get; private set; }
public int NotificationCount { get; private set; }
public int IdleTime { get; set; }
public int PingTime { get; set; }
public int TimeoutTime { get; set; }
public bool BatchEnabled { get; private set; }
public int BmProcessId { get; private set; }
public int PrProcessId { get; private set; }
public string IdsValue { get; set; }
//public List<string> ModuleIds { get; set; } = [];
public List<string> ModuleIds { get; set; } = [];
public string RefreshToken { get;set; }
public UserPlatFormType? UserPlatFormType { get; set; }
public UserRoleTypeEnum? UserRoleType { get; set; }
public string? EmployeeNumber { get; set; }
public void Map(User source)
{
Id = source.UserId;
LoginId = source.LoginId;
UserName = source.UserName;
ThemeName = source.ThemeName;
//ModuleIds = source.ModuleIds;
ModuleIds = source.ModuleIds;
LogoutTime = source.LogoutTime;
SchemeName = source.SchemeName;
MenuLayout = source.MenuLayout;
LoginStatus = source.LoginStatus;
//DbOnStartup = source.DbOnStartup;
//TimeoutTime = source.TimeoutTime;
//BmProcessId = source.BmProcessId;
//PrProcessId = source.PrProcessId;
//BatchEnabled = source.BatchEnabled;
//ViewOwnTaskOnly = source.ViewOwnTaskOnly;
//NotificationCount = source.NotificationCount;
//AuthRequiredAtLogin = source.AuthRequiredAtLogin;
//IdsValue = Newtonsoft.Json.JsonConvert.SerializeObject(source.ModuleIds);
UserPlatFormType = source.UserPlatFormType;
UserRoleType = source.UserRoleType;
EmployeeNumber = source.EmployeeNumber;
}
}

View File

@ -267,8 +267,8 @@ public class RefreshTokenService : IRefreshTokenService
UserId = userId,
TokenHash = tokenHash,
IpAddress = ipAddress,
CreatedAt = DateTime.UtcNow,
ExpiresAt = DateTime.UtcNow.AddDays(_settings.RefreshTokenDuration)
CreatedAt = DateTime.Now,
ExpiresAt = DateTime.Now.AddMinutes(_settings.RefreshTokenDuration)
};
AddAsync(tc,refreshToken);
@ -276,7 +276,7 @@ public class RefreshTokenService : IRefreshTokenService
return new GenerateRefreshTokenResponse
{
RefreshToken = tokenHash,
ExpireTime = DateTime.UtcNow.AddDays(_settings.RefreshTokenDuration)
ExpireTime = DateTime.Now.AddMinutes(_settings.RefreshTokenDuration)
};
}

View File

@ -95,220 +95,192 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems
#endregion
//if (user.LoginStatus != EnumLoginStatus.VersionMismatch)
//{
// #region Read User data using authentication data
if (user.LoginStatus != EnumLoginStatus.VersionMismatch)
{
#region Read User data using authentication data
// string commandText;
// if (!checkPwd)
// {
// commandText = SQLParser.MakeSQL("SELECT UserId, UserName, Status, MobileNo, EmailAddress, AuthReqAtlogin, AuthMethod,"
// + " AuthKey, AppId, AccessStatus, NeverExpire, LastPasswords, LastPassChgDate, ExpireDate, ThemeName, SchemeName, MenuLayout,"
// + " IsLocked, NextLoginTime, DBOnStartup, DAMultiLogin, ViewOwnTaskOnly, EmployeeId, LoginID, EmployeeCode FROM Users WHERE LoginID=%s", request.LoginId);
// }
// else
// {
// commandText = SQLParser.MakeSQL("SELECT UserId, UserName, Status, MobileNo, EmailAddress, AuthReqAtlogin, AuthMethod,"
// + " AuthKey, AppId, AccessStatus, NeverExpire, LastPasswords, LastPassChgDate, ExpireDate, ThemeName, SchemeName, MenuLayout,"
// + " IsLocked, NextLoginTime, DBOnStartup, DAMultiLogin, ViewOwnTaskOnly, EmployeeId, LoginID, EmployeeCode FROM Users"
// + " WHERE (LoginID=%s OR MobileNo=%s OR EmailAddress=%s) AND Password=%s", request.LoginId, request.LoginId, request.LoginId, password);
// }
string commandText = SQLParser.MakeSQL("SELECT UserId,LoginId, UserName, Status,AccessStatus,NeverExpires, MobileNo, EmailAddress, "
+ " IsLocked, UserPlatformId, UserRoleType, EmployeeNumber FROM Users"
+ " WHERE (LoginID=%s OR MobileNo=%s OR EmailAddress=%s) AND Password=%s", request.LoginId, request.LoginId, request.LoginId, password);
// using (IDataReader dr = tc.ExecuteReader(commandText: commandText))
// {
// if (dr.Read())
// {
// user = new User
// {
// Id = dr.GetInt32(0),
// UserName = dr.GetString(1),
// Status = (EnumStatus)dr.GetInt16(2),
// MobileNo = dr.GetString(3),
// EmailAddress = dr.GetString(4),
// AuthRequiredAtLogin = !dr.IsDBNull(5) && dr.GetInt16(5) > 0,
// AuthMethod = (EnumAuthenticationMethod)dr.GetInt16(6),
// AuthKey = dr.IsDBNull(7) ? string.Empty : dr.GetString(7),
// AppId = dr.IsDBNull(8) ? string.Empty : dr.GetString(8),
// AccessStatus = (EnumAccessStatus)dr.GetInt16(9),
// NeverExpires = !dr.IsDBNull(10) && dr.GetInt16(10) > 0,
// LastPasswords = dr.IsDBNull(11) ? string.Empty : dr.GetString(11),
// LastPassChgDate = dr.IsDBNull(12) ? null : dr.GetDateTime(12),
// ExpireDate = dr.IsDBNull(13) ? null : dr.GetDateTime(13),
// ThemeName = dr.IsDBNull(14) ? "yellow" : dr.GetString(14),
// SchemeName = dr.IsDBNull(15) ? "dark" : dr.GetString(15),
// MenuLayout = dr.IsDBNull(16) ? "static" : dr.GetString(16),
// IsLocked = !dr.IsDBNull(17) && dr.GetInt16(17) > 0,
// NextLoginTime = dr.IsDBNull(18) ? null : dr.GetDateTime(18),
// DbOnStartup = dr.GetInt16(19) != 0,
// DisallowMultiLogin = dr.GetInt16(20) != 0,
// ViewOwnTaskOnly = dr.GetInt16(21) != 0,
// EmployeeId = dr.IsDBNull(22) ? null : dr.GetInt32(22),
// LoginId = dr.GetString(23),
// EmployeeCode = dr.IsDBNull(24) ? string.Empty : dr.GetString(24),
using (IDataReader dr = tc.ExecuteReader(commandText: commandText))
{
if (dr.Read())
{
user = new User
{
UserId = dr.GetInt32(0),
LoginId = dr.GetString(1),
UserName = dr.GetString(2),
Status = (EnumStatus)dr.GetInt32(3),
AccessStatus = (EnumAccessStatus)dr.GetInt32(4),
NeverExpires = dr.GetBoolean(5),
MobileNo = dr.IsDBNull(6) ? null : dr.GetString(6),
EmailAddress = dr.IsDBNull(7) ? null : dr.GetString(7),
IsLocked = dr.GetBoolean(8),
UserPlatFormType = dr.IsDBNull(9) ? null : (UserPlatFormType)dr.GetInt32(9),
UserRoleType = dr.IsDBNull(10) ? null : (UserRoleTypeEnum)dr.GetInt32(10),
EmployeeNumber = dr.IsDBNull(11) ? null : dr.GetString(11),
LoginStatus = EnumLoginStatus.Success
};
}
dr.Close();
}
// TeamSpaceIds = [],
// IdleTime = idleTime,
// PingTime = pingTime,
// SystemDate = sysDate,
// TimeoutTime = timeoutTime,
// PrProcessId = prProcessId,
// BmProcessId = bmProcessId,
// BatchEnabled = batchEnabled,
// LoginStatus = EnumLoginStatus.Success
// };
// }
// dr.Close();
#endregion
// user.MinReportDate = marMonths <= 0 ? new DateTime(year: 2015, month: 1, day: 1, hour: 0, minute: 0, second: 0, kind: DateTimeKind.Local) : sysDate.AddMonths(-1 * marMonths);
// }
#region If the user was locked, try set set unlock if Time expired
// #endregion
if (!request.LoginId.ToLower().Equals(User.SuperUser_LoginId) && user.IsLocked)
{
int isSuccessful = 0;
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.VarChar, pValue: request.LoginId, size: 30),
SqlHelperExtension.CreateInParam(pName: "@LockTime", pType: SqlDbType.Int, pValue: lockTime),
SqlHelperExtension.CreateOutParam(pName: "@IsSuccessful", pType: SqlDbType.Int, pValue: isSuccessful),
];
_ = tc.ExecuteNonQuerySp(spName: "dbo.DoUnlockUser", parameterValues: p);
if (p[2] != null && p[2].Value != null && p[2].Value != DBNull.Value)
isSuccessful = Convert.ToInt32(p[2].Value);
// #region If the user was locked, try set set unlock if Time expired
if (isSuccessful == 1)
user.IsLocked = false;
}
// if (!request.LoginId.ToLower().Equals(User.SuperUser_LoginId) && user.IsLocked)
// {
// int isSuccessful = 0;
// SqlParameter[] p =
// [
// SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.VarChar, pValue: request.LoginId, size: 30),
// SqlHelperExtension.CreateInParam(pName: "@LockTime", pType: SqlDbType.Int, pValue: lockTime),
// SqlHelperExtension.CreateOutParam(pName: "@IsSuccessful", pType: SqlDbType.Int, pValue: isSuccessful),
// ];
// _ = tc.ExecuteNonQuerySp(spName: "dbo.DoUnlockUser", parameterValues: p);
// if (p[2] != null && p[2].Value != null && p[2].Value != DBNull.Value)
// isSuccessful = Convert.ToInt32(p[2].Value);
#endregion
// if (isSuccessful == 1)
// user.IsLocked = false;
// }
#region Keep log for unauthrise access and Set user lock if exceeds max try
// #endregion
if (!request.LoginId.ToLower().Equals(User.SuperUser_LoginId) && maxTryCount > 0 && user.LoginStatus == EnumLoginStatus.Unsuccessful)
{
int remainsTry = 0;
DateTime? nextLoginTime = null;
string tryLoginInfo = $"{request.LoginId}~{password}~13";
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.VarChar, pValue: request.LoginId, size: 30),
SqlHelperExtension.CreateInParam(pName: "@TryLoginInfo", pType: SqlDbType.VarChar, pValue: tryLoginInfo, size: 100),
SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20),
SqlHelperExtension.CreateInParam(pName: "@MaxTryCount", pType: SqlDbType.SmallInt, pValue: maxTryCount),
SqlHelperExtension.CreateInParam(pName: "@LockTime", pType: SqlDbType.Int, pValue: lockTime),
SqlHelperExtension.CreateOutParam(pName: "@RemainingTry", pType: SqlDbType.SmallInt, pValue: remainsTry),
SqlHelperExtension.CreateOutParam(pName: "@NextLoginTime", pType: SqlDbType.DateTime, pValue: nextLoginTime),
];
_ = tc.ExecuteNonQuerySp(spName: "dbo.LogUnauthorizeAccess", parameterValues: p);
if (p[5] != null && p[5].Value != null && p[5].Value != DBNull.Value)
remainsTry = Convert.ToInt32(p[5].Value);
// #region Keep log for unauthrise access and Set user lock if exceeds max try
if (p[6] != null && p[6].Value != null && p[6].Value != DBNull.Value)
nextLoginTime = Convert.ToDateTime(p[6].Value);
// if (!request.LoginId.ToLower().Equals(User.SuperUser_LoginId) && maxTryCount > 0 && user.LoginStatus == EnumLoginStatus.Unsuccessful)
// {
// int remainsTry = 0;
// DateTime? nextLoginTime = null;
// string tryLoginInfo = $"{request.LoginId}~{password}~13";
// SqlParameter[] p =
// [
// SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.VarChar, pValue: request.LoginId, size: 30),
// SqlHelperExtension.CreateInParam(pName: "@TryLoginInfo", pType: SqlDbType.VarChar, pValue: tryLoginInfo, size: 100),
// SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20),
// SqlHelperExtension.CreateInParam(pName: "@MaxTryCount", pType: SqlDbType.SmallInt, pValue: maxTryCount),
// SqlHelperExtension.CreateInParam(pName: "@LockTime", pType: SqlDbType.Int, pValue: lockTime),
// SqlHelperExtension.CreateOutParam(pName: "@RemainingTry", pType: SqlDbType.SmallInt, pValue: remainsTry),
// SqlHelperExtension.CreateOutParam(pName: "@NextLoginTime", pType: SqlDbType.DateTime, pValue: nextLoginTime),
// ];
// _ = tc.ExecuteNonQuerySp(spName: "dbo.LogUnauthorizeAccess", parameterValues: p);
// if (p[5] != null && p[5].Value != null && p[5].Value != DBNull.Value)
// remainsTry = Convert.ToInt32(p[5].Value);
if (remainsTry <= 0)
{
user.IsLocked = true;
if (lockTime <= 0)
{
user.NextLoginTime = nextLoginTime;
user.UnsuccessfulMsg = "Please contact with Head office to Unlock";
}
else
{
user.NextLoginTime = nextLoginTime;
user.UnsuccessfulMsg = $"You can Login after {user.NextLoginTime:dd-MMM-yyyy H:mm:ss}";
}
}
else
{
user.UnsuccessfulMsg = $"{remainsTry} More attempt{(remainsTry > 1 ? "s" : "")} remaining";
}
}
// if (p[6] != null && p[6].Value != null && p[6].Value != DBNull.Value)
// nextLoginTime = Convert.ToDateTime(p[6].Value);
#endregion
// if (remainsTry <= 0)
// {
// user.IsLocked = true;
// if (lockTime <= 0)
// {
// user.NextLoginTime = nextLoginTime;
// user.UnsuccessfulMsg = "Please contact with Head office to Unlock";
// }
// else
// {
// user.NextLoginTime = nextLoginTime;
// user.UnsuccessfulMsg = $"You can Login after {user.NextLoginTime:dd-MMM-yyyy H:mm:ss}";
// }
// }
// else
// {
// user.UnsuccessfulMsg = $"{remainsTry} More attempt{(remainsTry > 1 ? "s" : "")} remaining";
// }
// }
// #endregion
#region If login successful and user is active read module id for this user
// #region Generate and Save Otp if Otp is enabled and send thru SMS/Email
if (user.LoginStatus == EnumLoginStatus.Success && user.Status == EnumStatus.Authorized && !user.IsLocked)
{
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: user.UserId),
SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.VarChar, pValue: user.LoginId, size: 30),
SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20),
SqlHelperExtension.CreateInParam(pName: "@LoginTime", pType: SqlDbType.DateTime, pValue: DateTime.Now),
SqlHelperExtension.CreateInParam(pName: "@LogoutTime", pType: SqlDbType.DateTime, pValue: null)
];
// if (user.LoginStatus == EnumLoginStatus.Success && user.Status == EnumStatus.Authorized && (user.AuthMethod == EnumAuthenticationMethod.Email || user.AuthMethod == EnumAuthenticationMethod.MobileSMS))
// {
// string secretKey = $"{user.Id}~{user.LoginId}";
// secretKey = secretKey.EncodeAsBase32String(addPadding: false);
// user.AuthValue = TOtpService.GetCurrentPIN(secretKey: secretKey);
// SetAuthValue(tc: tc, authValue: user.AuthValue, validMinutes: 5, userId: user.Id);
// }
_ = tc.ExecuteNonQuerySp(spName: "dbo.SaveAccessLog", parameterValues: p);
}
// #endregion
#endregion
// #region If login successful and user is active read module id for this user
// if (user.LoginStatus == EnumLoginStatus.Success && user.Status == EnumStatus.Authorized && !user.IsLocked)
// {
// int logId = 0;
// DateTime? logoutTime = null;
// SqlParameter[] p =
// [
// SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: user.Id),
// SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20),
// SqlHelperExtension.CreateInParam(pName: "@AppId", pType: SqlDbType.VarChar, pValue: request.AppId, size: 250),
// SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.VarChar, pValue: user.LoginId, size: 30),
// SqlHelperExtension.CreateInParam(pName: "@LockTime", pType: SqlDbType.Int, pValue: lockTime),
// SqlHelperExtension.CreateInParam(pName: "@AttendanceLogin", pType: SqlDbType.Int, pValue: request.AttendanceLogin? 1:0),
// SqlHelperExtension.CreateInParam(pName: "@LocalIp", pType: SqlDbType.VarChar, pValue: request.IpAddress, size: 20),
// SqlHelperExtension.CreateInParam(pName: "@MacAddress", pType: SqlDbType.VarChar, pValue: request.MacAddress, size: 30),
// SqlHelperExtension.CreateInParam(pName: "@HostName", pType: SqlDbType.VarChar, pValue: request.HostName, size: 100),
// SqlHelperExtension.CreateInParam(pName: "@LoginRemarks", pType: SqlDbType.VarChar, pValue: request.LoginRemarks, size: 50)
// ];
// using (IDataReader dr = tc.ExecuteReaderSp(spName: "dbo.GetPermissionKeys", parameterValues: p))
// {
// user.ModuleIds = [];
// while (dr.Read())
// {
// string moduleId = dr.GetString(0);
// user.ModuleIds.Add(moduleId);
#region If login successful and user is active read module id for this user
// logId = dr.GetInt32(1);
if (user.LoginStatus == EnumLoginStatus.Success && user.Status == EnumStatus.Authorized && !user.IsLocked)
{
int logId = 0;
DateTime? logoutTime = null;
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: user.UserId),
SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20),
SqlHelperExtension.CreateInParam(pName: "@AppId", pType: SqlDbType.VarChar, pValue: request.AppId, size: 250),
SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.VarChar, pValue: user.LoginId, size: 30),
SqlHelperExtension.CreateInParam(pName: "@LockTime", pType: SqlDbType.Int, pValue: lockTime),
SqlHelperExtension.CreateInParam(pName: "@AttendanceLogin", pType: SqlDbType.Int, pValue: request.AttendanceLogin? 1:0),
SqlHelperExtension.CreateInParam(pName: "@LocalIp", pType: SqlDbType.VarChar, pValue: request.IpAddress, size: 20),
SqlHelperExtension.CreateInParam(pName: "@MacAddress", pType: SqlDbType.VarChar, pValue: request.MacAddress, size: 30),
SqlHelperExtension.CreateInParam(pName: "@HostName", pType: SqlDbType.VarChar, pValue: request.HostName, size: 100),
SqlHelperExtension.CreateInParam(pName: "@LoginRemarks", pType: SqlDbType.VarChar, pValue: request.LoginRemarks, size: 50)
];
using (IDataReader dr = tc.ExecuteReaderSp(spName: "dbo.GetPermissionKeys", parameterValues: p))
{
user.ModuleIds = [];
while (dr.Read())
{
string moduleId = dr.GetString(0);
user.ModuleIds.Add(moduleId);
// if (dr.GetInt16(2) != 0) //Alow add
// {
// user.ModuleIds.Add($"{moduleId}_1");
// }
// if (dr.GetInt16(3) != 0) //Alow edit
// {
// user.ModuleIds.Add($"{moduleId}_2");
// }
logId = dr.GetInt32(1);
// if (dr.GetInt16(4) != 0) //Allow Delete
// {
// user.ModuleIds.Add($"{moduleId}_3");
// }
if (dr.GetInt16(2) != 0) //Alow add
{
user.ModuleIds.Add($"{moduleId}_1");
}
if (dr.GetInt16(3) != 0) //Alow edit
{
user.ModuleIds.Add($"{moduleId}_2");
}
// logoutTime = dr.IsDBNull(5) ? null : dr.GetDateTime(5);
// }
// dr.Close();
// }
// user.LogId = logId;
// user.LogoutTime = logoutTime;
if (dr.GetInt16(4) != 0) //Allow Delete
{
user.ModuleIds.Add($"{moduleId}_3");
}
// //Read User TeamSpace Ids
// using (IDataReader dr = tc.ExecuteReader("SELECT TeamSpaceId FROM TeamSpaceUsers WHERE UserId=%n", user.Id))
// {
// while (dr.Read())
// {
// user.TeamSpaceIds.Add(dr.GetInt32(0));
// }
// dr.Close();
// }
logoutTime = dr.IsDBNull(5) ? null : dr.GetDateTime(5);
}
dr.Close();
}
user.LogoutTime = logoutTime;
// //Pending Notification count
// user.NotificationCount = GetPendingNotifCount(tc: tc, userId: user.Id);
// }
//Read User TeamSpace Ids
using (IDataReader dr = tc.ExecuteReader("SELECT TeamSpaceId FROM TeamSpaceUsers WHERE UserId=%n", user.UserId))
{
while (dr.Read())
{
user.TeamSpaceIds.Add(dr.GetInt32(0));
}
dr.Close();
}
// #endregion
//}
//Pending Notification count
//user.NotificationCount = GetPendingNotifCount(tc: tc, userId: user.Id);
}
#endregion
}
tc.End();
}

View File

@ -166,7 +166,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers
loginReponse.LoginId = loginReponse.LoginId;
loginReponse.AccessToken = userToken;
loginReponse.RefreshToken = refreshToken.RefreshToken;
loginReponse.AccessTokenExpiryTime = DateTime.UtcNow.AddHours(12);
loginReponse.AccessTokenExpiryTime = DateTime.Now.AddMinutes(_appSettings.AccessTokenDuration);
loginReponse.RefreshTokenExpiryTime = refreshToken.ExpireTime;
return Ok(MobileResponseBase<AppAuthUserResponse>.Success(loginReponse));
@ -217,7 +217,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers
Helper.CreateClaim("UserId", $"{userRefreshToken.UserId}"),
Helper.CreateClaim("HashKey", Guid.NewGuid().ToString())
]),
Expires = DateTime.UtcNow.AddHours(12),
Expires = DateTime.UtcNow.AddMinutes(_appSettings.AccessTokenDuration),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha512Signature)
};

View File

@ -1,16 +1,5 @@
using Asp.Versioning;
using OnlineSalesAutoCrop.CoreAPI.Configurations;
using OnlineSalesAutoCrop.CoreAPI.Models;
using OnlineSalesAutoCrop.CoreAPI.Models.Global;
using OnlineSalesAutoCrop.CoreAPI.Models.Objects;
using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Setups;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Systems;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Systems;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems;
using OnlineSalesAutoCrop.CoreAPI.SignalRHub;
using Google.Apis.Auth.OAuth2.Requests;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@ -18,6 +7,20 @@ using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using OnlineSalesAutoCrop.CoreAPI.Configurations;
using OnlineSalesAutoCrop.CoreAPI.Models;
using OnlineSalesAutoCrop.CoreAPI.Models.Global;
using OnlineSalesAutoCrop.CoreAPI.Models.Objects;
using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Setups;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Systems;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Systems;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Auth;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems;
using OnlineSalesAutoCrop.CoreAPI.SignalRHub;
using System;
using System.Collections.Generic;
using System.DirectoryServices;
@ -45,11 +48,11 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers.Web
[Authorize]
[ApiController]
[ApiVersion("1.0")]
[Route("api/users")]
[ValidateAntiForgeryToken]
[Route("api/v{version:apiVersion}/users")]
public class UserController(IUserService service, IOptions<AppSettings> appSettings, IEaseCache cache, ILogger<IntegrationAuthController> logger, IHubContext<NotificationHub, INotificationHub> hub) : ControllerBase
public class UserController(IUserService service, IOptions<AppSettings> appSettings, IEaseCache cache, ILogger<IntegrationAuthController> logger,
IHubContext<NotificationHub, INotificationHub> hub, IRefreshTokenService refreshTokenService) : ControllerBase
{
private readonly ILogger _logger = logger;
private readonly IEaseCache _cache = cache;
@ -57,6 +60,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers.Web
private readonly AppSettings _appSettings = appSettings?.Value;
private readonly DateTimeOffset _options = Helper.CreateEaseCacheOptions();
private readonly IHubContext<NotificationHub, INotificationHub> _hub = hub;
private readonly IRefreshTokenService _refreshTokenService= refreshTokenService;
/// <summary>
/// Login using your credential data retrieve from SqlServer
@ -215,17 +219,15 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers.Web
{
Subject = new ClaimsIdentity(
[
//Helper.CreateClaim("UserId", $"{user.Id}"),
//Helper.CreateClaim("LoginId", user.LoginId),
//Helper.CreateClaim("UserPwd", userPwd),
//Helper.CreateClaim("AuthKey", $"{user.AuthKey}"),
//Helper.CreateClaim("TeamSpaceIds", $"{string.Join(',',user.TeamSpaceIds)}"),
//Helper.CreateClaim("BatchEnabled", user.BatchEnabled ? "1" : "0"),
//Helper.CreateClaim("BmProcessId", $"{user.BmProcessId}"),
//Helper.CreateClaim("PrProcessId", $"{user.PrProcessId}"),
//Helper.CreateClaim("EmployeeId", $"{user.EmployeeCode}")
Helper.CreateClaim("UserId", $"{user.UserId}"),
Helper.CreateClaim("LoginId", user.LoginId),
Helper.CreateClaim("AuthKey", $"{user.AuthKey}"),
Helper.CreateClaim("TeamSpaceIds", $"{string.Join(',',user.TeamSpaceIds)}"),
Helper.CreateClaim("UserPlatformId", ((int)user.UserPlatFormType).ToString()),
Helper.CreateClaim("UserRoleType", $"{user.UserRoleType}"),
Helper.CreateClaim("EmployeeCode", $"{user.EmployeeNumber}")
]),
Expires = DateTime.UtcNow.AddHours(12),
Expires = DateTime.UtcNow.AddMinutes(_appSettings.AccessTokenDuration),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha512Signature)
};
@ -246,36 +248,16 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers.Web
response.AuthenticationToken = userToken;
response.LoginTime = $"{DateTime.Now:dd-MM-yy H:mm:ss}";
//await HttpContext.Session.SetModulesToSession(key: userToken, value: user.ModuleIds);
//if (user.LoginStatus == EnumLoginStatus.Success)
//{
// if (user.AuthMethod == EnumAuthenticationMethod.Email && !string.IsNullOrEmpty(user.EmailAddress) && !string.IsNullOrWhiteSpace(user.EmailAddress) && !string.IsNullOrWhiteSpace(user.AuthValue))
// {
// List<string> to = [.. user.EmailAddress.Split(separator: ';', options: StringSplitOptions.RemoveEmptyEntries)];
// await MailHelper.SendMailMessageAsync(settings: _appSettings, to: to,
// cc: null, bcc: null, attachments: null, embeddedImages: null, isHtmlBody: false, priority: System.Net.Mail.MailPriority.Normal,
// subject: "Your OTP", messageBody: string.Format("Your OTP: {0} and is valid for 5 minutes only", user.AuthValue));
// }
// else if (user.AuthMethod == EnumAuthenticationMethod.MobileSMS && !string.IsNullOrEmpty(user.MobileNo) && !string.IsNullOrWhiteSpace(user.MobileNo) && !string.IsNullOrWhiteSpace(user.AuthValue))
// {
// MailHelper.SendSMSOrWhatsAppMessage(settings: _appSettings, whatsAppMsg: false, msg: string.Format("Your OTP: {0} and is valid for 5 minutes only", user.AuthValue), mobileNumber: user.MobileNo);
// }
//}
GenerateRefreshTokenRequest refreshTokenRequest = new GenerateRefreshTokenRequest()
{
UserId = user.UserId,
IpAddress = ipAddress,
RawRefreshToken = string.Empty
};
//if (user.DisallowMultiLogin)
//{
// await _hub.Clients.All.NotifySubscriber(userId: user.Id, msgType: 1, itemId: 0, ipAddress: ipAddress);
// if (request.AttendanceLogin)
// await _hub.Clients.All.NotifySubscriber(userId: user.Id, msgType: 6, itemId: 0, ipAddress: ipAddress);
//}
//else
//{
// await _hub.Clients.All.NotifySubscriber(userId: user.Id, msgType: 2, itemId: 0, ipAddress: ipAddress);
// if (request.AttendanceLogin)
// await _hub.Clients.All.NotifySubscriber(userId: user.Id, msgType: 6, itemId: 0, ipAddress: ipAddress);
//}
//response.IdsValue = Ease.NetCore.Utility.Global.CipherFunctions.EncryptByAES(data: Newtonsoft.Json.JsonConvert.SerializeObject(user.ModuleIds), privateKey: cipherSecretKey, publicKey: cipherSecretKey, output: 2);
var refreshToken = await _refreshTokenService.GenerateRefreshTokenByUserAsync(refreshTokenRequest);
response.RefreshToken = refreshToken.RefreshToken;
return Ok(response);
}
catch (Exception ex)

View File

@ -33,7 +33,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Text;

View File

@ -84,8 +84,8 @@
"WaAuthToken": "024a6897584671d9f9fa588d7c94aa96",
"WaMsgSvcSid": "MG8401d33a9a3b2aea95619bda3e5757b5",
"WaSenderId": "+8801326755660",
"RefreshTokenDuration": "15",
"AccessTokenDuration": "60",
"RefreshTokenDuration": "2",
"AccessTokenDuration": "1",
"AutoCropSAPApi": {
"BaseUrl": "https://accl-test-pad-l06vsvh7.it-cpi004-rt.cfapps.ap11.hana.ondemand.com/",
"UserName": "sb-1635ae0e-9941-4998-962f-e23f592d29b5!b45657|it-rt-accl-test-pad-l06vsvh7!b68",

View File

@ -33,9 +33,6 @@ export class AppBreadcrumbComponent implements OnDestroy
{
if (this.authService?.currentUserValue)
{
if (this.authService.currentUserValue.dbOnStartup)
this.router.navigate(['/dashboard']);
else
this.router.navigate(['/home']);
}
}

View File

@ -6,6 +6,7 @@ import { Component, Input, OnInit, AfterViewInit, forwardRef, inject } from '@an
import { AppComponent } from './app.component';
import { UserService } from '../providers/user/user.service.';
import { MenuItemComponent } from './components/menu/menuitem.component';
import { MENU_DEFINITION, PROFILE_MENU, MenuNode, buildPermittedMenu } from './components/models/menu.definition';
@Component({
standalone: true,
@ -28,6 +29,7 @@ export class AppMenuComponent implements OnInit, AfterViewInit
public isAuthenicated: boolean = false;
private menuItems: any;
private routePaths: Set<string>;
ngOnInit()
{
@ -43,15 +45,14 @@ export class AppMenuComponent implements OnInit, AfterViewInit
}
this.reloadMenu();
this.modelUngrouped = [{ label: 'Main Menu', icon: 'fa fa-home', items: this.modelGrouped }];
}
private reloadMenu()
{
if (this.isAuthenicated && this.service?.authService?.currentUserValue?.moduleIds)
const currentUser = this.service?.authService?.currentUserValue;
if (this.isAuthenicated && currentUser?.moduleIds)
{
const dbOnStartup = this.service.authService.currentUserValue.dbOnStartup;
this.app.detailInfo = `Version: ${this.app.swVersion} | User: ${this.service.authService.currentUserValue.userName} | Login Time: ${this.service.authService.currentUserValue.loginTime}`;
this.app.detailInfo = `Version: ${this.app.swVersion} | User: ${currentUser.userName} | Login Time: ${currentUser.loginTime}`;
this.modelGrouped =
[
{
@ -59,7 +60,7 @@ export class AppMenuComponent implements OnInit, AfterViewInit
items:
[
{ label: 'Logout', icon: 'fa fa-power-off', routerLink: ['/logout'] },
{ label: dbOnStartup ? 'Dashboard' : 'Home', icon: dbOnStartup ? 'fa fa-dashboard' : 'fa fa-home', routerLink: [dbOnStartup ? '/dashboard' : '/home'] }
{ label: 'Home', icon: 'fa fa-home', routerLink: ['/home'] }
]
},
{
@ -79,6 +80,8 @@ export class AppMenuComponent implements OnInit, AfterViewInit
items: [{ label: 'Login', icon: 'fa fa-sign-in', routerLink: ['/login'] }]
}];
}
this.modelUngrouped = [{ label: 'Main Menu', icon: 'fa fa-home', items: this.modelGrouped }];
}
ngAfterViewInit()
@ -129,35 +132,18 @@ export class AppMenuComponent implements OnInit, AfterViewInit
private onAuthenication(value: any): void
{
this.isAuthenicated = value.loggedIn;
if (value.loggedIn && value.userId && value.userId !=0)
const currentUser = this.service.authService.currentUserValue;
if (value.loggedIn && value.userId && value.userId != 0 && currentUser)
{
this.service.loadMenu(value.userId).subscribe(
{
next: (resp: any) =>
{
this.menuItems = resp || [];
this.app.userId = this.service.authService.currentUserValue?.id;
this.app.loginId = this.service.authService.currentUserValue?.loginId;
this.app.layoutMode = this.service.authService.currentUserValue?.menuLayout;
this.app.notificationCount = this.service.authService.currentUserValue?.notificationCount;
this.menuItems = { items: this.buildMenuItems(currentUser.moduleIds) };
this.app.userId = currentUser.id;
this.app.loginId = currentUser.loginId;
this.app.layoutMode = currentUser.menuLayout;
this.app.notificationCount = currentUser.notificationCount;
this.app.autoScrollMenu = (this.app.layoutMode === 'static' || this.app.layoutMode === 'overlay') ? 'auto-scroll-menu' : '';
this.changeTheme(this.service.authService.currentUserValue?.themeName, this.service.authService.currentUserValue?.schemeName);
this.changeTheme(currentUser.themeName, currentUser.schemeName);
this.reloadMenu();
},
error: () =>
{
this.menuItems = [];
this.app.layoutMode = 'overlay'
this.app.loginId = this.app.autoScrollMenu = '';
this.app.userId = this.app.notificationCount = 0;
this.service.authService.loggedout(false);
this.reloadMenu();
this.router.navigate(['/login']);
}
});
}
else
{
@ -172,6 +158,28 @@ export class AppMenuComponent implements OnInit, AfterViewInit
this.router.navigate(['/login']);
}
}
/**
* Builds the activity menu out of the permission keys the login endpoint returned
* in `moduleIds`, the profile pages are always available to a logged in user.
*/
private buildMenuItems(moduleIds: string[]): MenuNode[]
{
const routeExists = (routerLink: string): boolean => this.registeredRoutes.has(routerLink);
const items: MenuNode[] = buildPermittedMenu([PROFILE_MENU], moduleIds, routeExists);
items.push(...buildPermittedMenu(MENU_DEFINITION, moduleIds, routeExists));
return items;
}
/** Paths registered in the router, keeps permitted but not yet implemented pages out of the sidebar. */
private get registeredRoutes(): Set<string>
{
this.routePaths ??= new Set(this.router.config.map(route => route.path).filter(path => !!path && path !== '**'));
return this.routePaths;
}
}
@Component({

View File

@ -0,0 +1,424 @@
/**
* Client side mirror of the server menu (GlobalFunctions.BuildMenu).
*
* The login response returns the permission keys of the logged in user in `moduleIds`,
* so the sidebar is built here instead of calling `v1/users/loadMenu`.
*
* `moduleIds` holds one entry per permitted module plus the operation suffixes:
* ELIT.1.2.2 -> select (the page itself)
* ELIT.1.2.2_1 -> add
* ELIT.1.2.2_2 -> edit
* ELIT.1.2.2_3 -> delete
*
* Only items that are visible on the server menu are listed below, permission only
* keys (ELIT.1.1A, ELIT.4.1.10, ...) never render so they are left out.
*/
export interface MenuNode
{
label: string;
icon?: string;
moduleId?: string;
routerLink?: string;
items?: MenuNode[];
}
/** Always available to a logged in user, the server adds these without any permission check. */
export const PROFILE_MENU: MenuNode =
{
label: 'User Profile', icon: 'fa fa-user-md',
items:
[
{ label: 'My Profile', icon: 'fa fa-user', routerLink: 'myprofile' },
{ label: 'Change My Password', icon: 'fa fa-unlock', routerLink: 'changemypwd' },
{ label: 'Change My Theme', icon: 'fa fa-user', routerLink: 'changemytheme' },
{ label: 'WhatsApp Message', icon: 'fa fa-whatsapp', routerLink: 'sendwhatsappmsg' },
{ label: 'Access Log', icon: 'fa fa-list-alt', routerLink: 'accesslog' }
]
};
export const MENU_DEFINITION: MenuNode[] =
[
{
moduleId: 'ELIT.1', label: 'System Setup', icon: 'fa fa-sun-o',
items:
[
{ moduleId: 'ELIT.1.3', label: 'Label Setting', icon: 'fa fa-sliders', routerLink: 'labelsetting' },
{ moduleId: 'ELIT.1.1', label: 'System Information', icon: 'fa fa-cog', routerLink: 'thissystem' },
{
moduleId: 'ELIT.1.2', label: 'User Management', icon: 'fa fa-users',
items:
[
{ moduleId: 'ELIT.1.2.1', label: 'Groups', icon: 'fa fa-address-book-o', routerLink: 'groups' },
{ moduleId: 'ELIT.1.2.2', label: 'Users', icon: 'fa fa-address-card-o', routerLink: 'users' },
{ moduleId: 'ELIT.1.2.4', label: 'Force Logout', icon: 'fa fa-address-card-o', routerLink: 'forcelogout' }
]
},
{ moduleId: 'ELIT.1.3A', label: 'Lookup Data', icon: 'fa fa-sliders', routerLink: 'lookupdata' },
{ moduleId: 'ELIT.1.4', label: 'Custom Report Setup', icon: 'fa fa-table', routerLink: 'customreports' },
{
moduleId: 'ELIT.1.5', label: 'Basic Setup', icon: 'fa fa-wrench',
items:
[
{ moduleId: 'ELIT.1.5.1', label: 'Clients', icon: 'fa fa-address-card-o', routerLink: 'clients' },
{ moduleId: 'ELIT.1.5.2', label: 'Suppliers', icon: 'fa fa-credit-card', routerLink: 'suppliers' },
{ moduleId: 'ELIT.1.5.3', label: 'Employees', icon: 'fa fa-user-plus', routerLink: 'employees' },
{ moduleId: 'ELIT.1.5.4', label: 'Project Leads', icon: 'fa fa-user-circle-o', routerLink: 'projectleads' },
{ moduleId: 'ELIT.1.5.5', label: 'Project Managers', icon: 'fa fa-user-circle-o', routerLink: 'projectmgts' },
{ moduleId: 'ELIT.1.5.6', label: 'Projects', icon: 'fa fa-product-hunt', routerLink: 'projects' },
{ moduleId: 'ELIT.1.5.7', label: 'Stores', icon: 'fa fa-pencil-square', routerLink: 'stores' },
{ moduleId: 'ELIT.1.5.8', label: 'Banks & Branches', icon: 'fa fa-university', routerLink: 'bankbranches' },
{ moduleId: 'ELIT.1.5.9', label: 'Bank Accounts', icon: 'fa fa-credit-card', routerLink: 'bankaccounts' },
{ moduleId: 'ELIT.1.5.10', label: 'GL Codes', icon: 'fa fa-book', routerLink: 'glcodes' },
{ moduleId: 'ELIT.1.5.11', label: 'User Clients & Projects', icon: 'fa fa-product-hunt', routerLink: 'userprojects' },
{ moduleId: 'ELIT.1.5.12', label: 'User Suppliers', icon: 'fa fa-product-hunt', routerLink: 'usersuppliers' }
]
}
]
},
{
moduleId: 'ELIT.2', label: 'Authentication Process', icon: 'fa fa-cogs',
items:
[
{ moduleId: 'ELIT.2.1', label: 'Authentication', icon: 'fa fa-check', routerLink: 'authentications' },
{ moduleId: 'ELIT.2.2', label: 'Authorization', icon: 'fa fa-check-square-o', routerLink: 'authorizations' },
{ moduleId: 'ELIT.2.3', label: 'Deactivation', icon: 'fa fa-window-close-o', routerLink: 'deactivations' }
]
},
{
moduleId: 'ELIT.3', label: 'General Ledger', icon: 'fa fa-university',
items:
[
{
moduleId: 'ELIT.3.1', label: 'Basic Setup', icon: 'fa fa-wrench',
items:
[
{ moduleId: 'ELIT.3.1.1', label: 'Chart of Accounts', icon: 'fa fa-th-list', routerLink: 'chartofaccounts' },
{ moduleId: 'ELIT.3.1.2', label: 'Fix GL Balance', icon: 'fa fa-wrench', routerLink: 'fixBalance' },
{ moduleId: 'ELIT.3.1.3', label: 'GL Head Identification', icon: 'fa fa-cog', routerLink: 'identifyglhead' }
]
},
{
moduleId: 'ELIT.3.2', label: 'Transactions', icon: 'fa fa-building',
items:
[
{ moduleId: 'ELIT.3.2.1', label: 'GL Transactions', icon: 'fa fa-archive', routerLink: 'gltrans' },
{ moduleId: 'ELIT.3.2.2', label: 'Upload Auto Voucher', icon: 'fa fa-upload', routerLink: 'gltranav' },
{ moduleId: 'ELIT.3.2.3', label: 'Year End Process', icon: 'fa fa-cog', routerLink: 'yearendprocess' },
{ moduleId: 'ELIT.3.2.4', label: 'Undo Year End', icon: 'fa fa-undo', routerLink: 'undoyearend' },
{ moduleId: 'ELIT.3.2.5', label: 'Authorize Transactions', icon: 'fa fa-check-square', routerLink: 'authgltrans' },
{ moduleId: 'ELIT.3.2.7', label: 'Rollback Transactions', icon: 'fa fa-undo', routerLink: 'rollbackgltrans' },
{ moduleId: 'ELIT.3.2.8', label: 'Dashboard Processor', icon: 'fa fa-cog', routerLink: 'dbprocessor' }
]
},
{
moduleId: 'ELIT.3.3', label: 'Reports', icon: 'fa fa-newspaper-o',
items:
[
{ moduleId: 'ELIT.3.3.1', label: 'Ledger', icon: 'fa fa-file-text-o', routerLink: 'glledger' },
{ moduleId: 'ELIT.3.3.2', label: 'Monthly Turnover', icon: 'fa fa-file-text-o', routerLink: 'turnover' },
{ moduleId: 'ELIT.3.3.3', label: 'Trial Balance', icon: 'fa fa-file-text-o', routerLink: 'trialbalance' },
{ moduleId: 'ELIT.3.3.8', label: 'Income & Expenditure', icon: 'fa fa-file-text-o', routerLink: 'incomeexpense' },
{ moduleId: 'ELIT.3.3.4', label: 'Day Book', icon: 'fa fa-file-text-o', routerLink: 'daybook' },
{ moduleId: 'ELIT.3.3.5', label: 'Bank/Cash Book', icon: 'fa fa-file-text-o', routerLink: 'bankcashbook' },
{ moduleId: 'ELIT.3.3.6', label: 'Schedule', icon: 'fa fa-file-text-o', routerLink: 'glschedule' },
{ moduleId: 'ELIT.3.3.7', label: 'Previous Final Reports', icon: 'fa fa-file-text-o', routerLink: 'prvfinalreports' }
]
}
]
},
{
moduleId: 'ELIT.4', label: 'Order To Cash', icon: 'fa fa-money',
items:
[
{
moduleId: 'ELIT.4.1', label: 'Basic Setup', icon: 'fa fa-wrench',
items:
[
{ moduleId: 'ELIT.4.1.1', label: 'Income GL Codes', icon: 'fa fa-pencil-square-o', routerLink: 'incomeglcodes' },
{ moduleId: 'ELIT.4.1.2', label: 'VAT Liability GL Codes', icon: 'fa fa-pencil-square', routerLink: 'vatglcodes' },
{ moduleId: 'ELIT.4.1.3', label: 'AIT GL Codes', icon: 'fa fa-book', routerLink: 'aitglcodes' }
]
},
{
moduleId: 'ELIT.4.2', label: 'Regular Activities', icon: 'fa fa-building',
items:
[
{ moduleId: 'ELIT.4.2.1', label: 'Purchase Orders', icon: 'fa fa-smile-o', routerLink: 'purchaseorders' },
{ moduleId: 'ELIT.4.2.2', label: 'Invoices', icon: 'fa fa-share-square', routerLink: 'invoices' },
{ moduleId: 'ELIT.4.2.3', label: 'Payments', icon: 'fa fa-money', routerLink: 'payments' },
{ moduleId: 'ELIT.4.2.4', label: 'Freeze PO/Invoice', icon: 'fa fa-archive', routerLink: 'archivepoinvs' },
{ moduleId: 'ELIT.4.2.8', label: 'VAT Challan Update', icon: 'fa fa-sliders', routerLink: 'updvatchlano2c' }
]
},
{
moduleId: 'ELIT.4.3', label: 'Reports', icon: 'fa fa-newspaper-o',
items:
[
{ moduleId: 'ELIT.4.3.1', label: 'GL Vouchers', icon: 'fa fa-th-list', routerLink: 'glvouchers' },
{ moduleId: 'ELIT.4.3.2', label: 'Transactional Reports', icon: 'fa fa-file-text-o', routerLink: 'txnreports' },
{ moduleId: 'ELIT.4.3.3', label: 'Contract Expiry Report', icon: 'fa fa-file-text-o', routerLink: 'contexpryreports' },
{ moduleId: 'ELIT.4.3.4', label: 'Customized Report', icon: 'fa fa-file-text-o', routerLink: 'customreporto2c' },
{ moduleId: 'ELIT.4.3.5', label: 'Client Ledger', icon: 'fa fa-file-text-o', routerLink: 'clientledgero2c' },
{ moduleId: 'ELIT.4.3.6', label: 'Monthly Trends', icon: 'fa fa-file-text-o', routerLink: 'monthlytrendso2c' }
]
}
]
},
{
moduleId: 'ELIT.9', label: 'Sales System', icon: 'fa fa-sitemap',
items:
[
{
moduleId: 'ELIT.9.1', label: 'Basic Setup', icon: 'fa fa-wrench',
items:
[
{ moduleId: 'ELIT.9.1.1', label: 'Market Hierarchies', icon: 'fa fa-pencil-square', routerLink: 'markethierarchies' },
{ moduleId: 'ELIT.9.1.2', label: 'Sales Points', icon: 'fa fa-pencil-square', routerLink: 'salespoints' },
{ moduleId: 'ELIT.9.1.3', label: 'Product Hierarchies', icon: 'fa fa-pencil-square', routerLink: 'producthierarchies' },
{ moduleId: 'ELIT.9.1.4', label: 'Brands', icon: 'fa fa-pencil-square', routerLink: 'brands' },
{ moduleId: 'ELIT.9.1.5', label: 'SKUs', icon: 'fa fa-pencil-square', routerLink: 'skus' },
{ moduleId: 'ELIT.9.1.6', label: 'Channel Hierarchies', icon: 'fa fa-pencil-square', routerLink: 'channelhierarchies' },
{ moduleId: 'ELIT.9.1.7', label: 'Customers', icon: 'fa fa-pencil-square', routerLink: 'customers' },
{ moduleId: 'ELIT.9.1.8', label: 'Sales Officers', icon: 'fa fa-pencil-square', routerLink: 'salesofficers' },
{ moduleId: 'ELIT.9.1.9', label: 'Product Prices', icon: 'fa fa-pencil-square', routerLink: 'skupricess' },
{ moduleId: 'ELIT.9.1.10', label: 'Product VAT-SD-Comm', icon: 'fa fa-pencil-square', routerLink: 'skuvattaxes' },
{ moduleId: 'ELIT.9.1.11', label: 'Customer Credit', icon: 'fa fa-pencil-square', routerLink: 'customercredit' },
{ moduleId: 'ELIT.9.1.12', label: 'External Products', icon: 'fa fa-pencil-square', routerLink: 'extnlproducts' },
{ moduleId: 'ELIT.9.1.13', label: 'Sales Promotions', icon: 'fa fa-pencil-square', routerLink: 'salespromotions' },
{ moduleId: 'ELIT.9.1.14', label: 'Order Authorization Limit', icon: 'fa fa-pencil-square', routerLink: 'ordrauthlimit' }
]
},
{
moduleId: 'ELIT.9.2', label: 'Regular Activities', icon: 'fa fa-building',
items:
[
{ moduleId: 'ELIT.9.2.1', label: 'Customer Opening Balance', icon: 'fa fa-puzzle-piece', routerLink: 'slscustsopnbal' },
{ moduleId: 'ELIT.9.2.2', label: 'Customer Adjustment', icon: 'fa fa-pencil-square', routerLink: 'slscustsadjustment' },
{ moduleId: 'ELIT.9.2.3', label: 'Initial Stock', icon: 'fa fa-puzzle-piece', routerLink: 'slsinitstocks' },
{ moduleId: 'ELIT.9.2.4', label: 'Receive Stocks', icon: 'fa fa-reply-all', routerLink: 'slsrcvstocks' },
{ moduleId: 'ELIT.9.2.5', label: 'Sales Orders', icon: 'fa fa-pencil-square', routerLink: 'slsorders' },
{ moduleId: 'ELIT.9.2.6', label: 'Spot Sales', icon: 'fa fa-pencil-square', routerLink: 'slsdirectinvoices' },
{ moduleId: 'ELIT.9.2.7', label: 'Sales Invoices', icon: 'fa fa-pencil-square', routerLink: 'slsinvoices' },
{ moduleId: 'ELIT.9.2.8', label: 'Payment Against Invoices', icon: 'fa fa-archive', routerLink: 'slsinvpayments' },
{ moduleId: 'ELIT.9.2.9', label: 'Stock Transfers', icon: 'fa fa-archive', routerLink: 'slsstockxfers' },
{ moduleId: 'ELIT.9.2.10', label: 'Receive Transferred Stocks', icon: 'fa fa-archive', routerLink: 'slsstockxferrcvs' },
{ moduleId: 'ELIT.9.2.11', label: 'Fix Transfer Anomalies', icon: 'fa fa-archive', routerLink: 'slsstockxferanmls' },
{ moduleId: 'ELIT.9.2.12', label: 'Sales Return', icon: 'fa fa-archive', routerLink: 'slsrtnstocks' },
{ moduleId: 'ELIT.9.2.13', label: 'Damage Stocks', icon: 'fa fa-archive', routerLink: 'slsdmgstocks' },
{ moduleId: 'ELIT.9.2.14', label: 'Stock Take', icon: 'fa fa-archive', routerLink: 'slsstocktakes' }
]
},
{
moduleId: 'ELIT.9.3', label: 'Reports', icon: 'fa fa-newspaper-o',
items:
[
{ moduleId: 'ELIT.9.3.1', label: 'Customer Balance', icon: 'fa fa-th-list', routerLink: 'salescustbalance' },
{ moduleId: 'ELIT.9.3.2', label: 'Customer Ledger', icon: 'fa fa-th-list', routerLink: 'salescustledger' },
{ moduleId: 'ELIT.9.3.3', label: 'Current Stock', icon: 'fa fa-file-text-o', routerLink: 'salescurrentstock' },
{ moduleId: 'ELIT.9.3.4', label: 'Stock Ledger', icon: 'fa fa-th-list', routerLink: 'salesstockledger' }
]
}
]
},
{
moduleId: 'ELIT.5', label: 'Purchase To Pay', icon: 'fa fa-shopping-cart',
items:
[
{
moduleId: 'ELIT.5.1', label: 'Basic Setup', icon: 'fa fa-wrench',
items:
[
{ moduleId: 'ELIT.5.1.1', label: 'Product Types', icon: 'fa fa-address-card-o', routerLink: 'prodtypes' },
{ moduleId: 'ELIT.5.1.2', label: 'Products', icon: 'fa fa-university', routerLink: 'products' },
{ moduleId: 'ELIT.5.1.3', label: 'WO Terms & Conditions', icon: 'fa fa-pencil-square-o', routerLink: 'woterms' },
{ moduleId: 'ELIT.5.1.4', label: 'VAT & AIT Rates', icon: 'fa fa-user-circle-o', routerLink: 'aitvatratep2p' }
]
},
{
moduleId: 'ELIT.5.2', label: 'Regular Activities', icon: 'fa fa-building',
items:
[
{ moduleId: 'ELIT.5.2.0', label: 'Initial Stock', icon: 'fa fa-puzzle-piece', routerLink: 'initstocksp2p' },
{ moduleId: 'ELIT.5.2.13', label: 'Purchase Requisitions', icon: 'fa fa-puzzle-piece', routerLink: 'purchrequisitionsp2p' },
{ moduleId: 'ELIT.5.2.1', label: 'Issue Work Orders', icon: 'fa fa-smile-o', routerLink: 'workordersp2p' },
{ moduleId: 'ELIT.5.2.12', label: 'Advance Payments', icon: 'fa fa-money', routerLink: 'advpaymntsp2p' },
{ moduleId: 'ELIT.5.2.2', label: 'Receive Items at CS', icon: 'fa fa-reply-all', routerLink: 'stksrcvdatcsp2p' },
{ moduleId: 'ELIT.5.2.3', label: 'Receive Invoices', icon: 'fa fa-money', routerLink: 'invoicesp2p' },
{ moduleId: 'ELIT.5.2.4', label: 'Cash Purchase', icon: 'fa fa-archive', routerLink: 'cashpurchases' },
{ moduleId: 'ELIT.5.2.5', label: 'Payment Against Invoice', icon: 'fa fa-archive', routerLink: 'paymentsp2p' },
{ moduleId: 'ELIT.5.2.6', label: 'Goods Transfer', icon: 'fa fa-archive', routerLink: 'gdstransfers' },
{ moduleId: 'ELIT.5.2.7', label: 'Receive Goods Transfer', icon: 'fa fa-archive', routerLink: 'gdsreceives' },
{ moduleId: 'ELIT.5.2.8', label: 'Fix Transfer Anomalies', icon: 'fa fa-archive', routerLink: 'transferanomalies' },
{ moduleId: 'ELIT.5.2.9', label: 'Consumption of Goods', icon: 'fa fa-archive', routerLink: 'gdscnsmpnsp2p' },
{ moduleId: 'ELIT.5.2.10', label: 'Damage of Goods', icon: 'fa fa-archive', routerLink: 'gdsdamagesp2p' },
{ moduleId: 'ELIT.5.2.11', label: 'Stock Take of Goods', icon: 'fa fa-archive', routerLink: 'stocktakes' }
]
},
{
moduleId: 'ELIT.5.3', label: 'Reports', icon: 'fa fa-newspaper-o',
items:
[
{ moduleId: 'ELIT.5.3.1', label: 'GL Vouchers', icon: 'fa fa-th-list', routerLink: 'glvouchersp2p' },
{ moduleId: 'ELIT.5.3.2', label: 'Current Stock', icon: 'fa fa-file-text-o', routerLink: 'currentstockp2p' },
{ moduleId: 'ELIT.5.3.3', label: 'Stock Ledger', icon: 'fa fa-file-text-o', routerLink: 'stockledgerp2p' },
{ moduleId: 'ELIT.5.3.4', label: 'Supplier Ledger', icon: 'fa fa-file-text-o', routerLink: 'supplierledgerp2p' }
]
}
]
},
{
moduleId: 'ELIT.6', label: 'Fixed Assets', icon: 'fa fa-sitemap',
items:
[
{
moduleId: 'ELIT.6.1', label: 'Basic Setup', icon: 'fa fa-wrench',
items:
[
{ moduleId: 'ELIT.6.1.1', label: 'Asset Types', icon: 'fa fa-address-card-o', routerLink: 'assettypes' },
{ moduleId: 'ELIT.6.1.2', label: 'Asset Categories', icon: 'fa fa-university', routerLink: 'assetcategories' },
{ moduleId: 'ELIT.6.1.3', label: 'Asset Items', icon: 'fa fa-credit-card', routerLink: 'assetitems' },
{ moduleId: 'ELIT.6.1.6', label: 'Asset Hierarchies', icon: 'fa fa-credit-card', routerLink: 'assethierarchies' },
{ moduleId: 'ELIT.6.1.4', label: 'Locations', icon: 'fa fa-pencil-square', routerLink: 'locations' },
{ moduleId: 'ELIT.6.1.5', label: 'VAT & AIT Rates', icon: 'fa fa-user-circle-o', routerLink: 'aitvatratefa' }
]
},
{
moduleId: 'ELIT.6.2', label: 'Regular Activities', icon: 'fa fa-building',
items:
[
{ moduleId: 'ELIT.6.2.0', label: 'Initial Fixed Assets', icon: 'fa fa-puzzle-piece', routerLink: 'initstocksfa' },
{ moduleId: 'ELIT.6.2.15', label: 'Purchase Requisitions', icon: 'fa fa-puzzle-piece', routerLink: 'purchrequisitionsfa' },
{ moduleId: 'ELIT.6.2.1', label: 'Issue Work Orders', icon: 'fa fa-smile-o', routerLink: 'workordersfa' },
{ moduleId: 'ELIT.6.2.14', label: 'Advance Payments', icon: 'fa fa-money', routerLink: 'advpaymntsfa' },
{ moduleId: 'ELIT.6.2.2', label: 'Received Items At CS', icon: 'fa fa-reply-all', routerLink: 'stksrcvdatcsfa' },
{ moduleId: 'ELIT.6.2.3', label: 'Receive Invoices', icon: 'fa fa-money', routerLink: 'invoicesfa' },
{ moduleId: 'ELIT.6.2.4', label: 'Payment Against Invoice', icon: 'fa fa-archive', routerLink: 'paymentsfa' },
{ moduleId: 'ELIT.6.2.5', label: 'Item Acquisition', icon: 'fa fa-smile-o', routerLink: 'itemsacquisition' },
{ moduleId: 'ELIT.6.2.5A', label: 'Update Item Data', icon: 'fa fa-smile-o', routerLink: 'updfaitmdescription' },
{ moduleId: 'ELIT.6.2.6', label: 'Item Installation', icon: 'fa fa-share-square', routerLink: 'astinstalns' },
{ moduleId: 'ELIT.6.2.7', label: 'Transfer Fixed Asset', icon: 'fa fa-money', routerLink: 'assetxfers' },
{ moduleId: 'ELIT.6.2.8', label: 'Write Off', icon: 'fa fa-archive', routerLink: 'astwriteoffs' },
{ moduleId: 'ELIT.6.2.9', label: 'Asset Sales', icon: 'fa fa-archive', routerLink: 'astsales' },
{ moduleId: 'ELIT.6.2.10', label: 'Add Asset Value', icon: 'fa fa-archive', routerLink: 'astaddvalues' },
{ moduleId: 'ELIT.6.2.11', label: 'Reduce Asset Value', icon: 'fa fa-archive', routerLink: 'assetreducesvalue' },
{ moduleId: 'ELIT.6.2.12', label: 'No Depreciation Mark', icon: 'fa fa-user-circle-o', routerLink: 'nodeprnmark' },
{ moduleId: 'ELIT.6.2.13', label: 'Depreciation Calculation', icon: 'fa fa-user-circle-o', routerLink: 'calcdepreciation' }
]
},
{
moduleId: 'ELIT.6.3', label: 'Reports', icon: 'fa fa-newspaper-o',
items:
[
{ moduleId: 'ELIT.6.3.1', label: 'GL Vouchers', icon: 'fa fa-th-list', routerLink: 'glvouchersfa' },
{ moduleId: 'ELIT.6.3.2', label: 'Current Stock', icon: 'fa fa-file-text-o', routerLink: 'currentstockfa' },
{ moduleId: 'ELIT.6.3.3', label: 'Stock Ledger', icon: 'fa fa-file-text-o', routerLink: 'stockledgerfa' },
{ moduleId: 'ELIT.6.3.4', label: 'Item Details', icon: 'fa fa-file-text-o', routerLink: 'faitemdetail' },
{ moduleId: 'ELIT.6.3.5', label: 'Fixed Asset List', icon: 'fa fa-file-text-o', routerLink: 'faitemsatglance' },
{ moduleId: 'ELIT.6.3.6', label: 'Waiting Acquisition', icon: 'fa fa-file-text-o', routerLink: 'faitemswtngaqstn' },
{ moduleId: 'ELIT.6.3.7', label: 'Acquired Assets', icon: 'fa fa-file-text-o', routerLink: 'faitemsacquire' },
{ moduleId: 'ELIT.6.3.8', label: 'Waiting Installation', icon: 'fa fa-file-text-o', routerLink: 'faitemswtnginstall' },
{ moduleId: 'ELIT.6.3.9', label: 'No Depreciation Marked', icon: 'fa fa-file-text-o', routerLink: 'faitemsnodprn' },
{ moduleId: 'ELIT.6.3.10', label: 'Depreciation Report', icon: 'fa fa-file-text-o', routerLink: 'faitemsdprnrpt' },
{ moduleId: 'ELIT.6.3.11', label: 'Supplier Ledger', icon: 'fa fa-file-text-o', routerLink: 'fasupplierledger' }
]
}
]
},
{
moduleId: 'ELIT.7', label: 'Task Management', icon: 'fa fa-users',
items:
[
{
moduleId: 'ELIT.7.1', label: 'Basic Setup', icon: 'fa fa-wrench',
items:
[
{ moduleId: 'ELIT.7.1.1', label: 'Developers', icon: 'fa fa-user-circle-o', routerLink: 'developers' },
{ moduleId: 'ELIT.7.1.2', label: 'Support Teams', icon: 'fa fa-user-circle-o', routerLink: 'supportteams' },
{ moduleId: 'ELIT.7.1.2A', label: 'QA Teams', icon: 'fa fa-user-circle-o', routerLink: 'qateams' },
{ moduleId: 'ELIT.7.1.3', label: 'Priorities', icon: 'fa fa-check-square-o', routerLink: 'priorities' },
{ moduleId: 'ELIT.7.1.4', label: 'Task Statuses', icon: 'fa fa-pencil-square', routerLink: 'taskstatuses' },
{ moduleId: 'ELIT.7.1.5', label: 'Task Categories', icon: 'fa fa-check-square-o', routerLink: 'taskcategories' },
{ moduleId: 'ELIT.7.1.6', label: 'Task Types', icon: 'fa fa-check-square-o', routerLink: 'tasktypes' },
{ moduleId: 'ELIT.7.1.7', label: 'Team Spaces', icon: 'fa fa-user-circle-o', routerLink: 'teamspaces' },
{ moduleId: 'ELIT.7.1.9', label: 'User Projects', icon: 'fa fa-product-hunt', routerLink: 'tsuserprojects' }
]
},
{
moduleId: 'ELIT.7.2', label: 'Regular Activities', icon: 'fa fa-building',
items:
[
{ moduleId: 'ELIT.7.2.1', label: 'Tasks', icon: 'fa fa-smile-o', routerLink: 'tasks' },
{ moduleId: 'ELIT.7.2.2', label: 'My Tasks', icon: 'fa fa-tasks', routerLink: 'mytasks' }
]
},
{
moduleId: 'ELIT.7.3', label: 'Reports', icon: 'fa fa-newspaper-o',
items: [{ moduleId: 'ELIT.7.3.1', label: 'Task Reports', icon: 'fa fa-file-text-o', routerLink: 'taskreport' }]
}
]
},
{
moduleId: 'ELIT.8', label: 'Attendance System', icon: 'fa fa-calendar',
items:
[
{
moduleId: 'ELIT.8.1', label: 'Basic Setup', icon: 'fa fa-wrench',
items:
[
{ moduleId: 'ELIT.8.1.1', label: 'Ip/Mac Entries', icon: 'fa fa-user-circle-o', routerLink: 'ipmacentries' },
{ moduleId: 'ELIT.8.1.2', label: 'Holiday Calendar', icon: 'fa fa-calendar', routerLink: 'calendar' },
{ moduleId: 'ELIT.8.1.3', label: 'Ramadan Calendar', icon: 'fa fa-calendar', routerLink: 'ramadan' }
]
},
{
moduleId: 'ELIT.8.2', label: 'Regular Activities', icon: 'fa fa-building',
items:
[
{ moduleId: 'ELIT.8.2.1', label: 'Employee Leaves', icon: 'fa fa-smile-o', routerLink: 'empleaves' },
{ moduleId: 'ELIT.8.2.2', label: 'Late Entries', icon: 'fa fa-tasks', routerLink: 'emplates' },
{ moduleId: 'ELIT.8.2.3', label: 'Client Visits', icon: 'fa fa-tasks', routerLink: 'empclntvsts' },
{ moduleId: 'ELIT.8.2.4', label: 'Home Offices', icon: 'fa fa-tasks', routerLink: 'emphmofcs' },
{ moduleId: 'ELIT.8.2.6', label: 'Asset Bookings', icon: 'fa fa-tasks', routerLink: 'assetbookings' }
]
},
{
moduleId: 'ELIT.8.3', label: 'Reports', icon: 'fa fa-newspaper-o',
items:
[
{ moduleId: 'ELIT.8.3.1', label: 'Login History', icon: 'fa fa-tasks', routerLink: 'loginhistory' },
{ moduleId: 'ELIT.8.3.2', label: 'Attendance Reports', icon: 'fa fa-file-text-o', routerLink: 'atnreports' }
]
}
]
}
];
/**
* Keeps the nodes the user is permitted to see.
*
* A group is kept only when its own key is permitted and at least one of its children survives,
* a page is kept only when its key is permitted and the route is actually registered in the app,
* so a permitted but not yet implemented page never lands in the sidebar as a dead link.
*/
export function buildPermittedMenu(nodes: MenuNode[], moduleIds: string[], routeExists: (routerLink: string) => boolean): MenuNode[]
{
const permitted: MenuNode[] = [];
for (const node of nodes ?? [])
{
if (node.moduleId && !moduleIds?.includes(node.moduleId))
continue;
if (node.items?.length)
{
const items = buildPermittedMenu(node.items, moduleIds, routeExists);
if (items.length)
permitted.push({ moduleId: node.moduleId, label: node.label, icon: node.icon, items: items });
}
else if (node.routerLink && routeExists(node.routerLink))
{
permitted.push({ moduleId: node.moduleId, label: node.label, icon: node.icon, routerLink: node.routerLink });
}
}
return permitted;
}

View File

@ -1,35 +1,39 @@
export class UserModel
{
id: number;
logId: number;
expires?: Date;
loginId: string;
userName: string;
systemDate: Date;
idleTime: number;
pingTime: number;
loginTime: string;
logoutTime?: Date;
validUser: boolean;
employeeId: number;
timeoutTime: number;
loginStatus: number;
bmProcessId: number;
prProcessId: number;
dbOnStartup: boolean;
batchEnabled: boolean;
viewOwnTaskOnly: boolean;
authMethod: number;
notificationCount: number;
pwdChangeRequired: boolean;
authenticationToken?: string;
authRequiredAtLogin: boolean;
idsValue: string;
moduleIds: any[] = [];
//Returned by the login endpoint since the token based authentication
refreshToken?: string;
userPlatFormType?: number;
userRoleType?: string;
employeeNumber?: string;
//Permission keys of the logged in user, "<moduleId>" = select, "_1" = add, "_2" = edit, "_3" = delete
moduleIds: string[] = [];
returnStatus: number;
returnMessage: any[] = [];
validationErrors: any[] = [];
//Not returned by the login endpoint anymore, defaults are applied on the client
logId?: number;
idleTime?: number;
pingTime?: number;
employeeId?: number;
timeoutTime?: number;
dbOnStartup?: boolean;
schemeName: string = 'dark';
themeName: string = 'yellow';
menuLayout: string = 'overlay';

View File

@ -67,9 +67,6 @@ export class LoginComponent implements OnInit
debugger;
if (this.authService.currentUserValue?.id)
{
if (this.authService.currentUserValue.dbOnStartup)
this.router.navigate(['/dashboard']);
else
this.router.navigate(['/home']);
}
else
@ -270,7 +267,40 @@ export class LoginComponent implements OnInit
{
this.loading = false;
this.spinnerService.hide();
//The endpoint answers 200 only for a valid user, guard anyway so a partial
//response never leaves a half logged in session behind
if (!data?.validUser || !data.id || !data.authenticationToken)
{
this.abortLogin(this.parseReturnMessage(data) || 'Login failed, please try again.');
return;
}
if (data.authRequiredAtLogin)
{
this.verifyOtp(data, sysDate, loginInf);
}
else if (data.pwdChangeRequired)
{
this.changePassword(data);
}
else
{
this.completeLogin(data, sysDate, loginInf);
}
},
error: (errorMessage: string) =>
{
this.loading = false;
this.spinnerService.hide();
this.abortLogin(errorMessage);
}
});
}
/** Second factor, the user is cached already so the otp call travels with the token. */
private verifyOtp(data: any, sysDate: string, loginInf: string): void
{
this.authService.loggedIn(data, false);
const modalOptions: NgbModalOptions = { backdrop: 'static', keyboard: false, backdropClass: 'customBackdrop', centered: true, windowClass: 'misc-width' };
@ -279,70 +309,37 @@ export class LoginComponent implements OnInit
modalRef.componentInstance.fromParent = { userId: data.id, authMethod: data.authMethod };
modalRef.result.then((result) =>
{
if (result)
if (!result)
{
this.abortLogin('');
return;
}
if (data.pwdChangeRequired)
{
this.authService.loggedIn(data, false);
const modalOptions1: NgbModalOptions = { backdrop: 'static', keyboard: false, backdropClass: 'customBackdrop', centered: true };
const modalRef1 = this.modalService.open(ChangePwdComponent, modalOptions1);
modalRef1.componentInstance.fromParent = { userId: data.id, loginId: data.loginId, userName: data.userName };
modalRef1.result.then(() =>
{
this.authService.loggedout();
});
}
this.changePassword(data);
else
{
this.authService.refreshToken().subscribe(
{
next: () =>
{
localStorage.setItem('OnlineSalesAutoCrop.sysdate', sysDate);
if (this.f.remberMe.value)
localStorage.setItem('OnlineSalesAutoCrop.rlap', loginInf);
else
localStorage.removeItem('OnlineSalesAutoCrop.rlap');
this.idleSvc.startMonitor();
this.authService.raiseLoginEvent(data.id, true);
const returnUrl = this.authService.currentUserValue?.dbOnStartup ? this.route.snapshot.queryParams['returnUrl'] ?? '/dashboard' : this.route.snapshot.queryParams['returnUrl'] ?? '/home';
this.router.navigate([returnUrl]);
this.completeLogin(data, sysDate, loginInf);
},
error: () =>
{
this.authService.loggedout();
() => this.abortLogin(''));
}
});
}
}
else
{
localStorage.removeItem('OnlineSalesAutoCrop.rlap');
localStorage.removeItem('OnlineSalesAutoCrop.sysdate');
this.authService.loggedout();
}
});
}
else if (data.pwdChangeRequired)
/** Expired/first time password, the user has to sign in again with the new one. */
private changePassword(data: any): void
{
this.authService.loggedIn(data, false);
const modalOptions: NgbModalOptions = { backdrop: 'static', keyboard: false, backdropClass: 'customBackdrop', centered: true };
const modalRef = this.modalService.open(ChangePwdComponent, modalOptions);
modalRef.componentInstance.fromParent = { userId: data.id, loginId: data.loginId, userName: data.userName };;
modalRef.componentInstance.fromParent = { userId: data.id, loginId: data.loginId, userName: data.userName };
modalRef.result.then(() =>
{
this.authService.loggedout();
});
modalRef.result.then(() => this.authService.loggedout(), () => this.authService.loggedout());
}
else
{
this.authService.refreshToken().subscribe(
{
next: () =>
/**
* The login response already carries the authentication token, the refresh token and the
* permission keys (moduleIds), so nothing else has to be fetched before entering the app.
* Raising the login event makes the sidebar rebuild itself from those permission keys.
*/
private completeLogin(data: any, sysDate: string, loginInf: string): void
{
localStorage.setItem('OnlineSalesAutoCrop.sysdate', sysDate);
if (this.f.remberMe.value)
@ -352,28 +349,25 @@ export class LoginComponent implements OnInit
this.idleSvc.startMonitor();
this.authService.raiseLoginEvent(data.id, true);
const returnUrl = this.authService.currentUserValue?.dbOnStartup ? this.route.snapshot.queryParams['returnUrl'] ?? '/dashboard' : this.route.snapshot.queryParams['returnUrl'] ?? '/home';
this.router.navigate([returnUrl]);
},
error: () =>
{
localStorage.removeItem('OnlineSalesAutoCrop.rlap');
localStorage.removeItem('OnlineSalesAutoCrop.sysdate');
this.authService.loggedout();
const returnUrl = this.route.snapshot.queryParams['returnUrl'] ?? '/home';
this.router.navigate([returnUrl]);
}
});
}
},
error: (errorMessage: string) =>
private abortLogin(errorMessage: string): void
{
this.loading = false;
this.spinnerService.hide();
localStorage.removeItem('OnlineSalesAutoCrop.rlap');
localStorage.removeItem('OnlineSalesAutoCrop.sysdate');
this.authService.loggedout();
if (errorMessage)
this.alertService.error(errorMessage);
}
});
private parseReturnMessage(data: any): string
{
return Array.isArray(data?.returnMessage) ? data.returnMessage.join('<br/>') : '';
}
}

View File

@ -25,7 +25,7 @@ export class AuthGuard
let keys: string[] = permissionkey.split(',');
for (const key of keys)
{
found = currentUser.moduleIds.includes(key);
found = currentUser.moduleIds?.includes(key) === true;
if (found)
break;
}
@ -33,7 +33,7 @@ export class AuthGuard
if (found === false)
this.router.navigate(['/accessdenied']);
return true;
return found;
}
}
else

View File

@ -59,12 +59,7 @@ export class AuthenticationService
return this.api.postAsync<any>('v1/users/login', params)
.pipe(map(user =>
{
user.moduleIds = JSON.parse(this.functionService.decipherData(user.idsValue));
user.idsValue = '';
const encString: string = this.functionService.encrypt(JSON.stringify(user));
localStorage.setItem('OnlineSalesAutoCrop.currentUser', encString);
this.currentUserSubject.next(user);
this.storeCurrentUser(user);
return user;
}));
}
@ -74,6 +69,31 @@ export class AuthenticationService
}
}
/**
* The login endpoint returns the permission keys in `moduleIds` and no longer returns the
* theme/idle settings, so the client defaults are applied here before the user is cached.
*/
public storeCurrentUser(user: any): void
{
user.moduleIds = user.moduleIds ?? [];
const defaults = new UserModel();
user.themeName = user.themeName || defaults.themeName;
user.schemeName = user.schemeName || defaults.schemeName;
user.menuLayout = user.menuLayout || defaults.menuLayout;
const encString: string = this.functionService.encrypt(JSON.stringify(user));
localStorage.setItem('OnlineSalesAutoCrop.currentUser', encString);
this.currentUserSubject.next(user);
}
/** True when the permission key is granted, pass "<moduleId>_1|_2|_3" to check add/edit/delete. */
public hasPermission(moduleId: string): boolean
{
return this.currentUserValue?.moduleIds?.includes(moduleId) === true;
}
public refreshToken()
{
try
@ -99,13 +119,13 @@ export class AuthenticationService
{
try
{
if (!this.currentUserValue?.logId)
if (!this.currentUserValue?.id)
{
localStorage.removeItem('OnlineSalesAutoCrop.currentUser');
return;
}
const params: any = { logId: this.currentUserValue.logId, attendanceLogout: attendanceLogout, ipAddress: ipAddress, macAddress: macAddress, hostName: hostName, logoutRemarks: logoutRemarks };
const params: any = { logId: this.currentUserValue.logId ?? 0, attendanceLogout: attendanceLogout, ipAddress: ipAddress, macAddress: macAddress, hostName: hostName, logoutRemarks: logoutRemarks };
return this.api.postAsync<any>('v1/users/logout', params)
.pipe(map(value =>
{
@ -142,7 +162,7 @@ export class AuthenticationService
{
try
{
const params: any = { logId: this.currentUserValue.logId };
const params: any = { logId: this.currentUserValue?.logId ?? 0 };
return this.api.postAsync<any>('v1/users/sessionExpired', params)
.pipe(map(value =>
{