add refresh token

This commit is contained in:
dibakor 2026-06-15 18:26:58 +06:00
parent 6ced7f4884
commit 0d5b708044
12 changed files with 5196 additions and 2859 deletions

View File

@ -53,6 +53,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Global
public string JwtAudience { get; set; } public string JwtAudience { get; set; }
public bool JwtValidateIssuer { get; set; } public bool JwtValidateIssuer { get; set; }
public bool JwtValidateAudience { get; set; } public bool JwtValidateAudience { get; set; }
public int RefreshTokenDuration { get; set; }
/// <summary> /// <summary>
/// Folder management /// Folder management
@ -85,6 +86,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Global
public string EmailSenderIp { get; set; } public string EmailSenderIp { get; set; }
public string EmailSenderId { get; set; } public string EmailSenderId { get; set; }
private string _emailSenderPwd; private string _emailSenderPwd;
public string EmailSenderPwd public string EmailSenderPwd
{ {

View File

@ -7,8 +7,9 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems
public const int SuperUser_Id = -9; public const int SuperUser_Id = -9;
public const string SuperUser_LoginId = "superuser"; public const string SuperUser_LoginId = "superuser";
public int Id { get; set; } public int UserId { get; set; }
public string LoginId { get; set; } public string LoginId { get; set; }
public string Password { get; set; }
public DateTime? LogoutTime { get; set; } public DateTime? LogoutTime { get; set; }
public string UserName { get; set; } public string UserName { get; set; }
public EnumStatus Status { get; set; } public EnumStatus Status { get; set; }
@ -25,6 +26,9 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems
public string SchemeName { get; set; } public string SchemeName { get; set; }
public string MenuLayout { get; set; } public string MenuLayout { get; set; }
public bool IsLocked { get; set; } public bool IsLocked { get; set; }
public EnumLoginStatus LoginStatus { get; set; }
public DateTime? NextLoginTime { get; set; }
} }
public class LoginHistory public class LoginHistory
@ -38,12 +42,14 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems
public class AccessLog public class AccessLog
{ {
public int AccessLogId { get; set; }
public int UserId { get; set; }
public string LoginId { get; set; } public string LoginId { get; set; }
public DateTime LoginTime { get; set; } public DateTime LoginTime { get; set; }
public string LoginIp { get; set; } public string LoginIp { get; set; }
public DateTime? LogoutTime { get; set; } public DateTime? LogoutTime { get; set; }
public string LogoutIp { get; set; } public string LogoutIp { get; set; }
public string LoginStatus { get; set; } public EnumLoginStatus LoginStatus { get; set; }
} }
public class UserForceLogout public class UserForceLogout

View File

@ -0,0 +1,38 @@
using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using System.Text;
namespace OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations;
public class IntegrstionLoginRequest
{
[Required, NotNull, StringLength(maximumLength: 150, MinimumLength = 3, ErrorMessage = "Login Id must be between 4 and 30 characters.")]
public string LoginId { get; set; }
[Required, NotNull, StringLength(maximumLength: 150, MinimumLength = 5, ErrorMessage = "Password must be between 1 and 30 characters.")]
public string Password { get; set; }
}
public class IntegrationRefreshTokenRequest
{
public string RefreshToken { get; set; }
}
public class InsertRefreshTokenRequest : RefreshToken
{
public string RefreshToken { get; set; }
}
public class RevokedRefreshTokenRequest
{
public string RefreshToken { get; set; }
}
public class GenerateRefreshTokenRequest
{
public int UserId { get; set; }
public string IpAddress { get; set; }
}

View File

