complete auth for mobile api

This commit is contained in:
dibakor 2026-07-06 17:23:05 +06:00
parent 43efe23587
commit 1b738897d2
16 changed files with 1095 additions and 55 deletions

View File

@ -718,4 +718,22 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models
[Description("Percentage Based Discount")] [Description("Percentage Based Discount")]
ZDS2 = 3 ZDS2 = 3
} }
public enum UserPlatFormType : short
{
SAP=1,
AppUser =2,
}
public enum UserRoleTypeEnum
{
[Description("SAP User")]
SapUser = 1,
[Description("Order Collector")]
OrderCollector = 2,
[Description("OrderValidator")]
OrderValidator = 3,
[Description("OrderApprover")]
OrderApprover =4
}
} }

View File

@ -26,9 +26,14 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems
public string SchemeName { get; set; } public string SchemeName { get; set; }
public string MenuLayout { get; set; } public string MenuLayout { get; set; }
public bool IsLocked { get; set; } public bool IsLocked { get; set; }
public EnumLoginStatus LoginStatus { get; set; }
public DateTime? NextLoginTime { get; set; }
public UserPlatFormType? UserPlatFormType { get; set; }
public UserRoleTypeEnum? UserRoleType { get; set; }
public string? EmployeeNumber { get; set; }
public string? DesignationCode { get; set; }
public string? DesignationName { get; set; }
public EnumLoginStatus LoginStatus { get; set; }
public DateTime? NextLoginTime { get; set; }
} }
public class LoginHistory public class LoginHistory

View File

@ -0,0 +1,14 @@

using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
namespace OnlineSalesAutoCrop.CoreAPI.Models.Requests.Common;
public class AuthLoginRequest
{
[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; }
}

View File

@ -1,20 +1,14 @@
 using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems;
using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems; using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Common;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
namespace OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations; namespace OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations;
public class IntegrationLoginRequest public class IntegrationLoginRequest : AuthLoginRequest
{ {
[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 class IntegrationRefreshTokenRequest
{ {
public string RefreshToken { get; set; } public string RefreshToken { get; set; }

View File

@ -0,0 +1,61 @@

using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Common;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
namespace OnlineSalesAutoCrop.CoreAPI.Models.Requests.MobileApp;
public class MobileAppAuthRequest : AuthLoginRequest
{
}
public class AuthLogoutRequest
{
[Required, NotNull, StringLength(maximumLength: 10, MinimumLength = 3, ErrorMessage = "Login Id must be between 3 and 10 characters.")]
public string LoginId { get; set; }
}
public class AppUserChangePasswordRequest
{
[Required, NotNull, StringLength(maximumLength: 10, MinimumLength = 3, ErrorMessage = "Login Id must be between 3 and 10 characters.")]
public string LoginId { get; set; }
[Required, NotNull, StringLength(maximumLength: 20, MinimumLength = 5, ErrorMessage = "Password must be between 5 and 20 characters.")]
public string Password { get; set; }
[Required, NotNull, StringLength(maximumLength: 20, MinimumLength = 5, ErrorMessage = "Password must be between 5 and 20 characters.")]
public string NewPassword { get; set; }
[Required, NotNull, StringLength(maximumLength: 20, MinimumLength = 5, ErrorMessage = "Password must be between 5 and 20 characters.")]
[Compare("NewPassword", ErrorMessage = "Password and Confirm Password do not match.")]
public string ConfirmPassword { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!string.IsNullOrWhiteSpace(NewPassword) &&
!string.IsNullOrWhiteSpace(ConfirmPassword) &&
Password != ConfirmPassword)
{
yield return new ValidationResult(
"Password and Confirm Password do not match.",
[nameof(NewPassword), nameof(ConfirmPassword)]
);
}
if (!string.IsNullOrWhiteSpace(LoginId) &&
!string.IsNullOrWhiteSpace(NewPassword) &&
Password.Equals(LoginId, StringComparison.OrdinalIgnoreCase))
{
yield return new ValidationResult(
"Password must not be the same as your Login ID.",
[nameof(NewPassword)]
);
}
}
}

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace OnlineSalesAutoCrop.CoreAPI.Models.Requests.Systems; namespace OnlineSalesAutoCrop.CoreAPI.Models.Requests.Systems;
@ -219,3 +220,43 @@ public class PayslipRequest
[Required, NotNull] [Required, NotNull]
public DateTime YearMonth { get; set; } public DateTime YearMonth { get; set; }
} }
public class AppUserCreateRequest
{
[Required(ErrorMessage = "Login ID is required.")]
[MaxLength(100, ErrorMessage = "Login ID cannot exceed 100 characters.")]
public string LoginId { get; set; } = string.Empty;
[Required(ErrorMessage = "Username is required.")]
[MaxLength(150, ErrorMessage = "Username cannot exceed 150 characters.")]
public string UserName { get; set; } = string.Empty;
[Required]
public int Status { get; set; }
[Required]
public int AccessStatus { get; set; }
[Phone(ErrorMessage = "Invalid mobile number format.")]
[MaxLength(20, ErrorMessage = "Mobile number cannot exceed 20 characters.")]
public string? MobileNo { get; set; }
[Required(ErrorMessage = "Email address is required.")]
[EmailAddress(ErrorMessage = "Invalid email address format.")]
[MaxLength(200, ErrorMessage = "Email address cannot exceed 200 characters.")]
public string EmailAddress { get; set; } = string.Empty;
public int IsLocked { get; set; }
[Required(ErrorMessage = "Password is required.")]
[MaxLength(500, ErrorMessage = "Password hash length cannot exceed 500 characters.")]
public string Password { get; set; } = string.Empty;
public int? UserPlatformId { get; set; }
public int? UserRoleType { get; set; }
[MaxLength(10,ErrorMessage = "EmployeeNumber hash length cannot exceed 10 characters")]
public string? EmployeeNumber { get; set; }
}

View File

@ -17,7 +17,6 @@ public class IntegrationRrefreshTokenResponse : ResponseBase
public string RefreshToken { get; set; } = string.Empty; public string RefreshToken { get; set; } = string.Empty;
} }
public class RefreshTokenResponse : ResponseBase public class RefreshTokenResponse : ResponseBase
{ {
public int UserId { get; set; } public int UserId { get; set; }

View File

@ -0,0 +1,41 @@

using System;
namespace OnlineSalesAutoCrop.CoreAPI.Models.Responses.MobileApp;
public class AppAuthUserResponse : ResponseBase
{
public int UserId { get; set; }
public string LoginId { get; set; }
public string Name { get; set; }
public string? Email { get; set; }
public string? MobileNumber { get; set; }
public string EmployeeNumber { get; set; }
public string DesignationCode { get; set; }
public string DesignationName { get; set; }
public bool IsLocked { get; set; }
public UserRoleTypeEnum UserRole { get; set; }
public string AccessToken { get; set; }
public DateTime AccessTokenExpiryTime { get; set; }
public string RefreshToken { get; set; }
public DateTime RefreshTokenExpiryTime { get; set; }
public EnumLoginStatus LoginStatus { get; set; }
public bool IsAuthorized { get; set; }
public bool IsInitialPassword { get; set; }
}
public class AuthLogoutResponse : ResponseBase
{
public int UserId { get; set; }
public string LoginId { get; set; }
public bool IsSuccess { get; set; }
}
public class AppChangePasswordResponse : ResponseBase
{
public int UserId { get; set; }
public string LoginId { get; set; }
public bool IsSuccess { get; set; }
}

View File

@ -1,4 +1,5 @@
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations; using Ease.NetCore.DataAccess;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations; using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -9,4 +10,5 @@ public interface IRefreshTokenService
Task<GenerateRefreshTokenResponse> GenerateRefreshTokenByUserAsync(GenerateRefreshTokenRequest request); Task<GenerateRefreshTokenResponse> GenerateRefreshTokenByUserAsync(GenerateRefreshTokenRequest request);
Task<string> GenerateRefreshTokenAsync(string refreshToken, string ipAddress); Task<string> GenerateRefreshTokenAsync(string refreshToken, string ipAddress);
Task<UserByRefreshTokenResponse> GetUserByRefreshTokenAsync(string refreshToken); Task<UserByRefreshTokenResponse> GetUserByRefreshTokenAsync(string refreshToken);
Task<bool> InvalidRefreshTokenAsync(TransactionContext tc, int userId);
} }

