diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Global/AppSettings.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Global/AppSettings.cs index 7769ee2..c58dd84 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Global/AppSettings.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Global/AppSettings.cs @@ -53,11 +53,12 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Global public string JwtAudience { get; set; } public bool JwtValidateIssuer { get; set; } public bool JwtValidateAudience { get; set; } - - /// - /// Folder management - /// - public string UploadFolder { get; set; } + public int RefreshTokenDuration { get; set; } + + /// + /// Folder management + /// + public string UploadFolder { get; set; } public string ProfileImageFolder { get; set; } public string ReportFolder { get; set; } public string FileProcessFolder { get; set; } @@ -85,6 +86,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Global public string EmailSenderIp { get; set; } public string EmailSenderId { get; set; } + private string _emailSenderPwd; public string EmailSenderPwd { diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Objects/Systems/User.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Objects/Systems/User.cs index 615923c..2155064 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Objects/Systems/User.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Objects/Systems/User.cs @@ -7,8 +7,9 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems public const int SuperUser_Id = -9; public const string SuperUser_LoginId = "superuser"; - public int Id { get; set; } + public int UserId { get; set; } public string LoginId { get; set; } + public string Password { get; set; } public DateTime? LogoutTime { get; set; } public string UserName { get; set; } public EnumStatus Status { get; set; } @@ -25,7 +26,10 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems public string SchemeName { get; set; } public string MenuLayout { get; set; } public bool IsLocked { get; set; } - } + public EnumLoginStatus LoginStatus { get; set; } + public DateTime? NextLoginTime { get; set; } + + } public class LoginHistory { @@ -38,13 +42,15 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems public class AccessLog { + public int AccessLogId { get; set; } + public int UserId { get; set; } public string LoginId { get; set; } public DateTime LoginTime { get; set; } public string LoginIp { get; set; } public DateTime? LogoutTime { get; set; } public string LogoutIp { get; set; } - public string LoginStatus { get; set; } - } + public EnumLoginStatus LoginStatus { get; set; } + } public class UserForceLogout { diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/Integrations/IntegrationAuthRequest.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/Integrations/IntegrationAuthRequest.cs new file mode 100644 index 0000000..06d73c9 --- /dev/null +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/Integrations/IntegrationAuthRequest.cs @@ -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; } +} \ No newline at end of file diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/Systems/UserRequest.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/Systems/UserRequest.cs index 303cd6a..29958b3 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/Systems/UserRequest.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/Systems/UserRequest.cs @@ -3,219 +3,218 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; 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.")] - public string AccountId { get; set; } - } - - public class LoginRequest - { - [Required, NotNull, StringLength(maximumLength: 150, MinimumLength = 4, ErrorMessage = "Login Id must be between 4 and 30 characters.")] - public string LoginId { get; set; } - - [Required, NotNull, StringLength(maximumLength: 150, MinimumLength = 1, ErrorMessage = "Password must be between 1 and 30 characters.")] - public string Password { get; set; } - - public string AppId { get; set; } - - [Required, NotNull, StringLength(maximumLength: 8, MinimumLength = 5, ErrorMessage = "Version must be 5 and 8 digits (Example: 0.0.0 or 99.99.99)")] - public string AppVersion { get; set; } - - public bool AttendanceLogin { get; set; } - public string HostName { get; set; } - public string IpAddress { get; set; } - public string MacAddress { get; set; } - public string LoginRemarks { get; set; } - } - - public class OtpValidationRequest - { - public int UserId { get; set; } - - public EnumAuthenticationMethod AuthMethod { get; set; } - - [Required, NotNull, StringLength(6, MinimumLength = 6, ErrorMessage = "Otp must be 6 digit number.")] - public string OtpCode { get; set; } - } - - public class ByUserIdRequest - { - public int UserId { get; set; } - } - - public class SendPasswordRequest - { - [Required, NotNull, StringLength(maximumLength: 300, MinimumLength = 1, ErrorMessage = "User Id is required.")] - public string UserId { get; set; } - public string MobileNo { get; set; } - public string EmailAddress { get; set; } - } - - public class UserUnlockRequest : ByUserIdRequest - { - [Required, NotNull, StringLength(maximumLength: 30, MinimumLength = 1, ErrorMessage = "Login Id must be between 1 and 30 characters.")] - public string LoginId { get; set; } - } - - public class ResetPasswordRequest : ByUserIdRequest - { - [Required, NotNull, StringLength(maximumLength: 30, MinimumLength = 1, ErrorMessage = "Password must be between 1 and 30 characters.")] - public string Password { get; set; } - - [Required, NotNull, StringLength(maximumLength: 30, MinimumLength = 1, ErrorMessage = "Confirm Password must be between 1 and 30 characters.")] - public string ConfirmPassword { get; set; } - } - - public class PasswordChangeRequest : ResetPasswordRequest - { - [Required, NotNull, StringLength(maximumLength: 30, MinimumLength = 1, ErrorMessage = "Old Password must be between 1 and 30 characters.")] - public string OldPassword { get; set; } - } - - public class LogoutRequest - { - public int LogId { get; set; } - public bool AttendanceLogout { get; set; } - public string IpAddress { get; set; } - public string MacAddress { get; set; } - public string HostName { get; set; } - public string LogoutRemarks { get; set; } - } - - public class UserThemeRequest - { - [Required, NotNull, StringLength(15, MinimumLength = 1, ErrorMessage = "Menu Layout must be between 1 to 15 characters.")] - public string MenuLayout { get; set; } - - [Required, NotNull, StringLength(15, MinimumLength = 1, ErrorMessage = "Theme Name must be between 1 to 15 characters.")] - public string ThemeName { get; set; } - - [Required, NotNull, StringLength(10, MinimumLength = 1, ErrorMessage = "Scheme Name must be between 1 to 10 characters.")] - public string SchemeName { get; set; } - } - - public class UserRequestBase - { - [Required, NotNull, StringLength(30, MinimumLength = 3, ErrorMessage = "Login Id must be between 3 to 30 characters.")] - public string LoginId { get; set; } - - [Required, NotNull, StringLength(75, MinimumLength = 3, ErrorMessage = "User Name must be between 3 to 75 characters.")] - public string UserName { get; set; } - - [Required, NotNull, StringLength(50, MinimumLength = 0, ErrorMessage = "Designation must be between 0 to 50 characters.")] - public string Designation { get; set; } - - [StringLength(15, MinimumLength = 11, ErrorMessage = "Mobile number must be 11 characters.")] - [RegularExpression(@"^[01]{2}[123456789]{1}[0-9]{8}$", ErrorMessage = "Mobile number is invalid.")] - public string MobileNo { get; set; } - - [Required, NotNull, StringLength(100, MinimumLength = 5, ErrorMessage = "Email address must be between 5 to 100 characters.")] - [RegularExpression(@"^(([a-zA-Z0-9_\-\.]+)\@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)(\s*;\s*|\s*$))*$", ErrorMessage = "Email address is invalid.")] - public string EmailAddress { get; set; } = string.Empty; - public bool AuthReqAtlogin { get; set; } - public bool NeverExpire { get; set; } - public bool DbOnStartup { get; set; } - public bool DisallowMultiLogin { get; set; } - public EnumStatus Status { get; set; } - public short SeqId { get; set; } - public bool ViewToAll { get; set; } - public bool CanUseAttendanceSystem { get; set; } - public EnumAuthenticationMethod AuthMethod { get; set; } - public EnumAccessStatus AccessStatus { get; set; } - public List GroupIds { get; set; } - } - - public class UserRequest : UserRequestBase - { - [Required, Range(minimum: 1, maximum: int.MaxValue, ConvertValueInInvariantCulture = true, ErrorMessage = "Select valid user (1 to 99999999).")] - public int UserId { get; set; } - - public string AuthKey { get; set; } - } - - public class NewUserRequest : UserRequestBase - { - public int? EmployeeId { get; set; } - public string EmployeeCode { get; set; } - - [Required, NotNull, StringLength(30, MinimumLength = 1, ErrorMessage = "User Name must be between 1 to 30 characters.")] - public string Password { get; set; } - } - - public class UserSearchRequest : ValueStatusAndPageAndSortSearchRequest - { - public bool CheckOwner { get; set; } - } - - public class ForceUserLogoutRequest - { - public List UserIds { get; set; } - } - - public class ByUserAttributesRequest : ByUserIdRequest - { - [Required, Range(minimum: 1, maximum: 3, ConvertValueInInvariantCulture = true, ErrorMessage = "Client type must be 1 to 3")] - public int ClientType { get; set; } - } - - public class UpdateMyInfoRequest - { - [Required, Range(minimum: 1, maximum: int.MaxValue, ConvertValueInInvariantCulture = true, ErrorMessage = "Employee Id must be 1 to 2147483647")] - public int EmployeeId { get; set; } - - [NotNull, StringLength(120, MinimumLength = 0, ErrorMessage = "Address be between 0 to 120 characters.")] - public string Address { get; set; } - - public string ContactNo { get; set; } - } - - public class UserAttributesRequest - { - [Required, Range(minimum: 1, maximum: int.MaxValue, ConvertValueInInvariantCulture = true, ErrorMessage = "Select valid user")] - public int UserId { get; set; } - - [Required, Range(minimum: 1, maximum: 3, ConvertValueInInvariantCulture = true, ErrorMessage = "Client type must be 1 to 3")] - public int ClientType { get; set; } - public List UkIds { get; set; } - } - public class AccessLogSearchRequest - { - public int AccessType { get; set; } - public string LoginId { get; set; } - public DateTime StartDate { get; set; } - public DateTime EndDate { get; set; } - } - - public class ByTeamSpaceAndBasicUserRequest - { - public int TeamSpaceId { get; set; } - public int ProjectId { get; set; } - } - - public class BasicUserByTeamSpaceRequest : ByTeamSpaceAndBasicUserRequest - { - public int UserId { get; set; } - } - - public class BasicUserSearchRequest : BasicUserByTeamSpaceRequest - { - public bool ApplyFilter { get; set; } - } - - public class UserLimitAuthorizeRequest - { - [Required, Range(minimum: 1, maximum: int.MaxValue, ConvertValueInInvariantCulture = true, ErrorMessage = "Select valid user (1 to 99999999).")] - public int UserId { get; set; } - - public decimal MaxAuthorizeAmount { get; set; } - } - - public class PayslipRequest - { - [Required, NotNull] - public DateTime YearMonth { get; set; } - } + [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 class LoginRequest +{ + [Required, NotNull, StringLength(maximumLength: 150, MinimumLength = 4, ErrorMessage = "Login Id must be between 4 and 30 characters.")] + public string LoginId { get; set; } + + [Required, NotNull, StringLength(maximumLength: 150, MinimumLength = 1, ErrorMessage = "Password must be between 1 and 30 characters.")] + public string Password { get; set; } + + public string AppId { get; set; } + + [Required, NotNull, StringLength(maximumLength: 8, MinimumLength = 5, ErrorMessage = "Version must be 5 and 8 digits (Example: 0.0.0 or 99.99.99)")] + public string AppVersion { get; set; } + + public bool AttendanceLogin { get; set; } + public string HostName { get; set; } + public string IpAddress { get; set; } + public string MacAddress { get; set; } + public string LoginRemarks { get; set; } +} + +public class OtpValidationRequest +{ + public int UserId { get; set; } + + public EnumAuthenticationMethod AuthMethod { get; set; } + + [Required, NotNull, StringLength(6, MinimumLength = 6, ErrorMessage = "Otp must be 6 digit number.")] + public string OtpCode { get; set; } +} + +public class ByUserIdRequest +{ + public int UserId { get; set; } +} + +public class SendPasswordRequest +{ + [Required, NotNull, StringLength(maximumLength: 300, MinimumLength = 1, ErrorMessage = "User Id is required.")] + public string UserId { get; set; } + public string MobileNo { get; set; } + public string EmailAddress { get; set; } +} + +public class UserUnlockRequest : ByUserIdRequest +{ + [Required, NotNull, StringLength(maximumLength: 30, MinimumLength = 1, ErrorMessage = "Login Id must be between 1 and 30 characters.")] + public string LoginId { get; set; } +} + +public class ResetPasswordRequest : ByUserIdRequest +{ + [Required, NotNull, StringLength(maximumLength: 30, MinimumLength = 1, ErrorMessage = "Password must be between 1 and 30 characters.")] + public string Password { get; set; } + + [Required, NotNull, StringLength(maximumLength: 30, MinimumLength = 1, ErrorMessage = "Confirm Password must be between 1 and 30 characters.")] + public string ConfirmPassword { get; set; } +} + +public class PasswordChangeRequest : ResetPasswordRequest +{ + [Required, NotNull, StringLength(maximumLength: 30, MinimumLength = 1, ErrorMessage = "Old Password must be between 1 and 30 characters.")] + public string OldPassword { get; set; } +} + +public class LogoutRequest +{ + public int LogId { get; set; } + public bool AttendanceLogout { get; set; } + public string IpAddress { get; set; } + public string MacAddress { get; set; } + public string HostName { get; set; } + public string LogoutRemarks { get; set; } +} + +public class UserThemeRequest +{ + [Required, NotNull, StringLength(15, MinimumLength = 1, ErrorMessage = "Menu Layout must be between 1 to 15 characters.")] + public string MenuLayout { get; set; } + + [Required, NotNull, StringLength(15, MinimumLength = 1, ErrorMessage = "Theme Name must be between 1 to 15 characters.")] + public string ThemeName { get; set; } + + [Required, NotNull, StringLength(10, MinimumLength = 1, ErrorMessage = "Scheme Name must be between 1 to 10 characters.")] + public string SchemeName { get; set; } +} + +public class UserRequestBase +{ + [Required, NotNull, StringLength(30, MinimumLength = 3, ErrorMessage = "Login Id must be between 3 to 30 characters.")] + public string LoginId { get; set; } + + [Required, NotNull, StringLength(75, MinimumLength = 3, ErrorMessage = "User Name must be between 3 to 75 characters.")] + public string UserName { get; set; } + + [Required, NotNull, StringLength(50, MinimumLength = 0, ErrorMessage = "Designation must be between 0 to 50 characters.")] + public string Designation { get; set; } + + [StringLength(15, MinimumLength = 11, ErrorMessage = "Mobile number must be 11 characters.")] + [RegularExpression(@"^[01]{2}[123456789]{1}[0-9]{8}$", ErrorMessage = "Mobile number is invalid.")] + public string MobileNo { get; set; } + + [Required, NotNull, StringLength(100, MinimumLength = 5, ErrorMessage = "Email address must be between 5 to 100 characters.")] + [RegularExpression(@"^(([a-zA-Z0-9_\-\.]+)\@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)(\s*;\s*|\s*$))*$", ErrorMessage = "Email address is invalid.")] + public string EmailAddress { get; set; } = string.Empty; + public bool AuthReqAtlogin { get; set; } + public bool NeverExpire { get; set; } + public bool DbOnStartup { get; set; } + public bool DisallowMultiLogin { get; set; } + public EnumStatus Status { get; set; } + public short SeqId { get; set; } + public bool ViewToAll { get; set; } + public bool CanUseAttendanceSystem { get; set; } + public EnumAuthenticationMethod AuthMethod { get; set; } + public EnumAccessStatus AccessStatus { get; set; } + public List GroupIds { get; set; } +} + +public class UserRequest : UserRequestBase +{ + [Required, Range(minimum: 1, maximum: int.MaxValue, ConvertValueInInvariantCulture = true, ErrorMessage = "Select valid user (1 to 99999999).")] + public int UserId { get; set; } + + public string AuthKey { get; set; } +} + +public class NewUserRequest : UserRequestBase +{ + public int? EmployeeId { get; set; } + public string EmployeeCode { get; set; } + + [Required, NotNull, StringLength(30, MinimumLength = 1, ErrorMessage = "User Name must be between 1 to 30 characters.")] + public string Password { get; set; } +} + +public class UserSearchRequest : ValueStatusAndPageAndSortSearchRequest +{ + public bool CheckOwner { get; set; } +} + +public class ForceUserLogoutRequest +{ + public List UserIds { get; set; } +} + +public class ByUserAttributesRequest : ByUserIdRequest +{ + [Required, Range(minimum: 1, maximum: 3, ConvertValueInInvariantCulture = true, ErrorMessage = "Client type must be 1 to 3")] + public int ClientType { get; set; } +} + +public class UpdateMyInfoRequest +{ + [Required, Range(minimum: 1, maximum: int.MaxValue, ConvertValueInInvariantCulture = true, ErrorMessage = "Employee Id must be 1 to 2147483647")] + public int EmployeeId { get; set; } + + [NotNull, StringLength(120, MinimumLength = 0, ErrorMessage = "Address be between 0 to 120 characters.")] + public string Address { get; set; } + + public string ContactNo { get; set; } +} + +public class UserAttributesRequest +{ + [Required, Range(minimum: 1, maximum: int.MaxValue, ConvertValueInInvariantCulture = true, ErrorMessage = "Select valid user")] + public int UserId { get; set; } + + [Required, Range(minimum: 1, maximum: 3, ConvertValueInInvariantCulture = true, ErrorMessage = "Client type must be 1 to 3")] + public int ClientType { get; set; } + public List UkIds { get; set; } +} +public class AccessLogSearchRequest +{ + public int AccessType { get; set; } + public string LoginId { get; set; } + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } +} + +public class ByTeamSpaceAndBasicUserRequest +{ + public int TeamSpaceId { get; set; } + public int ProjectId { get; set; } +} + +public class BasicUserByTeamSpaceRequest : ByTeamSpaceAndBasicUserRequest +{ + public int UserId { get; set; } +} + +public class BasicUserSearchRequest : BasicUserByTeamSpaceRequest +{ + public bool ApplyFilter { get; set; } +} + +public class UserLimitAuthorizeRequest +{ + [Required, Range(minimum: 1, maximum: int.MaxValue, ConvertValueInInvariantCulture = true, ErrorMessage = "Select valid user (1 to 99999999).")] + public int UserId { get; set; } + + public decimal MaxAuthorizeAmount { get; set; } +} + +public class PayslipRequest +{ + [Required, NotNull] + public DateTime YearMonth { get; set; } } diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Responses/Integrations/IntegrationAuthResponse.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Responses/Integrations/IntegrationAuthResponse.cs new file mode 100644 index 0000000..357301c --- /dev/null +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Responses/Integrations/IntegrationAuthResponse.cs @@ -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; } +} \ No newline at end of file diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Contracts/Auth/IRefreshTokenService.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Contracts/Auth/IRefreshTokenService.cs new file mode 100644 index 0000000..c83520f --- /dev/null +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Contracts/Auth/IRefreshTokenService.cs @@ -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 GetByTokenHashAsync(string tokenHash); + Task AddAsync(InsertRefreshTokenRequest refreshToken); + Task RevokeAsync(RevokedRefreshTokenRequest token); + Task RevokeAllForUserAsync(int userId); + Task GenerateRefreshToken(GenerateRefreshTokenRequest request); +} diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Contracts/Systems/IUserService.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Contracts/Systems/IUserService.cs index cb15bc5..197e0de 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Contracts/Systems/IUserService.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Contracts/Systems/IUserService.cs @@ -34,7 +34,6 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems Task FindAccountAsync(string accountId); Task GetAuthorizeLimitAsync(int userId); Task GetAttributesAsync(int userId, int clientType); - Task GetDashboardData(int userId, bool canViewLeave, bool canViewLate, bool canViewClientVisit, bool canViewHomeOffice, bool viewAll); Task LoadNotificationCountAsync(int userId); diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/Auth/RefreshTokenService.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/Auth/RefreshTokenService.cs new file mode 100644 index 0000000..0aabbdb --- /dev/null +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/Auth/RefreshTokenService.cs @@ -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 settings) + { + _settings = settings.Value; + } + + + public async Task 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 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 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 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 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 GenerateRefreshToken() + { + throw new NotImplementedException(); + } + + + // ----- private helpers ----- + + private async Task 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); + } +} diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/Systems/UserService.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/Systems/UserService.cs index bf38091..b9b000b 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/Systems/UserService.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/Systems/UserService.cs @@ -17,2595 +17,2622 @@ using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; +using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Auth; +using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations; namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems { - public class UserService(IOptions settings, IOptions menuSettings) : IUserService - { - private readonly AppSettings _settings = settings?.Value; - private readonly MenuSettings _menuSettings = menuSettings?.Value; - - /// - /// - /// - /// - /// - /// - /// - /// - public async Task LoginAsync(LoginRequest request, string ipAddress, bool checkPwd) - { - 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 LoadNotificationCountAsync(int userId) - { - int value = 0; - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); - try - { - value = GetPendingNotifCount(tc: tc, userId: userId); - tc.End(); - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return value; - } - - /// - /// - /// - /// - /// - public async Task FindAccountAsync(string accountId) - { - FindAccountResponse response = new(); - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); - try - { - using (IDataReader dr = tc.ExecuteReader("SELECT UserID, EmailAddress, MobileNo FROM Users WHERE LoginID=%s OR EmailAddress=%s OR MobileNo=%s", accountId, accountId, accountId)) - { - if (dr.Read()) - { - response.UserId = dr.GetInt32(0); - response.EmailAddress = dr.GetString(1); - response.PhoneNo = dr.GetString(2); - response.Value = true; - } - dr.Close(); - } - tc.End(); - - if (!response.Value) - { - response.ReturnMessage.Add($"[{accountId}] is not a valid Login Id/Email address/Mobile number."); - } - else - { - if (response.PhoneNo.Length > 8) - response.PhoneNoMasked = string.Concat(response.PhoneNo.AsSpan(0, response.PhoneNo.Length - 8), "*****", response.PhoneNo.AsSpan(response.PhoneNo.Length - 3, 3)); - - int idx = response.EmailAddress.IndexOf('@'); - if (idx != -1) - { - string firstPart = response.EmailAddress[..idx]; - if (firstPart.Length >= 5) - firstPart = string.Concat(firstPart.AsSpan(0, firstPart.Length - 5), "*****"); - - response.EmailAddressMasked = string.Concat(firstPart, response.EmailAddress.AsSpan(idx, response.EmailAddress.Length - idx)); - } - } - - response.ReturnStatus = 200; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return response; - } - - /// - /// - /// - /// - /// - /// - public async Task ValidateAuthValueAsync(string authValue, int userId) - { - bool returnValue = false; - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); - try - { - SqlParameter[] p = - [ - SqlHelperExtension.CreateInParam(pName: "@AuthValue", pType: SqlDbType.VarChar, pValue: authValue, size: 10), - SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId), - SqlHelperExtension.CreateOutParam(pName: "@Valid", pType: SqlDbType.SmallInt, pValue: 0) - ]; - _ = tc.ExecuteNonQuerySp(spName: "dbo.ValidateAuthValue", parameterValues: p); - if (p[2].Value != null && p[2].Value != DBNull.Value) - returnValue = Convert.ToInt16(p[2].Value) > 0; - - 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 AddUserAsync(NewUserRequest user, string ipAddress, int createdBy) - { - bool returnValue; - try - { - #region Password Policy if Does not change password on next login - - if (user.AccessStatus != EnumAccessStatus.FirstTime) - { - ReadPwdParams(userId: 0, loginId: user.LoginId, enfStgPwd: out bool enfStgPwd, minLen: out short minLen, maxLen: out short maxLen, reservedWords: out string reservedWords); - ValidatePwdPolicies(password: user.Password, minLen: minLen, maxLen: maxLen, enfStgPwd: enfStgPwd, reservedWords: reservedWords); - } - - #endregion - - int userId = 0; - user.Password = EncryptPassword(password: user.Password); - - if (!string.IsNullOrEmpty(user.EmailAddress)) - user.EmailAddress = user.EmailAddress.Replace(" ", ""); - - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); - try - { - SqlParameter[] p = - [ - SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.VarChar, pValue: user.LoginId, size: 30), - SqlHelperExtension.CreateInParam(pName: "@UserName", pType: SqlDbType.VarChar, pValue: user.UserName, size: 75), - SqlHelperExtension.CreateInParam(pName: "@Designation", pType: SqlDbType.VarChar, pValue: user.Designation, size: 50), - SqlHelperExtension.CreateInParam(pName: "@Password", pType: SqlDbType.VarChar, pValue: user.Password, size: 75), - SqlHelperExtension.CreateInParam(pName: "@MobileNo", pType: SqlDbType.VarChar, pValue: user.MobileNo, size: 15), - SqlHelperExtension.CreateInParam(pName: "@EmailAddress", pType: SqlDbType.VarChar, pValue: user.EmailAddress, size: 100), - SqlHelperExtension.CreateInParam(pName: "@EmployeeId", pType: SqlDbType.Int, pValue: DataReader.GetNullValue(user.EmployeeId)), - SqlHelperExtension.CreateInParam(pName: "@EmployeeCode", pType: SqlDbType.VarChar, pValue: DataReader.GetNullValue(user.EmployeeCode), size: 15), - SqlHelperExtension.CreateInParam(pName: "@AuthReqAtlogin", pType: SqlDbType.SmallInt, pValue: user.AuthReqAtlogin ? 1 : 0), - SqlHelperExtension.CreateInParam(pName: "@AuthMethod", pType: SqlDbType.SmallInt, pValue: user.AuthMethod), - SqlHelperExtension.CreateInParam(pName: "@DBOnStartup", pType: SqlDbType.SmallInt, pValue: user.DbOnStartup ? 1 : 0), - SqlHelperExtension.CreateInParam(pName: "@Status", pType: SqlDbType.SmallInt, pValue: user.Status), - SqlHelperExtension.CreateInParam(pName: "@SeqId", pType: SqlDbType.SmallInt, pValue: user.SeqId), - SqlHelperExtension.CreateInParam(pName: "@CanUseAtnSys", pType: SqlDbType.SmallInt, pValue: user.CanUseAttendanceSystem? 1 : 0), - SqlHelperExtension.CreateInParam(pName: "@ViewToAll", pType: SqlDbType.SmallInt, pValue: user.ViewToAll? 1 : 0), - SqlHelperExtension.CreateInParam(pName: "@AccessStatus", pType: SqlDbType.SmallInt, pValue: user.AccessStatus), - SqlHelperExtension.CreateInParam(pName: "@NeverExpire", pType: SqlDbType.SmallInt, pValue: user.NeverExpire ? 1 : 0), - SqlHelperExtension.CreateInParam(pName: "@DAMultiLogin", pType: SqlDbType.SmallInt, pValue: user.DisallowMultiLogin ? 1 : 0), - SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20), - SqlHelperExtension.CreateInParam(pName: "@CreatedBy", pType: SqlDbType.Int, pValue: createdBy), - SqlHelperExtension.CreateOutParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId), - ]; - _ = tc.ExecuteNonQuerySp(spName: "dbo.InsertUser", parameterValues: p); - userId = (p[20]?.Value != null && p[20]?.Value != DBNull.Value) ? Convert.ToInt32(p[20].Value) : 0; - - if (user.GroupIds != null && user.GroupIds.Count > 0 && userId > 0) - { - List ownerGroupsIds = []; - if (createdBy == User.SuperUser_Id) - { - ownerGroupsIds.AddRange(user.GroupIds); - } - else - { - using IDataReader dr = tc.ExecuteReader("SELECT GroupId FROM Groups WHERE (CreatedBy=%n OR ViewToAll=1)", createdBy); - while (dr.Read()) - { - ownerGroupsIds.Add(dr.GetInt32(0)); - } - dr.Close(); - } - - StringBuilder sb = new(); - foreach (int groupId in user.GroupIds) - { - if (!ownerGroupsIds.Contains(groupId)) - continue; - - sb.Append($"INSERT INTO UserGroups(UserId,GroupId) VALUES({userId},{groupId});"); - } - _ = tc.ExecuteNonQuery(commandText: sb.ToString()); - } - - tc.End(); - - #region Copy File - - try - { - string destFileName = System.IO.Path.Combine(_settings.ProfileImageFolder, $"{userId}.png"); - string sourceFileName = System.IO.Path.Combine(_settings.ProfileImageFolder, "default-user.png"); - if (System.IO.File.Exists(path: sourceFileName)) - { - System.IO.File.Copy(sourceFileName: sourceFileName, destFileName: destFileName); - } - } - catch - { - //Do nothing - } - - #endregion - - returnValue = true; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return returnValue; - } - - /// - /// - /// - /// - /// - /// - /// - public async Task EditUserAsync(UserRequest user, string ipAddress, int modifiedBy) - { - bool returnValue = false; - try - { - if (!string.IsNullOrEmpty(user.EmailAddress)) - user.EmailAddress = user.EmailAddress.Replace(" ", ""); - - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); - try - { - SqlParameter[] p = - [ - SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.VarChar, pValue: user.LoginId, size: 30), - SqlHelperExtension.CreateInParam(pName: "@UserName", pType: SqlDbType.VarChar, pValue: user.UserName, size: 75), - SqlHelperExtension.CreateInParam(pName: "@Designation", pType: SqlDbType.VarChar, pValue: user.Designation, size: 50), - SqlHelperExtension.CreateInParam(pName: "@MobileNo", pType: SqlDbType.VarChar, pValue: user.MobileNo, size: 15), - SqlHelperExtension.CreateInParam(pName: "@EmailAddress", pType: SqlDbType.VarChar, pValue: user.EmailAddress ?? string.Empty, size: 100), - SqlHelperExtension.CreateInParam(pName: "@AuthReqAtlogin", pType: SqlDbType.SmallInt, pValue: user.AuthReqAtlogin ? 1 : 0), - SqlHelperExtension.CreateInParam(pName: "@AuthMethod", pType: SqlDbType.SmallInt, pValue: user.AuthMethod), - SqlHelperExtension.CreateInParam(pName: "@AuthKey", pType: SqlDbType.VarChar, pValue: user.AuthKey, size: 100), - SqlHelperExtension.CreateInParam(pName: "@DBOnStartup", pType: SqlDbType.SmallInt, pValue: user.DbOnStartup ? 1 : 0), - SqlHelperExtension.CreateInParam(pName: "@Status", pType: SqlDbType.SmallInt, pValue: user.Status), - SqlHelperExtension.CreateInParam(pName: "@SeqId", pType: SqlDbType.SmallInt, pValue: user.SeqId), - SqlHelperExtension.CreateInParam(pName: "@CanUseAtnSys", pType: SqlDbType.SmallInt, pValue: user.CanUseAttendanceSystem? 1 : 0), - SqlHelperExtension.CreateInParam(pName: "@ViewToAll", pType: SqlDbType.SmallInt, pValue: user.ViewToAll? 1 : 0), - SqlHelperExtension.CreateInParam(pName: "@AccessStatus", pType: SqlDbType.SmallInt, pValue: user.AccessStatus), - SqlHelperExtension.CreateInParam(pName: "@NeverExpire", pType: SqlDbType.SmallInt, pValue: user.NeverExpire ? 1 : 0), - SqlHelperExtension.CreateInParam(pName: "@DAMultiLogin", pType: SqlDbType.SmallInt, pValue: user.DisallowMultiLogin ? 1 : 0), - SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20), - SqlHelperExtension.CreateInParam(pName: "@ModifiedBy", pType: SqlDbType.Int, pValue: modifiedBy), - SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: user.UserId) - ]; - _ = tc.ExecuteNonQuerySp(spName: "dbo.UpdateUser", parameterValues: p); - - if (user.GroupIds != null && user.GroupIds.Count > 0 && user.UserId > 0) - { - List ownerGroupsIds = []; - if (modifiedBy == User.SuperUser_Id) - { - ownerGroupsIds.AddRange(user.GroupIds); - } - else - { - using IDataReader dr = tc.ExecuteReader("SELECT GroupId FROM Groups WHERE (CreatedBy=%n OR ViewToAll=1)", modifiedBy); - while (dr.Read()) - { - ownerGroupsIds.Add(dr.GetInt32(0)); - } - dr.Close(); - } - - StringBuilder sb = new(); - foreach (int groupId in user.GroupIds) - { - if (!ownerGroupsIds.Contains(groupId)) - continue; - - sb.Append($"INSERT INTO UserGroups(UserId,GroupId) VALUES({user.UserId},{groupId});"); - } - _ = tc.ExecuteNonQuery(commandText: sb.ToString()); - } - - tc.End(); - - #region Copy File - - try - { - string destFileName = System.IO.Path.Combine(_settings.ProfileImageFolder, $"{user.UserId}.png"); - string sourceFileName = System.IO.Path.Combine(_settings.ProfileImageFolder, "default-user.png"); - if (System.IO.File.Exists(path: sourceFileName) && !System.IO.File.Exists(path: destFileName)) - { - System.IO.File.Copy(sourceFileName: sourceFileName, destFileName: destFileName); - } - } - catch - { - //Do nothing - } - - #endregion - - returnValue = true; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return returnValue; - } - - /// - /// - /// - /// - /// - /// - public async Task DeleteUserAsync(int userId, int deletedBy) - { - bool returnValue; - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); - try - { - SqlParameter[] p = - [ - SqlHelperExtension.CreateInParam(pName: "@DeletedBy", pType: SqlDbType.Int, pValue: deletedBy), - SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId) - ]; - _ = tc.ExecuteNonQuerySp(spName: "dbo.DeleteUser", parameterValues: p); - - tc.End(); - - #region Delete File - - try - { - string fileSpec = System.IO.Path.Combine(_settings.ProfileImageFolder, $"{userId}.png"); - if (System.IO.File.Exists(path: fileSpec)) - System.IO.File.Delete(path: fileSpec); - } - catch - { - //Do nothing - } - - #endregion - returnValue = true; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return returnValue; - } - - /// - /// - /// - /// - /// - /// - /// - public async Task UnlockUserAsync(int userId, string loginId, int unlockedBy) - { - bool returnValue; - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); - try - { - SqlParameter[] p = - [ - SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId), - SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.VarChar, pValue: loginId, size: 30), - SqlHelperExtension.CreateInParam(pName: "@UnlockedBy", pType: SqlDbType.Int, pValue: unlockedBy) - ]; - _ = tc.ExecuteNonQuerySp(spName: "dbo.UnlockUser", parameterValues: p); - - tc.End(); - - returnValue = true; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return returnValue; - } - - /// - /// - /// - /// - /// - /// - /// - /// - public async Task ResetPasswordAsync(int userId, string newPassword, string ipAddress, int changedBy) - { - bool returnValue = false; - - try - { - try - { - newPassword = EncryptPassword(password: newPassword); - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); - try - { - SqlParameter[] p = - [ - SqlHelperExtension.CreateInParam(pName: "@NewPwd", pType: SqlDbType.VarChar, pValue: newPassword, size: 75), - SqlHelperExtension.CreateInParam(pName: "@LastPwds", pType: SqlDbType.VarChar, pValue: string.Empty, size: 700), - SqlHelperExtension.CreateInParam(pName: "@LastPwdChgDate", pType: SqlDbType.DateTime, pValue: DateTime.Now), - SqlHelperExtension.CreateInParam(pName: "@PwdExpireDate", pType: SqlDbType.DateTime, pValue: DateTime.Now), - SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20), - SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId), - SqlHelperExtension.CreateInParam(pName: "@ChangedBy", pType: SqlDbType.Int, pValue: changedBy), - SqlHelperExtension.CreateInParam(pName: "@ResetPwd", pType: SqlDbType.SmallInt, pValue: 1) - ]; - _ = tc.ExecuteNonQuerySp(spName: "dbo.UpdateUserPassword", parameterValues: p); - - tc.End(); - - returnValue = true; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return returnValue; - } - - /// - /// - /// - /// - /// - /// - /// - /// - public async Task SendPasswordAsync(int userId, string newPassword, string ipAddress) - { - bool returnValue = false; - - try - { - newPassword = EncryptPassword(password: newPassword); - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); - try - { - SqlParameter[] p = - [ - SqlHelperExtension.CreateInParam(pName: "@NewPwd", pType: SqlDbType.VarChar, pValue: newPassword, size: 75), - SqlHelperExtension.CreateInParam(pName: "LastPwds", pType: SqlDbType.VarChar, pValue: string.Empty, size: 700), - SqlHelperExtension.CreateInParam(pName: "@LastPwdChgDate", pType: SqlDbType.DateTime, pValue: DateTime.Now), - SqlHelperExtension.CreateInParam(pName: "@PwdExpireDate", pType: SqlDbType.DateTime, pValue: DateTime.Now), - SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20), - SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId), - SqlHelperExtension.CreateInParam(pName: "@ChangedBy", pType: SqlDbType.Int, pValue: userId), - SqlHelperExtension.CreateInParam(pName: "@ResetPwd", pType: SqlDbType.SmallInt, pValue: 1) - ]; - _ = tc.ExecuteNonQuerySp(spName: "dbo.UpdateUserPassword", parameterValues: p); - - tc.End(); - - returnValue = true; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return returnValue; - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - public async Task ChangePasswordAsync(int userId, string oldPassword, string newPassword, string ipAddress, int changedBy) - { - bool returnValue = false; - - try - { - bool loginIdCantPwd = false; - DateTime? lastPassChgDate = null; - List actPwdHistories = []; - Queue pwdHistories = new(); - EnumAccessStatus accessStatus = EnumAccessStatus.LoggedOut; - short pwdMinLen = 0, pwdMaxLen = 0, daLastPwds = 0, expiryDays = 0; - bool neverExpire = false, enfStgPwd = false, prvntAtckPwdReuse = false; - string loginId = string.Empty, password = string.Empty, lastPasswords = string.Empty, reservedWords = string.Empty; - - #region Reading Data from DB - - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); - try - { - using IDataReader dr = tc.ExecuteReader("SELECT A.LoginId, A.Password, A.LastPasswords, A.LastPassChgDate, A.NeverExpire, A.AccessStatus," - + " B.EnfStgPwd, B.PwdMinLen, B.PwdMaxLen, B.ExpiryDays, B.DALastPwds, B.PwdRsvdWords, B.loginIdCantPwd, B.PrvntAtckPwdReuse" - + " FROM Users A LEFT OUTER JOIN ThisSystem B ON A.UserID=A.UserID WHERE A.UserID=%n", userId); - - if (dr.Read()) - { - loginId = dr.GetString(0); - password = dr.GetString(1); - lastPasswords = dr.IsDBNull(2) ? string.Empty : dr.GetString(2); - lastPassChgDate = dr.IsDBNull(3) ? null : dr.GetDateTime(3); - neverExpire = !dr.IsDBNull(4) && dr.GetInt16(4) > 0; - accessStatus = (EnumAccessStatus)dr.GetInt16(5); - enfStgPwd = !dr.IsDBNull(6) && dr.GetInt16(6) > 0; - pwdMinLen = dr.IsDBNull(7) ? Convert.ToInt16(0) : dr.GetInt16(7); - pwdMaxLen = dr.IsDBNull(8) ? Convert.ToInt16(30) : dr.GetInt16(8); - expiryDays = dr.IsDBNull(9) ? Convert.ToInt16(0) : dr.GetInt16(9); - daLastPwds = dr.IsDBNull(10) ? Convert.ToInt16(0) : dr.GetInt16(10); - reservedWords = dr.IsDBNull(11) ? string.Empty : dr.GetString(11); - loginIdCantPwd = !dr.IsDBNull(12) && dr.GetInt16(12) != 0; - prvntAtckPwdReuse = !dr.IsDBNull(13) && dr.GetInt16(13) != 0; - } - dr.Close(); - tc.End(); - - if (loginIdCantPwd && !string.IsNullOrEmpty(loginId)) - { - reservedWords = string.IsNullOrEmpty(reservedWords) ? loginId : $"{reservedWords},{loginId}"; - } - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - #endregion - - #region Validation - - oldPassword = EncryptPassword(password: oldPassword); - if (!oldPassword.Equals(password)) - throw new InvalidOperationException("Old Password does not match with system."); - - #region Password Histories - - if (!string.IsNullOrEmpty(lastPasswords) && !string.IsNullOrWhiteSpace(lastPasswords)) - { - string[] pwds = lastPasswords.Split(','); - foreach (string pwd in pwds) - { - pwdHistories.Enqueue(pwd); - } - } - - #endregion - - #region Actual Password Histories - - string[] pwdsHists = [.. pwdHistories]; - if (daLastPwds > 0 && pwdsHists.Length > 0) - { - for (int idx = pwdsHists.Length - 1; idx >= 0; idx--) - { - if (!actPwdHistories.Contains(pwdsHists[idx]) && actPwdHistories.Count <= daLastPwds) - actPwdHistories.Add(pwdsHists[idx]); - } - } - - #endregion - - if (lastPassChgDate.HasValue && prvntAtckPwdReuse && accessStatus != EnumAccessStatus.FirstTime && lastPassChgDate.Value.Date >= DateTime.Today.Date) - { - throw new InvalidOperationException("Password should be at least one day old to change again."); - } - CheckPasswordHistory(password: newPassword, minLen: pwdMinLen, maxLen: pwdMaxLen, enfStgPwd: enfStgPwd, reservedWords: reservedWords, daLastPwds: daLastPwds, pwdHistories: pwdHistories, actPwdHistories: actPwdHistories); - - #endregion - - #region Updateing Data - - if (pwdHistories.ToArray().Length > 0) - lastPasswords = string.Join(",", [.. pwdHistories]); - - DateTime? expireDate = null; - lastPassChgDate = DateTime.Now; - if (!neverExpire && expiryDays > 0) - expireDate = DateTime.Now.AddDays(expiryDays); - - try - { - newPassword = EncryptPassword(password: newPassword); - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); - try - { - SqlParameter[] p = - [ - SqlHelperExtension.CreateInParam(pName: "@NewPwd", pType: SqlDbType.VarChar, pValue: newPassword, size: 75), - SqlHelperExtension.CreateInParam(pName: "@LastPwds", pType: SqlDbType.VarChar, pValue: lastPasswords, size: 700), - SqlHelperExtension.CreateInParam(pName: "@LastPwdChgDate", pType: SqlDbType.DateTime, pValue: lastPassChgDate), - SqlHelperExtension.CreateInParam(pName: "@PwdExpireDate", pType: SqlDbType.DateTime, pValue: expireDate), - SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20), - SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId), - SqlHelperExtension.CreateInParam(pName: "@ChangedBy", pType: SqlDbType.Int, pValue: changedBy), - SqlHelperExtension.CreateInParam(pName: "@ResetPwd", pType: SqlDbType.SmallInt, pValue: 0) - ]; - _ = tc.ExecuteNonQuerySp(spName: "dbo.UpdateUserPassword", parameterValues: p); - - tc.End(); - - returnValue = true; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - #endregion - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return returnValue; - } - - /// - /// - /// - /// - /// - /// - /// - /// - public async Task UpdateMyThemeAsync(int userId, string menuLayout, string themeName, string schemeName) - { - bool returnValue = false; - - try - { - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); - try - { - SqlParameter[] p = - [ - SqlHelperExtension.CreateInParam(pName: "@MenuLayout", pType: SqlDbType.VarChar, pValue: menuLayout, size: 15), - SqlHelperExtension.CreateInParam(pName: "@ThemeName", pType: SqlDbType.VarChar, pValue: themeName, size: 15), - SqlHelperExtension.CreateInParam(pName: "@SchemeName", pType: SqlDbType.VarChar, pValue: schemeName, size: 10), - SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId) - ]; - _ = tc.ExecuteNonQuerySp(spName: "dbo.UpdateMyTheme", parameterValues: p); - - tc.End(); - - returnValue = true; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return returnValue; - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - public async Task UpdateMyInfoAsync(string address, string contactNo, int modifiedBy, int emplyeeId) - { - bool returnValue = false; - - try - { - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); - try - { - SqlParameter[] p = - [ - SqlHelperExtension.CreateInParam(pName: "@Address", pType: SqlDbType.VarChar, pValue: address, size: 120), - SqlHelperExtension.CreateInParam(pName: "@ContactNo", pType: SqlDbType.VarChar, pValue: contactNo, size: 25), - SqlHelperExtension.CreateInParam(pName: "@ModifiedBy", pType: SqlDbType.Int, pValue: modifiedBy), - SqlHelperExtension.CreateInParam(pName: "@EmployeeId", pType: SqlDbType.Int, pValue: emplyeeId) - ]; - _ = tc.ExecuteNonQuerySp(spName: "dbo.UpdateMyInfo", parameterValues: p); - - tc.End(); - - returnValue = true; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return returnValue; - } - - /// - /// - /// - /// - /// - /// - /// - public async Task GetAttributesAsync(int userId, int clientType) - { - UserAttributesResponse response = new() { ReturnStatus = 200 }; - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); - try - { - using (IDataReader dr = tc.ExecuteReader("SELECT LoginId FROM Users WHERE UserId=%n", userId)) - { - if (dr.Read()) - { - response = new UserAttributesResponse - { - UserId = userId, - LoginId = dr.GetString(0), - ReturnStatus = 200, - UkIds = [], - }; - } - dr.Close(); - } - - #region Client & Projects - - if (response.UserId > 0) - { - using IDataReader dr = tc.ExecuteReader($"SELECT ProjectId, ClientId FROM UserClients WHERE UserId=%n AND ProjectId IS {(clientType == 1 ? "NOT " : "")}NULL", response.UserId); - while (dr.Read()) - { - string ukId = $"{(dr.IsDBNull(0) ? 0 : dr.GetInt32(0))}|{dr.GetInt32(1)}"; - response.UkIds.Add(ukId); - } - dr.Close(); - } - - response.HasSetup = response.UkIds.Count > 0; - - #endregion - - 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 DeleteAttributesAsync(int userId, int clientType, int deletedBy) - { - bool returnValue; - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); - try - { - SqlParameter[] p = - [ - SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId), - SqlHelperExtension.CreateInParam(pName: "@ClientType", pType: SqlDbType.SmallInt, pValue: clientType), - SqlHelperExtension.CreateInParam(pName: "@DeletedBy", pType: SqlDbType.Int, pValue: deletedBy) - ]; - _ = tc.ExecuteNonQuerySp(spName: "dbo.DeleteUserAttributes", parameterValues: p); - - tc.End(); - - returnValue = true; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return returnValue; - } - - /// - /// - /// - /// - /// - /// - /// - public async Task GetUsersAsync(UserSearchRequest request, int userId) - { - UserSearchResponse response = new(); - try - { - string andClause = string.Empty; - if (!string.IsNullOrEmpty(request.Criteria)) - { - string criteria = request.Criteria.Replace("'", "''"); - andClause = $" AND (LoginID='{criteria}' OR MobileNo='{criteria}' OR EmailAddress='{criteria}' OR UserName LIKE '%{criteria}%' OR Designation LIKE '%{criteria}%')"; - } - - if (request.Status > 0) - andClause += SQLParser.MakeSQL(" AND Status=%n", request.Status); - - if (request.CheckOwner) - andClause += SQLParser.MakeSQL(" AND (CreatedBy=%n OR ViewToAll=1)", userId); - - string sortField = request.SortField switch - { - "userName" => "UserName", - "designation" => "Designation", - "mobileNo" => "MobileNo", - "emailAddress" => "EmailAddress", - "statusDetail" => "Status", - "loginId" => "LoginId", - _ => "SeqId, LoginId" - }; - - string sortOrder = request.SortOrder switch - { - "desc" => "DESC", - _ => "ASC", - }; - - string commandText = request.Skip + request.PageSize <= 0 ? - SQLParser.MakeSQL("SELECT UserId, LoginId, UserName, Designation, MobileNo, EmailAddress, Status, SeqId, IsLocked, CanUseAtnSys, Password," - + " COUNT(*) OVER() AS TotalRows FROM Users WHERE UserId!=-9%q ORDER BY %q %q", andClause, sortField, sortOrder) - : SQLParser.MakeSQL("SELECT A.UserId, A.LoginId, A.UserName, A.Designation, A.MobileNo, A.EmailAddress, A.Status, A.SeqId, A.IsLocked," - + " A.CanUseAtnSys, A.Password, A.TotalRows FROM(SELECT UserId, LoginId, UserName, Designation, MobileNo, EmailAddress, Status, SeqId," - + " IsLocked, CanUseAtnSys, Password, ROW_NUMBER() OVER(ORDER BY %q %q) AS RN, COUNT(*) OVER() AS TotalRows FROM Users WHERE UserId!=-9%q) A" - + " WHERE A.RN>%n AND A.RN<=%n", sortField, sortOrder, andClause, request.Skip, request.Skip + request.PageSize); - - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); - try - { - int totalRows = 0; - using IDataReader dr = tc.ExecuteReader(commandText: commandText); - while (dr.Read()) - { - UserSearch item = new() - { - UserId = dr.GetInt32(0), - LoginId = dr.GetString(1), - UserName = dr.GetString(2), - Designation = dr.GetString(3), - MobileNo = dr.GetString(4), - EmailAddress = dr.GetString(5), - Status = (EnumStatus)dr.GetInt16(6), - SeqId = dr.GetInt16(7), - IsLocked = !dr.IsDBNull(8) && dr.GetInt16(8) != 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); - - response.Value.Add(item); - } - dr.Close(); - - response.TotalRows = totalRows; - - tc.End(); - response.ReturnStatus = 200; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return response; - } - - /// - /// - /// - /// - /// - /// - /// - /// - public async Task GetUsersByTeamSpaceAsync(string teamSpaceIds, int projectId, int userId) - { - UserBasicInfoResponse response = new(); - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); - try - { - string andClause = string.Empty; - if (userId > 0) - { - andClause = $" AND UserID={userId}"; - } - else - { - if (!string.IsNullOrEmpty(teamSpaceIds)) - { - andClause = $" AND UserID IN(SELECT UserId FROM TeamSpaceUsers WHERE TeamSpaceId IN({teamSpaceIds}))"; - } - - if (projectId > 0) - { - andClause += $" AND UserID IN(SELECT UserId FROM TeamSpaceUserProjects WHERE ProjectId={projectId})"; - } - } - - string commandText = $"SELECT UserID, LoginID, UserName, Designation FROM Users WHERE UserID!={User.SuperUser_Id} AND Status=8{andClause} ORDER BY SeqId, UserName"; - using IDataReader dr = tc.ExecuteReader(commandText: commandText); - while (dr.Read()) - { - UserBasicInfo item = new() - { - UserId = dr.GetInt32(0), - LoginId = dr.GetString(1), - UserName = dr.GetString(2), - Designation = dr.GetString(3), - }; - - response.Value.Add(item); - } - dr.Close(); - - tc.End(); - response.ReturnStatus = 200; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return response; - } - - /// - /// - /// - /// - /// - /// - /// - /// - public async Task GetUsersBasicAsync(bool applyFilter, string teamSpaceIds, int projectId) - { - UserBasicInfoResponse response = new(); - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); - try - { - string commandText; - if (applyFilter) - { - if (!string.IsNullOrEmpty(teamSpaceIds)) - { - commandText = $"SELECT UserID, LoginID, UserName, Designation FROM Users WHERE UserID!={User.SuperUser_Id}" - + $" AND Status=8 AND UserID IN(SELECT UserId FROM TeamSpaceUsers WHERE TeamSpaceId IN({teamSpaceIds}))"; - } - else - { - commandText = $"SELECT UserID, LoginID, UserName, Designation FROM Users WHERE UserID!={User.SuperUser_Id} AND Status=8"; - } - - if (projectId > 0) - { - commandText += $" AND UserID IN(SELECT UserId FROM UserClients WHERE ProjectID={projectId})"; - } - } - else - { - commandText = $"SELECT UserID, LoginID, UserName, Designation FROM Users WHERE UserID!={User.SuperUser_Id} AND Status=8"; - } - commandText += " ORDER BY SeqId, UserName"; - - using IDataReader dr = tc.ExecuteReader(commandText: commandText); - while (dr.Read()) - { - UserBasicInfo item = new() - { - UserId = dr.GetInt32(0), - LoginId = dr.GetString(1), - UserName = dr.GetString(2), - Designation = dr.GetString(3), - }; - - response.Value.Add(item); - } - dr.Close(); - - tc.End(); - response.ReturnStatus = 200; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return response; - } - - /// - /// - /// - /// - /// - /// - public async Task GetForceLogoutUsersAsync(int createdBy) - { - UserForceLogoutResponse response = new(); - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); - try - { - string commandText; - if (createdBy == User.SuperUser_Id) - commandText = $"SELECT UserID, LoginID, UserName FROM Users WHERE UserID!={User.SuperUser_Id} AND Status=8 ORDER BY LoginID"; - else - commandText = $"SELECT UserID, LoginID, UserName FROM Users WHERE (CreatedBy={createdBy} OR ViewToAll=1) AND Status=8 ORDER BY LoginID"; - - using IDataReader dr = tc.ExecuteReader(commandText: commandText); - while (dr.Read()) - { - UserForceLogout item = new() - { - UserId = dr.GetInt32(0), - LoginId = dr.GetString(1), - UserName = dr.GetString(2) - }; - - response.Value.Add(item); - } - dr.Close(); - - tc.End(); - response.ReturnStatus = 200; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return response; - } - - /// - /// - /// - /// - /// - /// - public async Task GetAttendanceUsersAsync(int userId) - { - UserBasicInfoResponse response = new(); - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); - try - { - string andClause = userId > 0 ? $" AND UserID={userId}" : string.Empty; - using IDataReader dr = tc.ExecuteReader(commandText: $"SELECT UserID, LoginID, UserName, Designation FROM Users WHERE EmployeeId IS NOT NULL AND Status=8{andClause} ORDER BY SeqId, UserName"); - while (dr.Read()) - { - UserBasicInfo item = new() - { - UserId = dr.GetInt32(0), - LoginId = dr.GetString(1), - UserName = dr.GetString(2), - Designation = dr.GetString(3), - }; - - response.Value.Add(item); - } - dr.Close(); - - tc.End(); - response.ReturnStatus = 200; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return response; - } - - /// - /// - /// - /// - /// - /// - /// - public async Task ForceLogoutNowAsync(List userIds, string ipAddress) - { - bool returnValue; - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); - try - { - SqlParameter[] p = - [ - SqlHelperExtension.CreateInParam(pName: "@UserIDs", pType: SqlDbType.VarChar, pValue: string.Join(",", userIds.ToArray()), size: 8000), - SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20) - ]; - _ = tc.ExecuteNonQuerySp(spName: "dbo.DoForceLogout", parameterValues: p); - - tc.End(); - - returnValue = true; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return returnValue; - } - - /// - /// - /// - /// - /// - public async Task GetUserAsync(int userId) - { - UserGetResponse response = new() { ReturnStatus = 200 }; - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); - try - { - using (IDataReader dr = tc.ExecuteReader("SELECT LoginId, UserName, Designation, MobileNo, EmailAddress, IsLocked, Status," - + " AccessStatus, NeverExpire, AuthReqAtlogin, AuthMethod, DBOnStartup, DAMultiLogin, EmployeeCode, SeqId, CanUseAtnSys, ViewToAll" - + " FROM Users WHERE UserId=%n", userId)) - { - if (dr.Read()) - { - response = new UserGetResponse - { - UserId = userId, - LoginId = dr.GetString(0), - UserName = dr.GetString(1), - Designation = dr.GetString(2), - MobileNo = dr.GetString(3), - EmailAddress = dr.GetString(4), - IsLocked = !dr.IsDBNull(5) && dr.GetInt16(5) > 0, - Status = (EnumStatus)dr.GetInt16(6), - AccessStatus = (EnumAccessStatus)dr.GetInt16(7), - NeverExpire = !dr.IsDBNull(8) && dr.GetInt16(8) != 0, - AuthRequiredAtLogin = !dr.IsDBNull(9) && dr.GetInt16(9) != 0, - AuthMethod = (EnumAuthenticationMethod)dr.GetInt16(10), - DbOnStartup = !dr.IsDBNull(11) && dr.GetInt16(11) != 0, - DisallowMultiLogin = !dr.IsDBNull(12) && dr.GetInt16(12) != 0, - EmployeeCode = dr.IsDBNull(13) ? string.Empty : dr.GetString(13), - SeqId = dr.GetInt16(14), - CanUseAttendanceSystem = !dr.IsDBNull(15) && dr.GetInt16(15) != 0, - ViewToAll = !dr.IsDBNull(16) && dr.GetInt16(16) != 0, - ReturnStatus = 200 - }; - } - dr.Close(); - } - - response.GroupIds = []; - using (IDataReader dr = tc.ExecuteReader("SELECT GroupId FROM UserGroups WHERE UserID=%n", userId)) - { - while (dr.Read()) - { - response.GroupIds.Add(dr.GetInt32(0)); - } - 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 GetUserProfileAsync(int userId) - { - UserProfileResponse response = new() { ReturnStatus = 200 }; - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); - try - { - int employeeId = 0; - using (IDataReader dr = tc.ExecuteReader("SELECT LoginId, UserName, Designation, LastPassChgDate, ExpireDate, EmployeeId FROM Users WHERE UserId=%n", userId)) - { - if (dr.Read()) - { - response = new UserProfileResponse - { - UserId = userId, - LoginId = dr.GetString(0), - UserName = dr.GetString(1), - Designation = dr.GetString(2), - PwdLastChangedTime = dr.IsDBNull(3) ? null : dr.GetDateTime(3), - NextPwdDate = dr.IsDBNull(4) ? null : dr.GetDateTime(4), - ReturnStatus = 200 - }; - - employeeId = dr.IsDBNull(5) ? 0 : dr.GetInt32(5); - } - dr.Close(); - } - - if (employeeId > 0) - { - using IDataReader dr = tc.ExecuteReader("SELECT Address, ContactNo FROM Employees WHERE EmployeeId=%n", employeeId); - if (dr.Read()) - { - response.Address = dr.GetString(0); - response.ContactNo = dr.GetString(1); - } - dr.Close(); - } - - response.LoginHistories = []; - using (IDataReader dr = tc.ExecuteReader("SELECT TOP 10 LoginIPAddress, LoginTime, LogoutIPAddress, LogoutTime" - + " FROM AccessLog WHERE UserID=%n ORDER BY LoginTime DESC", userId)) - { - int slNo = 0; - while (dr.Read()) - { - slNo++; - LoginHistory item = new() - { - SlNo = slNo, - LoginIp = dr.GetString(0), - LoginTime = dr.GetDateTime(1), - LogoutIp = dr.IsDBNull(2) ? string.Empty : dr.GetString(2), - LogoutTime = dr.IsDBNull(3) ? null : dr.GetDateTime(3), - }; - response.LoginHistories.Add(item: item); - } - 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 GetUserPermissionsAsync(int userId) - { - MenuResponse response = new() { Item = new() { Items = [] } }; - MenuItem pi = new() { Label = "User Profile", Icon = "fa fa-user-md", RouterLink = string.Empty, Items = [] }; - pi.Items.Add(new() { Label = "My Profile", Icon = "fa fa-user", RouterLink = "/myprofile" }); - pi.Items.Add(new() { Label = "Change My Password", Icon = "fa fa-unlock", RouterLink = "/changemypwd" }); - pi.Items.Add(new() { Label = "Change My Theme", Icon = "fa fa-user", RouterLink = "/changemytheme" }); - pi.Items.Add(new() { Label = "WhatsApp Message", Icon = "fa fa-whatsapp", RouterLink = "/sendwhatsappmsg" }); - pi.Items.Add(new() { Label = "Access Log", Icon = "fa fa-list-alt", RouterLink = "/accesslog" }); - response.Item.Items.Add(pi); - - List pmns = []; - 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.GetUserPermissions", parameterValues: p); - while (dr.Read()) - { - GroupPermission item = new() - { - ModuleId = dr.GetString(0), - AllowSelect = dr.GetInt16(1) != 0, - AllowAdd = dr.GetInt16(2) != 0, - AllowEdit = dr.GetInt16(3) != 0, - AllowDelete = dr.GetInt16(4) != 0 - }; - pmns.Add(item); - } - dr.Close(); - - tc.End(); - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - - #region Making Hierarchy - - List items = []; - List fullMenu = GlobalFunctions.BuildMenu(); - IEnumerable data = fullMenu.Where(x => (string.IsNullOrEmpty(x.ParentId) || string.IsNullOrWhiteSpace(x.ParentId)) && x.Visible && pmns.Where(y => y.AllowSelect).Select(z => z.ModuleId).Contains(x.ModuleId)); - foreach (GroupPermission datum in data) - { - MakeHierarchy(parent: datum, data: fullMenu, pmns: pmns); - } - items.AddRange(data); - - if (_menuSettings?.MenuItems?.Count > 0) - { - foreach (GroupPermission src in items) - { - MenuItem dst = src.Copy(); - dst.Label = _menuSettings.GetItem(src.ModuleId, src.ModuleName).Value; - response.Item.Items.Add(dst); - - CopyHierarchy(src: src, dst: dst); - } - } - else - { - foreach (GroupPermission src in items) - { - MenuItem dst = src.Copy(); - response.Item.Items.Add(dst); - - CopyHierarchy(src: src, dst: dst); - } - } - items.Clear(); - - #endregion - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return response; - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public async Task 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 ? $"Home Office: {dr.GetInt32(1)}" : $"Home Office: {dr.GetInt32(1)}"); - else if (dr.GetInt16(0) == 3) - sb.Append(canViewClientVisit && viewAll ? $"Client Visit: {dr.GetInt32(1)}" : $"Client Visit: {dr.GetInt32(1)}"); - else if (dr.GetInt16(0) == 2) - sb.Append(canViewLate && viewAll ? $"Late: {dr.GetInt32(1)}" : $"Late: {dr.GetInt32(1)}"); - else - sb.Append(canViewLeave && viewAll ? $"Leave: {dr.GetInt32(1)}" : $"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; - } - - /// - /// - /// - /// - /// - /// - - - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public async Task UploadDocumentAsync(int userId, int id, int documentOf, string orgFileName, string fileName) - { - 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), - SqlHelperExtension.CreateInParam(pName: "@Id", pType: SqlDbType.Int, pValue: id), - SqlHelperExtension.CreateInParam(pName: "@DocumentOf", pType: SqlDbType.SmallInt, pValue: documentOf), - SqlHelperExtension.CreateInParam(pName: "@OrgFileName", pType: SqlDbType.VarChar, pValue: orgFileName, size: 200), - SqlHelperExtension.CreateInParam(pName: "@FileName", pType: SqlDbType.VarChar, pValue: fileName, size: 100) - ]; - _ = tc.ExecuteNonQuerySp(spName: "dbo.UploadDocument", parameterValues: p); - - 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 LogoutAsync(string ipAddress, int userId, int logId, bool attendanceLogout, string loginId, string localIp, string macAddress, string hostName, string logoutRemarks) - { - bool returnValue = false; - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); - try - { - SqlParameter[] p; - #region Attendance Logout and Valid Ip Address - - if (attendanceLogout && userId != User.SuperUser_Id) - { - string errMsg = string.Empty; - p = - [ - SqlHelperExtension.CreateInParam(pName: "@UserID", pType: SqlDbType.Int, pValue: userId), - SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.VarChar, pValue: loginId, size: 30), - SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: localIp, size: 20), - SqlHelperExtension.CreateInParam(pName: "@MacAddress", pType: SqlDbType.VarChar, pValue: macAddress, size: 30), - SqlHelperExtension.CreateInParam(pName: "@HostName", pType: SqlDbType.VarChar, pValue: hostName, size: 100), - SqlHelperExtension.CreateInParam(pName: "@LogoutRemarks", pType: SqlDbType.VarChar, pValue: logoutRemarks, size: 50), - SqlHelperExtension.CreateOutParam(pName: "@ErrMsg", pType: SqlDbType.VarChar, pValue: errMsg, size: 300) - ]; - _ = tc.ExecuteNonQuerySp(spName: "dbo.LogoutFromAttendance", parameterValues: p); - - errMsg = (p[6] == null || p[6].Value == null || p[6].Value == DBNull.Value) ? string.Empty : Convert.ToString(p[6].Value); - if (!string.IsNullOrEmpty(errMsg)) - { - throw new InvalidOperationException(errMsg); - } - } - - #endregion - - //Keep access history - p = - [ - SqlHelperExtension.CreateInParam(pName: "@UserID", pType: SqlDbType.Int, pValue: userId), - SqlHelperExtension.CreateInParam(pName: "@Status", pType: SqlDbType.TinyInt, pValue: 3), - SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20), - SqlHelperExtension.CreateInParam(pName: "@LogID", pType: SqlDbType.Int, pValue: logId), - ]; - _ = tc.ExecuteNonQuerySp(spName: "dbo.CreateAccessLog", parameterValues: p); - - 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 GetAccessLogAsync(int accessType, string loginId, DateTime startDate, DateTime endDate) - { - AccessLogResponse response = new() { Value = [] }; - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); - try - { - SqlParameter[] p = - [ - SqlHelperExtension.CreateInParam(pName: "@AccessType", pType: SqlDbType.SmallInt, pValue: accessType), - SqlHelperExtension.CreateInParam(pName: "@LoginID", pType: SqlDbType.VarChar, pValue: loginId, size: 30), - SqlHelperExtension.CreateInParam(pName: "@StartDate", pType: SqlDbType.DateTime, pValue: startDate), - SqlHelperExtension.CreateInParam(pName: "@EndDate", pType: SqlDbType.DateTime, pValue: endDate), - ]; - using IDataReader dr = tc.ExecuteReaderSp(spName: "dbo.GetAccessLogData", parameterValues: p); - while (dr.Read()) - { - AccessLog item = new() - { - LoginId = dr.GetString(0), - LoginTime = dr.GetDateTime(1), - LoginIp = dr.GetString(2), - LogoutTime = dr.IsDBNull(3) ? null : dr.GetDateTime(3), - LogoutIp = dr.IsDBNull(4) ? null : dr.GetString(4), - LoginStatus = dr.GetString(5), - }; - - response.Value.Add(item); - } - dr.Close(); - - tc.End(); - response.ReturnStatus = 200; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return response; - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - public async Task SaveAuthorizeLimitAsync(decimal maxAuthLimit, int userId, string ipAddress, string savedBy) - { - bool returnValue; - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); - try - { - SqlParameter[] p = - [ - - SqlHelperExtension.CreateInParam(pName: "@MaxAuthLimit", pType: SqlDbType.Decimal, pValue: maxAuthLimit), - SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId), - SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20), - SqlHelperExtension.CreateInParam(pName: "@SavedBy", pType: SqlDbType.VarChar, pValue: savedBy, size: 30) - ]; - _ = tc.ExecuteNonQuerySp(spName: "dbo.SaveAuthorizeLimit", parameterValues: p); - - tc.End(); - - returnValue = true; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - return returnValue; - } - - public async Task GetAuthorizeLimitAsync(int userId) - { - UserAuthorizeLimitResponse response = new() { Value = 0 }; - try - { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); - try - { - using IDataReader dr = tc.ExecuteReader(commandText: "SELECT LoginId, MaxAuthLimit FROM dbo.Users WHERE UserId=%n", args: userId); - if (dr.Read()) - { - response.LoginId = dr.GetString(0); - response.Value = dr.IsDBNull(1) ? 0 : dr.GetDecimal(1); - } - dr.Close(); - - tc.End(); - response.ReturnStatus = 200; - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - - return response; - } - - #region Private functions - - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - private void ReadPwdParams(int userId, string loginId, out bool enfStgPwd, out short minLen, out short maxLen, out string reservedWords) - { - try - { - enfStgPwd = false; - minLen = maxLen = 0; - reservedWords = string.Empty; - using TransactionContext tc = TransactionContext.Begin(_settings.DefaultConnection.ConnectionNode); - try - { - bool loginIdCantPwd = false; - using (IDataReader dr = tc.ExecuteReader("SELECT EnfStgPwd, PwdMinLen, PwdMaxLen, PwdRsvdWords, LoginIdCantPwd FROM ThisSystem")) - { - if (dr.Read()) - { - enfStgPwd = Convert.ToBoolean(dr.GetInt16(0)); - minLen = dr.GetInt16(1); - maxLen = dr.GetInt16(2); - reservedWords = dr.IsDBNull(3) ? string.Empty : dr.GetString(3); - loginIdCantPwd = !dr.IsDBNull(4) && dr.GetInt16(4) != 0; - } - dr.Close(); - } - - if (string.IsNullOrEmpty(loginId) && loginIdCantPwd && userId > 0) - { - using IDataReader dr = tc.ExecuteReader("SELECT LoginId FROM Users WHERE UserId=%n", userId); - if (dr.Read()) - { - loginId = dr.IsDBNull(0) ? string.Empty : dr.GetString(0); - } - dr.Close(); - } - tc.End(); - - if (loginIdCantPwd && !string.IsNullOrEmpty(loginId)) - { - reservedWords = string.IsNullOrEmpty(reservedWords) ? loginId : $"{reservedWords},{loginId}"; - } - } - catch (Exception ie) - { - tc?.HandleError(); - - throw DBCustomError.GenerateCustomError(ie); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - private void CheckPasswordHistory(string password, short minLen, short maxLen, bool enfStgPwd, string reservedWords, int daLastPwds, Queue pwdHistories, List actPwdHistories) - { - try - { - ValidatePwdPolicies(password: password, minLen: minLen, maxLen: maxLen, enfStgPwd: enfStgPwd, reservedWords: reservedWords); - - password = EncryptPassword(password: password); - if (daLastPwds > 0 && actPwdHistories.Contains(password)) - throw new InvalidOperationException($"You cannot use this password, because it was used in last {daLastPwds} passwords."); - - if (pwdHistories.Count >= 5) - pwdHistories.Dequeue(); - - pwdHistories.Enqueue(password); - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - } - - /// - /// Validates a password against specified policies, including length constraints, reserved words, and strong password requirements. - /// - /// This method performs validation based on the provided policies and throws an exception if the password does not comply. - /// Ensure that all parameters are correctly configured before calling this method. - /// The password to validate. Cannot be null or empty. - /// The minimum allowed length for the password. Must be greater than 0. - /// The maximum allowed length for the password. Must be greater than 0. - /// A value indicating whether strong password enforcement is enabled. If , the password - /// must contain at least one uppercase letter, one lowercase letter, one number, and one special character (!, @, #, $, %, ^, &, *, ?). - /// A comma-separated list of reserved words that cannot be included in the password. Cannot be null or empty. - /// Thrown if the password violates any of the specified policies, including: - /// Contains a reserved word. Does not meet the minimum length requirement. - /// Exceeds the maximum length requirement. Fails strong password enforcement when is . - private static void ValidatePwdPolicies(string password, short minLen, short maxLen, bool enfStgPwd, string reservedWords) - { - try - { - if (!string.IsNullOrEmpty(password) && !string.IsNullOrEmpty(reservedWords)) - { - List words = [.. reservedWords.Split(',')]; - foreach (string word in words) - { - if (password.Contains(word, StringComparison.InvariantCultureIgnoreCase)) - throw new InvalidOperationException($"[{word}] can not be used in password."); - } - } - - if (minLen > 0 && password.Length < minLen) - throw new InvalidOperationException($"Minimum length of Password is {minLen}"); - - if (maxLen > 0 && password.Length > maxLen) - throw new InvalidOperationException($"Maximum length of Password is {maxLen}"); - - if (enfStgPwd && !Global.StringFunctions.IsStrongPassword(password)) - throw new InvalidOperationException("Must have Upper & Lower case character, a Number and a Special character (!, @, #, $, %, ^, &, *, ?)."); - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - } - - /// - /// - /// - /// - /// - private string EncryptPassword(string password) - { - try - { - string pwdSecretKey = GlobalFunctions.ConvertFromBase64String(_settings.PwdSecretKey); - return Global.CipherFunctions.EncryptByAES(privateKey: pwdSecretKey, publicKey: pwdSecretKey, data: password); - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - } - - /// - /// - /// - /// - /// - /// - /// - private static void SetAuthValue(TransactionContext tc, string authValue, int validMinutes, int userId) - { - try - { - SqlParameter[] p = - [ - SqlHelperExtension.CreateInParam(pName: "@AuthValue", pType: SqlDbType.VarChar, pValue: authValue, size: 10), - SqlHelperExtension.CreateInParam(pName: "@ValidMinutes", pType: SqlDbType.Int, pValue: validMinutes), - SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId) - ]; - _ = tc.ExecuteNonQuerySp(spName: "dbo.SetAuthValue", parameterValues: p); - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - } - - /// - /// - /// - /// - /// - /// - /// - private static int GetPendingNotifCount(TransactionContext tc, int userId) - { - try - { - SqlParameter[] p = - [ - SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId), - SqlHelperExtension.CreateOutParam(pName: "@NotifCount", pType: SqlDbType.Int, pValue: 0) - ]; - _ = tc.ExecuteNonQuerySp(spName: "dbo.GetPendingAuthCountByUser", parameterValues: p); - return (p[1].Value != null && p[1].Value != DBNull.Value) ? Convert.ToInt32(p[1].Value) : 0; - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - } - - /// - /// - /// - /// - /// - /// - private static void MakeHierarchy(GroupPermission parent, List data, List pmns) - { - try - { - IEnumerable children = data.Where(x => x.ParentId == parent.ModuleId && x.Visible && pmns.Where(y => y.AllowSelect).Select(z => z.ModuleId).Contains(x.ModuleId)); - parent.Children.AddRange(children); - - foreach (GroupPermission child in parent.Children) - { - MakeHierarchy(parent: child, data: data, pmns: pmns); - } - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - } - - /// - /// - /// - /// - /// - private void CopyHierarchy(GroupPermission src, MenuItem dst) - { - try - { - if (src.Children?.Count > 0) - dst.Items = []; - - foreach (GroupPermission srcChild in src.Children) - { - MenuItem dstChild = srcChild.Copy(); - dstChild.Label = _menuSettings?.GetItem(dstChild.ModuleId, dstChild.Label).Value; - dst.Items.Add(dstChild); - - CopyHierarchy(src: srcChild, dst: dstChild); - } - - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } - } + public class UserService(IOptions settings, IOptions menuSettings) : IUserService + { + private readonly AppSettings _settings = settings?.Value; + private readonly MenuSettings _menuSettings = menuSettings?.Value; + + /// + /// + /// + /// + /// + /// + /// + /// + public async Task LoginAsync(LoginRequest request, string ipAddress, bool checkPwd) + { + 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 IntegrationLoginAsync(IntegrstionLoginRequest request, string ipAddress, bool checkPwd) + { + User user = null; + try + { + string password = EncryptPassword(password: request.Password); + + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); + try + { + 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 User data using authentication data + + string commandText = SQLParser.MakeSQL("SELECT UserId,Password,LoginId, UserName, Status, MobileNo, EmailAddress," + + " AuthKey, AuthValue, IsLocked FROM Users WHERE LoginID=%s", request.LoginId); + + using (IDataReader dr = tc.ExecuteReader(commandText: commandText)) + { + if (dr.Read()) + { + user = new User + { + UserId = dr.GetInt32(0), + Password = dr.GetString(1), + LoginId = dr.GetString(2), + UserName = dr.GetString(3), + Status = (EnumStatus)dr.GetInt32(4), + MobileNo = dr.GetString(5), + EmailAddress = dr.GetString(6), + AuthKey = dr.GetString(7), + AuthValue = dr.GetString(8), + IsLocked = dr.GetBoolean(9), + LoginStatus = EnumLoginStatus.Success + }; + } + dr.Close(); + } + + 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 + + #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 If login successful and user is active read module id for this user + + 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: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20), + SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.VarChar, pValue: user.LoginId, size: 30), + SqlHelperExtension.CreateInParam(pName: "@LockTime", pType: SqlDbType.Int, pValue: lockTime), + SqlHelperExtension.CreateInParam(pName: "@LoginTime", pType: SqlDbType.DateTime, pValue: DateTime.Now) + ]; + + _ = await tc.ExecuteNonQuerySpAsync(spName: "dbo.SaveAccessLog", parameterValues: p); + } + + #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 LoadNotificationCountAsync(int userId) + { + int value = 0; + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); + try + { + value = GetPendingNotifCount(tc: tc, userId: userId); + tc.End(); + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return value; + } + + /// + /// + /// + /// + /// + public async Task FindAccountAsync(string accountId) + { + FindAccountResponse response = new(); + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); + try + { + using (IDataReader dr = tc.ExecuteReader("SELECT UserID, EmailAddress, MobileNo FROM Users WHERE LoginID=%s OR EmailAddress=%s OR MobileNo=%s", accountId, accountId, accountId)) + { + if (dr.Read()) + { + response.UserId = dr.GetInt32(0); + response.EmailAddress = dr.GetString(1); + response.PhoneNo = dr.GetString(2); + response.Value = true; + } + dr.Close(); + } + tc.End(); + + if (!response.Value) + { + response.ReturnMessage.Add($"[{accountId}] is not a valid Login Id/Email address/Mobile number."); + } + else + { + if (response.PhoneNo.Length > 8) + response.PhoneNoMasked = string.Concat(response.PhoneNo.AsSpan(0, response.PhoneNo.Length - 8), "*****", response.PhoneNo.AsSpan(response.PhoneNo.Length - 3, 3)); + + int idx = response.EmailAddress.IndexOf('@'); + if (idx != -1) + { + string firstPart = response.EmailAddress[..idx]; + if (firstPart.Length >= 5) + firstPart = string.Concat(firstPart.AsSpan(0, firstPart.Length - 5), "*****"); + + response.EmailAddressMasked = string.Concat(firstPart, response.EmailAddress.AsSpan(idx, response.EmailAddress.Length - idx)); + } + } + + response.ReturnStatus = 200; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return response; + } + + /// + /// + /// + /// + /// + /// + public async Task ValidateAuthValueAsync(string authValue, int userId) + { + bool returnValue = false; + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@AuthValue", pType: SqlDbType.VarChar, pValue: authValue, size: 10), + SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId), + SqlHelperExtension.CreateOutParam(pName: "@Valid", pType: SqlDbType.SmallInt, pValue: 0) + ]; + _ = tc.ExecuteNonQuerySp(spName: "dbo.ValidateAuthValue", parameterValues: p); + if (p[2].Value != null && p[2].Value != DBNull.Value) + returnValue = Convert.ToInt16(p[2].Value) > 0; + + 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 AddUserAsync(NewUserRequest user, string ipAddress, int createdBy) + { + bool returnValue; + try + { + #region Password Policy if Does not change password on next login + + if (user.AccessStatus != EnumAccessStatus.FirstTime) + { + ReadPwdParams(userId: 0, loginId: user.LoginId, enfStgPwd: out bool enfStgPwd, minLen: out short minLen, maxLen: out short maxLen, reservedWords: out string reservedWords); + ValidatePwdPolicies(password: user.Password, minLen: minLen, maxLen: maxLen, enfStgPwd: enfStgPwd, reservedWords: reservedWords); + } + + #endregion + + int userId = 0; + user.Password = EncryptPassword(password: user.Password); + + if (!string.IsNullOrEmpty(user.EmailAddress)) + user.EmailAddress = user.EmailAddress.Replace(" ", ""); + + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.VarChar, pValue: user.LoginId, size: 30), + SqlHelperExtension.CreateInParam(pName: "@UserName", pType: SqlDbType.VarChar, pValue: user.UserName, size: 75), + SqlHelperExtension.CreateInParam(pName: "@Designation", pType: SqlDbType.VarChar, pValue: user.Designation, size: 50), + SqlHelperExtension.CreateInParam(pName: "@Password", pType: SqlDbType.VarChar, pValue: user.Password, size: 75), + SqlHelperExtension.CreateInParam(pName: "@MobileNo", pType: SqlDbType.VarChar, pValue: user.MobileNo, size: 15), + SqlHelperExtension.CreateInParam(pName: "@EmailAddress", pType: SqlDbType.VarChar, pValue: user.EmailAddress, size: 100), + SqlHelperExtension.CreateInParam(pName: "@EmployeeId", pType: SqlDbType.Int, pValue: DataReader.GetNullValue(user.EmployeeId)), + SqlHelperExtension.CreateInParam(pName: "@EmployeeCode", pType: SqlDbType.VarChar, pValue: DataReader.GetNullValue(user.EmployeeCode), size: 15), + SqlHelperExtension.CreateInParam(pName: "@AuthReqAtlogin", pType: SqlDbType.SmallInt, pValue: user.AuthReqAtlogin ? 1 : 0), + SqlHelperExtension.CreateInParam(pName: "@AuthMethod", pType: SqlDbType.SmallInt, pValue: user.AuthMethod), + SqlHelperExtension.CreateInParam(pName: "@DBOnStartup", pType: SqlDbType.SmallInt, pValue: user.DbOnStartup ? 1 : 0), + SqlHelperExtension.CreateInParam(pName: "@Status", pType: SqlDbType.SmallInt, pValue: user.Status), + SqlHelperExtension.CreateInParam(pName: "@SeqId", pType: SqlDbType.SmallInt, pValue: user.SeqId), + SqlHelperExtension.CreateInParam(pName: "@CanUseAtnSys", pType: SqlDbType.SmallInt, pValue: user.CanUseAttendanceSystem? 1 : 0), + SqlHelperExtension.CreateInParam(pName: "@ViewToAll", pType: SqlDbType.SmallInt, pValue: user.ViewToAll? 1 : 0), + SqlHelperExtension.CreateInParam(pName: "@AccessStatus", pType: SqlDbType.SmallInt, pValue: user.AccessStatus), + SqlHelperExtension.CreateInParam(pName: "@NeverExpire", pType: SqlDbType.SmallInt, pValue: user.NeverExpire ? 1 : 0), + SqlHelperExtension.CreateInParam(pName: "@DAMultiLogin", pType: SqlDbType.SmallInt, pValue: user.DisallowMultiLogin ? 1 : 0), + SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20), + SqlHelperExtension.CreateInParam(pName: "@CreatedBy", pType: SqlDbType.Int, pValue: createdBy), + SqlHelperExtension.CreateOutParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId), + ]; + _ = tc.ExecuteNonQuerySp(spName: "dbo.InsertUser", parameterValues: p); + userId = (p[20]?.Value != null && p[20]?.Value != DBNull.Value) ? Convert.ToInt32(p[20].Value) : 0; + + if (user.GroupIds != null && user.GroupIds.Count > 0 && userId > 0) + { + List ownerGroupsIds = []; + if (createdBy == User.SuperUser_Id) + { + ownerGroupsIds.AddRange(user.GroupIds); + } + else + { + using IDataReader dr = tc.ExecuteReader("SELECT GroupId FROM Groups WHERE (CreatedBy=%n OR ViewToAll=1)", createdBy); + while (dr.Read()) + { + ownerGroupsIds.Add(dr.GetInt32(0)); + } + dr.Close(); + } + + StringBuilder sb = new(); + foreach (int groupId in user.GroupIds) + { + if (!ownerGroupsIds.Contains(groupId)) + continue; + + sb.Append($"INSERT INTO UserGroups(UserId,GroupId) VALUES({userId},{groupId});"); + } + _ = tc.ExecuteNonQuery(commandText: sb.ToString()); + } + + tc.End(); + + #region Copy File + + try + { + string destFileName = System.IO.Path.Combine(_settings.ProfileImageFolder, $"{userId}.png"); + string sourceFileName = System.IO.Path.Combine(_settings.ProfileImageFolder, "default-user.png"); + if (System.IO.File.Exists(path: sourceFileName)) + { + System.IO.File.Copy(sourceFileName: sourceFileName, destFileName: destFileName); + } + } + catch + { + //Do nothing + } + + #endregion + + returnValue = true; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return returnValue; + } + + /// + /// + /// + /// + /// + /// + /// + public async Task EditUserAsync(UserRequest user, string ipAddress, int modifiedBy) + { + bool returnValue = false; + try + { + if (!string.IsNullOrEmpty(user.EmailAddress)) + user.EmailAddress = user.EmailAddress.Replace(" ", ""); + + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.VarChar, pValue: user.LoginId, size: 30), + SqlHelperExtension.CreateInParam(pName: "@UserName", pType: SqlDbType.VarChar, pValue: user.UserName, size: 75), + SqlHelperExtension.CreateInParam(pName: "@Designation", pType: SqlDbType.VarChar, pValue: user.Designation, size: 50), + SqlHelperExtension.CreateInParam(pName: "@MobileNo", pType: SqlDbType.VarChar, pValue: user.MobileNo, size: 15), + SqlHelperExtension.CreateInParam(pName: "@EmailAddress", pType: SqlDbType.VarChar, pValue: user.EmailAddress ?? string.Empty, size: 100), + SqlHelperExtension.CreateInParam(pName: "@AuthReqAtlogin", pType: SqlDbType.SmallInt, pValue: user.AuthReqAtlogin ? 1 : 0), + SqlHelperExtension.CreateInParam(pName: "@AuthMethod", pType: SqlDbType.SmallInt, pValue: user.AuthMethod), + SqlHelperExtension.CreateInParam(pName: "@AuthKey", pType: SqlDbType.VarChar, pValue: user.AuthKey, size: 100), + SqlHelperExtension.CreateInParam(pName: "@DBOnStartup", pType: SqlDbType.SmallInt, pValue: user.DbOnStartup ? 1 : 0), + SqlHelperExtension.CreateInParam(pName: "@Status", pType: SqlDbType.SmallInt, pValue: user.Status), + SqlHelperExtension.CreateInParam(pName: "@SeqId", pType: SqlDbType.SmallInt, pValue: user.SeqId), + SqlHelperExtension.CreateInParam(pName: "@CanUseAtnSys", pType: SqlDbType.SmallInt, pValue: user.CanUseAttendanceSystem? 1 : 0), + SqlHelperExtension.CreateInParam(pName: "@ViewToAll", pType: SqlDbType.SmallInt, pValue: user.ViewToAll? 1 : 0), + SqlHelperExtension.CreateInParam(pName: "@AccessStatus", pType: SqlDbType.SmallInt, pValue: user.AccessStatus), + SqlHelperExtension.CreateInParam(pName: "@NeverExpire", pType: SqlDbType.SmallInt, pValue: user.NeverExpire ? 1 : 0), + SqlHelperExtension.CreateInParam(pName: "@DAMultiLogin", pType: SqlDbType.SmallInt, pValue: user.DisallowMultiLogin ? 1 : 0), + SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20), + SqlHelperExtension.CreateInParam(pName: "@ModifiedBy", pType: SqlDbType.Int, pValue: modifiedBy), + SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: user.UserId) + ]; + _ = tc.ExecuteNonQuerySp(spName: "dbo.UpdateUser", parameterValues: p); + + if (user.GroupIds != null && user.GroupIds.Count > 0 && user.UserId > 0) + { + List ownerGroupsIds = []; + if (modifiedBy == User.SuperUser_Id) + { + ownerGroupsIds.AddRange(user.GroupIds); + } + else + { + using IDataReader dr = tc.ExecuteReader("SELECT GroupId FROM Groups WHERE (CreatedBy=%n OR ViewToAll=1)", modifiedBy); + while (dr.Read()) + { + ownerGroupsIds.Add(dr.GetInt32(0)); + } + dr.Close(); + } + + StringBuilder sb = new(); + foreach (int groupId in user.GroupIds) + { + if (!ownerGroupsIds.Contains(groupId)) + continue; + + sb.Append($"INSERT INTO UserGroups(UserId,GroupId) VALUES({user.UserId},{groupId});"); + } + _ = tc.ExecuteNonQuery(commandText: sb.ToString()); + } + + tc.End(); + + #region Copy File + + try + { + string destFileName = System.IO.Path.Combine(_settings.ProfileImageFolder, $"{user.UserId}.png"); + string sourceFileName = System.IO.Path.Combine(_settings.ProfileImageFolder, "default-user.png"); + if (System.IO.File.Exists(path: sourceFileName) && !System.IO.File.Exists(path: destFileName)) + { + System.IO.File.Copy(sourceFileName: sourceFileName, destFileName: destFileName); + } + } + catch + { + //Do nothing + } + + #endregion + + returnValue = true; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return returnValue; + } + + /// + /// + /// + /// + /// + /// + public async Task DeleteUserAsync(int userId, int deletedBy) + { + bool returnValue; + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@DeletedBy", pType: SqlDbType.Int, pValue: deletedBy), + SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId) + ]; + _ = tc.ExecuteNonQuerySp(spName: "dbo.DeleteUser", parameterValues: p); + + tc.End(); + + #region Delete File + + try + { + string fileSpec = System.IO.Path.Combine(_settings.ProfileImageFolder, $"{userId}.png"); + if (System.IO.File.Exists(path: fileSpec)) + System.IO.File.Delete(path: fileSpec); + } + catch + { + //Do nothing + } + + #endregion + returnValue = true; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return returnValue; + } + + /// + /// + /// + /// + /// + /// + /// + public async Task UnlockUserAsync(int userId, string loginId, int unlockedBy) + { + bool returnValue; + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId), + SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.VarChar, pValue: loginId, size: 30), + SqlHelperExtension.CreateInParam(pName: "@UnlockedBy", pType: SqlDbType.Int, pValue: unlockedBy) + ]; + _ = tc.ExecuteNonQuerySp(spName: "dbo.UnlockUser", parameterValues: p); + + tc.End(); + + returnValue = true; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return returnValue; + } + + /// + /// + /// + /// + /// + /// + /// + /// + public async Task ResetPasswordAsync(int userId, string newPassword, string ipAddress, int changedBy) + { + bool returnValue = false; + + try + { + try + { + newPassword = EncryptPassword(password: newPassword); + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@NewPwd", pType: SqlDbType.VarChar, pValue: newPassword, size: 75), + SqlHelperExtension.CreateInParam(pName: "@LastPwds", pType: SqlDbType.VarChar, pValue: string.Empty, size: 700), + SqlHelperExtension.CreateInParam(pName: "@LastPwdChgDate", pType: SqlDbType.DateTime, pValue: DateTime.Now), + SqlHelperExtension.CreateInParam(pName: "@PwdExpireDate", pType: SqlDbType.DateTime, pValue: DateTime.Now), + SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20), + SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId), + SqlHelperExtension.CreateInParam(pName: "@ChangedBy", pType: SqlDbType.Int, pValue: changedBy), + SqlHelperExtension.CreateInParam(pName: "@ResetPwd", pType: SqlDbType.SmallInt, pValue: 1) + ]; + _ = tc.ExecuteNonQuerySp(spName: "dbo.UpdateUserPassword", parameterValues: p); + + tc.End(); + + returnValue = true; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return returnValue; + } + + /// + /// + /// + /// + /// + /// + /// + /// + public async Task SendPasswordAsync(int userId, string newPassword, string ipAddress) + { + bool returnValue = false; + + try + { + newPassword = EncryptPassword(password: newPassword); + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@NewPwd", pType: SqlDbType.VarChar, pValue: newPassword, size: 75), + SqlHelperExtension.CreateInParam(pName: "LastPwds", pType: SqlDbType.VarChar, pValue: string.Empty, size: 700), + SqlHelperExtension.CreateInParam(pName: "@LastPwdChgDate", pType: SqlDbType.DateTime, pValue: DateTime.Now), + SqlHelperExtension.CreateInParam(pName: "@PwdExpireDate", pType: SqlDbType.DateTime, pValue: DateTime.Now), + SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20), + SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId), + SqlHelperExtension.CreateInParam(pName: "@ChangedBy", pType: SqlDbType.Int, pValue: userId), + SqlHelperExtension.CreateInParam(pName: "@ResetPwd", pType: SqlDbType.SmallInt, pValue: 1) + ]; + _ = tc.ExecuteNonQuerySp(spName: "dbo.UpdateUserPassword", parameterValues: p); + + tc.End(); + + returnValue = true; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return returnValue; + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public async Task ChangePasswordAsync(int userId, string oldPassword, string newPassword, string ipAddress, int changedBy) + { + bool returnValue = false; + + try + { + bool loginIdCantPwd = false; + DateTime? lastPassChgDate = null; + List actPwdHistories = []; + Queue pwdHistories = new(); + EnumAccessStatus accessStatus = EnumAccessStatus.LoggedOut; + short pwdMinLen = 0, pwdMaxLen = 0, daLastPwds = 0, expiryDays = 0; + bool neverExpire = false, enfStgPwd = false, prvntAtckPwdReuse = false; + string loginId = string.Empty, password = string.Empty, lastPasswords = string.Empty, reservedWords = string.Empty; + + #region Reading Data from DB + + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); + try + { + using IDataReader dr = tc.ExecuteReader("SELECT A.LoginId, A.Password, A.LastPasswords, A.LastPassChgDate, A.NeverExpire, A.AccessStatus," + + " B.EnfStgPwd, B.PwdMinLen, B.PwdMaxLen, B.ExpiryDays, B.DALastPwds, B.PwdRsvdWords, B.loginIdCantPwd, B.PrvntAtckPwdReuse" + + " FROM Users A LEFT OUTER JOIN ThisSystem B ON A.UserID=A.UserID WHERE A.UserID=%n", userId); + + if (dr.Read()) + { + loginId = dr.GetString(0); + password = dr.GetString(1); + lastPasswords = dr.IsDBNull(2) ? string.Empty : dr.GetString(2); + lastPassChgDate = dr.IsDBNull(3) ? null : dr.GetDateTime(3); + neverExpire = !dr.IsDBNull(4) && dr.GetInt16(4) > 0; + accessStatus = (EnumAccessStatus)dr.GetInt16(5); + enfStgPwd = !dr.IsDBNull(6) && dr.GetInt16(6) > 0; + pwdMinLen = dr.IsDBNull(7) ? Convert.ToInt16(0) : dr.GetInt16(7); + pwdMaxLen = dr.IsDBNull(8) ? Convert.ToInt16(30) : dr.GetInt16(8); + expiryDays = dr.IsDBNull(9) ? Convert.ToInt16(0) : dr.GetInt16(9); + daLastPwds = dr.IsDBNull(10) ? Convert.ToInt16(0) : dr.GetInt16(10); + reservedWords = dr.IsDBNull(11) ? string.Empty : dr.GetString(11); + loginIdCantPwd = !dr.IsDBNull(12) && dr.GetInt16(12) != 0; + prvntAtckPwdReuse = !dr.IsDBNull(13) && dr.GetInt16(13) != 0; + } + dr.Close(); + tc.End(); + + if (loginIdCantPwd && !string.IsNullOrEmpty(loginId)) + { + reservedWords = string.IsNullOrEmpty(reservedWords) ? loginId : $"{reservedWords},{loginId}"; + } + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + #endregion + + #region Validation + + oldPassword = EncryptPassword(password: oldPassword); + if (!oldPassword.Equals(password)) + throw new InvalidOperationException("Old Password does not match with system."); + + #region Password Histories + + if (!string.IsNullOrEmpty(lastPasswords) && !string.IsNullOrWhiteSpace(lastPasswords)) + { + string[] pwds = lastPasswords.Split(','); + foreach (string pwd in pwds) + { + pwdHistories.Enqueue(pwd); + } + } + + #endregion + + #region Actual Password Histories + + string[] pwdsHists = [.. pwdHistories]; + if (daLastPwds > 0 && pwdsHists.Length > 0) + { + for (int idx = pwdsHists.Length - 1; idx >= 0; idx--) + { + if (!actPwdHistories.Contains(pwdsHists[idx]) && actPwdHistories.Count <= daLastPwds) + actPwdHistories.Add(pwdsHists[idx]); + } + } + + #endregion + + if (lastPassChgDate.HasValue && prvntAtckPwdReuse && accessStatus != EnumAccessStatus.FirstTime && lastPassChgDate.Value.Date >= DateTime.Today.Date) + { + throw new InvalidOperationException("Password should be at least one day old to change again."); + } + CheckPasswordHistory(password: newPassword, minLen: pwdMinLen, maxLen: pwdMaxLen, enfStgPwd: enfStgPwd, reservedWords: reservedWords, daLastPwds: daLastPwds, pwdHistories: pwdHistories, actPwdHistories: actPwdHistories); + + #endregion + + #region Updateing Data + + if (pwdHistories.ToArray().Length > 0) + lastPasswords = string.Join(",", [.. pwdHistories]); + + DateTime? expireDate = null; + lastPassChgDate = DateTime.Now; + if (!neverExpire && expiryDays > 0) + expireDate = DateTime.Now.AddDays(expiryDays); + + try + { + newPassword = EncryptPassword(password: newPassword); + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@NewPwd", pType: SqlDbType.VarChar, pValue: newPassword, size: 75), + SqlHelperExtension.CreateInParam(pName: "@LastPwds", pType: SqlDbType.VarChar, pValue: lastPasswords, size: 700), + SqlHelperExtension.CreateInParam(pName: "@LastPwdChgDate", pType: SqlDbType.DateTime, pValue: lastPassChgDate), + SqlHelperExtension.CreateInParam(pName: "@PwdExpireDate", pType: SqlDbType.DateTime, pValue: expireDate), + SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20), + SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId), + SqlHelperExtension.CreateInParam(pName: "@ChangedBy", pType: SqlDbType.Int, pValue: changedBy), + SqlHelperExtension.CreateInParam(pName: "@ResetPwd", pType: SqlDbType.SmallInt, pValue: 0) + ]; + _ = tc.ExecuteNonQuerySp(spName: "dbo.UpdateUserPassword", parameterValues: p); + + tc.End(); + + returnValue = true; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + #endregion + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return returnValue; + } + + /// + /// + /// + /// + /// + /// + /// + /// + public async Task UpdateMyThemeAsync(int userId, string menuLayout, string themeName, string schemeName) + { + bool returnValue = false; + + try + { + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@MenuLayout", pType: SqlDbType.VarChar, pValue: menuLayout, size: 15), + SqlHelperExtension.CreateInParam(pName: "@ThemeName", pType: SqlDbType.VarChar, pValue: themeName, size: 15), + SqlHelperExtension.CreateInParam(pName: "@SchemeName", pType: SqlDbType.VarChar, pValue: schemeName, size: 10), + SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId) + ]; + _ = tc.ExecuteNonQuerySp(spName: "dbo.UpdateMyTheme", parameterValues: p); + + tc.End(); + + returnValue = true; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return returnValue; + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public async Task UpdateMyInfoAsync(string address, string contactNo, int modifiedBy, int emplyeeId) + { + bool returnValue = false; + + try + { + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@Address", pType: SqlDbType.VarChar, pValue: address, size: 120), + SqlHelperExtension.CreateInParam(pName: "@ContactNo", pType: SqlDbType.VarChar, pValue: contactNo, size: 25), + SqlHelperExtension.CreateInParam(pName: "@ModifiedBy", pType: SqlDbType.Int, pValue: modifiedBy), + SqlHelperExtension.CreateInParam(pName: "@EmployeeId", pType: SqlDbType.Int, pValue: emplyeeId) + ]; + _ = tc.ExecuteNonQuerySp(spName: "dbo.UpdateMyInfo", parameterValues: p); + + tc.End(); + + returnValue = true; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return returnValue; + } + + /// + /// + /// + /// + /// + /// + /// + public async Task GetAttributesAsync(int userId, int clientType) + { + UserAttributesResponse response = new() { ReturnStatus = 200 }; + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); + try + { + using (IDataReader dr = tc.ExecuteReader("SELECT LoginId FROM Users WHERE UserId=%n", userId)) + { + if (dr.Read()) + { + response = new UserAttributesResponse + { + UserId = userId, + LoginId = dr.GetString(0), + ReturnStatus = 200, + UkIds = [], + }; + } + dr.Close(); + } + + #region Client & Projects + + if (response.UserId > 0) + { + using IDataReader dr = tc.ExecuteReader($"SELECT ProjectId, ClientId FROM UserClients WHERE UserId=%n AND ProjectId IS {(clientType == 1 ? "NOT " : "")}NULL", response.UserId); + while (dr.Read()) + { + string ukId = $"{(dr.IsDBNull(0) ? 0 : dr.GetInt32(0))}|{dr.GetInt32(1)}"; + response.UkIds.Add(ukId); + } + dr.Close(); + } + + response.HasSetup = response.UkIds.Count > 0; + + #endregion + + 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 DeleteAttributesAsync(int userId, int clientType, int deletedBy) + { + bool returnValue; + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId), + SqlHelperExtension.CreateInParam(pName: "@ClientType", pType: SqlDbType.SmallInt, pValue: clientType), + SqlHelperExtension.CreateInParam(pName: "@DeletedBy", pType: SqlDbType.Int, pValue: deletedBy) + ]; + _ = tc.ExecuteNonQuerySp(spName: "dbo.DeleteUserAttributes", parameterValues: p); + + tc.End(); + + returnValue = true; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return returnValue; + } + + /// + /// + /// + /// + /// + /// + /// + public async Task GetUsersAsync(UserSearchRequest request, int userId) + { + UserSearchResponse response = new(); + try + { + string andClause = string.Empty; + if (!string.IsNullOrEmpty(request.Criteria)) + { + string criteria = request.Criteria.Replace("'", "''"); + andClause = $" AND (LoginID='{criteria}' OR MobileNo='{criteria}' OR EmailAddress='{criteria}' OR UserName LIKE '%{criteria}%' OR Designation LIKE '%{criteria}%')"; + } + + if (request.Status > 0) + andClause += SQLParser.MakeSQL(" AND Status=%n", request.Status); + + if (request.CheckOwner) + andClause += SQLParser.MakeSQL(" AND (CreatedBy=%n OR ViewToAll=1)", userId); + + string sortField = request.SortField switch + { + "userName" => "UserName", + "designation" => "Designation", + "mobileNo" => "MobileNo", + "emailAddress" => "EmailAddress", + "statusDetail" => "Status", + "loginId" => "LoginId", + _ => "SeqId, LoginId" + }; + + string sortOrder = request.SortOrder switch + { + "desc" => "DESC", + _ => "ASC", + }; + + string commandText = request.Skip + request.PageSize <= 0 ? + SQLParser.MakeSQL("SELECT UserId, LoginId, UserName, Designation, MobileNo, EmailAddress, Status, SeqId, IsLocked, CanUseAtnSys, Password," + + " COUNT(*) OVER() AS TotalRows FROM Users WHERE UserId!=-9%q ORDER BY %q %q", andClause, sortField, sortOrder) + : SQLParser.MakeSQL("SELECT A.UserId, A.LoginId, A.UserName, A.Designation, A.MobileNo, A.EmailAddress, A.Status, A.SeqId, A.IsLocked," + + " A.CanUseAtnSys, A.Password, A.TotalRows FROM(SELECT UserId, LoginId, UserName, Designation, MobileNo, EmailAddress, Status, SeqId," + + " IsLocked, CanUseAtnSys, Password, ROW_NUMBER() OVER(ORDER BY %q %q) AS RN, COUNT(*) OVER() AS TotalRows FROM Users WHERE UserId!=-9%q) A" + + " WHERE A.RN>%n AND A.RN<=%n", sortField, sortOrder, andClause, request.Skip, request.Skip + request.PageSize); + + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); + try + { + int totalRows = 0; + using IDataReader dr = tc.ExecuteReader(commandText: commandText); + while (dr.Read()) + { + UserGetResponse item = new() + { + UserId = dr.GetInt32(0), + LoginId = dr.GetString(1), + UserName = dr.GetString(2), + Designation = dr.GetString(3), + MobileNo = dr.GetString(4), + EmailAddress = dr.GetString(5), + Status = (EnumStatus)dr.GetInt16(6), + SeqId = dr.GetInt16(7), + IsLocked = !dr.IsDBNull(8) && dr.GetInt16(8) != 0, + CanUseAttendanceSystem = !dr.IsDBNull(9) && dr.GetInt16(9) != 0, + }; + totalRows = dr.GetInt32(11); + + } + dr.Close(); + + response.TotalRows = totalRows; + + tc.End(); + response.ReturnStatus = 200; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return response; + } + + /// + /// + /// + /// + /// + /// + /// + /// + public async Task GetUsersByTeamSpaceAsync(string teamSpaceIds, int projectId, int userId) + { + UserBasicInfoResponse response = new(); + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); + try + { + string andClause = string.Empty; + if (userId > 0) + { + andClause = $" AND UserID={userId}"; + } + else + { + if (!string.IsNullOrEmpty(teamSpaceIds)) + { + andClause = $" AND UserID IN(SELECT UserId FROM TeamSpaceUsers WHERE TeamSpaceId IN({teamSpaceIds}))"; + } + + if (projectId > 0) + { + andClause += $" AND UserID IN(SELECT UserId FROM TeamSpaceUserProjects WHERE ProjectId={projectId})"; + } + } + + string commandText = $"SELECT UserID, LoginID, UserName, Designation FROM Users WHERE UserID!={User.SuperUser_Id} AND Status=8{andClause} ORDER BY SeqId, UserName"; + using IDataReader dr = tc.ExecuteReader(commandText: commandText); + while (dr.Read()) + { + UserBasicInfo item = new() + { + UserId = dr.GetInt32(0), + LoginId = dr.GetString(1), + UserName = dr.GetString(2), + Designation = dr.GetString(3), + }; + + response.Value.Add(item); + } + dr.Close(); + + tc.End(); + response.ReturnStatus = 200; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return response; + } + + /// + /// + /// + /// + /// + /// + /// + /// + public async Task GetUsersBasicAsync(bool applyFilter, string teamSpaceIds, int projectId) + { + UserBasicInfoResponse response = new(); + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); + try + { + string commandText; + if (applyFilter) + { + if (!string.IsNullOrEmpty(teamSpaceIds)) + { + commandText = $"SELECT UserID, LoginID, UserName, Designation FROM Users WHERE UserID!={User.SuperUser_Id}" + + $" AND Status=8 AND UserID IN(SELECT UserId FROM TeamSpaceUsers WHERE TeamSpaceId IN({teamSpaceIds}))"; + } + else + { + commandText = $"SELECT UserID, LoginID, UserName, Designation FROM Users WHERE UserID!={User.SuperUser_Id} AND Status=8"; + } + + if (projectId > 0) + { + commandText += $" AND UserID IN(SELECT UserId FROM UserClients WHERE ProjectID={projectId})"; + } + } + else + { + commandText = $"SELECT UserID, LoginID, UserName, Designation FROM Users WHERE UserID!={User.SuperUser_Id} AND Status=8"; + } + commandText += " ORDER BY SeqId, UserName"; + + using IDataReader dr = tc.ExecuteReader(commandText: commandText); + while (dr.Read()) + { + UserBasicInfo item = new() + { + UserId = dr.GetInt32(0), + LoginId = dr.GetString(1), + UserName = dr.GetString(2), + Designation = dr.GetString(3), + }; + + response.Value.Add(item); + } + dr.Close(); + + tc.End(); + response.ReturnStatus = 200; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return response; + } + + /// + /// + /// + /// + /// + /// + public async Task GetForceLogoutUsersAsync(int createdBy) + { + UserForceLogoutResponse response = new(); + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); + try + { + string commandText; + if (createdBy == User.SuperUser_Id) + commandText = $"SELECT UserID, LoginID, UserName FROM Users WHERE UserID!={User.SuperUser_Id} AND Status=8 ORDER BY LoginID"; + else + commandText = $"SELECT UserID, LoginID, UserName FROM Users WHERE (CreatedBy={createdBy} OR ViewToAll=1) AND Status=8 ORDER BY LoginID"; + + using IDataReader dr = tc.ExecuteReader(commandText: commandText); + while (dr.Read()) + { + UserForceLogout item = new() + { + UserId = dr.GetInt32(0), + LoginId = dr.GetString(1), + UserName = dr.GetString(2) + }; + + response.Value.Add(item); + } + dr.Close(); + + tc.End(); + response.ReturnStatus = 200; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return response; + } + + /// + /// + /// + /// + /// + /// + public async Task GetAttendanceUsersAsync(int userId) + { + UserBasicInfoResponse response = new(); + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); + try + { + string andClause = userId > 0 ? $" AND UserID={userId}" : string.Empty; + using IDataReader dr = tc.ExecuteReader(commandText: $"SELECT UserID, LoginID, UserName, Designation FROM Users WHERE EmployeeId IS NOT NULL AND Status=8{andClause} ORDER BY SeqId, UserName"); + while (dr.Read()) + { + UserBasicInfo item = new() + { + UserId = dr.GetInt32(0), + LoginId = dr.GetString(1), + UserName = dr.GetString(2), + Designation = dr.GetString(3), + }; + + response.Value.Add(item); + } + dr.Close(); + + tc.End(); + response.ReturnStatus = 200; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return response; + } + + /// + /// + /// + /// + /// + /// + /// + public async Task ForceLogoutNowAsync(List userIds, string ipAddress) + { + bool returnValue; + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@UserIDs", pType: SqlDbType.VarChar, pValue: string.Join(",", userIds.ToArray()), size: 8000), + SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20) + ]; + _ = tc.ExecuteNonQuerySp(spName: "dbo.DoForceLogout", parameterValues: p); + + tc.End(); + + returnValue = true; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return returnValue; + } + + /// + /// + /// + /// + /// + public async Task GetUserAsync(int userId) + { + UserGetResponse response = new() { ReturnStatus = 200 }; + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); + try + { + using (IDataReader dr = tc.ExecuteReader("SELECT LoginId, UserName, Designation, MobileNo, EmailAddress, IsLocked, Status," + + " AccessStatus, NeverExpire, AuthReqAtlogin, AuthMethod, DBOnStartup, DAMultiLogin, EmployeeCode, SeqId, CanUseAtnSys, ViewToAll" + + " FROM Users WHERE UserId=%n", userId)) + { + if (dr.Read()) + { + response = new UserGetResponse + { + UserId = userId, + LoginId = dr.GetString(0), + UserName = dr.GetString(1), + Designation = dr.GetString(2), + MobileNo = dr.GetString(3), + EmailAddress = dr.GetString(4), + IsLocked = !dr.IsDBNull(5) && dr.GetInt16(5) > 0, + Status = (EnumStatus)dr.GetInt16(6), + AccessStatus = (EnumAccessStatus)dr.GetInt16(7), + NeverExpire = !dr.IsDBNull(8) && dr.GetInt16(8) != 0, + AuthRequiredAtLogin = !dr.IsDBNull(9) && dr.GetInt16(9) != 0, + AuthMethod = (EnumAuthenticationMethod)dr.GetInt16(10), + DbOnStartup = !dr.IsDBNull(11) && dr.GetInt16(11) != 0, + DisallowMultiLogin = !dr.IsDBNull(12) && dr.GetInt16(12) != 0, + EmployeeCode = dr.IsDBNull(13) ? string.Empty : dr.GetString(13), + SeqId = dr.GetInt16(14), + CanUseAttendanceSystem = !dr.IsDBNull(15) && dr.GetInt16(15) != 0, + ViewToAll = !dr.IsDBNull(16) && dr.GetInt16(16) != 0, + ReturnStatus = 200 + }; + } + dr.Close(); + } + + response.GroupIds = []; + using (IDataReader dr = tc.ExecuteReader("SELECT GroupId FROM UserGroups WHERE UserID=%n", userId)) + { + while (dr.Read()) + { + response.GroupIds.Add(dr.GetInt32(0)); + } + 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 GetUserProfileAsync(int userId) + { + UserProfileResponse response = new() { ReturnStatus = 200 }; + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); + try + { + int employeeId = 0; + using (IDataReader dr = tc.ExecuteReader("SELECT LoginId, UserName, Designation, LastPassChgDate, ExpireDate, EmployeeId FROM Users WHERE UserId=%n", userId)) + { + if (dr.Read()) + { + response = new UserProfileResponse + { + UserId = userId, + LoginId = dr.GetString(0), + UserName = dr.GetString(1), + Designation = dr.GetString(2), + PwdLastChangedTime = dr.IsDBNull(3) ? null : dr.GetDateTime(3), + NextPwdDate = dr.IsDBNull(4) ? null : dr.GetDateTime(4), + ReturnStatus = 200 + }; + + employeeId = dr.IsDBNull(5) ? 0 : dr.GetInt32(5); + } + dr.Close(); + } + + if (employeeId > 0) + { + using IDataReader dr = tc.ExecuteReader("SELECT Address, ContactNo FROM Employees WHERE EmployeeId=%n", employeeId); + if (dr.Read()) + { + response.Address = dr.GetString(0); + response.ContactNo = dr.GetString(1); + } + dr.Close(); + } + + response.LoginHistories = []; + using (IDataReader dr = tc.ExecuteReader("SELECT TOP 10 LoginIPAddress, LoginTime, LogoutIPAddress, LogoutTime" + + " FROM AccessLog WHERE UserID=%n ORDER BY LoginTime DESC", userId)) + { + int slNo = 0; + while (dr.Read()) + { + slNo++; + LoginHistory item = new() + { + SlNo = slNo, + LoginIp = dr.GetString(0), + LoginTime = dr.GetDateTime(1), + LogoutIp = dr.IsDBNull(2) ? string.Empty : dr.GetString(2), + LogoutTime = dr.IsDBNull(3) ? null : dr.GetDateTime(3), + }; + response.LoginHistories.Add(item: item); + } + 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 GetUserPermissionsAsync(int userId) + { + MenuResponse response = new() { Item = new() { Items = [] } }; + MenuItem pi = new() { Label = "User Profile", Icon = "fa fa-user-md", RouterLink = string.Empty, Items = [] }; + pi.Items.Add(new() { Label = "My Profile", Icon = "fa fa-user", RouterLink = "/myprofile" }); + pi.Items.Add(new() { Label = "Change My Password", Icon = "fa fa-unlock", RouterLink = "/changemypwd" }); + pi.Items.Add(new() { Label = "Change My Theme", Icon = "fa fa-user", RouterLink = "/changemytheme" }); + pi.Items.Add(new() { Label = "WhatsApp Message", Icon = "fa fa-whatsapp", RouterLink = "/sendwhatsappmsg" }); + pi.Items.Add(new() { Label = "Access Log", Icon = "fa fa-list-alt", RouterLink = "/accesslog" }); + response.Item.Items.Add(pi); + + List pmns = []; + 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.GetUserPermissions", parameterValues: p); + while (dr.Read()) + { + GroupPermission item = new() + { + ModuleId = dr.GetString(0), + AllowSelect = dr.GetInt16(1) != 0, + AllowAdd = dr.GetInt16(2) != 0, + AllowEdit = dr.GetInt16(3) != 0, + AllowDelete = dr.GetInt16(4) != 0 + }; + pmns.Add(item); + } + dr.Close(); + + tc.End(); + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + + #region Making Hierarchy + + List items = []; + List fullMenu = GlobalFunctions.BuildMenu(); + IEnumerable data = fullMenu.Where(x => (string.IsNullOrEmpty(x.ParentId) || string.IsNullOrWhiteSpace(x.ParentId)) && x.Visible && pmns.Where(y => y.AllowSelect).Select(z => z.ModuleId).Contains(x.ModuleId)); + foreach (GroupPermission datum in data) + { + MakeHierarchy(parent: datum, data: fullMenu, pmns: pmns); + } + items.AddRange(data); + + if (_menuSettings?.MenuItems?.Count > 0) + { + foreach (GroupPermission src in items) + { + MenuItem dst = src.Copy(); + dst.Label = _menuSettings.GetItem(src.ModuleId, src.ModuleName).Value; + response.Item.Items.Add(dst); + + CopyHierarchy(src: src, dst: dst); + } + } + else + { + foreach (GroupPermission src in items) + { + MenuItem dst = src.Copy(); + response.Item.Items.Add(dst); + + CopyHierarchy(src: src, dst: dst); + } + } + items.Clear(); + + #endregion + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return response; + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + + /// + /// + /// + /// + /// + /// + + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public async Task UploadDocumentAsync(int userId, int id, int documentOf, string orgFileName, string fileName) + { + 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), + SqlHelperExtension.CreateInParam(pName: "@Id", pType: SqlDbType.Int, pValue: id), + SqlHelperExtension.CreateInParam(pName: "@DocumentOf", pType: SqlDbType.SmallInt, pValue: documentOf), + SqlHelperExtension.CreateInParam(pName: "@OrgFileName", pType: SqlDbType.VarChar, pValue: orgFileName, size: 200), + SqlHelperExtension.CreateInParam(pName: "@FileName", pType: SqlDbType.VarChar, pValue: fileName, size: 100) + ]; + _ = tc.ExecuteNonQuerySp(spName: "dbo.UploadDocument", parameterValues: p); + + 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 LogoutAsync(string ipAddress, int userId, int logId, bool attendanceLogout, string loginId, string localIp, string macAddress, string hostName, string logoutRemarks) + { + bool returnValue = false; + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); + try + { + SqlParameter[] p; + #region Attendance Logout and Valid Ip Address + + if (attendanceLogout && userId != User.SuperUser_Id) + { + string errMsg = string.Empty; + p = + [ + SqlHelperExtension.CreateInParam(pName: "@UserID", pType: SqlDbType.Int, pValue: userId), + SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.VarChar, pValue: loginId, size: 30), + SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: localIp, size: 20), + SqlHelperExtension.CreateInParam(pName: "@MacAddress", pType: SqlDbType.VarChar, pValue: macAddress, size: 30), + SqlHelperExtension.CreateInParam(pName: "@HostName", pType: SqlDbType.VarChar, pValue: hostName, size: 100), + SqlHelperExtension.CreateInParam(pName: "@LogoutRemarks", pType: SqlDbType.VarChar, pValue: logoutRemarks, size: 50), + SqlHelperExtension.CreateOutParam(pName: "@ErrMsg", pType: SqlDbType.VarChar, pValue: errMsg, size: 300) + ]; + _ = tc.ExecuteNonQuerySp(spName: "dbo.LogoutFromAttendance", parameterValues: p); + + errMsg = (p[6] == null || p[6].Value == null || p[6].Value == DBNull.Value) ? string.Empty : Convert.ToString(p[6].Value); + if (!string.IsNullOrEmpty(errMsg)) + { + throw new InvalidOperationException(errMsg); + } + } + + #endregion + + //Keep access history + p = + [ + SqlHelperExtension.CreateInParam(pName: "@UserID", pType: SqlDbType.Int, pValue: userId), + SqlHelperExtension.CreateInParam(pName: "@Status", pType: SqlDbType.TinyInt, pValue: 3), + SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20), + SqlHelperExtension.CreateInParam(pName: "@LogID", pType: SqlDbType.Int, pValue: logId), + ]; + _ = tc.ExecuteNonQuerySp(spName: "dbo.CreateAccessLog", parameterValues: p); + + 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 GetAccessLogAsync(int accessType, string loginId, DateTime startDate, DateTime endDate) + { + AccessLogResponse response = new() { Value = [] }; + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@AccessType", pType: SqlDbType.SmallInt, pValue: accessType), + SqlHelperExtension.CreateInParam(pName: "@LoginID", pType: SqlDbType.VarChar, pValue: loginId, size: 30), + SqlHelperExtension.CreateInParam(pName: "@StartDate", pType: SqlDbType.DateTime, pValue: startDate), + SqlHelperExtension.CreateInParam(pName: "@EndDate", pType: SqlDbType.DateTime, pValue: endDate), + ]; + using IDataReader dr = tc.ExecuteReaderSp(spName: "dbo.GetAccessLogData", parameterValues: p); + while (dr.Read()) + { + AccessLog item = new() + { + LoginId = dr.GetString(0), + LoginTime = dr.GetDateTime(1), + LoginIp = dr.GetString(2), + LogoutTime = dr.IsDBNull(3) ? null : dr.GetDateTime(3), + LogoutIp = dr.IsDBNull(4) ? null : dr.GetString(4) + }; + + response.Value.Add(item); + } + dr.Close(); + + tc.End(); + response.ReturnStatus = 200; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return response; + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public async Task SaveAuthorizeLimitAsync(decimal maxAuthLimit, int userId, string ipAddress, string savedBy) + { + bool returnValue; + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); + try + { + SqlParameter[] p = + [ + + SqlHelperExtension.CreateInParam(pName: "@MaxAuthLimit", pType: SqlDbType.Decimal, pValue: maxAuthLimit), + SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId), + SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20), + SqlHelperExtension.CreateInParam(pName: "@SavedBy", pType: SqlDbType.VarChar, pValue: savedBy, size: 30) + ]; + _ = tc.ExecuteNonQuerySp(spName: "dbo.SaveAuthorizeLimit", parameterValues: p); + + tc.End(); + + returnValue = true; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + return returnValue; + } + + public async Task GetAuthorizeLimitAsync(int userId) + { + UserAuthorizeLimitResponse response = new() { Value = 0 }; + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); + try + { + using IDataReader dr = tc.ExecuteReader(commandText: "SELECT LoginId, MaxAuthLimit FROM dbo.Users WHERE UserId=%n", args: userId); + if (dr.Read()) + { + response.LoginId = dr.GetString(0); + response.Value = dr.IsDBNull(1) ? 0 : dr.GetDecimal(1); + } + dr.Close(); + + tc.End(); + response.ReturnStatus = 200; + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + + return response; + } + + #region Private functions + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + private void ReadPwdParams(int userId, string loginId, out bool enfStgPwd, out short minLen, out short maxLen, out string reservedWords) + { + try + { + enfStgPwd = false; + minLen = maxLen = 0; + reservedWords = string.Empty; + using TransactionContext tc = TransactionContext.Begin(_settings.DefaultConnection.ConnectionNode); + try + { + bool loginIdCantPwd = false; + using (IDataReader dr = tc.ExecuteReader("SELECT EnfStgPwd, PwdMinLen, PwdMaxLen, PwdRsvdWords, LoginIdCantPwd FROM ThisSystem")) + { + if (dr.Read()) + { + enfStgPwd = Convert.ToBoolean(dr.GetInt16(0)); + minLen = dr.GetInt16(1); + maxLen = dr.GetInt16(2); + reservedWords = dr.IsDBNull(3) ? string.Empty : dr.GetString(3); + loginIdCantPwd = !dr.IsDBNull(4) && dr.GetInt16(4) != 0; + } + dr.Close(); + } + + if (string.IsNullOrEmpty(loginId) && loginIdCantPwd && userId > 0) + { + using IDataReader dr = tc.ExecuteReader("SELECT LoginId FROM Users WHERE UserId=%n", userId); + if (dr.Read()) + { + loginId = dr.IsDBNull(0) ? string.Empty : dr.GetString(0); + } + dr.Close(); + } + tc.End(); + + if (loginIdCantPwd && !string.IsNullOrEmpty(loginId)) + { + reservedWords = string.IsNullOrEmpty(reservedWords) ? loginId : $"{reservedWords},{loginId}"; + } + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + private void CheckPasswordHistory(string password, short minLen, short maxLen, bool enfStgPwd, string reservedWords, int daLastPwds, Queue pwdHistories, List actPwdHistories) + { + try + { + ValidatePwdPolicies(password: password, minLen: minLen, maxLen: maxLen, enfStgPwd: enfStgPwd, reservedWords: reservedWords); + + password = EncryptPassword(password: password); + if (daLastPwds > 0 && actPwdHistories.Contains(password)) + throw new InvalidOperationException($"You cannot use this password, because it was used in last {daLastPwds} passwords."); + + if (pwdHistories.Count >= 5) + pwdHistories.Dequeue(); + + pwdHistories.Enqueue(password); + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + } + + /// + /// Validates a password against specified policies, including length constraints, reserved words, and strong password requirements. + /// + /// This method performs validation based on the provided policies and throws an exception if the password does not comply. + /// Ensure that all parameters are correctly configured before calling this method. + /// The password to validate. Cannot be null or empty. + /// The minimum allowed length for the password. Must be greater than 0. + /// The maximum allowed length for the password. Must be greater than 0. + /// A value indicating whether strong password enforcement is enabled. If , the password + /// must contain at least one uppercase letter, one lowercase letter, one number, and one special character (!, @, #, $, %, ^, &, *, ?). + /// A comma-separated list of reserved words that cannot be included in the password. Cannot be null or empty. + /// Thrown if the password violates any of the specified policies, including: + /// Contains a reserved word. Does not meet the minimum length requirement. + /// Exceeds the maximum length requirement. Fails strong password enforcement when is . + private static void ValidatePwdPolicies(string password, short minLen, short maxLen, bool enfStgPwd, string reservedWords) + { + try + { + if (!string.IsNullOrEmpty(password) && !string.IsNullOrEmpty(reservedWords)) + { + List words = [.. reservedWords.Split(',')]; + foreach (string word in words) + { + if (password.Contains(word, StringComparison.InvariantCultureIgnoreCase)) + throw new InvalidOperationException($"[{word}] can not be used in password."); + } + } + + if (minLen > 0 && password.Length < minLen) + throw new InvalidOperationException($"Minimum length of Password is {minLen}"); + + if (maxLen > 0 && password.Length > maxLen) + throw new InvalidOperationException($"Maximum length of Password is {maxLen}"); + + if (enfStgPwd && !Global.StringFunctions.IsStrongPassword(password)) + throw new InvalidOperationException("Must have Upper & Lower case character, a Number and a Special character (!, @, #, $, %, ^, &, *, ?)."); + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + } + + /// + /// + /// + /// + /// + private string EncryptPassword(string password) + { + try + { + string pwdSecretKey = GlobalFunctions.ConvertFromBase64String(_settings.PwdSecretKey); + return Global.CipherFunctions.EncryptByAES(privateKey: pwdSecretKey, publicKey: pwdSecretKey, data: password); + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + } + + /// + /// + /// + /// + /// + /// + /// + private static void SetAuthValue(TransactionContext tc, string authValue, int validMinutes, int userId) + { + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@AuthValue", pType: SqlDbType.VarChar, pValue: authValue, size: 10), + SqlHelperExtension.CreateInParam(pName: "@ValidMinutes", pType: SqlDbType.Int, pValue: validMinutes), + SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId) + ]; + _ = tc.ExecuteNonQuerySp(spName: "dbo.SetAuthValue", parameterValues: p); + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + } + + /// + /// + /// + /// + /// + /// + /// + private static int GetPendingNotifCount(TransactionContext tc, int userId) + { + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId), + SqlHelperExtension.CreateOutParam(pName: "@NotifCount", pType: SqlDbType.Int, pValue: 0) + ]; + _ = tc.ExecuteNonQuerySp(spName: "dbo.GetPendingAuthCountByUser", parameterValues: p); + return (p[1].Value != null && p[1].Value != DBNull.Value) ? Convert.ToInt32(p[1].Value) : 0; + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + } + + /// + /// + /// + /// + /// + /// + private static void MakeHierarchy(GroupPermission parent, List data, List pmns) + { + try + { + IEnumerable children = data.Where(x => x.ParentId == parent.ModuleId && x.Visible && pmns.Where(y => y.AllowSelect).Select(z => z.ModuleId).Contains(x.ModuleId)); + parent.Children.AddRange(children); + + foreach (GroupPermission child in parent.Children) + { + MakeHierarchy(parent: child, data: data, pmns: pmns); + } + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + } + + /// + /// + /// + /// + /// + private void CopyHierarchy(GroupPermission src, MenuItem dst) + { + try + { + if (src.Children?.Count > 0) + dst.Items = []; + + foreach (GroupPermission srcChild in src.Children) + { + MenuItem dstChild = srcChild.Copy(); + dstChild.Label = _menuSettings?.GetItem(dstChild.ModuleId, dstChild.Label).Value; + dst.Items.Add(dstChild); + + CopyHierarchy(src: srcChild, dst: dstChild); + } + + } + catch (Exception e) + { + throw new InvalidOperationException(e.Message, e); + } + } public Task SaveAttributesAsync(int userId, int clientType, string ipAddress, int attributeSetBy, List ukIds) { diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/IntegretionApi/AuthController.cs b/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/IntegretionApi/AuthController.cs new file mode 100644 index 0000000..b3f75d2 --- /dev/null +++ b/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/IntegretionApi/AuthController.cs @@ -0,0 +1,2042 @@ +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 Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.Logging; +using Microsoft.IdentityModel.Tokens; +using System; +using System.Collections.Generic; +using System.DirectoryServices; +using System.IdentityModel.Tokens.Jwt; +using System.IO; +using System.Linq; +using System.Runtime.Versioning; +using System.Security.Claims; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Options; +using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Auth; + +namespace OnlineSalesAutoCrop.CoreAPI.Controllers.V1 +{ + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + [Authorize] + [ApiController] + [ApiVersion("1.0")] + [ValidateAntiForgeryToken] + [Route("api/v{version:apiVersion}/users")] + + public class AuthController(IUserService service, IOptions appSettings, IEaseCache cache, ILogger logger, IRefreshTokenService refreshTokenService) : ControllerBase + { + private readonly ILogger _logger = logger; + private readonly IEaseCache _cache = cache; + private readonly IUserService _service = service; + private readonly IRefreshTokenService _refreshTokenService = refreshTokenService; + private readonly AppSettings _appSettings = appSettings?.Value; + private readonly DateTimeOffset _options = Helper.CreateEaseCacheOptions(); + + /// + /// Login using your credential data retrieve from SqlServer + /// + /// + /// + /// + /// If login successful ValidUser: true + /// If login successful Return ValidUser: true and UserName: not empty + [HttpPost("login")] + [AllowAnonymous] + [IgnoreAntiforgeryToken] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(LoginResponse))] + public async Task Login([FromBody] LoginRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + LoginResponse response = new(); + if (string.IsNullOrEmpty(request.LoginId)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Login ID is required."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + if (string.IsNullOrEmpty(request.Password)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Password is required."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + if (string.IsNullOrEmpty(request.AppVersion)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Version is required."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + string ipAddress = string.Empty; + try + { + #region Decrypt LoginID + + string cipherSecretKey = GlobalFunctions.ConvertFromBase64String(_appSettings.CipherSecretKey); + request.LoginId = Helper.DecryptData(secret: cipherSecretKey, data: request.LoginId); + request.Password = Helper.DecryptData(secret: cipherSecretKey, data: request.Password); + request.AppId = Helper.DecryptData(secret: cipherSecretKey, data: request.AppId); + request.HostName = Helper.DecryptData(secret: cipherSecretKey, data: request.HostName); + request.IpAddress = Helper.DecryptData(secret: cipherSecretKey, data: request.IpAddress); + request.MacAddress = Helper.DecryptData(secret: cipherSecretKey, data: request.MacAddress); + if (request.LoginId.Equals("*Key/Data Error*") || request.Password.Equals("*Key/Data Error*") || request.AppId.Equals("*Key/Data Error*")) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Key or Data Error...!"); + return BadRequest(response); + } + + #endregion + + bool checkPwd = true; + #region If AD (Active Directory) authentication is enabled do validate + + if (_appSettings.ADConfig.Enabled) + { + checkPwd = false; + #pragma warning disable CA1416 // Validate platform compatibility + int adLoginStatus = GetADLoginStatus(loginId: request.LoginId, password: request.Password); + #pragma warning restore CA1416 // Validate platform compatibility + if (adLoginStatus != 1) + { + response.LoginStatus = EnumLoginStatus.Unsuccessful; + response.ReturnStatus = StatusCodes.Status403Forbidden; + if (adLoginStatus == 2) + response.ReturnMessage.Add("Active Directory User is DISABLED."); + else if (adLoginStatus == 3) + response.ReturnMessage.Add("Active Directory User's Password has EXPIRED."); + else + response.ReturnMessage.Add("Active Directory User/Password is INVALID."); + + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + } + + #endregion + + ipAddress = Request.HttpContext.GetIpAddress(); + User user = await _service.LoginAsync(request: request, ipAddress: ipAddress, checkPwd: checkPwd); + + if (user == null || user.UserId == 0) + { + response.LoginStatus = EnumLoginStatus.Error; + response.ReturnStatus = StatusCodes.Status403Forbidden; + response.ReturnMessage.Add(checkPwd ? "Login ID/Password is invalid." : "You are not Authorized to login into the System"); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + if (user.LoginStatus == EnumLoginStatus.Unsuccessful) + { + string usm = string.Empty; + if (user != null && !string.IsNullOrEmpty(user.UnsuccessfulMsg)) + usm = $" ({user.UnsuccessfulMsg})"; + + response.LoginStatus = user.LoginStatus; + response.ReturnStatus = StatusCodes.Status403Forbidden; + response.ReturnMessage.Add(checkPwd ? $"Login ID/Password is invalid{usm}." : "You are not Authorized to login into the System"); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + + if (user.IsLocked) + { + response.LoginStatus = user.LoginStatus; + response.ReturnStatus = StatusCodes.Status403Forbidden; + if (!user.NextLoginTime.HasValue) + response.ReturnMessage.Add("You are locked, please contact Head office."); + else + response.ReturnMessage.Add($"You can Login after {user.NextLoginTime:dd-MMM-yyyy H:mm:ss}"); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + if (user.Status != EnumStatus.Authorized) + { + response.LoginStatus = user.LoginStatus; + response.ReturnStatus = StatusCodes.Status403Forbidden; + response.ReturnMessage.Add("You are not Authorized to Login into the System, Please contact with System Administrator."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + response.ValidUser = user.LoginStatus == EnumLoginStatus.Success; + if (!response.ValidUser) + { + response.LoginStatus = user.LoginStatus; + response.ReturnStatus = StatusCodes.Status403Forbidden; + response.ReturnMessage.Add("Unknown error."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + response.Map(user); + response.ValidUser = true; + response.ReturnStatus = StatusCodes.Status200OK; + string pwdSecretKey = GlobalFunctions.ConvertFromBase64String(_appSettings.PwdSecretKey); + string userPwd = Ease.NetCore.Utility.Global.CipherFunctions.EncryptByAES(privateKey: pwdSecretKey, publicKey: pwdSecretKey, data: request.Password); + byte[] key = Encoding.ASCII.GetBytes(_appSettings.JwtCryptoKey); + JwtSecurityTokenHandler tokenHandler = new(); + var tokenDescriptor = new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity( + [ + Helper.CreateClaim("LoginId", user.LoginId), + Helper.CreateClaim("Email", user.EmailAddress), + Helper.CreateClaim("AuthKey", $"{user.AuthKey}"), + Helper.CreateClaim("HashKey", Guid.NewGuid().ToString()) + ]), + Expires = DateTime.UtcNow.AddHours(12), + SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha512Signature) + }; + + SecurityToken token = tokenHandler.CreateToken(tokenDescriptor); + string userToken = tokenHandler.WriteToken(token); + string refreshToken = _refreshTokenService.GenerateRefreshToken(); + + + + //If token length is greater than or equal to 4096 (4KB) then return error + //because cookie can not store more than 4KB data and we are storing this token in cookie for authentication + if (userToken.Length >= 4096) //4Kb + { + response.LoginStatus = user.LoginStatus; + response.ReturnStatus = StatusCodes.Status431RequestHeaderFieldsTooLarge; + response.ReturnMessage.Add("Authentication Token is too large for cookie."); + return StatusCode(StatusCodes.Status431RequestHeaderFieldsTooLarge, response); + } + + response.Expires = tokenDescriptor.Expires; + response.AuthenticationToken = userToken; + response.LoginTime = $"{DateTime.Now:dd-MM-yy H:mm:ss}"; + + return Ok(response); + } + catch (Exception ex) + { + string msg = $"{request?.LoginId}~{ipAddress}"; + _logger.LogError(exception: ex, message: msg); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("loadMenu")] + public async Task LoadMenu([FromBody] ByUserIdRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + try + { + MenuResponse response = await _service.GetUserPermissionsAsync(userId: request.UserId); + return Ok(response.Item); + } + catch (Exception ex) + { + _logger.LogError(ex); + return StatusCode(StatusCodes.Status500InternalServerError, (ex.InnerException != null ? ex.InnerException.Message : ex.Message)); + } + } + + /// + /// + /// + /// + /// + [IgnoreAntiforgeryToken] + [HttpPost("validateOtp")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))] + public async Task ValidateOtp([FromBody] OtpValidationRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + BooleanResponse response = new() { Value = false, ReturnStatus = StatusCodes.Status200OK }; + if (string.IsNullOrEmpty(request.OtpCode) || request.OtpCode.Length != 6) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Otp must be 6 digit."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + try + { + if (request.AuthMethod == EnumAuthenticationMethod.ThirdPartyAuthenticator) + { + string secretKey = HttpContext.User.GetClaimValue("AuthKey"); + if (string.IsNullOrEmpty(secretKey)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Authentication key is required."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + TOtpService otp = new(); + DateTime now = DateTime.UtcNow; + response.Value = otp.ValidateTwoFactorPIN(secretKey, request.OtpCode, now); + } + else + { + response.Value = await _service.ValidateAuthValueAsync(request.OtpCode, request.UserId); + } + + if (!response.Value) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("This is not a valid Otp."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + else + { + return Ok(response); + } + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status406NotAcceptable; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("addUser")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))] + public async Task AddUser([FromBody] NewUserRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + bool permitted = await HttpContext.IsPermitted("ELIT.1.2.2_1"); + if (!permitted) + { + response.ReturnStatus = StatusCodes.Status403Forbidden; + response.ReturnMessage.Add("You are not authorize to Add User."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + try + { + string ipAddress = Request.HttpContext.GetIpAddress(); + int createdBy = HttpContext.User.GetClaimValue(Constants.UserId); + response.Value = await _service.AddUserAsync(user: request, ipAddress: ipAddress, createdBy: createdBy); + response.ReturnMessage.Add("User added successfully..."); + + //Cache + _cache.Clear("User"); + + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("editUser")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))] + public async Task EditUser([FromBody] UserRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + bool permitted = await HttpContext.IsPermitted("ELIT.1.2.2_2"); + if (!permitted) + { + response.ReturnStatus = StatusCodes.Status403Forbidden; + response.ReturnMessage.Add("You are not authorize to Update User."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + if (request.AuthMethod == EnumAuthenticationMethod.ThirdPartyAuthenticator && (string.IsNullOrEmpty(request.AuthKey) || string.IsNullOrWhiteSpace(request.AuthKey) || request.AuthKey.Length <= 0)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("For third party Authenticator, Authentication key is required."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + try + { + string ipAddress = Request.HttpContext.GetIpAddress(); + int modifiedBy = HttpContext.User.GetClaimValue(Constants.UserId); + response.Value = await _service.EditUserAsync(user: request, ipAddress: ipAddress, modifiedBy: modifiedBy); + response.ReturnMessage.Add("User edited successfully..."); + + //Cache + _cache.Clear("User"); + + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("deleteUser")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))] + public async Task DeleteUser([FromBody] ByUserIdRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + bool permitted = await HttpContext.IsPermitted("ELIT.1.2.2_3"); + if (!permitted) + { + response.ReturnStatus = StatusCodes.Status403Forbidden; + response.ReturnMessage.Add("You are not authorize to Delete User."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + try + { + int deletedBy = HttpContext.User.GetClaimValue(Constants.UserId); + response.Value = await _service.DeleteUserAsync(userId: request.UserId, deletedBy: deletedBy); + response.ReturnMessage.Add("User deleted successfully..."); + + //Cache + _cache.Clear("User"); + + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("unlockUser")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))] + public async Task UnlockUser([FromBody] UserUnlockRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + bool permitted = await HttpContext.IsPermitted("ELIT.1.2.2_2"); + if (!permitted) + { + response.ReturnStatus = StatusCodes.Status403Forbidden; + response.ReturnMessage.Add("You are not authorize to Unlock User."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + try + { + int unlockedBy = HttpContext.User.GetClaimValue(Constants.UserId); + response.Value = await _service.UnlockUserAsync(userId: request.UserId, loginId: request.LoginId, unlockedBy: unlockedBy); + response.ReturnMessage.Add("User Unlocked successfully..."); + + //Cache + _cache.Clear("User"); + + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("resetPassword")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))] + public async Task ResetPassword([FromBody] ResetPasswordRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + if (request.UserId == 0) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("User is not valid."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + if (string.IsNullOrEmpty(request.Password) || string.IsNullOrWhiteSpace(request.Password)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Invalid parameter value Password."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + if (string.IsNullOrEmpty(request.ConfirmPassword) || string.IsNullOrWhiteSpace(request.ConfirmPassword)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Invalid parameter value Confirm Password."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + #region Decrypt Password + + string cipherSecretKey = GlobalFunctions.ConvertFromBase64String(_appSettings.CipherSecretKey); + request.Password = Helper.DecryptData(secret: cipherSecretKey, data: request.Password); + request.ConfirmPassword = Helper.DecryptData(secret: cipherSecretKey, data: request.ConfirmPassword); + + #endregion + + if (!request.Password.Equals(request.ConfirmPassword)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("New password and confirm password are not same."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + bool permitted = await HttpContext.IsPermitted("ELIT.1.2.3_2"); + if (!permitted) + { + response.ReturnStatus = StatusCodes.Status403Forbidden; + response.ReturnMessage.Add("You are not authorize to Reset Password."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + try + { + string ipAddress = Request.HttpContext.GetIpAddress(); + int changedBy = HttpContext.User.GetClaimValue(Constants.UserId); + response.Value = await _service.ResetPasswordAsync(userId: request.UserId, newPassword: request.ConfirmPassword, ipAddress: ipAddress, changedBy: changedBy); + await _hub.Clients.All.NotifySubscriber(userId: request.UserId, msgType: 1, itemId: 0, ipAddress: ipAddress); + response.ReturnMessage.Add("Password Reset successfully, User must change password at next Login."); + _cache.Clear("User"); + + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [IgnoreAntiforgeryToken] + [HttpPost("changePassword")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))] + public async Task ChangePassword([FromBody] PasswordChangeRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + if (request.UserId == 0) + { + response.ReturnStatus = StatusCodes.Status403Forbidden; + response.ReturnMessage.Add("Your not a valid user."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + if (string.IsNullOrEmpty(request.OldPassword) || string.IsNullOrWhiteSpace(request.OldPassword)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Invalid parameter value Old Password."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + if (string.IsNullOrEmpty(request.Password) || string.IsNullOrWhiteSpace(request.Password)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Invalid parameter value Password."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + if (string.IsNullOrEmpty(request.ConfirmPassword) || string.IsNullOrWhiteSpace(request.ConfirmPassword)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Invalid parameter value Confirm Password."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + #region Decrypt Password + + string cipherSecretKey = GlobalFunctions.ConvertFromBase64String(_appSettings.CipherSecretKey); + request.OldPassword = Helper.DecryptData(secret: cipherSecretKey, data: request.OldPassword); + request.Password = Helper.DecryptData(secret: cipherSecretKey, data: request.Password); + request.ConfirmPassword = Helper.DecryptData(secret: cipherSecretKey, data: request.ConfirmPassword); + + #endregion + + if (!request.Password.Equals(request.ConfirmPassword)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("New password and confirm password are not same."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + try + { + string ipAddress = Request.HttpContext.GetIpAddress(); + int changedBy = HttpContext.User.GetClaimValue(Constants.UserId); + response.Value = await _service.ChangePasswordAsync(userId: request.UserId, oldPassword: request.OldPassword, newPassword: request.ConfirmPassword, ipAddress: ipAddress, changedBy: changedBy); + await _hub.Clients.All.NotifySubscriber(userId: request.UserId, msgType: 1, itemId: 0, ipAddress: ipAddress); + _cache.Clear("User"); + + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("updateMyPassword")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))] + public async Task UpdateMyPassword([FromBody] PasswordChangeRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + if (string.IsNullOrEmpty(request.OldPassword) || string.IsNullOrWhiteSpace(request.OldPassword)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Invalid parameter value Old Password."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + if (string.IsNullOrEmpty(request.Password) || string.IsNullOrWhiteSpace(request.Password)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Invalid parameter value Password."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + if (string.IsNullOrEmpty(request.ConfirmPassword) || string.IsNullOrWhiteSpace(request.ConfirmPassword)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Invalid parameter value Confirm Password."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + #region Decrypt Password + + string cipherSecretKey = GlobalFunctions.ConvertFromBase64String(_appSettings.CipherSecretKey); + request.OldPassword = Helper.DecryptData(secret: cipherSecretKey, data: request.OldPassword); + request.Password = Helper.DecryptData(secret: cipherSecretKey, data: request.Password); + request.ConfirmPassword = Helper.DecryptData(secret: cipherSecretKey, data: request.ConfirmPassword); + + #endregion + + if (!request.Password.Equals(request.ConfirmPassword)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("New password and confirm password are not same."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + try + { + string ipAddress = Request.HttpContext.GetIpAddress(); + int userId = HttpContext.User.GetClaimValue(Constants.UserId); + response.Value = await _service.ChangePasswordAsync(userId: userId, oldPassword: request.OldPassword, newPassword: request.ConfirmPassword, ipAddress: ipAddress, changedBy: userId); + await _hub.Clients.All.NotifySubscriber(userId: userId, msgType: 1, itemId: 0, ipAddress: ipAddress); + response.ReturnMessage.Add("Password changed successfully."); + _cache.Clear("User"); + + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("updateMyTheme")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))] + public async Task UpdateMyTheme([FromBody] UserThemeRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + try + { + int userId = HttpContext.User.GetClaimValue(Constants.UserId); + response.Value = await _service.UpdateMyThemeAsync(userId: userId, menuLayout: request.MenuLayout, themeName: request.ThemeName, schemeName: request.SchemeName); + response.ReturnMessage.Add("Your theme set successfully. Need re-login to see the effect."); + _cache.Clear("User"); + + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("updateMyInfo")] + [AllowAnonymous, IgnoreAntiforgeryToken] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))] + public async Task UpdateMyInfo([FromBody] UpdateMyInfoRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + try + { + if (request.EmployeeId < 0) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("System allow to update only Your Information."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + int modifiedBy = HttpContext.User.GetClaimValue(Constants.UserId); + response.Value = await _service.UpdateMyInfoAsync(address: request.Address, contactNo: request.ContactNo, modifiedBy: modifiedBy, emplyeeId: request.EmployeeId); + response.ReturnMessage.Add("Your Information updated successfully."); + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("getAttributes")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserAttributesResponse))] + [ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(UserAttributesResponse))] + public async Task GetAttributes([FromBody] ByUserAttributesRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + UserAttributesResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + try + { + string key = "UserAttributes"; + string key2 = $"{request.UserId}~{request.ClientType}"; + if (!_cache.TryGetValue(key: key, key2: key2, value: out response)) + { + response = await _service.GetAttributesAsync(userId: request.UserId, clientType: request.ClientType); + + //Cache + _ = _cache.Set(key: key, key2: key2, value: response, options: _options); + } + response.ReturnStatus = StatusCodes.Status200OK; + + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("saveAttributes")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))] + public async Task SaveAttributes([FromBody] UserAttributesRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + if (request.UkIds == null || request.UkIds.Count <= 0) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("There is no data to save."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + string msg, key; + if (request.ClientType == 2) + { + key = "ELIT.1.5.12_2"; + msg = "Supplier(s) to user"; + } + else + { + key = "ELIT.1.5.11_2"; + msg = "Project(s) to user"; + } + + bool permitted = await HttpContext.IsPermitted(key); + if (!permitted) + { + response.ReturnStatus = StatusCodes.Status403Forbidden; + response.ReturnMessage.Add($"You are not authorize to Assign {msg}."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + try + { + string ipAddress = Request.HttpContext.GetIpAddress(); + int attributeSetBy = HttpContext.User.GetClaimValue(Constants.UserId); + response.Value = await _service.SaveAttributesAsync(userId: request.UserId, clientType: request.ClientType, ipAddress: ipAddress, attributeSetBy: attributeSetBy, ukIds: request.UkIds); + response.ReturnMessage.Add($"{msg} Assigned successfully..."); + + //Cache + _cache.Clear("User"); + + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("deleteAttributes")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))] + public async Task DeleteAttributes([FromBody] ByUserAttributesRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + string msg, key; + if (request.ClientType == 2) + { + key = "ELIT.1.5.12_3"; + msg = "Supplier(s) from user"; + } + else + { + key = "ELIT.1.5.11_3"; + msg = "Project(s) from user"; + } + + bool permitted = await HttpContext.IsPermitted(key); + if (!permitted) + { + response.ReturnStatus = StatusCodes.Status403Forbidden; + response.ReturnMessage.Add($"You are not authorize to Unassign {msg}."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + try + { + int deletedBy = HttpContext.User.GetClaimValue(Constants.UserId); + response.Value = await _service.DeleteAttributesAsync(userId: request.UserId, clientType: request.ClientType, deletedBy: deletedBy); + response.ReturnMessage.Add($"{msg} Unassigned successfully..."); + + //Cache + _cache.Clear("User"); + + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// Returns users according to search criteria. + /// + /// + /// top 50 users + /// + /// Top 50 users + /// If the item is null. + [ValidateSession] + [HttpPost("getUsers")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserSearchResponse))] + [ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(UserSearchResponse))] + public async Task GetUsers([FromBody] UserSearchRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + UserSearchResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + try + { + int userId = HttpContext.User.GetClaimValue(Constants.UserId); + request.CheckOwner = userId != Models.Objects.Systems.User.SuperUser_Id; + + string key = "Users"; + string key2 = $"{request.Criteria}~{request.Status}~{request.SortField}~{request.SortOrder}~{request.Skip}~{request.PageSize}~{request.CheckOwner}~{userId}"; + if (!_cache.TryGetValue(key: key, key2: key2, value: out response)) + { + response = await _service.GetUsersAsync(request: request, userId: userId); + _ = _cache.Set(key: key, key2: key2, value: response, options: _options); + } + + response.ReturnStatus = StatusCodes.Status200OK; + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("getUsersBasic")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserBasicInfoResponse))] + [ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(UserBasicInfoResponse))] + public async Task GetUsersBasic([FromBody] BasicUserSearchRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + UserBasicInfoResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + try + { + string teamSpaceIds = request.TeamSpaceId > 0 ? $"{request.TeamSpaceId}" : HttpContext.User.GetClaimValue("TeamSpaceIds"); + response = await _service.GetUsersBasicAsync(applyFilter: request.ApplyFilter, teamSpaceIds: teamSpaceIds, projectId: request.ProjectId); + response.ReturnStatus = StatusCodes.Status200OK; + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("getUsersByTeamSpace")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserBasicInfoResponse))] + [ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(UserBasicInfoResponse))] + public async Task GetUsersByTeamSpace([FromBody] BasicUserByTeamSpaceRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + UserBasicInfoResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + try + { + string teamSpaceIds = request.TeamSpaceId > 0 ? $"{request.TeamSpaceId}" : HttpContext.User.GetClaimValue("TeamSpaceIds"); + response = await _service.GetUsersByTeamSpaceAsync(teamSpaceIds: teamSpaceIds, projectId: request.ProjectId, userId: request.UserId); + response.ReturnStatus = StatusCodes.Status200OK; + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("getAttendanceUsers")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserBasicInfoResponse))] + [ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(UserBasicInfoResponse))] + public async Task GetAttendanceUsers([FromBody] ByUserIdRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + UserBasicInfoResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + try + { + response = await _service.GetAttendanceUsersAsync(userId: request.UserId); + response.ReturnStatus = StatusCodes.Status200OK; + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + [ValidateSession] + [HttpPost("getUsersForForceLogout")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserForceLogoutResponse))] + [ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(UserForceLogoutResponse))] + public async Task GetForceLogoutUsers([FromBody] NoContentRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + UserForceLogoutResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + try + { + int userId = HttpContext.User.GetClaimValue(Constants.UserId); + response = await _service.GetForceLogoutUsersAsync(createdBy: userId); + + response.ReturnStatus = StatusCodes.Status200OK; + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + [ValidateSession] + [HttpPost("forceLogoutNow")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))] + [ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(BooleanResponse))] + public async Task ForceLogoutNow([FromBody] ForceUserLogoutRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + if (request.UserIds == null || request.UserIds.Count <= 0) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Parameter value is null/no User was selected."); + return BadRequest(response); + } + + bool permitted = await HttpContext.IsPermitted("ELIT.1.2.4_2"); + if (!permitted) + { + response.ReturnStatus = StatusCodes.Status403Forbidden; + response.ReturnMessage.Add("You are not authorize to Do Force Logout."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + try + { + string ipAddress = Request.HttpContext.GetIpAddress(); + response.Value = await _service.ForceLogoutNowAsync(userIds: request.UserIds, ipAddress: ipAddress); + foreach (int userId in request.UserIds) + { + await _hub.Clients.All.NotifySubscriber(userId: userId, msgType: 1, itemId: 0, ipAddress: ipAddress); + } + response.ReturnMessage.Add("Process completed successfully..."); + response.ReturnStatus = StatusCodes.Status200OK; + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("getUser")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserGetResponse))] + [ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(UserGetResponse))] + public async Task GetUser([FromBody] ByUserIdRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + UserGetResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + try + { + string key = "User"; + string key2 = $"{request.UserId}"; + if (!_cache.TryGetValue(key: key, key2: key2, value: out response)) + { + response = await _service.GetUserAsync(userId: request.UserId); + _ = _cache.Set(key: key, key2: key2, value: response, options: _options); + } + response.ReturnStatus = StatusCodes.Status200OK; + return Ok(response); + + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + [ValidateSession] + [HttpPost("getCurrentUser")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserGetResponse))] + public async Task GetCurrentUser([FromBody] NoContentRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + UserGetResponse response; + try + { + int userId = HttpContext.User.GetClaimValue(Constants.UserId); + + string key = "UserCurrent"; + string key2 = $"{userId}"; + if (!_cache.TryGetValue(key: key, key2: key2, value: out response)) + { + response = await _service.GetUserAsync(userId: userId); + response.ReturnStatus = StatusCodes.Status200OK; + _ = _cache.Set(key: key, key2: key2, value: response, options: _options); + } + + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response = new UserGetResponse() { ReturnStatus = StatusCodes.Status500InternalServerError }; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + [ValidateSession] + [HttpPost("getMyProfile")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserProfileResponse))] + public async Task GetMyProfile([FromBody] NoContentRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + UserProfileResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + try + { + int userId = HttpContext.User.GetClaimValue(Constants.UserId); + response = await _service.GetUserProfileAsync(userId: userId); + response.HasPayslipPath = !string.IsNullOrEmpty(_appSettings.PayslipPath); + response.HasAIApiKey = !string.IsNullOrEmpty(_appSettings.ApiKeyOpenAI) || !string.IsNullOrEmpty(_appSettings.ApiKeyGoogle); + response.ReturnStatus = StatusCodes.Status200OK; + + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("getMyPayslip")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(StringResponse))] + public IActionResult GetMyPayslip([FromBody] PayslipRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + StringResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + if (string.IsNullOrEmpty(_appSettings.PayslipPath)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Payslip path is not in the system."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + string employeeId = HttpContext.User.GetClaimValue("EmployeeId"); + if (string.IsNullOrEmpty(employeeId)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("You are not a valid Employee to view payslip."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + try + { + string path = $"{employeeId}_{request.YearMonth:yyyy}_{request.YearMonth:MM}.pdf"; + path = Path.Combine(_appSettings.PayslipPath, path); + if (System.IO.File.Exists(path)) + { + response.Value = Convert.ToBase64String(System.IO.File.ReadAllBytes(path: path)); + return Ok(response); + } + else + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add($"Payslip does not exists for the Month: {request.YearMonth:MMMM, yyyy}"); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + [ValidateSession] + [HttpPost("getDashboardData")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(DashboardDataResponse))] + [ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(DashboardDataResponse))] + public async Task GetDashboardData([FromBody] NoContentRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + DashboardDataResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + try + { + bool viewAll = await HttpContext.IsPermitted("ELIT.8.2.5_2"); + bool canViewLate = await HttpContext.IsPermitted("ELIT.8.2.2"); + bool canViewLeave = await HttpContext.IsPermitted("ELIT.8.2.1"); + int userId = HttpContext.User.GetClaimValue(Constants.UserId); + bool canViewHomeOffice = await HttpContext.IsPermitted("ELIT.8.2.4"); + bool canViewClientVisit = await HttpContext.IsPermitted("ELIT.8.2.3"); + + response = await _service.GetDashboardData(userId: userId, canViewLeave: canViewLeave, canViewLate: canViewLate, + canViewClientVisit: canViewClientVisit, canViewHomeOffice: canViewHomeOffice, viewAll: viewAll); + response.ReturnStatus = StatusCodes.Status200OK; + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("sendQrCodeViaEmail")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))] + public IActionResult SendQrCodeViaEmail([FromForm] QRCodeUploadRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + try + { + if (string.IsNullOrEmpty(request.EmailAddress)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("There is no email address to send mail."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + if (string.IsNullOrEmpty(request.FileName)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("There is no image to send to send mail."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + if (request.FileData.Length <= 0) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("There is no image to send to send mail."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + Result result = ImageFileValidator.Validate(request.FileData); + if (!result.Acceptable) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("This is not a valid image file."); + return BadRequest(response); + } + + var fileSpec = Path.Combine(_appSettings.UploadFolder, request.FileName); + if (System.IO.File.Exists(fileSpec)) + System.IO.File.Delete(fileSpec); + + using (var stream = new FileStream(fileSpec, FileMode.Create)) + { + request.FileData.CopyTo(stream); + } + + bool sent = MailHelper.SendMailMessage(settings: _appSettings, to: [request.EmailAddress], + cc: null, bcc: null, attachments: [fileSpec], embeddedImages: null, isHtmlBody: false, + priority: System.Net.Mail.MailPriority.High, subject: "QR Code", messageBody: "Scan image"); + + if (sent) + { + if (System.IO.File.Exists(fileSpec)) + System.IO.File.Delete(fileSpec); + + response.Value = sent; + response.ReturnMessage.Add($"Successfully mail sent to {request.EmailAddress}"); + return Ok(response); + } + else + { + response.Value = sent; + response.ReturnMessage.Add($"Cannot send mail to {request.EmailAddress}"); + return StatusCode(StatusCodes.Status422UnprocessableEntity, response); + } + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("uploadProfileImage")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))] + public IActionResult UploadProfileImage([FromForm] FileUploadRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + try + { + if (string.IsNullOrEmpty(request.FileName)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("There is no Image to set Profile image."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + if (request.FileData.Length <= 0) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("There is no image to set Profile image."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + Result result = ImageFileValidator.Validate(request.FileData); + if (!result.Acceptable) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("This is not a valid image file."); + return BadRequest(response); + } + + long maxSz = 20 * 1024; + if (request.FileData.Length > maxSz) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Maximum size allowed is 20 Kb"); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + var fileSpec = Path.Combine(_appSettings.ProfileImageFolder, request.FileName); + if (System.IO.File.Exists(fileSpec)) + System.IO.File.Delete(fileSpec); + + using (var stream = new FileStream(fileSpec, FileMode.Create)) + { + request.FileData.CopyTo(stream); + } + response.Value = true; + response.ReturnMessage.Add("Refresh page to view your profile image."); + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("uploadDocument")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))] + public async Task UploadDocument([FromForm] UploadDocumentRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + try + { + if (string.IsNullOrEmpty(request.FileName)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("There is no valid file to process."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + if (request.FileData.Length <= 0) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("There is no valid data to process."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + string fileName, fileSpec; + if (request.DocumentOf == 6) + { + string[] allowedExtensions = [".xlsx", ".xls"]; + string fileExtension = Path.GetExtension(request.FileName).ToLowerInvariant(); + if (!allowedExtensions.Contains(fileExtension)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Only excel file is allowed to process."); + return BadRequest(response); + } + + if (fileExtension.EndsWith(".xls")) + { + Result result = ExcelFileValidator.Validate(request.FileData); + if (!result.Acceptable) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("This is not a valid Excel file."); + return BadRequest(response); + } + } + else + { + Result result = ExcelxFileValidator.Validate(request.FileData); + if (!result.Acceptable) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("This is not a valid Excel file."); + return BadRequest(response); + } + } + + fileName = request.FileName; + fileSpec = Path.Combine(_appSettings.UploadFolder, fileName); + if (System.IO.File.Exists(fileSpec)) + System.IO.File.Delete(fileSpec); + } + else if (request.DocumentOf == 2) + { + if (!request.FileName.ToLower().EndsWith(".csv")) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Only csv file is allowed to process."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + fileName = request.FileName; + fileSpec = Path.Combine(_appSettings.UploadFolder, fileName); + if (System.IO.File.Exists(fileSpec)) + System.IO.File.Delete(fileSpec); + } + else + { + if (!request.FileName.ToLower().EndsWith(".pdf")) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Only pdf file is allowed to Process."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + long maxSz = 10 * 1024 * 1024; + if (request.FileData.Length > maxSz) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Maximum allowable size is 10 MB"); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + fileName = $"{request.Id}_{request.DocumentOf}.pdf"; + fileSpec = Path.Combine(_appSettings.UploadFolder, fileName); + if (System.IO.File.Exists(fileSpec)) + System.IO.File.Delete(fileSpec); + } + + using (var stream = new FileStream(fileSpec, FileMode.Create)) + { + request.FileData.CopyTo(stream); + } + + int userId = HttpContext.User.GetClaimValue(Constants.UserId); + response.Value = await _service.UploadDocumentAsync(userId: userId, id: request.Id, documentOf: request.DocumentOf, orgFileName: request.FileName, fileName: fileName); + response.ReturnMessage.Add("File Uploaded successfully."); + + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("getDocument")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(StringResponse))] + public IActionResult GetDocument([FromBody] FileViewRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + StringResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + try + { + if (string.IsNullOrEmpty(request.FileName)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("There is no Image to set Profile image."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + string path = Path.Combine(_appSettings.UploadFolder, request.FileName); + if (System.IO.File.Exists(path)) + { + response.Value = Convert.ToBase64String(System.IO.File.ReadAllBytes(path: path)); + } + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [HttpPost("findAccount")] + [AllowAnonymous, IgnoreAntiforgeryToken] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(FindAccountResponse))] + public async Task FindAccount([FromBody] FindAccountRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + FindAccountResponse response = new(); + if (string.IsNullOrEmpty(request.AccountId)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Login Id or Email address or Mobile number is required."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + try + { + string key = GlobalFunctions.ConvertFromBase64String(_appSettings.CipherSecretKey); + request.AccountId = Helper.DecryptData(secret: key, data: request.AccountId); + response = await _service.FindAccountAsync(accountId: request.AccountId); + + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [HttpPost("sendPassword")] + [AllowAnonymous, IgnoreAntiforgeryToken] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))] + public async Task SendPassword([FromBody] SendPasswordRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + if (string.IsNullOrEmpty(request.UserId)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("User Id is required."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + if (string.IsNullOrEmpty(request.MobileNo)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Mobile number is required."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + if (string.IsNullOrEmpty(request.EmailAddress)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Email address is required."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + try + { + string key = GlobalFunctions.ConvertFromBase64String(_appSettings.CipherSecretKey); + string decipherValue = Helper.DecryptData(secret: key, data: request.UserId); + if (string.IsNullOrEmpty(decipherValue)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("User Id is required."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + if (!int.TryParse(decipherValue, out int userId)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("User Id is required."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + if (userId == 0) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("User Id is required."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + request.MobileNo = Helper.DecryptData(secret: key, data: request.MobileNo); + request.EmailAddress = Helper.DecryptData(secret: key, data: request.EmailAddress); + if (string.IsNullOrEmpty(request.MobileNo) && string.IsNullOrEmpty(request.EmailAddress)) + { + response.ReturnStatus = StatusCodes.Status417ExpectationFailed; + response.ReturnMessage.Add("Mobile number and Email address both cannot be empty."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + //Do reset password + string newPassword = $"{new Random().Next(100000, 999999)}"; + string ipAddress = Request.HttpContext.GetIpAddress(); + response.Value = await _service.SendPasswordAsync(userId: userId, newPassword: newPassword, ipAddress: ipAddress); + if (response.Value) + { + if (!string.IsNullOrEmpty(request.EmailAddress) && !string.IsNullOrWhiteSpace(request.EmailAddress)) + { + List to = [.. request.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: "One Time Password", messageBody: $"Your one time password (use as Old password): {newPassword} and must change password at next Login."); + } + if (!string.IsNullOrEmpty(request.MobileNo) && !string.IsNullOrWhiteSpace(request.MobileNo)) + { + MailHelper.SendSMSOrWhatsAppMessage(settings: _appSettings, whatsAppMsg: false, msg: $"Your one time password (use as Old password): {newPassword} and must change password at next Login.", mobileNumber: request.MobileNo); + } + + response.ReturnMessage.Add("Password sent to your Email address and/or Mobile number, User must change password at next Login."); + } + else + { + response.ReturnMessage.Add("Cannot do action on your request."); + } + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("saveAuthorizeLimit")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))] + [ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(BooleanResponse))] + public async Task SaveAuthorizeLimit([FromBody] UserLimitAuthorizeRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + bool permitted = await HttpContext.IsPermitted("ELIT.9.1.14_1") || await HttpContext.IsPermitted("ELIT.9.1.14_2"); + if (!permitted) + { + response.ReturnStatus = StatusCodes.Status403Forbidden; + response.ReturnMessage.Add("You are not authorize to Update Authorization Limit."); + return StatusCode(StatusCodes.Status417ExpectationFailed, response); + } + + try + { + string ipAddress = Request.HttpContext.GetIpAddress(); + string loginId = HttpContext.User.GetClaimValue(Constants.LoginId); + response.Value = await _service.SaveAuthorizeLimitAsync(maxAuthLimit: request.MaxAuthorizeAmount, userId: request.UserId, ipAddress: ipAddress, savedBy: loginId); + response.ReturnMessage.Add("Process completed successfully..."); + response.ReturnStatus = StatusCodes.Status200OK; + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("getAuthorizeLimit")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserAuthorizeLimitResponse))] + [ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(UserAuthorizeLimitResponse))] + public async Task GetAuthorizeLimit([FromBody] ByUserIdRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + UserAuthorizeLimitResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + try + { + response = await _service.GetAuthorizeLimitAsync(userId: request.UserId); + response.ReturnStatus = StatusCodes.Status200OK; + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + [HttpPost("logOut")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))] + public async Task LogOut([FromBody] LogoutRequest request) + { + BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + try + { + string key = HttpContext.GetAuthenticationToken(); + string ipAddress = Request.HttpContext.GetIpAddress(); + int userId = HttpContext.User.GetClaimValue(Constants.UserId); + string loginId = HttpContext.User.GetClaimValue(Constants.LoginId); + + if (request.AttendanceLogout) + { + string cipherSecretKey = GlobalFunctions.ConvertFromBase64String(_appSettings.CipherSecretKey); + request.IpAddress = Helper.DecryptData(secret: cipherSecretKey, data: request.IpAddress); + request.MacAddress = Helper.DecryptData(secret: cipherSecretKey, data: request.MacAddress); + request.HostName = Helper.DecryptData(secret: cipherSecretKey, data: request.HostName); + } + _ = await _service.LogoutAsync(ipAddress: ipAddress, userId: userId, logId: request.LogId, attendanceLogout: request.AttendanceLogout, loginId: loginId, localIp: request.IpAddress, macAddress: request.MacAddress, hostName: request.HostName, logoutRemarks: request.LogoutRemarks); + _cache.Clear(pattern: key); + _ = await HttpContext.ClearSessionAsync(); + + response.Value = true; + if (request.AttendanceLogout) + { + await _hub.Clients.All.NotifySubscriber(userId: userId, msgType: 6, itemId: 0, ipAddress: ipAddress); + } + + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// 0 + [AllowAnonymous] + [IgnoreAntiforgeryToken] + [HttpPost("sessionExpired")] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task SessionExpired([FromBody] LogoutRequest request) + { + try + { + int userId = HttpContext.User.GetClaimValue(Constants.UserId); + string ipAddress = Request.HttpContext.GetIpAddress(); + _ = await _service.LogoutAsync(ipAddress: ipAddress, userId: userId, logId: request.LogId, attendanceLogout: false, loginId: string.Empty, localIp: string.Empty, macAddress: string.Empty, hostName: string.Empty, logoutRemarks: string.Empty); + _ = await HttpContext.ClearSessionAsync(); + } + catch (Exception ex) + { + _logger.LogError(ex); + } + } + + /// + /// + /// + /// + /// + /// + [SupportedOSPlatform("windows")] + private int GetADLoginStatus(string loginId, string password) + { + try + { + const string displayNameAttribute = "DisplayName"; + const string samAccountNameAttribute = "SAMAccountName"; + const string userAccountControlAttribute = "useraccountcontrol"; + + string username = (string.IsNullOrEmpty(_appSettings.ADConfig.Domain) || string.IsNullOrWhiteSpace(_appSettings.ADConfig.Domain)) ? loginId : $"{loginId}@{_appSettings.ADConfig.Domain}"; + using DirectoryEntry entry = new(path: _appSettings.ADConfig.Path, username: username, password: password); + using DirectorySearcher searcher = new(searchRoot: entry); + searcher.Filter = $"({samAccountNameAttribute}={loginId})"; + searcher.PropertiesToLoad.Add(value: displayNameAttribute); + searcher.PropertiesToLoad.Add(value: samAccountNameAttribute); + searcher.PropertiesToLoad.Add(value: userAccountControlAttribute); + var result = searcher.FindOne(); + if (result == null) + return 0; + + ResultPropertyValueCollection displayName = result.Properties[name: displayNameAttribute]; + ResultPropertyValueCollection samAccountName = result.Properties[name: samAccountNameAttribute]; + ResultPropertyValueCollection userAccountControl = result.Properties[name: userAccountControlAttribute]; + int uacFlag = (userAccountControl != null && userAccountControl.Count > 0) ? Convert.ToInt32(userAccountControl[0]) : 0; + if ((uacFlag & 0x000002) == 0x000002) //Disabled + return 2; + else if ((uacFlag & 0x800000) == 0x800000) //Password expired + return 3; + + if (displayName != null && displayName.Count > 0 && samAccountName != null && samAccountName.Count > 0) + return 1; + else + return 0; + } + catch (Exception ex) + { + _logger.LogError(ex); + return 0; + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("getAccessLog")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(AccessLogResponse))] + [ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(AccessLogResponse))] + public async Task GetAccessLog([FromBody] AccessLogSearchRequest request) + { + ArgumentNullException.ThrowIfNull(request); + + AccessLogResponse response = new() { ReturnStatus = StatusCodes.Status200OK }; + try + { + string loginId = HttpContext.User.GetClaimValue(Constants.LoginId); + bool permitted = await HttpContext.IsPermitted("ELIT.1.2.5_2"); + if (!(permitted || loginId.Equals(value: Models.Objects.Systems.User.SuperUser_LoginId, comparisonType: StringComparison.OrdinalIgnoreCase))) + { + request.LoginId = loginId; + } + response = await _service.GetAccessLogAsync(accessType: request.AccessType, loginId: request.LoginId, startDate: request.StartDate, endDate: request.EndDate); + response.ReturnStatus = StatusCodes.Status200OK; + + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex); + response.ReturnStatus = StatusCodes.Status500InternalServerError; + response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, response); + } + } + + /// + /// + /// + /// + /// + [ValidateSession] + [HttpPost("loadNotificationCount")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(int))] + public async Task LoadNotificationCount([FromBody] NoContentRequest request) + { + ArgumentNullException.ThrowIfNull(request); + try + { + int value = HttpContext.User.GetClaimValue(Constants.UserId); + value = await _service.LoadNotificationCountAsync(userId: value); + + return Ok(value); + } + catch (Exception ex) + { + _logger.LogError(ex); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + } +} \ No newline at end of file diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/V1/UserController.cs b/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/V1/UserController.cs index 94d2267..d5f9c9c 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/V1/UserController.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/V1/UserController.cs @@ -3,7 +3,6 @@ using OnlineSalesAutoCrop.CoreAPI.Configurations; using OnlineSalesAutoCrop.CoreAPI.Models; using OnlineSalesAutoCrop.CoreAPI.Models.Global; using OnlineSalesAutoCrop.CoreAPI.Models.Objects; -using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Setups; using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems; using OnlineSalesAutoCrop.CoreAPI.Models.Requests; using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Setups; @@ -19,7 +18,6 @@ using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; -using Microsoft.Reporting.NETCore; using System; using System.Collections.Generic; using System.DirectoryServices; @@ -51,7 +49,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers.V1 [ValidateAntiForgeryToken] [Route("api/v{version:apiVersion}/users")] - public class UserController(IUserService service, IOptions appSettings, IEaseCache cache, ILogger logger, IHubContext hub) : ControllerBase + public class UserController(IUserService service, IOptions appSettings, IEaseCache cache, ILogger logger, IHubContext hub) : ControllerBase { private readonly ILogger _logger = logger; private readonly IEaseCache _cache = cache; diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/appsettings.json b/Api/OnlineSalesAutoCrop.CoreAPI/appsettings.json index ce97578..b8d9626 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI/appsettings.json +++ b/Api/OnlineSalesAutoCrop.CoreAPI/appsettings.json @@ -27,51 +27,6 @@ "CorsMethods": "POST", "CorsOrigins": "http://localhost:4200,http://localhost:5050,http://localhost:7777", "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": { "ConnectionString": "Data Source=210.4.65.222,3341;Initial Catalog=OnlineSalesAutoCrop;User ID=OnlineSalesAutoCropSysUser;Password=OnlineSalesAutoCrop;Encrypt=false;", @@ -128,7 +83,8 @@ "WaAccountSid": "AC0138ad79a532f653c35072dad10e52b9", "WaAuthToken": "024a6897584671d9f9fa588d7c94aa96", "WaMsgSvcSid": "MG8401d33a9a3b2aea95619bda3e5757b5", - "WaSenderId": "+8801326755660" + "WaSenderId": "+8801326755660", + "RefreshTokenDuration": "15" }, "MenuSettings": {