@ -3,16 +3,16 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace OnlineSalesAutoCrop.CoreAPI.Models.Requests.Systems namespace OnlineSalesAutoCrop.CoreAPI.Models.Requests.Systems;
public class FindAccountRequest
{ {
public class FindAccountRequest
{
[Required, NotNull, StringLength(maximumLength: 200, MinimumLength = 4, ErrorMessage = "Login Id or Email address or Mobile number must be between 4 and 100 characters.")] [Required, NotNull, StringLength(maximumLength: 200, MinimumLength = 4, ErrorMessage = "Login Id or Email address or Mobile number must be between 4 and 100 characters.")]
public string AccountId { get; set; } public string AccountId { get; set; }
} }
public class LoginRequest public class LoginRequest
{ {
[Required, NotNull, StringLength(maximumLength: 150, MinimumLength = 4, ErrorMessage = "Login Id must be between 4 and 30 characters.")] [Required, NotNull, StringLength(maximumLength: 150, MinimumLength = 4, ErrorMessage = "Login Id must be between 4 and 30 characters.")]
public string LoginId { get; set; } public string LoginId { get; set; }
@ -29,64 +29,64 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Requests.Systems
public string IpAddress { get; set; } public string IpAddress { get; set; }
public string MacAddress { get; set; } public string MacAddress { get; set; }
public string LoginRemarks { get; set; } public string LoginRemarks { get; set; }
} }
public class OtpValidationRequest public class OtpValidationRequest
{ {
public int UserId { get; set; } public int UserId { get; set; }
public EnumAuthenticationMethod AuthMethod { get; set; } public EnumAuthenticationMethod AuthMethod { get; set; }
[Required, NotNull, StringLength(6, MinimumLength = 6, ErrorMessage = "Otp must be 6 digit number.")] [Required, NotNull, StringLength(6, MinimumLength = 6, ErrorMessage = "Otp must be 6 digit number.")]
public string OtpCode { get; set; } public string OtpCode { get; set; }
} }
public class ByUserIdRequest public class ByUserIdRequest
{ {
public int UserId { get; set; } public int UserId { get; set; }
} }
public class SendPasswordRequest public class SendPasswordRequest
{ {
[Required, NotNull, StringLength(maximumLength: 300, MinimumLength = 1, ErrorMessage = "User Id is required.")] [Required, NotNull, StringLength(maximumLength: 300, MinimumLength = 1, ErrorMessage = "User Id is required.")]
public string UserId { get; set; } public string UserId { get; set; }
public string MobileNo { get; set; } public string MobileNo { get; set; }
public string EmailAddress { get; set; } public string EmailAddress { get; set; }
} }
public class UserUnlockRequest : ByUserIdRequest public class UserUnlockRequest : ByUserIdRequest
{ {
[Required, NotNull, StringLength(maximumLength: 30, MinimumLength = 1, ErrorMessage = "Login Id must be between 1 and 30 characters.")] [Required, NotNull, StringLength(maximumLength: 30, MinimumLength = 1, ErrorMessage = "Login Id must be between 1 and 30 characters.")]
public string LoginId { get; set; } public string LoginId { get; set; }
} }
public class ResetPasswordRequest : ByUserIdRequest public class ResetPasswordRequest : ByUserIdRequest
{ {
[Required, NotNull, StringLength(maximumLength: 30, MinimumLength = 1, ErrorMessage = "Password must be between 1 and 30 characters.")] [Required, NotNull, StringLength(maximumLength: 30, MinimumLength = 1, ErrorMessage = "Password must be between 1 and 30 characters.")]
public string Password { get; set; } public string Password { get; set; }
[Required, NotNull, StringLength(maximumLength: 30, MinimumLength = 1, ErrorMessage = "Confirm Password must be between 1 and 30 characters.")] [Required, NotNull, StringLength(maximumLength: 30, MinimumLength = 1, ErrorMessage = "Confirm Password must be between 1 and 30 characters.")]
public string ConfirmPassword { get; set; } public string ConfirmPassword { get; set; }
} }
public class PasswordChangeRequest : ResetPasswordRequest public class PasswordChangeRequest : ResetPasswordRequest
{ {
[Required, NotNull, StringLength(maximumLength: 30, MinimumLength = 1, ErrorMessage = "Old Password must be between 1 and 30 characters.")] [Required, NotNull, StringLength(maximumLength: 30, MinimumLength = 1, ErrorMessage = "Old Password must be between 1 and 30 characters.")]
public string OldPassword { get; set; } public string OldPassword { get; set; }
} }
public class LogoutRequest public class LogoutRequest
{ {
public int LogId { get; set; } public int LogId { get; set; }
public bool AttendanceLogout { get; set; } public bool AttendanceLogout { get; set; }
public string IpAddress { get; set; } public string IpAddress { get; set; }
public string MacAddress { get; set; } public string MacAddress { get; set; }
public string HostName { get; set; } public string HostName { get; set; }
public string LogoutRemarks { get; set; } public string LogoutRemarks { get; set; }
} }
public class UserThemeRequest public class UserThemeRequest
{ {
[Required, NotNull, StringLength(15, MinimumLength = 1, ErrorMessage = "Menu Layout must be between 1 to 15 characters.")] [Required, NotNull, StringLength(15, MinimumLength = 1, ErrorMessage = "Menu Layout must be between 1 to 15 characters.")]
public string MenuLayout { get; set; } public string MenuLayout { get; set; }
@ -95,10 +95,10 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Requests.Systems
[Required, NotNull, StringLength(10, MinimumLength = 1, ErrorMessage = "Scheme Name must be between 1 to 10 characters.")] [Required, NotNull, StringLength(10, MinimumLength = 1, ErrorMessage = "Scheme Name must be between 1 to 10 characters.")]
public string SchemeName { get; set; } public string SchemeName { get; set; }
} }
public class UserRequestBase public class UserRequestBase
{ {
[Required, NotNull, StringLength(30, MinimumLength = 3, ErrorMessage = "Login Id must be between 3 to 30 characters.")] [Required, NotNull, StringLength(30, MinimumLength = 3, ErrorMessage = "Login Id must be between 3 to 30 characters.")]
public string LoginId { get; set; } public string LoginId { get; set; }
@ -126,43 +126,43 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Requests.Systems
public EnumAuthenticationMethod AuthMethod { get; set; } public EnumAuthenticationMethod AuthMethod { get; set; }
public EnumAccessStatus AccessStatus { get; set; } public EnumAccessStatus AccessStatus { get; set; }
public List<int> GroupIds { get; set; } public List<int> GroupIds { get; set; }
} }
public class UserRequest : UserRequestBase public class UserRequest : UserRequestBase
{ {
[Required, Range(minimum: 1, maximum: int.MaxValue, ConvertValueInInvariantCulture = true, ErrorMessage = "Select valid user (1 to 99999999).")] [Required, Range(minimum: 1, maximum: int.MaxValue, ConvertValueInInvariantCulture = true, ErrorMessage = "Select valid user (1 to 99999999).")]
public int UserId { get; set; } public int UserId { get; set; }
public string AuthKey { get; set; } public string AuthKey { get; set; }
} }
public class NewUserRequest : UserRequestBase public class NewUserRequest : UserRequestBase
{ {
public int? EmployeeId { get; set; } public int? EmployeeId { get; set; }
public string EmployeeCode { get; set; } public string EmployeeCode { get; set; }
[Required, NotNull, StringLength(30, MinimumLength = 1, ErrorMessage = "User Name must be between 1 to 30 characters.")] [Required, NotNull, StringLength(30, MinimumLength = 1, ErrorMessage = "User Name must be between 1 to 30 characters.")]
public string Password { get; set; } public string Password { get; set; }
} }
public class UserSearchRequest : ValueStatusAndPageAndSortSearchRequest public class UserSearchRequest : ValueStatusAndPageAndSortSearchRequest
{ {
public bool CheckOwner { get; set; } public bool CheckOwner { get; set; }
} }
public class ForceUserLogoutRequest public class ForceUserLogoutRequest
{ {
public List<int> UserIds { get; set; } public List<int> UserIds { get; set; }
} }
public class ByUserAttributesRequest : ByUserIdRequest public class ByUserAttributesRequest : ByUserIdRequest
{ {
[Required, Range(minimum: 1, maximum: 3, ConvertValueInInvariantCulture = true, ErrorMessage = "Client type must be 1 to 3")] [Required, Range(minimum: 1, maximum: 3, ConvertValueInInvariantCulture = true, ErrorMessage = "Client type must be 1 to 3")]
public int ClientType { get; set; } public int ClientType { get; set; }
} }
public class UpdateMyInfoRequest public class UpdateMyInfoRequest
{ {
[Required, Range(minimum: 1, maximum: int.MaxValue, ConvertValueInInvariantCulture = true, ErrorMessage = "Employee Id must be 1 to 2147483647")] [Required, Range(minimum: 1, maximum: int.MaxValue, ConvertValueInInvariantCulture = true, ErrorMessage = "Employee Id must be 1 to 2147483647")]
public int EmployeeId { get; set; } public int EmployeeId { get; set; }
@ -170,52 +170,51 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Requests.Systems
public string Address { get; set; } public string Address { get; set; }
public string ContactNo { get; set; } public string ContactNo { get; set; }
} }
public class UserAttributesRequest public class UserAttributesRequest
{ {
[Required, Range(minimum: 1, maximum: int.MaxValue, ConvertValueInInvariantCulture = true, ErrorMessage = "Select valid user")] [Required, Range(minimum: 1, maximum: int.MaxValue, ConvertValueInInvariantCulture = true, ErrorMessage = "Select valid user")]
public int UserId { get; set; } public int UserId { get; set; }
[Required, Range(minimum: 1, maximum: 3, ConvertValueInInvariantCulture = true, ErrorMessage = "Client type must be 1 to 3")] [Required, Range(minimum: 1, maximum: 3, ConvertValueInInvariantCulture = true, ErrorMessage = "Client type must be 1 to 3")]
public int ClientType { get; set; } public int ClientType { get; set; }
public List<string> UkIds { get; set; } public List<string> UkIds { get; set; }
} }
public class AccessLogSearchRequest public class AccessLogSearchRequest
{ {
public int AccessType { get; set; } public int AccessType { get; set; }
public string LoginId { get; set; } public string LoginId { get; set; }
public DateTime StartDate { get; set; } public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; } public DateTime EndDate { get; set; }
} }
public class ByTeamSpaceAndBasicUserRequest public class ByTeamSpaceAndBasicUserRequest
{ {
public int TeamSpaceId { get; set; } public int TeamSpaceId { get; set; }
public int ProjectId { get; set; } public int ProjectId { get; set; }
} }
public class BasicUserByTeamSpaceRequest : ByTeamSpaceAndBasicUserRequest public class BasicUserByTeamSpaceRequest : ByTeamSpaceAndBasicUserRequest
{ {
public int UserId { get; set; } public int UserId { get; set; }
} }
public class BasicUserSearchRequest : BasicUserByTeamSpaceRequest public class BasicUserSearchRequest : BasicUserByTeamSpaceRequest
{ {
public bool ApplyFilter { get; set; } public bool ApplyFilter { get; set; }
} }
public class UserLimitAuthorizeRequest public class UserLimitAuthorizeRequest
{ {
[Required, Range(minimum: 1, maximum: int.MaxValue, ConvertValueInInvariantCulture = true, ErrorMessage = "Select valid user (1 to 99999999).")] [Required, Range(minimum: 1, maximum: int.MaxValue, ConvertValueInInvariantCulture = true, ErrorMessage = "Select valid user (1 to 99999999).")]
public int UserId { get; set; } public int UserId { get; set; }
public decimal MaxAuthorizeAmount { get; set; } public decimal MaxAuthorizeAmount { get; set; }
} }
public class PayslipRequest public class PayslipRequest
{ {
[Required, NotNull] [Required, NotNull]
public DateTime YearMonth { get; set; } public DateTime YearMonth { get; set; }
}
} }