View File

@ -1,7 +1,10 @@
using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Setups; using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Setups;
using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems; using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Common;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations; using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.MobileApp;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Systems; using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Systems;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.MobileApp;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Systems; using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Systems;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -13,10 +16,14 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems
{ {
Task<bool> ValidateAuthValueAsync(string authValue, int userId); Task<bool> ValidateAuthValueAsync(string authValue, int userId);
Task<User> LoginAsync(LoginRequest request, string ipAddress, bool checkPwd); Task<User> LoginAsync(LoginRequest request, string ipAddress, bool checkPwd);
Task<AppAuthUserResponse> LoginAsync(MobileAppAuthRequest request, string ipAddress);
Task<User> IntegrationLoginAsync(IntegrationLoginRequest request, string ipAddress, bool checkPwd); Task<User> IntegrationLoginAsync(IntegrationLoginRequest request, string ipAddress, bool checkPwd);
Task<bool> LogoutAsync(string ipAddress, int userId, int logId, bool attendanceLogout, string loginId, string localIp, string macAddress, string hostName, string logoutRemarks); Task<bool> LogoutAsync(string ipAddress, int userId, int logId, bool attendanceLogout, string loginId, string localIp, string macAddress, string hostName, string logoutRemarks);
Task<AuthLogoutResponse> LogoutAsync(AuthLogoutRequest request, string ipAddress);
Task<AppChangePasswordResponse> ChangePasswordAsync(AppUserChangePasswordRequest request, string ipAddress);
Task<bool> DeleteUserAsync(int userId, int deletedBy); Task<bool> DeleteUserAsync(int userId, int deletedBy);
Task<bool> ForceLogoutNowAsync(List<int> userIds, string ipAddress); Task<bool> ForceLogoutNowAsync(List<int> userIds, string ipAddress);
Task<bool> UnlockUserAsync(int userId, string loginId, int unlockedBy); Task<bool> UnlockUserAsync(int userId, string loginId, int unlockedBy);

View File

@ -3,6 +3,7 @@ using Ease.NetCore.DataAccess.SQL;
using Microsoft.Data.SqlClient; using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using OnlineSalesAutoCrop.CoreAPI.Models.Global; using OnlineSalesAutoCrop.CoreAPI.Models.Global;
using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations; using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations; using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Auth; using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Auth;
@ -116,6 +117,22 @@ public class RefreshTokenService : IRefreshTokenService
return refreshTokenResponse; return refreshTokenResponse;
} }
public async Task<bool> InvalidRefreshTokenAsync( TransactionContext tc, int userId)
{
bool response = false;
try
{
RevokeRefreshTokenByUserIdAsync(tc, userId);
response = true;
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message, e);
}
return response;
}
#region Private Methods #region Private Methods
// ----- private helpers ----- // ----- private helpers -----
@ -145,7 +162,7 @@ public class RefreshTokenService : IRefreshTokenService
SqlHelperExtension.CreateInParam(pName: "@TokenHash", pType: SqlDbType.VarChar, pValue: refreshToken.TokenHash), SqlHelperExtension.CreateInParam(pName: "@TokenHash", pType: SqlDbType.VarChar, pValue: refreshToken.TokenHash),
SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: refreshToken.IpAddress), SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: refreshToken.IpAddress),
SqlHelperExtension.CreateInParam(pName: "@CreatedAt", pType: SqlDbType.DateTime, pValue: DateTime.Now), SqlHelperExtension.CreateInParam(pName: "@CreatedAt", pType: SqlDbType.DateTime, pValue: DateTime.Now),
SqlHelperExtension.CreateInParam(pName: "@ExpiredAt", pType: SqlDbType.DateTime, pValue: DateTime.Now.AddMinutes(_settings.RefreshTokenDuration)), SqlHelperExtension.CreateInParam(pName: "@ExpiredAt", pType: SqlDbType.DateTime, pValue: refreshToken.ExpiresAt),
]; ];
_ = tc.ExecuteNonQuerySp(spName: "dbo.InsertRefreshToken", parameterValues: p); _ = tc.ExecuteNonQuerySp(spName: "dbo.InsertRefreshToken", parameterValues: p);
@ -259,7 +276,7 @@ public class RefreshTokenService : IRefreshTokenService
return new GenerateRefreshTokenResponse return new GenerateRefreshTokenResponse
{ {
RefreshToken = tokenHash, RefreshToken = tokenHash,
ExpireTime = DateTime.UtcNow.AddMinutes(_settings.RefreshTokenDuration) ExpireTime = DateTime.UtcNow.AddDays(_settings.RefreshTokenDuration)
}; };
} }
@ -277,5 +294,7 @@ public class RefreshTokenService : IRefreshTokenService
return Convert.ToBase64String(bytes); return Convert.ToBase64String(bytes);
} }
#endregion #endregion
} }