View File

@ -0,0 +1,30 @@
using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems;
using System;
using System.Collections.Generic;
using System.Text;
namespace OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations;
public class IntegrationLoginResponse
{
public string LoginId { get; set; }
public string AccessToken { get; set; } = string.Empty;
public string RefreshToken { get; set; } = string.Empty;
public DateTime AccessTokenExpiry { get; set; }
}
public class RefreshTokenResponse
{
public string UserId { get; set; }
public string TokenHash { get; set; }
public string IpAddress { get; set; }
public DateTime ExpiredAt { get; set; }
public DateTime? RevokedAt { get; set; }
public string DeviceInfo { get; set; }
}
public class GenerateRefreshTokenResponse
{
public string RefreshToken { get; set; }
public DateTime ExpireTime { get; set; }
}

View File

@ -0,0 +1,14 @@
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations;
using System.Threading.Tasks;
namespace OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Auth;
public interface IRefreshTokenService
{
Task<RefreshTokenResponse?> GetByTokenHashAsync(string tokenHash);
Task<bool> AddAsync(InsertRefreshTokenRequest refreshToken);
Task<bool> RevokeAsync(RevokedRefreshTokenRequest token);
Task<bool> RevokeAllForUserAsync(int userId);
Task<GenerateRefreshTokenResponse> GenerateRefreshToken(GenerateRefreshTokenRequest request);
}

View File

@ -34,7 +34,6 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems
Task<FindAccountResponse> FindAccountAsync(string accountId); Task<FindAccountResponse> FindAccountAsync(string accountId);
Task<UserAuthorizeLimitResponse> GetAuthorizeLimitAsync(int userId); Task<UserAuthorizeLimitResponse> GetAuthorizeLimitAsync(int userId);
Task<UserAttributesResponse> GetAttributesAsync(int userId, int clientType); Task<UserAttributesResponse> GetAttributesAsync(int userId, int clientType);
Task<DashboardDataResponse> GetDashboardData(int userId, bool canViewLeave, bool canViewLate, bool canViewClientVisit, bool canViewHomeOffice, bool viewAll);
Task<int> LoadNotificationCountAsync(int userId); Task<int> LoadNotificationCountAsync(int userId);

View File

@ -0,0 +1,226 @@
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml.VariantTypes;
using Ease.NetCore.DataAccess;
using Ease.NetCore.DataAccess.SQL;
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Options;
using MySqlX.XDevAPI.Common;
using OnlineSalesAutoCrop.CoreAPI.Models.Global;
using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Systems;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Auth;
using System;
using System.Data;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Auth;
public class RefreshTokenService : IRefreshTokenService
{
private readonly AppSettings _settings;
public RefreshTokenService(IOptions<AppSettings> settings)
{
_settings = settings.Value;
}
public async Task<bool> AddAsync(InsertRefreshTokenRequest refreshToken)
{
bool returnValue = false;
try
{
using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true);
await AddAsync(tc, refreshToken);
tc.End();
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message, e);
}
return returnValue;
}
private async Task<bool> AddAsync( TransactionContext tc, InsertRefreshTokenRequest refreshToken)
{
bool returnValue = false;
try
{
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.VarChar, pValue: refreshToken.UserId),
SqlHelperExtension.CreateInParam(pName: "@TokenHash", pType: SqlDbType.VarChar, pValue: refreshToken.TokenHash),
SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: refreshToken.IpAddress),
SqlHelperExtension.CreateInParam(pName: "@CreatedAt", pType: SqlDbType.DateTime, pValue: DateTime.Now),
SqlHelperExtension.CreateInParam(pName: "@ExpiredAt", pType: SqlDbType.DateTime, pValue: DateTime.Now.AddMinutes(_settings.RefreshTokenDuration)),
];
_ = await tc.ExecuteNonQuerySpAsync(spName: "dbo.InsertRefreshToken", parameterValues: p);
returnValue = true;
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message, e);
}
return returnValue;
}
public async Task<RefreshTokenResponse> GetByTokenHashAsync(string tokenHash)
{
RefreshTokenResponse response = new();
try
{
using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode);
try
{
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@TokenHash", pType: SqlDbType.VarChar, pValue: tokenHash, size: 10)
];
using (IDataReader dr =await tc.ExecuteReaderSpAsync("dbo.GetRefreshTokenByTokenHash", parameterValues: p))
{
if (dr.Read())
{
response.UserId = dr.GetString(0);
response.TokenHash = dr.GetString(1);
response.IpAddress = dr.GetString(2);
response.ExpiredAt = dr.GetDateTime(3);
response.RevokedAt = dr.IsDBNull(4) ? null: dr.GetDateTime(4);
}
dr.Close();
}
tc.End();
}
catch (Exception ie)
{
tc?.HandleError();
throw DBCustomError.GenerateCustomError(ie);
}
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message, e);
}
return response;
}
public async Task<bool> RevokeAllForUserAsync(int userId)
{
bool returnValue = false;
try
{
using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true);
try
{
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId)
];
_ = await tc.ExecuteNonQuerySpAsync(spName: "dbo.RevokedAllRefreshToken", parameterValues: p);
returnValue = true;
tc.End();
}
catch (Exception ie)
{
tc?.HandleError();
throw DBCustomError.GenerateCustomError(ie);
}
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message, e);
}
return returnValue;
}
public async Task<bool> RevokeAsync(RevokedRefreshTokenRequest token)
{
bool returnValue = false;
try
{
using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true);
try
{
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@RefreshToken", pType: SqlDbType.NVarChar, pValue: token.RefreshToken)
];
_ = await tc.ExecuteNonQuerySpAsync(spName: "dbo.RevokedAllRefreshToken", parameterValues: p);
returnValue = true;
tc.End();
}
catch (Exception ie)
{
tc?.HandleError();
throw DBCustomError.GenerateCustomError(ie);
}
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message, e);
}
return returnValue;
}
public async Task<GenerateRefreshTokenResponse> GenerateRefreshToken()
{
throw new NotImplementedException();
}
// ----- private helpers -----
private async Task<LoginResponse> IssueTokensAsync(TransactionContext tc, User user, string deviceInfo, string ipAddress)
{
var refreshToken = new InsertRefreshTokenRequest
{
UserId = user.UserId,
TokenHash = HashToken(GenerateRowToken()),
IpAddress = ipAddress,
CreatedAt = DateTime.UtcNow,
ExpiresAt = DateTime.UtcNow.AddDays(_settings.RefreshTokenDuration)
};
await AddAsync(tc,refreshToken);
return new LoginResponse
{
AccessToken = accessToken,
RefreshToken = rawRefreshToken,
AccessTokenExpiry = DateTime.UtcNow.AddMinutes(_settings.AccessTokenExpiryMinutes)
};
}
private static string HashToken(string token)
{
var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(token));
return Convert.ToBase64String(bytes);
}
private string GenerateRowToken()
{
var bytes = new byte[64];
using var rng = RandomNumberGenerator.Create();
rng.GetBytes(bytes);
return Convert.ToBase64String(bytes);
}
}

View File