View File

@ -1,14 +1,20 @@
using Ease.NetCore.DataAccess; using Ease.NetCore.DataAccess;
using Ease.NetCore.DataAccess.SQL; using Ease.NetCore.DataAccess.SQL;
using Ease.NetCore.Utility;
using Microsoft.Data.SqlClient; using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using OnlineSalesAutoCrop.CoreAPI.Models;
using OnlineSalesAutoCrop.CoreAPI.Models.Global; using OnlineSalesAutoCrop.CoreAPI.Models.Global;
using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations; using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Systems;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations; using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Integrations; using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Integrations; namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Integrations;
@ -16,10 +22,12 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Integrations;
public class IntegrationService : IIntegrationService public class IntegrationService : IIntegrationService
{ {
private readonly AppSettings _settings; private readonly AppSettings _settings;
private readonly IUserService _userService;
public IntegrationService(IOptions<AppSettings> options) public IntegrationService(IOptions<AppSettings> options, IUserService userService)
{ {
_settings = options.Value; _settings = options.Value;
_userService = userService;
} }
@ -142,6 +150,28 @@ public class IntegrationService : IIntegrationService
response.IsUpdated = false; response.IsUpdated = false;
} }
bool isUserExist = await IsUserExistByEmployeeNumberAsync(tc, request.EmployeeVendorCode);
if (!isUserExist)
{
AppUserCreateRequest appUser = new()
{
LoginId = request.EmployeeVendorCode,
UserName = request.EmployeeVendorCode,
Status = (int)EnumStatus.Authorized,
AccessStatus = (int)EnumAccessStatus.FirstTime,
MobileNo = request.MobileNo,
EmailAddress = request.Mail,
IsLocked = 0,
Password ="12345",
UserRoleType = request.Designation == "01"? (int)UserRoleTypeEnum.OrderCollector : null,
UserPlatformId = (int)UserPlatFormType.AppUser,
EmployeeNumber = request.EmployeeVendorCode
};
await AddUserAsync(tc, appUser);
}
response.EmployeeVendorCode = request.EmployeeVendorCode; response.EmployeeVendorCode = request.EmployeeVendorCode;
response.CompanyCode = request.CompanyCode; response.CompanyCode = request.CompanyCode;
tc.End(); tc.End();
@ -717,6 +747,93 @@ public class IntegrationService : IIntegrationService
return response; return response;
} }
public async Task<bool> AddUserAsync(TransactionContext tc, AppUserCreateRequest user)
{
bool returnValue;
try
{
int userId = 0;
user.Password = EncryptPassword(password: user.Password);
if (!string.IsNullOrEmpty(user.EmailAddress))
user.EmailAddress = user.EmailAddress.Replace(" ", "");
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.VarChar, pValue: user.LoginId, size: 100),
SqlHelperExtension.CreateInParam(pName: "@UserName", pType: SqlDbType.VarChar, pValue: user.UserName, size: 150),
SqlHelperExtension.CreateInParam(pName: "@Status", pType: SqlDbType.Int, pValue: user.Status),
SqlHelperExtension.CreateInParam(pName: "@AccessStatus", pType: SqlDbType.Int, pValue: user.AccessStatus),
SqlHelperExtension.CreateInParam(pName: "@MobileNo", pType: SqlDbType.VarChar, pValue: DataReader.GetNullValue(user.MobileNo), size: 20),
SqlHelperExtension.CreateInParam(pName: "@EmailAddress", pType: SqlDbType.VarChar, pValue: user.EmailAddress, size: 200),
SqlHelperExtension.CreateInParam(pName: "@IsLocked", pType: SqlDbType.Int, pValue: user.IsLocked),
SqlHelperExtension.CreateInParam(pName: "@Password", pType: SqlDbType.NVarChar, pValue: user.Password, size: 500),
SqlHelperExtension.CreateInParam(pName: "@UserPlatformId", pType: SqlDbType.Int, pValue: DataReader.GetNullValue(user.UserPlatformId)),
SqlHelperExtension.CreateInParam(pName: "@UserRoleType", pType: SqlDbType.Int, pValue: DataReader.GetNullValue(user.UserRoleType)),
SqlHelperExtension.CreateInParam(pName: "@EmployeeNumber", pType: SqlDbType.VarChar, pValue: DataReader.GetNullValue(user.EmployeeNumber), size: 10),
SqlHelperExtension.CreateOutParam(pName: "@NewUserId", pType: SqlDbType.Int) // index 11 — output
];
_ = tc.ExecuteNonQuerySp(spName: "dbo.InsertUser", parameterValues: p);
userId = (p[11]?.Value != null && p[11]?.Value != DBNull.Value)
? Convert.ToInt32(p[11].Value)
: 0;
returnValue = userId > 0;
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message, e);
}
return returnValue;
}
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 async Task<bool> IsUserExistByEmployeeNumberAsync(TransactionContext tc, string employeeNumber)
{
bool response = false;
try
{
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@EmployeeNumber", pType: SqlDbType.NVarChar, pValue: employeeNumber)
];
using (IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.IsUserExistByEmployeeNumber", parameterValues: p))
{
if (dr.Read())
{
response = dr.GetInt32(0) > 0 ? true: false;
}
dr.Close();
}
}
catch (Exception ex)
{
throw DBCustomError.GenerateCustomError(ex);
}
return response;
}
private bool InsertEmployee(TransactionContext tc, IntegrationEmployeeRequest request) private bool InsertEmployee(TransactionContext tc, IntegrationEmployeeRequest request)
{ {
bool returnValue = false; bool returnValue = false;
@ -726,40 +843,40 @@ public class IntegrationService : IIntegrationService
SqlParameter[] p = SqlParameter[] p =
[ [
SqlHelperExtension.CreateInParam(pName: "@EmployeeVendorCode", pType: SqlDbType.NVarChar, pValue: request.EmployeeVendorCode), SqlHelperExtension.CreateInParam(pName: "@EmployeeVendorCode", pType: SqlDbType.NVarChar, pValue: request.EmployeeVendorCode),
SqlHelperExtension.CreateInParam(pName: "@EmployeeVendorName", pType: SqlDbType.NVarChar, pValue: request.EmployeeVendorName), SqlHelperExtension.CreateInParam(pName: "@EmployeeVendorName", pType: SqlDbType.NVarChar, pValue: request.EmployeeVendorName),
SqlHelperExtension.CreateInParam(pName: "@Designation", pType: SqlDbType.NVarChar, pValue: request.Designation), SqlHelperExtension.CreateInParam(pName: "@Designation", pType: SqlDbType.NVarChar, pValue: request.Designation),
SqlHelperExtension.CreateInParam(pName: "@DesignationDescription", pType: SqlDbType.NVarChar, pValue: request.DesignationDescription), SqlHelperExtension.CreateInParam(pName: "@DesignationDescription", pType: SqlDbType.NVarChar, pValue: request.DesignationDescription),
SqlHelperExtension.CreateInParam(pName: "@MobileNo", pType: SqlDbType.NVarChar, pValue: request.MobileNo), SqlHelperExtension.CreateInParam(pName: "@MobileNo", pType: SqlDbType.NVarChar, pValue: request.MobileNo),
SqlHelperExtension.CreateInParam(pName: "@Mail", pType: SqlDbType.NVarChar, pValue: request.Mail), SqlHelperExtension.CreateInParam(pName: "@Mail", pType: SqlDbType.NVarChar, pValue: request.Mail),
SqlHelperExtension.CreateInParam(pName: "@CompanyCode", pType: SqlDbType.NVarChar, pValue: request.CompanyCode), SqlHelperExtension.CreateInParam(pName: "@CompanyCode", pType: SqlDbType.NVarChar, pValue: request.CompanyCode),
SqlHelperExtension.CreateInParam(pName: "@CompanyName", pType: SqlDbType.NVarChar, pValue: request.CompanyName), SqlHelperExtension.CreateInParam(pName: "@CompanyName", pType: SqlDbType.NVarChar, pValue: request.CompanyName),
SqlHelperExtension.CreateInParam(pName: "@SalesOrg", pType: SqlDbType.NVarChar, pValue: request.SalesOrg), SqlHelperExtension.CreateInParam(pName: "@SalesOrg", pType: SqlDbType.NVarChar, pValue: request.SalesOrg),
SqlHelperExtension.CreateInParam(pName: "@SalesOrgDescription", pType: SqlDbType.NVarChar, pValue: request.SalesOrgDescription), SqlHelperExtension.CreateInParam(pName: "@SalesOrgDescription", pType: SqlDbType.NVarChar, pValue: request.SalesOrgDescription),
SqlHelperExtension.CreateInParam(pName: "@DistChannel", pType: SqlDbType.NVarChar, pValue: request.DistChannel), SqlHelperExtension.CreateInParam(pName: "@DistChannel", pType: SqlDbType.NVarChar, pValue: request.DistChannel),
SqlHelperExtension.CreateInParam(pName: "@DistChannelDescription", pType: SqlDbType.NVarChar, pValue: request.DistChannelDescription), SqlHelperExtension.CreateInParam(pName: "@DistChannelDescription", pType: SqlDbType.NVarChar, pValue: request.DistChannelDescription),
SqlHelperExtension.CreateInParam(pName: "@Division", pType: SqlDbType.NVarChar, pValue: request.Division), SqlHelperExtension.CreateInParam(pName: "@Division", pType: SqlDbType.NVarChar, pValue: request.Division),
SqlHelperExtension.CreateInParam(pName: "@DivisionDescription", pType: SqlDbType.NVarChar, pValue: request.DivisionDescription), SqlHelperExtension.CreateInParam(pName: "@DivisionDescription", pType: SqlDbType.NVarChar, pValue: request.DivisionDescription),
SqlHelperExtension.CreateInParam(pName: "@RegionAreaCode", pType: SqlDbType.NVarChar, pValue: request.RegionAreaCode), SqlHelperExtension.CreateInParam(pName: "@RegionAreaCode", pType: SqlDbType.NVarChar, pValue: request.RegionAreaCode),
SqlHelperExtension.CreateInParam(pName: "@RegionAreaDescription", pType: SqlDbType.NVarChar, pValue: request.RegionAreaDescription), SqlHelperExtension.CreateInParam(pName: "@RegionAreaDescription", pType: SqlDbType.NVarChar, pValue: request.RegionAreaDescription),
SqlHelperExtension.CreateInParam(pName: "@AreaAVSalesUnitCode", pType: SqlDbType.NVarChar, pValue: request.AreaAVSalesUnitCode), SqlHelperExtension.CreateInParam(pName: "@AreaAVSalesUnitCode", pType: SqlDbType.NVarChar, pValue: request.AreaAVSalesUnitCode),
SqlHelperExtension.CreateInParam(pName: "@AreaAVSalesUnitDescription", pType: SqlDbType.NVarChar, pValue: request.AreaAVSalesUnitDescription), SqlHelperExtension.CreateInParam(pName: "@AreaAVSalesUnitDescription", pType: SqlDbType.NVarChar, pValue: request.AreaAVSalesUnitDescription),
SqlHelperExtension.CreateInParam(pName: "@Territory", pType: SqlDbType.NVarChar, pValue: request.Territory), SqlHelperExtension.CreateInParam(pName: "@Territory", pType: SqlDbType.NVarChar, pValue: request.Territory),
SqlHelperExtension.CreateInParam(pName: "@TerritoryDescription", pType: SqlDbType.NVarChar, pValue: request.TerritoryDescription), SqlHelperExtension.CreateInParam(pName: "@TerritoryDescription", pType: SqlDbType.NVarChar, pValue: request.TerritoryDescription),
SqlHelperExtension.CreateInParam(pName: "@Plant", pType: SqlDbType.NVarChar, pValue: request.Plant), SqlHelperExtension.CreateInParam(pName: "@Plant", pType: SqlDbType.NVarChar, pValue: request.Plant),
SqlHelperExtension.CreateInParam(pName: "@PlantDescription", pType: SqlDbType.NVarChar, pValue: request.PlantDescription), SqlHelperExtension.CreateInParam(pName: "@PlantDescription", pType: SqlDbType.NVarChar, pValue: request.PlantDescription),
SqlHelperExtension.CreateInParam(pName: "@Status", pType: SqlDbType.NChar, pValue: request.Status), SqlHelperExtension.CreateInParam(pName: "@Status", pType: SqlDbType.NChar, pValue: request.Status),
SqlHelperExtension.CreateInParam(pName: "@StatusDescription", pType: SqlDbType.NVarChar, pValue: request.StatusDescription) SqlHelperExtension.CreateInParam(pName: "@StatusDescription", pType: SqlDbType.NVarChar, pValue: request.StatusDescription)
]; ];
_ = tc.ExecuteNonQuerySp(spName: "dbo.InsertEmployee", parameterValues: p); _ = tc.ExecuteNonQuerySp(spName: "dbo.InsertEmployee", parameterValues: p);

View File

@ -1,28 +1,33 @@
using Ease.NetCore.DataAccess; using Ease.NetCore.DataAccess;
using Ease.NetCore.DataAccess.SQL; using Ease.NetCore.DataAccess.SQL;
using Ease.NetCore.Utility; using Ease.NetCore.Utility;
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Options;
using OnlineSalesAutoCrop.CoreAPI.Models; using OnlineSalesAutoCrop.CoreAPI.Models;
using OnlineSalesAutoCrop.CoreAPI.Models.Global; using OnlineSalesAutoCrop.CoreAPI.Models.Global;
using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems; using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Common;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.MobileApp;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Systems; using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Systems;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.MobileApp;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Systems; using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Systems;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Auth;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems; using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems;
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Options;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations;
namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems
{ {
public class UserService(IOptions<AppSettings> settings, IOptions<MenuSettings> menuSettings) : IUserService public class UserService(IOptions<AppSettings> settings, IOptions<MenuSettings> menuSettings, IRefreshTokenService refreshTokenService) : IUserService
{ {
private readonly AppSettings _settings = settings?.Value; private readonly AppSettings _settings = settings?.Value;
private readonly MenuSettings _menuSettings = menuSettings?.Value; private readonly MenuSettings _menuSettings = menuSettings?.Value;
private readonly IRefreshTokenService _refreshTokenService = refreshTokenService;
/// <summary> /// <summary>
/// ///
@ -348,6 +353,192 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems
} }
public async Task<AppAuthUserResponse> LoginAsync(MobileAppAuthRequest request, string ipAddress)
{
AppAuthUserResponse response = null;
try
{
string password = EncryptPassword(password: request.Password);
User user = null;
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;
bool initialPassword = false;
#region Read User data using authentication data
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.NVarChar, pValue: request.LoginId)
];
using (IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetUserByLogin", parameterValues: p))
{
if (dr.Read())
{
user = new User();
{
user.UserId = dr.GetInt32(0);
user.Password = dr.GetString(1);
user.LoginId = dr.GetString(2);
user.UserName = dr.GetString(3);
user.Status = (EnumStatus)dr.GetInt32(4);
user.MobileNo = dr.GetString(5);
user.EmailAddress = dr.GetString(6);
user.AuthKey = dr.GetString(7);
user.AuthValue = dr.GetString(8);
user.IsLocked = dr.GetBoolean(9);
user.LoginStatus = EnumLoginStatus.Success;
user.UserRoleType = (UserRoleTypeEnum)dr.GetInt32(10);
user.EmployeeNumber = dr.GetString(11);
user.DesignationCode = dr.GetString(12);
user.DesignationName = dr.GetString(13);
};
initialPassword = dr.GetInt32(14) > 0;
}
dr.Close();
}
if (user is null)
{
tc.End();
throw new InvalidOperationException($"User not found with login: {request.LoginId}");
}
if (!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;
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";
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)
{
p =
[
SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: user.UserId),
SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.VarChar, pValue: user.LoginId, size: 30),
SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20),
SqlHelperExtension.CreateInParam(pName: "@LoginTime", pType: SqlDbType.DateTime, pValue: DateTime.Now),
SqlHelperExtension.CreateInParam(pName: "@LogoutTime", pType: SqlDbType.DateTime, pValue: null)
];
_ = tc.ExecuteNonQuerySp(spName: "dbo.SaveAccessLog", parameterValues: p);
}
#endregion
response = new()
{
UserId = user.UserId,
Name = user.UserName,
LoginId = user.LoginId,
Email = user.EmailAddress,
MobileNumber = user.MobileNo,
IsLocked = user.IsLocked,
UserRole = user.UserRoleType.Value,
LoginStatus = user.LoginStatus,
IsAuthorized = EnumStatus.Authorized == user.Status,
EmployeeNumber = user.EmployeeNumber,
DesignationCode = user.DesignationCode,
DesignationName = user.DesignationName,
IsInitialPassword = initialPassword
};
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<User> IntegrationLoginAsync(IntegrationLoginRequest request, string ipAddress, bool checkPwd) public async Task<User> IntegrationLoginAsync(IntegrationLoginRequest request, string ipAddress, bool checkPwd)
{ {
User user = null; User user = null;
@ -365,7 +556,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems
#region Read User data using authentication data #region Read User data using authentication data
string commandText = SQLParser.MakeSQL("SELECT UserId,Password,LoginId, UserName, Status, ISNULL(MobileNo,'')MobileNo ,ISNULL( EmailAddress,'')EmailAddress," string commandText = SQLParser.MakeSQL("SELECT UserId,Password,LoginId, UserName, Status, ISNULL(MobileNo,'')MobileNo ,ISNULL( EmailAddress,'')EmailAddress,"
+ " ISNULL(AuthKey,'') AuthKey, ISNULL(AuthValue,'')AuthValue, IsLocked FROM Users WHERE LoginID=%s", request.LoginId); + " ISNULL(AuthKey,'') AuthKey, ISNULL(AuthValue,'')AuthValue, IsLocked FROM Users WHERE LoginID=%s and UserPlatformId=1 ", request.LoginId);
using (IDataReader dr = tc.ExecuteReader(commandText: commandText)) using (IDataReader dr = tc.ExecuteReader(commandText: commandText))
{ {
@ -505,6 +696,8 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems
return user; return user;
} }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
@ -2231,6 +2424,206 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems
return returnValue; return returnValue;
} }
public async Task<AuthLogoutResponse> LogoutAsync(AuthLogoutRequest request, string ipAddress)
{
AuthLogoutResponse response = null;
try
{
User user = null;
using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true);
try
{
DateTime sysDate = DateTime.Today.Date;
string appVer = string.Empty, alParams = string.Empty;
#region Read User data using authentication data
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.NVarChar, pValue: request.LoginId)
];
using (IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetUserByLogin", parameterValues: p))
{
if (dr.Read())
{
user = new User();
{
user.UserId = dr.GetInt32(0);
user.Password = dr.GetString(1);
user.LoginId = dr.GetString(2);
user.UserName = dr.GetString(3);
user.Status = (EnumStatus)dr.GetInt32(4);
user.MobileNo = dr.GetString(5);
user.EmailAddress = dr.GetString(6);
user.AuthKey = dr.GetString(7);
user.AuthValue = dr.GetString(8);
user.IsLocked = dr.GetBoolean(9);
user.LoginStatus = EnumLoginStatus.Success;
user.UserRoleType = (UserRoleTypeEnum)dr.GetInt32(10);
user.EmployeeNumber = dr.GetString(11);
user.DesignationCode = dr.GetString(12);
user.DesignationName = dr.GetString(13);
}
;
}
dr.Close();
}
if (user is null)
{
tc.End();
throw new InvalidOperationException($"User not found with login: {request.LoginId}");
}
#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)
{
p =
[
SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: user.UserId),
SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.VarChar, pValue: user.LoginId, size: 30),
SqlHelperExtension.CreateInParam(pName: "@IpAddress", pType: SqlDbType.VarChar, pValue: ipAddress, size: 20),
SqlHelperExtension.CreateInParam(pName: "@LoginTime", pType: SqlDbType.DateTime, pValue: null),
SqlHelperExtension.CreateInParam(pName: "@LogoutTime", pType: SqlDbType.DateTime, pValue: DateTime.Now)
];
_ = tc.ExecuteNonQuerySp(spName: "dbo.SaveAccessLog", parameterValues: p);
}
#endregion
await _refreshTokenService.InvalidRefreshTokenAsync(tc, user.UserId);
response = new()
{
UserId = user.UserId,
LoginId = user.LoginId,
IsSuccess = true
};
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<AppChangePasswordResponse> ChangePasswordAsync(AppUserChangePasswordRequest request, string ipAddress)
{
AppChangePasswordResponse response = null;
try
{
string password = EncryptPassword(password: request.Password);
User user = null;
using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true);
try
{
DateTime sysDate = DateTime.Today.Date;
string appVer = string.Empty, alParams = string.Empty;
#region Read User data using authentication data
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@LoginId", pType: SqlDbType.NVarChar, pValue: request.LoginId)
];
using (IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetUserByLogin", parameterValues: p))
{
if (dr.Read())
{
user = new User();
{
user.UserId = dr.GetInt32(0);
user.Password = dr.GetString(1);
user.LoginId = dr.GetString(2);
user.UserName = dr.GetString(3);
user.Status = (EnumStatus)dr.GetInt32(4);
user.MobileNo = dr.GetString(5);
user.EmailAddress = dr.GetString(6);
user.AuthKey = dr.GetString(7);
user.AuthValue = dr.GetString(8);
user.IsLocked = dr.GetBoolean(9);
user.LoginStatus = EnumLoginStatus.Success;
user.UserRoleType = (UserRoleTypeEnum)dr.GetInt32(10);
user.EmployeeNumber = dr.GetString(11);
user.DesignationCode = dr.GetString(12);
user.DesignationName = dr.GetString(13);
}
;
}
dr.Close();
}
if (user is null)
{
tc.End();
throw new InvalidOperationException($"User not found with login: {request.LoginId}");
}
if (!password.Equals(user.Password))
{
tc.End();
throw new InvalidOperationException($"Password mismatch for login: {request.LoginId}");
}
#endregion
string newPassword = EncryptPassword(request.ConfirmPassword);
p =
[
SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: user.UserId),
SqlHelperExtension.CreateInParam(pName: "@Password", pType: SqlDbType.VarChar, pValue: newPassword, size: 30),
SqlHelperExtension.CreateInParam(pName: "@LastPassword", pType: SqlDbType.VarChar, pValue: password, size: 20),
SqlHelperExtension.CreateInParam(pName: "@LastPasswordChnageDate", pType: SqlDbType.DateTime, pValue: DateTime.Now)
];
_ = tc.ExecuteNonQuerySp(spName: "dbo.ChangePassword", parameterValues: p);
response = new()
{
UserId = user.UserId,
LoginId = user.LoginId,
IsSuccess = true
};
tc.End();
}
catch (Exception ie)
{
tc?.HandleError();
throw DBCustomError.GenerateCustomError(ie);
}
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message, e);
}
return response;
}
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
@ -2635,12 +3028,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems
throw new NotImplementedException(); throw new NotImplementedException();
} }
#endregion #endregion
} }
internal class UserSearchResponse
{
}
} }

View File

@ -230,7 +230,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers
loginReponse.AccessToken = userToken; loginReponse.AccessToken = userToken;
loginReponse.RefreshToken = refreshToken.RefreshToken; loginReponse.RefreshToken = refreshToken.RefreshToken;
loginReponse.AccessTokenExpiry = refreshToken.ExpireTime; loginReponse.AccessTokenExpiry = refreshToken.ExpireTime;
return StatusCode(StatusCodes.Status431RequestHeaderFieldsTooLarge, loginReponse); return StatusCode(StatusCodes.Status200OK, loginReponse);
} }
catch (Exception ex) catch (Exception ex)
@ -295,7 +295,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers
response.AccessToken = userToken; response.AccessToken = userToken;
response.RefreshToken = refreshToken; response.RefreshToken = refreshToken;
return StatusCode(StatusCodes.Status431RequestHeaderFieldsTooLarge, response); return StatusCode(StatusCodes.Status200OK, response);
} }
catch (Exception ex) catch (Exception ex)

View File

@ -10,7 +10,7 @@ using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Integrations;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace OnlineSalesAutoCrop.CoreAPI.Controllers.IntegretionApi namespace OnlineSalesAutoCrop.CoreAPI.Controllers
{ {
/// <summary> /// <summary>
/// ///

View File

@ -0,0 +1,334 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using OnlineSalesAutoCrop.CoreAPI;
using OnlineSalesAutoCrop.CoreAPI.Models;
using OnlineSalesAutoCrop.CoreAPI.Models.Global;
using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Common;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.MobileApp;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.MobileApp;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Systems;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Auth;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems;
using System;
using System.DirectoryServices;
using System.IdentityModel.Tokens.Jwt;
using System.Runtime.Versioning;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
namespace OnlineSalesAutoCrop.CoreAPI.Controllers
{
/// <summary>
///
/// </summary>
/// <remarks>
///
/// </remarks>
/// <param name="service"></param>
/// <param name="appSettings"></param>
/// <param name="cache"></param>
/// <param name="logger"></param>
/// <param name="refreshTokenService"></param>
[Authorize]
[ApiController]
[ApiVersion("1.0")]
[ValidateAntiForgeryToken]
[Route("api/mobile/v{version:apiVersion}/auth")]
public class MobileAuthController(IUserService service, IOptions<AppSettings> appSettings, IEaseCache cache, ILogger<IntegrationAuthController> 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();
/// <summary>
/// Login using your credential data retrieve from SqlServer
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="request"></param>
/// <returns>If login successful ValidUser: true</returns>
/// <response code="200">If login successful Return ValidUser: true and UserName: not empty</response>
[HttpPost("Login")]
[AllowAnonymous]
[IgnoreAntiforgeryToken]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(MobileAppAuthRequest))]
public async Task<IActionResult> Login([FromBody] MobileAppAuthRequest request)
{
ArgumentNullException.ThrowIfNull(request);
AppAuthUserResponse loginReponse = new();
if (string.IsNullOrEmpty(request.LoginId))
{
loginReponse.ReturnStatus = StatusCodes.Status417ExpectationFailed;
loginReponse.ReturnMessage.Add("Login ID is required.");
return StatusCode(StatusCodes.Status417ExpectationFailed, loginReponse);
}
if (string.IsNullOrEmpty(request.Password))
{
loginReponse.ReturnStatus = StatusCodes.Status417ExpectationFailed;
loginReponse.ReturnMessage.Add("Password is required.");
return StatusCode(StatusCodes.Status417ExpectationFailed, loginReponse);
}
string ipAddress = string.Empty;
try
{
#region Decrypt LoginID
string cipherSecretKey = GlobalFunctions.ConvertFromBase64String(_appSettings.CipherSecretKey);
#endregion
bool checkPwd = true;
ipAddress = Request.HttpContext.GetIpAddress();
loginReponse = await _service.LoginAsync(request: request, ipAddress: ipAddress);
if (loginReponse == null || loginReponse.UserId == 0)
{
loginReponse.LoginStatus = EnumLoginStatus.Error;
loginReponse.ReturnStatus = StatusCodes.Status403Forbidden;
loginReponse.ReturnMessage.Add(checkPwd ? "Login ID/Password is invalid." : "You are not Authorized to login into the System");
return StatusCode(StatusCodes.Status417ExpectationFailed, loginReponse);
}
if (loginReponse.LoginStatus == EnumLoginStatus.Unsuccessful)
{
loginReponse.LoginStatus = loginReponse.LoginStatus;
loginReponse.ReturnStatus = StatusCodes.Status403Forbidden;
loginReponse.ReturnMessage.Add(checkPwd ? $"Login ID/Password is invalid for {request.LoginId}." : "You are not Authorized to login into the System");
return StatusCode(StatusCodes.Status417ExpectationFailed, loginReponse);
}
if (loginReponse.IsLocked)
{
loginReponse.LoginStatus = loginReponse.LoginStatus;
loginReponse.ReturnStatus = StatusCodes.Status403Forbidden;
loginReponse.ReturnMessage.Add($"Your Account is locked . Please try again after few minutes");
return StatusCode(StatusCodes.Status417ExpectationFailed, loginReponse);
}
if (!loginReponse.IsAuthorized )
{
loginReponse.LoginStatus = loginReponse.LoginStatus;
loginReponse.ReturnStatus = StatusCodes.Status403Forbidden;
loginReponse.ReturnMessage.Add("You are not Authorized to Login into the System, Please contact with System Administrator.");
return StatusCode(StatusCodes.Status417ExpectationFailed, loginReponse);
}
loginReponse.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", loginReponse.LoginId),
Helper.CreateClaim("Email", loginReponse.Email),
Helper.CreateClaim("Mobile", $"{loginReponse.MobileNumber}"),
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);
GenerateRefreshTokenRequest refreshTokenRequest = new GenerateRefreshTokenRequest()
{
UserId = loginReponse.UserId,
IpAddress = ipAddress,
RawRefreshToken = string.Empty
};
var refreshToken = await _refreshTokenService.GenerateRefreshTokenByUserAsync(refreshTokenRequest);
//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
{
loginReponse.LoginStatus = loginReponse.LoginStatus;
loginReponse.ReturnStatus = StatusCodes.Status431RequestHeaderFieldsTooLarge;
loginReponse.ReturnMessage.Add("Authentication Token is too large for cookie.");
return StatusCode(StatusCodes.Status431RequestHeaderFieldsTooLarge, loginReponse);
}
loginReponse.ReturnStatus = loginReponse.ReturnStatus;
loginReponse.ReturnMessage = loginReponse.ReturnMessage;
loginReponse.LoginId = loginReponse.LoginId;
loginReponse.AccessToken = userToken;
loginReponse.RefreshToken = refreshToken.RefreshToken;
loginReponse.AccessTokenExpiryTime = DateTime.UtcNow.AddHours(12);
loginReponse.RefreshTokenExpiryTime = refreshToken.ExpireTime;
return StatusCode(StatusCodes.Status200OK, loginReponse);
}
catch (Exception ex)
{
string msg = $"{request?.LoginId}~{ipAddress}";
_logger.LogError(exception: ex, message: msg);
loginReponse.ReturnStatus = StatusCodes.Status500InternalServerError;
loginReponse.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, loginReponse);
}
}
/// <summary>
/// Login using your credential data retrieve from SqlServer
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="request"></param>
/// <returns>If login successful ValidUser: true</returns>
/// <response code="200">If login successful Return ValidUser: true and UserName: not empty</response>
[HttpPost("RefreshToken")]
[AllowAnonymous]
[IgnoreAntiforgeryToken]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IntegrationLoginResponse))]
public async Task<IActionResult> GetRefreshToken([FromBody] IntegrationRefreshTokenRequest request)
{
IntegrationRrefreshTokenResponse response = new();
try
{
var userRefreshToken = await _refreshTokenService.GetUserByRefreshTokenAsync(request.RefreshToken);
if (!userRefreshToken.IsActive)
{
string msg = $"Refresh Token is not active: {request?.RefreshToken}";
_logger.LogError(message: msg);
response.ReturnStatus = StatusCodes.Status401Unauthorized;
response.ReturnMessage.Add(msg);
return StatusCode(StatusCodes.Status401Unauthorized, response);
}
byte[] key = Encoding.ASCII.GetBytes(_appSettings.JwtCryptoKey);
JwtSecurityTokenHandler tokenHandler = new();
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(
[
Helper.CreateClaim("LoginId", userRefreshToken.LoginId),
Helper.CreateClaim("Email", userRefreshToken.EmailAddress),
Helper.CreateClaim("AuthKey", $"{userRefreshToken.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 ipAddress = Request.HttpContext.GetIpAddress();
var refreshToken = await _refreshTokenService.GenerateRefreshTokenAsync(request.RefreshToken, ipAddress);
response.AccessToken = userToken;
response.RefreshToken = refreshToken;
return StatusCode(StatusCodes.Status200OK, response);
}
catch (Exception ex)
{
string msg = $"{request?.RefreshToken}";
_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);
}
}
/// <summary>
/// Login using your credential data retrieve from SqlServer
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="request"></param>
/// <returns>If login successful ValidUser: true</returns>
/// <response code="200">If login successful Return ValidUser: true and UserName: not empty</response>
[HttpPost("Logout")]
[Authorize]
[IgnoreAntiforgeryToken]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(AuthLogoutResponse))]
public async Task<IActionResult> Logout([FromBody] AuthLogoutRequest request)
{
AuthLogoutResponse response = new();
try
{
string ipAddress = Request.HttpContext.GetIpAddress();
response = await _service.LogoutAsync(request, ipAddress);
response.ReturnStatus = StatusCodes.Status200OK;
response.ReturnMessage.Add("Successfully Logout");
return StatusCode(StatusCodes.Status200OK, response);
}
catch (Exception ex)
{
string msg = $"Exception occur on logout {request?.LoginId}";
_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);
}
}
/// <summary>
/// Login using your credential data retrieve from SqlServer
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="request"></param>
/// <returns>If login successful ValidUser: true</returns>
/// <response code="200">If login successful Return ValidUser: true and UserName: not empty</response>
[HttpPost("ChangePassword")]
[Authorize]
[IgnoreAntiforgeryToken]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(AppChangePasswordResponse))]
public async Task<IActionResult> ChangePassword([FromBody] AppUserChangePasswordRequest request)
{
AppChangePasswordResponse response = new();
try
{
string ipAddress = Request.HttpContext.GetIpAddress();
response = await _service.ChangePasswordAsync(request, ipAddress);
response.ReturnStatus = StatusCodes.Status200OK;
response.ReturnMessage.Add("Successfully Changed the Password");
return StatusCode(StatusCodes.Status200OK, response);
}
catch (Exception ex)
{
string msg = $"Exception occur on logout {request?.LoginId}";
_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);
}
}
}
}