@ -17,6 +17,8 @@ using System.Data;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Auth;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations;
namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems
{ {
@ -36,6 +38,322 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems
public async Task<User> LoginAsync(LoginRequest request, string ipAddress, bool checkPwd) public async Task<User> LoginAsync(LoginRequest request, string ipAddress, bool checkPwd)
{ {
User user = new() { LoginId = request.LoginId, LoginStatus = EnumLoginStatus.Unsuccessful }; User user = new() { LoginId = request.LoginId, LoginStatus = EnumLoginStatus.Unsuccessful };
//try
//{
// string password = EncryptPassword(password: request.Password);
// using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true);
// try
// {
// #region Attendance Login and Valid Ip Address
// if (request.AttendanceLogin && !request.LoginId.ToLower().Equals(User.SuperUser_LoginId))
// {
// string errMsg = string.Empty;
// SqlParameter[] p =
// [
// SqlHelperExtension.CreateInParam(pName: "@IpAddress", 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: "@LoginId", pType: SqlDbType.VarChar, pValue: request.LoginId, size: 50),
// SqlHelperExtension.CreateOutParam(pName: "@ErrMsg", pType: SqlDbType.VarChar, pValue: errMsg, size: 300)
// ];
// _ = tc.ExecuteNonQuerySp(spName: "dbo.IsValidIpOrMacAddress", parameterValues: p);
// errMsg = (p[4] == null || p[4].Value == null || p[4].Value == DBNull.Value) ? string.Empty : Convert.ToString(p[4].Value);
// if (!string.IsNullOrEmpty(errMsg))
// {
// tc.End();
// throw new InvalidOperationException(errMsg);
// }
// }
// #endregion
// bool batchEnabled = false;
// DateTime sysDate = DateTime.Today.Date;
// string appVer = string.Empty, alParams = string.Empty;
// int maxTryCount = 5, lockTime = 1, marMonths = 24, idleTime = 0, timeoutTime = 0, pingTime = 0, bmProcessId = 0, prProcessId = 0;
// #region Read Params from ThisSystem
// using (IDataReader dr = tc.ExecuteReader("SELECT MaxTryCount, LockTime, MarMonths, CAST(GETDATE() as date), AppVersion, AutoLogoutParams, BatchEnabled, BmProcessId, PrProcessId FROM ThisSystem"))
// {
// if (dr.Read())
// {
// maxTryCount = dr.GetInt16(0);
// lockTime = dr.GetInt16(1);
// marMonths = dr.GetInt16(2);
// sysDate = dr.GetDateTime(3);
// appVer = dr.GetString(4);
// alParams = dr.IsDBNull(5) ? string.Empty : dr.GetString(5);
// batchEnabled = !dr.IsDBNull(6) && dr.GetInt16(6) != 0;
// bmProcessId = dr.IsDBNull(7) ? 0 : dr.GetInt16(7);
// prProcessId = dr.IsDBNull(8) ? 0 : dr.GetInt16(8);
// }
// dr.Close();
// }
// if (!string.IsNullOrEmpty(alParams))
// {
// string[] times = alParams.Split(separator: ',', options: StringSplitOptions.RemoveEmptyEntries);
// if (times.Length == 3)
// {
// if (!int.TryParse(times[0], out idleTime))
// idleTime = 0;
// if (!int.TryParse(times[1], out timeoutTime))
// timeoutTime = 0;
// if (!int.TryParse(times[2], out pingTime))
// pingTime = 0;
// }
// }
// if (!request.LoginId.ToLower().Equals(User.SuperUser_LoginId) && !request.AppVersion.Equals(appVer))
// {
// user.UnsuccessfulMsg = appVer;
// user.LoginStatus = EnumLoginStatus.VersionMismatch;
// }
// #endregion
// 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);
// }
// 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),
// TeamSpaceIds = [],
// IdleTime = idleTime,
// PingTime = pingTime,
// SystemDate = sysDate,
// TimeoutTime = timeoutTime,
// PrProcessId = prProcessId,
// BmProcessId = bmProcessId,
// BatchEnabled = batchEnabled,
// LoginStatus = EnumLoginStatus.Success
// };
// }
// dr.Close();
// 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);
// }
// #endregion
// #region If the user was locked, try set set unlock if Time expired
// 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);
// if (isSuccessful == 1)
// user.IsLocked = false;
// }
// #endregion
// #region Keep log for unauthrise access and Set user lock if exceeds max try
// 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 (p[6] != null && p[6].Value != null && p[6].Value != DBNull.Value)
// nextLoginTime = Convert.ToDateTime(p[6].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";
// }
// }
// #endregion
// #region Generate and Save Otp if Otp is enabled and send thru SMS/Email
// 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);
// }
// #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);
// logId = dr.GetInt32(1);
// if (dr.GetInt16(2) != 0) //Alow add
// {
// user.ModuleIds.Add($"{moduleId}_1");
// }
// if (dr.GetInt16(3) != 0) //Alow edit
// {
// user.ModuleIds.Add($"{moduleId}_2");
// }
// if (dr.GetInt16(4) != 0) //Allow Delete
// {
// user.ModuleIds.Add($"{moduleId}_3");
// }
// logoutTime = dr.IsDBNull(5) ? null : dr.GetDateTime(5);
// }
// dr.Close();
// }
// user.LogId = logId;
// user.LogoutTime = logoutTime;
// //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();
// }
// //Pending Notification count
// user.NotificationCount = GetPendingNotifCount(tc: tc, userId: user.Id);
// }
// #endregion
// }
// tc.End();
// }
// catch (Exception ie)
// {
// tc?.HandleError();
// throw DBCustomError.GenerateCustomError(ie);
// }
//}
//catch (Exception e)
//{
// throw new InvalidOperationException(e.Message, e);
//}
return user;
}
public async Task<User> IntegrationLoginAsync(IntegrstionLoginRequest request, string ipAddress, bool checkPwd)
{
User user = null;
try try
{ {
string password = EncryptPassword(password: request.Password); string password = EncryptPassword(password: request.Password);
@ -43,97 +361,14 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems
using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true);
try try
{ {
#region Attendance Login and Valid Ip Address
if (request.AttendanceLogin && !request.LoginId.ToLower().Equals(User.SuperUser_LoginId))
{
string errMsg = string.Empty;
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@IpAddress", 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: "@LoginId", pType: SqlDbType.VarChar, pValue: request.LoginId, size: 50),
SqlHelperExtension.CreateOutParam(pName: "@ErrMsg", pType: SqlDbType.VarChar, pValue: errMsg, size: 300)
];
_ = tc.ExecuteNonQuerySp(spName: "dbo.IsValidIpOrMacAddress", parameterValues: p);
errMsg = (p[4] == null || p[4].Value == null || p[4].Value == DBNull.Value) ? string.Empty : Convert.ToString(p[4].Value);
if (!string.IsNullOrEmpty(errMsg))
{
tc.End();
throw new InvalidOperationException(errMsg);
}
}
#endregion
bool batchEnabled = false;
DateTime sysDate = DateTime.Today.Date; DateTime sysDate = DateTime.Today.Date;
string appVer = string.Empty, alParams = string.Empty; string appVer = string.Empty, alParams = string.Empty;
int maxTryCount = 5, lockTime = 1, marMonths = 24, idleTime = 0, timeoutTime = 0, pingTime = 0, bmProcessId = 0, prProcessId = 0; int maxTryCount = 5, lockTime = 1, marMonths = 24, idleTime = 0, timeoutTime = 0, pingTime = 0, bmProcessId = 0, prProcessId = 0;
#region Read Params from ThisSystem
using (IDataReader dr = tc.ExecuteReader("SELECT MaxTryCount, LockTime, MarMonths, CAST(GETDATE() as date), AppVersion, AutoLogoutParams, BatchEnabled, BmProcessId, PrProcessId FROM ThisSystem"))
{
if (dr.Read())
{
maxTryCount = dr.GetInt16(0);
lockTime = dr.GetInt16(1);
marMonths = dr.GetInt16(2);
sysDate = dr.GetDateTime(3);
appVer = dr.GetString(4);
alParams = dr.IsDBNull(5) ? string.Empty : dr.GetString(5);
batchEnabled = !dr.IsDBNull(6) && dr.GetInt16(6) != 0;
bmProcessId = dr.IsDBNull(7) ? 0 : dr.GetInt16(7);
prProcessId = dr.IsDBNull(8) ? 0 : dr.GetInt16(8);
}
dr.Close();
}
if (!string.IsNullOrEmpty(alParams))
{
string[] times = alParams.Split(separator: ',', options: StringSplitOptions.RemoveEmptyEntries);
if (times.Length == 3)
{
if (!int.TryParse(times[0], out idleTime))
idleTime = 0;
if (!int.TryParse(times[1], out timeoutTime))
timeoutTime = 0;
if (!int.TryParse(times[2], out pingTime))
pingTime = 0;
}
}
if (!request.LoginId.ToLower().Equals(User.SuperUser_LoginId) && !request.AppVersion.Equals(appVer))
{
user.UnsuccessfulMsg = appVer;
user.LoginStatus = EnumLoginStatus.VersionMismatch;
}
#endregion
if (user.LoginStatus != EnumLoginStatus.VersionMismatch)
{
#region Read User data using authentication data #region Read User data using authentication data
string commandText; string commandText = SQLParser.MakeSQL("SELECT UserId,Password,LoginId, UserName, Status, MobileNo, EmailAddress,"
if (!checkPwd) + " AuthKey, AuthValue, IsLocked FROM Users WHERE LoginID=%s", request.LoginId);
{
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);
}
using (IDataReader dr = tc.ExecuteReader(commandText: commandText)) using (IDataReader dr = tc.ExecuteReader(commandText: commandText))
{ {
@ -141,48 +376,35 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems
{ {
user = new User user = new User
{ {
Id = dr.GetInt32(0), UserId = dr.GetInt32(0),
UserName = dr.GetString(1), Password = dr.GetString(1),
Status = (EnumStatus)dr.GetInt16(2), LoginId = dr.GetString(2),
MobileNo = dr.GetString(3), UserName = dr.GetString(3),
EmailAddress = dr.GetString(4), Status = (EnumStatus)dr.GetInt32(4),
AuthRequiredAtLogin = !dr.IsDBNull(5) && dr.GetInt16(5) > 0, MobileNo = dr.GetString(5),
AuthMethod = (EnumAuthenticationMethod)dr.GetInt16(6), EmailAddress = dr.GetString(6),
AuthKey = dr.IsDBNull(7) ? string.Empty : dr.GetString(7), AuthKey = dr.GetString(7),
AppId = dr.IsDBNull(8) ? string.Empty : dr.GetString(8), AuthValue = dr.GetString(8),
AccessStatus = (EnumAccessStatus)dr.GetInt16(9), IsLocked = dr.GetBoolean(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),
TeamSpaceIds = [],
IdleTime = idleTime,
PingTime = pingTime,
SystemDate = sysDate,
TimeoutTime = timeoutTime,
PrProcessId = prProcessId,
BmProcessId = bmProcessId,
BatchEnabled = batchEnabled,
LoginStatus = EnumLoginStatus.Success LoginStatus = EnumLoginStatus.Success
}; };
} }
dr.Close(); dr.Close();
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);
} }
if (user is null)
{
tc.End();
throw new InvalidOperationException($"User not found with login: {request.LoginId}");
}
if (!request.Password.Equals(user.Password))
{
tc.End();
throw new InvalidOperationException($"Password mismatch for login: {request.LoginId}");
}
#endregion #endregion
#region If the user was locked, try set set unlock if Time expired #region If the user was locked, try set set unlock if Time expired
@ -252,84 +474,24 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems
#endregion #endregion
#region Generate and Save Otp if Otp is enabled and send thru SMS/Email
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);
}
#endregion
#region If login successful and user is active read module id for this user #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) if (user.LoginStatus == EnumLoginStatus.Success && user.Status == EnumStatus.Authorized && !user.IsLocked)
{ {
int logId = 0;
DateTime? logoutTime = null;
SqlParameter[] p = SqlParameter[] p =
[ [
SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: user.Id), SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: user.UserId),
SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20), 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: "@LoginId", pType: SqlDbType.VarChar, pValue: user.LoginId, size: 30),
SqlHelperExtension.CreateInParam(pName: "@LockTime", pType: SqlDbType.Int, pValue: lockTime), SqlHelperExtension.CreateInParam(pName: "@LockTime", pType: SqlDbType.Int, pValue: lockTime),
SqlHelperExtension.CreateInParam(pName: "@AttendanceLogin", pType: SqlDbType.Int, pValue: request.AttendanceLogin? 1:0), SqlHelperExtension.CreateInParam(pName: "@LoginTime", pType: SqlDbType.DateTime, pValue: DateTime.Now)
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);
logId = dr.GetInt32(1); _ = await tc.ExecuteNonQuerySpAsync(spName: "dbo.SaveAccessLog", parameterValues: p);
if (dr.GetInt16(2) != 0) //Alow add
{
user.ModuleIds.Add($"{moduleId}_1");
}
if (dr.GetInt16(3) != 0) //Alow edit
{
user.ModuleIds.Add($"{moduleId}_2");
}
if (dr.GetInt16(4) != 0) //Allow Delete
{
user.ModuleIds.Add($"{moduleId}_3");
}
logoutTime = dr.IsDBNull(5) ? null : dr.GetDateTime(5);
}
dr.Close();
}
user.LogId = logId;
user.LogoutTime = logoutTime;
//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();
}
//Pending Notification count
user.NotificationCount = GetPendingNotifCount(tc: tc, userId: user.Id);
} }
#endregion #endregion
}
tc.End(); tc.End();
} }
@ -347,7 +509,6 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems
return user; return user;
} }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
@ -1367,7 +1528,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems
using IDataReader dr = tc.ExecuteReader(commandText: commandText); using IDataReader dr = tc.ExecuteReader(commandText: commandText);
while (dr.Read()) while (dr.Read())
{ {
UserSearch item = new() UserGetResponse item = new()
{ {
UserId = dr.GetInt32(0), UserId = dr.GetInt32(0),
LoginId = dr.GetString(1), LoginId = dr.GetString(1),
@ -1380,10 +1541,8 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems
IsLocked = !dr.IsDBNull(8) && dr.GetInt16(8) != 0, IsLocked = !dr.IsDBNull(8) && dr.GetInt16(8) != 0,
CanUseAttendanceSystem = !dr.IsDBNull(9) && dr.GetInt16(9) != 0, CanUseAttendanceSystem = !dr.IsDBNull(9) && dr.GetInt16(9) != 0,
}; };
item.AuthId = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{item.LoginId}~{dr.GetString(10)}"));
totalRows = dr.GetInt32(11); totalRows = dr.GetInt32(11);
response.Value.Add(item);
} }
dr.Close(); dr.Close();
@ -1946,137 +2105,6 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems
/// <param name="viewAll"></param> /// <param name="viewAll"></param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="InvalidOperationException"></exception> /// <exception cref="InvalidOperationException"></exception>
public async Task<DashboardDataResponse> GetDashboardData(int userId, bool canViewLeave, bool canViewLate, bool canViewClientVisit, bool canViewHomeOffice, bool viewAll)
{
DashboardDataResponse response = new()
{
GroupData = [],
ReturnStatus = 200,
Group1Data = new() { Items = [] },
Group2Data = new() { Items = [] },
Group3Data = new() { Items = [] },
Group4Data = new() { Items = [] },
};
try
{
using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode);
try
{
SqlParameter[] p = [SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId)];
using IDataReader dr = tc.ExecuteReaderSp(spName: "dbo.GetDashBoard", parameterValues: p);
#region Dashboard1 (Project, PO, Invoice, payment)
while (dr.Read())
{
DashboardItem item = new()
{
DivStyleCss = dr.GetString(0),
Title = dr.GetString(1),
TitleCss = dr.GetString(2),
Value = $"{dr.GetInt32(3)}",
ValueCss = dr.GetString(4),
Id = dr.GetInt32(5),
Href = dr.GetString(6),
};
response.GroupData.Add(item);
}
dr.NextResult();
#endregion
#region Dashboard2 (Sales, Acounting, Purchase to Pay, Fixed Assests)
while (dr.Read())
{
int groupId = dr.GetInt16(0);
string groupName = dr.GetString(1);
Dashboard2Item item = new()
{
Title = dr.GetString(2),
Value = dr.GetDecimal(3),
TitleStyle = dr.GetString(4),
ValueStyle = dr.GetString(5),
ValueFormat = dr.GetString(6),
};
if (groupId == 1)
{
response.Group1Data.GroupName = groupName;
response.Group1Data.Items.Add(item);
}
else if (groupId == 2)
{
response.Group2Data.GroupName = groupName;
response.Group2Data.Items.Add(item);
}
else if (groupId == 3)
{
response.Group3Data.GroupName = groupName;
response.Group3Data.Items.Add(item);
}
else if (groupId == 4)
{
response.Group4Data.GroupName = groupName;
response.Group4Data.Items.Add(item);
}
}
dr.NextResult();
#endregion
#region Dashboard1 (Today's Attendance)
StringBuilder sb = new();
while (dr.Read())
{
if (sb.Length > 0)
sb.Append(" | ");
if (dr.GetInt16(0) == 4)
sb.Append(canViewHomeOffice && viewAll ? $"<a class=\"hand-cursor\" href=\"#/emphmofcs\">Home Office: {dr.GetInt32(1)}</a>" : $"Home Office: {dr.GetInt32(1)}");
else if (dr.GetInt16(0) == 3)
sb.Append(canViewClientVisit && viewAll ? $"<a class=\"hand-cursor\" href=\"#/empclntvsts\">Client Visit: {dr.GetInt32(1)}</a>" : $"Client Visit: {dr.GetInt32(1)}");
else if (dr.GetInt16(0) == 2)
sb.Append(canViewLate && viewAll ? $"<a class=\"hand-cursor\" href=\"#/emplates\">Late: {dr.GetInt32(1)}</a>" : $"Late: {dr.GetInt32(1)}");
else
sb.Append(canViewLeave && viewAll ? $"<a class=\"hand-cursor\" href=\"#/empleaves\">Leave: {dr.GetInt32(1)}</a>" : $"Leave: {dr.GetInt32(1)}");
}
dr.Close();
if (sb.Length > 0)
{
int rmndr = (response.GroupData.Count + 1) % 2;
DashboardItem item = new()
{
Id = 999,
Href = "",
Value = sb.ToString(),
Title = "No-shows Today",
TitleCss = "db-style title",
DivStyleCss = rmndr == 1 ? "db-style default" : "db-style alternate",
ValueCss = rmndr == 1 ? "default-detail attendance" : "alternate-detail attendance"
};
response.GroupData.Add(item);
}
#endregion
tc.End();
}
catch (Exception ie)
{
tc?.HandleError();
throw DBCustomError.GenerateCustomError(ie);
}
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message, e);
}
return response;
}
/// <summary> /// <summary>
/// ///
@ -2240,8 +2268,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems
LoginTime = dr.GetDateTime(1), LoginTime = dr.GetDateTime(1),
LoginIp = dr.GetString(2), LoginIp = dr.GetString(2),
LogoutTime = dr.IsDBNull(3) ? null : dr.GetDateTime(3), LogoutTime = dr.IsDBNull(3) ? null : dr.GetDateTime(3),
LogoutIp = dr.IsDBNull(4) ? null : dr.GetString(4), LogoutIp = dr.IsDBNull(4) ? null : dr.GetString(4)
LoginStatus = dr.GetString(5),
}; };
response.Value.Add(item); response.Value.Add(item);

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,6 @@ using OnlineSalesAutoCrop.CoreAPI.Configurations;
using OnlineSalesAutoCrop.CoreAPI.Models; using OnlineSalesAutoCrop.CoreAPI.Models;
using OnlineSalesAutoCrop.CoreAPI.Models.Global; using OnlineSalesAutoCrop.CoreAPI.Models.Global;
using OnlineSalesAutoCrop.CoreAPI.Models.Objects; using OnlineSalesAutoCrop.CoreAPI.Models.Objects;
using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Setups;
using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems; using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests; using OnlineSalesAutoCrop.CoreAPI.Models.Requests;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Setups; using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Setups;
@ -19,7 +18,6 @@ using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Microsoft.Reporting.NETCore;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.DirectoryServices; using System.DirectoryServices;
@ -51,7 +49,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers.V1
[ValidateAntiForgeryToken] [ValidateAntiForgeryToken]
[Route("api/v{version:apiVersion}/users")] [Route("api/v{version:apiVersion}/users")]
public class UserController(IUserService service, IOptions<AppSettings> appSettings, IEaseCache cache, ILogger<UserController> logger, IHubContext<NotificationHub, INotificationHub> hub) : ControllerBase public class UserController(IUserService service, IOptions<AppSettings> appSettings, IEaseCache cache, ILogger<AuthController> logger, IHubContext<NotificationHub, INotificationHub> hub) : ControllerBase
{ {
private readonly ILogger _logger = logger; private readonly ILogger _logger = logger;
private readonly IEaseCache _cache = cache; private readonly IEaseCache _cache = cache;

View File

@ -27,51 +27,6 @@
"CorsMethods": "POST", "CorsMethods": "POST",
"CorsOrigins": "http://localhost:4200,http://localhost:5050,http://localhost:7777", "CorsOrigins": "http://localhost:4200,http://localhost:5050,http://localhost:7777",
"DbConfig": [ "DbConfig": [
{
"ConnectionNode": {
"ConnectionString": "Data Source=103.197.204.162,3341;Initial Catalog=OnlineSalesAutoCrop;User ID=OnlineSalesAutoCropSysUser;Password=OnlineSalesAutoCrop;Encrypt=false;",
"EncryptKey": "",
"Key": "spadb1",
"Provider": "sql",
"SqlSyntax": "SQL"
}
},
{
"ConnectionNode": {
"ConnectionString": "V3DJ2Y3A4Tzp9y3WKGINJWSjQdvo7QxL+U4VfYkfrfbZB8b4sNPPgJM5J17wFLAmaOzvDmGBK0hN5TX8iOYEdntndR1isy1SqPZgoyEshX+87OQYsKVKrl85foY49BJGB75CdAuVXoizsLsDrKcNzw==",
"Key": "spadb2",
"Provider": "sql",
"SqlSyntax": "SQL",
"EncryptKey": "RWFzZURoMTk5MlRvMjAyMw=="
}
},
{
"ConnectionNode": {
"ConnectionString": "M6aE4Nur0oHi9ddFe/IC/VlXYQTfJSafpWzjFeSHfNrozfGF/ZJMBLEMLe9SwVt0SUisTwL+5v5l1TYsYkpn9Dgo+K2H2X+OMsql1pf1vww=",
"Key": "spadb3",
"Provider": "sql",
"SqlSyntax": "SQL",
"EncryptKey": "RWFzZURoMTk5MlRvMjAyMw=="
}
},
{
"ConnectionNode": {
"ConnectionString": "Server=Russel;Initial Catalog=OnlineSalesAutoCrop;Integrated Security=True;TrustServerCertificate=True;Encrypt=true;",
"Key": "spadb4",
"Provider": "sql",
"SqlSyntax": "SQL",
"EncryptKey": ""
}
},
{
"ConnectionNode": {
"ConnectionString": "Data Source=100.100.100.80;Initial Catalog=OnlineSalesAutoCrop;User ID=OnlineSalesAutoCropSysuser;Password=[password];Encrypt=false;",
"Key": "spadb6",
"Provider": "sql",
"SqlSyntax": "SQL",
"EncryptKey": ""
}
},
{ {
"ConnectionNode": { "ConnectionNode": {
"ConnectionString": "Data Source=210.4.65.222,3341;Initial Catalog=OnlineSalesAutoCrop;User ID=OnlineSalesAutoCropSysUser;Password=OnlineSalesAutoCrop;Encrypt=false;", "ConnectionString": "Data Source=210.4.65.222,3341;Initial Catalog=OnlineSalesAutoCrop;User ID=OnlineSalesAutoCropSysUser;Password=OnlineSalesAutoCrop;Encrypt=false;",
@ -128,7 +83,8 @@
"WaAccountSid": "AC0138ad79a532f653c35072dad10e52b9", "WaAccountSid": "AC0138ad79a532f653c35072dad10e52b9",
"WaAuthToken": "024a6897584671d9f9fa588d7c94aa96", "WaAuthToken": "024a6897584671d9f9fa588d7c94aa96",
"WaMsgSvcSid": "MG8401d33a9a3b2aea95619bda3e5757b5", "WaMsgSvcSid": "MG8401d33a9a3b2aea95619bda3e5757b5",
"WaSenderId": "+8801326755660" "WaSenderId": "+8801326755660",
"RefreshTokenDuration": "15"
}, },
"MenuSettings": { "MenuSettings": {