UNPKG

@tomei/sso

Version:
1,085 lines 117 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.User = void 0; const general_1 = require("@tomei/general"); const user_repository_1 = require("./user.repository"); const system_repository_1 = require("../system/system.repository"); const login_history_repository_1 = require("../login-history/login-history.repository"); const password_hash_service_1 = require("../password-hash/password-hash.service"); const user_group_repository_1 = require("../user-group/user-group.repository"); const staff_entity_1 = require("../../models/staff.entity"); const system_privilege_entity_1 = require("../../models/system-privilege.entity"); const yn_enum_1 = require("../../enum/yn.enum"); const enum_1 = require("../../enum"); const config_1 = require("@tomei/config"); const sequelize_1 = require("sequelize"); const activity_history_1 = require("@tomei/activity-history"); const group_entity_1 = require("../../models/group.entity"); const group_system_access_repository_1 = require("../group-system-access/group-system-access.repository"); const group_repository_1 = require("../group/group.repository"); const system_entity_1 = require("../../models/system.entity"); const user_system_access_repository_1 = require("../user-system-access/user-system-access.repository"); const group_system_access_entity_1 = require("../../models/group-system-access.entity"); const user_privilege_repository_1 = require("../user-privilege/user-privilege.repository"); const user_object_privilege_repository_1 = require("../user-object-privilege/user-object-privilege.repository"); const group_privilege_entity_1 = require("../../models/group-privilege.entity"); const group_object_privilege_repository_1 = require("../group-object-privilege/group-object-privilege.repository"); const speakeasy = require("speakeasy"); const login_status_enum_1 = require("../../enum/login-status.enum"); const redis_service_1 = require("../../redis-client/redis.service"); const login_user_1 = require("./login-user"); const session_service_1 = require("../../session/session.service"); const crypto_1 = require("crypto"); const user_entity_1 = require("../../models/user.entity"); const user_reporting_hierarchy_repository_1 = require("../user-reporting-hierarchy/user-reporting-hierarchy.repository"); const user_password_history_1 = require("../user-password-history/user-password-history"); class User extends general_1.UserBase { get SessionService() { return this._SessionService; } get UserId() { return parseInt(this.ObjectId); } set UserId(value) { this.ObjectId = value.toString(); } get Password() { return this._Password; } set Password(value) { this._Password = value; } get Status() { return this._Status; } set Status(value) { this._Status = value; } get UserName() { return this._UserName; } set UserName(value) { this._UserName = value; } get DefaultPasswordChangedYN() { return this._DefaultPasswordChangedYN; } set DefaultPasswordChangedYN(value) { this._DefaultPasswordChangedYN = value; } get FirstLoginAt() { return this._FirstLoginAt; } set FirstLoginAt(value) { this._FirstLoginAt = value; } get LastLoginAt() { return this._LastLoginAt; } set LastLoginAt(value) { this._LastLoginAt = value; } get MFAEnabled() { return this._MFAEnabled; } set MFAEnabled(value) { this._MFAEnabled = value; } get MFAConfig() { return this._MFAConfig; } set MFAConfig(value) { this._MFAConfig = value; } get MFABypassYN() { return this._MFABypassYN; } set MFABypassYN(value) { this._MFABypassYN = value; } get RecoveryEmail() { return this._RecoveryEmail; } set RecoveryEmail(value) { this._RecoveryEmail = value; } get FailedLoginAttemptCount() { return this._FailedLoginAttemptCount; } set FailedLoginAttemptCount(value) { this._FailedLoginAttemptCount = value; } get LastFailedLoginAt() { return this._LastFailedLoginAt; } set LastFailedLoginAt(value) { this._LastFailedLoginAt = value; } get LastPasswordChangedAt() { return this._LastPasswordChangedAt; } set LastPasswordChangedAt(value) { this._LastPasswordChangedAt = value; } get NeedToChangePasswordYN() { return this._NeedToChangePasswordYN; } set NeedToChangePasswordYN(value) { this._NeedToChangePasswordYN = value; } get CreatedById() { return this._CreatedById; } set CreatedById(value) { this._CreatedById = value; } get CreatedAt() { return this._CreatedAt; } set CreatedAt(value) { this._CreatedAt = value; } get UpdatedById() { return this._UpdatedById; } set UpdatedById(value) { this._UpdatedById = value; } get UpdatedAt() { return this._UpdatedAt; } set UpdatedAt(value) { this._UpdatedAt = value; } get PasscodeHash() { return this._PasscodeHash; } set PasscodeHash(value) { this._PasscodeHash = value; } get PasscodeUpdatedAt() { return this._PasscodeUpdatedAt; } set PasscodeUpdatedAt(value) { this._PasscodeUpdatedAt = value; } getDetails() { return __awaiter(this, void 0, void 0, function* () { return { FullName: this.FullName, UserName: this.UserName, IDNo: this.IDNo, IDType: this.IDType, Email: this.Email, ContactNo: this.ContactNo, }; }); } constructor(sessionService, dbTransaction, userInfo) { super(); this.ObjectName = 'User'; this.TableName = 'sso_Users'; this.ObjectType = 'User'; this._SessionService = sessionService; if (dbTransaction) { this._dbTransaction = dbTransaction; } if (userInfo) { this.UserId = userInfo.UserId; this.UserName = userInfo.UserName; this.FullName = userInfo.FullName; this.IDNo = userInfo.IDNo; this.IDType = userInfo.IDType; this.Email = userInfo.Email; this.ContactNo = userInfo.ContactNo; this.Password = userInfo.Password; this.staffs = userInfo.staffs; this.Status = userInfo.Status; this.DefaultPasswordChangedYN = userInfo.DefaultPasswordChangedYN; this.FirstLoginAt = userInfo.FirstLoginAt; this.LastLoginAt = userInfo.LastLoginAt; this.MFAEnabled = userInfo.MFAEnabled; this.MFAConfig = userInfo.MFAConfig; this.MFABypassYN = userInfo.MFABypassYN; this.RecoveryEmail = userInfo.RecoveryEmail; this.FailedLoginAttemptCount = userInfo.FailedLoginAttemptCount; this.LastFailedLoginAt = userInfo.LastFailedLoginAt; this.LastPasswordChangedAt = userInfo.LastPasswordChangedAt; this.NeedToChangePasswordYN = userInfo.NeedToChangePasswordYN; this.PasscodeHash = userInfo.PasscodeHash; this.PasscodeUpdatedAt = userInfo.PasscodeUpdatedAt; this.CreatedById = userInfo.CreatedById; this.CreatedAt = userInfo.CreatedAt; this.UpdatedById = userInfo.UpdatedById; this.UpdatedAt = userInfo.UpdatedAt; } } static init(sessionService_1, userId_1) { return __awaiter(this, arguments, void 0, function* (sessionService, userId, dbTransaction = null) { User._RedisService = yield redis_service_1.RedisService.init(); if (userId) { if (dbTransaction) { User._Repository = new user_repository_1.UserRepository(); } const user = yield User._Repository.findOne({ where: { UserId: userId, }, include: [ { model: staff_entity_1.default, }, ], transaction: dbTransaction, }); if (!user) { throw new Error('Invalid credentials.'); } if (user) { const userAttr = { UserId: user.UserId, UserName: user.UserName, FullName: (user === null || user === void 0 ? void 0 : user.FullName) || null, IDNo: (user === null || user === void 0 ? void 0 : user.IdNo) || null, IDType: (user === null || user === void 0 ? void 0 : user.IdType) || null, ContactNo: (user === null || user === void 0 ? void 0 : user.ContactNo) || null, Email: user.Email, Password: user.Password, Status: user.Status, DefaultPasswordChangedYN: user.DefaultPasswordChangedYN, FirstLoginAt: user.FirstLoginAt, LastLoginAt: user.LastLoginAt, MFAEnabled: user.MFAEnabled, MFAConfig: user.MFAConfig, MFABypassYN: user.MFABypassYN, RecoveryEmail: user.RecoveryEmail, FailedLoginAttemptCount: user.FailedLoginAttemptCount, LastFailedLoginAt: user.LastFailedLoginAt, LastPasswordChangedAt: user.LastPasswordChangedAt, NeedToChangePasswordYN: user.NeedToChangePasswordYN, PasscodeHash: user.PasscodeHash, PasscodeUpdatedAt: user.PasscodeUpdatedAt, CreatedById: user.CreatedById, CreatedAt: user.CreatedAt, UpdatedById: user.UpdatedById, UpdatedAt: user.UpdatedAt, staffs: user === null || user === void 0 ? void 0 : user.Staff, }; return new User(sessionService, dbTransaction, userAttr); } else { throw new Error('User not found'); } } return new User(sessionService, dbTransaction); }); } static initUsingEmail(sessionService_1, email_1) { return __awaiter(this, arguments, void 0, function* (sessionService, email, dbTransaction = null) { User._RedisService = yield redis_service_1.RedisService.init(); if (email) { if (dbTransaction) { User._Repository = new user_repository_1.UserRepository(); } const user = yield User._Repository.findOne({ where: { Email: email, }, include: [ { model: staff_entity_1.default, }, ], transaction: dbTransaction, }); if (!user) { throw new Error('Invalid email.'); } if (user) { const userAttr = { UserId: user.UserId, UserName: user.UserName, FullName: (user === null || user === void 0 ? void 0 : user.FullName) || null, IDNo: (user === null || user === void 0 ? void 0 : user.IdNo) || null, IDType: (user === null || user === void 0 ? void 0 : user.IdType) || null, ContactNo: (user === null || user === void 0 ? void 0 : user.ContactNo) || null, Email: user.Email, Password: user.Password, Status: user.Status, DefaultPasswordChangedYN: user.DefaultPasswordChangedYN, FirstLoginAt: user.FirstLoginAt, LastLoginAt: user.LastLoginAt, MFAEnabled: user.MFAEnabled, MFAConfig: user.MFAConfig, MFABypassYN: user.MFABypassYN, RecoveryEmail: user.RecoveryEmail, FailedLoginAttemptCount: user.FailedLoginAttemptCount, LastFailedLoginAt: user.LastFailedLoginAt, LastPasswordChangedAt: user.LastPasswordChangedAt, NeedToChangePasswordYN: user.NeedToChangePasswordYN, PasscodeHash: user.PasscodeHash, PasscodeUpdatedAt: user.PasscodeUpdatedAt, CreatedById: user.CreatedById, CreatedAt: user.CreatedAt, UpdatedById: user.UpdatedById, UpdatedAt: user.UpdatedAt, staffs: user === null || user === void 0 ? void 0 : user.Staff, }; return new User(sessionService, dbTransaction, userAttr); } else { throw new Error('User not found'); } } }); } setEmail(email, dbTransaction) { return __awaiter(this, void 0, void 0, function* () { try { if (this.Email === email) { return; } const user = yield User._Repository.findOne({ where: { Email: email, }, transaction: dbTransaction, }); if (user) { throw new general_1.ClassError('LoginUser', 'LoginUserErrMsg0X', 'Email already exists'); } this.Email = email; } catch (error) { throw error; } }); } login(systemCode, email, password, ipAddress, dbTransaction) { return __awaiter(this, void 0, void 0, function* () { try { if (!this.ObjectId) { const user = yield User._Repository.findOne({ transaction: dbTransaction, where: { Email: email, Status: { [sequelize_1.Op.or]: [enum_1.UserStatus.ACTIVE, enum_1.UserStatus.LOCKED], }, }, include: [ { model: staff_entity_1.default, }, ], }); if (user) { const userAttr = { UserId: user.UserId, UserName: user.UserName, FullName: (user === null || user === void 0 ? void 0 : user.FullName) || null, IDNo: (user === null || user === void 0 ? void 0 : user.IdNo) || null, IDType: (user === null || user === void 0 ? void 0 : user.IdType) || null, ContactNo: (user === null || user === void 0 ? void 0 : user.ContactNo) || null, Email: user.Email, Password: user.Password, Status: user.Status, DefaultPasswordChangedYN: user.DefaultPasswordChangedYN, FirstLoginAt: user.FirstLoginAt, LastLoginAt: user.LastLoginAt, MFAEnabled: user.MFAEnabled, MFAConfig: user.MFAConfig, MFABypassYN: user.MFABypassYN, RecoveryEmail: user.RecoveryEmail, FailedLoginAttemptCount: user.FailedLoginAttemptCount, LastFailedLoginAt: user.LastFailedLoginAt, LastPasswordChangedAt: user.LastPasswordChangedAt, NeedToChangePasswordYN: user.NeedToChangePasswordYN, PasscodeHash: user.PasscodeHash, PasscodeUpdatedAt: user.PasscodeUpdatedAt, CreatedById: user.CreatedById, CreatedAt: user.CreatedAt, UpdatedById: user.UpdatedById, UpdatedAt: user.UpdatedAt, staffs: (user === null || user === void 0 ? void 0 : user.Staff) || null, }; this.UserId = userAttr.UserId; this.FullName = userAttr.FullName; this.IDNo = userAttr.IDNo; this.Email = userAttr.Email; this.ContactNo = userAttr.ContactNo; this.Password = userAttr.Password; this.Status = userAttr.Status; this.DefaultPasswordChangedYN = userAttr.DefaultPasswordChangedYN; this.FirstLoginAt = userAttr.FirstLoginAt; this.LastLoginAt = userAttr.LastLoginAt; this.MFAEnabled = userAttr.MFAEnabled; this.MFAConfig = userAttr.MFAConfig; this.RecoveryEmail = userAttr.RecoveryEmail; this.FailedLoginAttemptCount = userAttr.FailedLoginAttemptCount; this.LastFailedLoginAt = userAttr.LastFailedLoginAt; this.LastPasswordChangedAt = userAttr.LastPasswordChangedAt; this.NeedToChangePasswordYN = userAttr.NeedToChangePasswordYN; this.CreatedById = userAttr.CreatedById; this.CreatedAt = userAttr.CreatedAt; this.UpdatedById = userAttr.UpdatedById; this.UpdatedAt = userAttr.UpdatedAt; this.staffs = userAttr.staffs; } else { console.error('User not found for email:', email); throw new general_1.ClassError('User', 'UserErrMsg0X', 'Invalid Credentials'); } } if (this.ObjectId && this.Email !== email) { console.error('Email mismatch:', this.Email, email); throw new Error('Invalid credentials.'); } const check2FA = yield User.check2FA(this, dbTransaction); try { const system = yield User._SystemRepository.findOne({ where: { SystemCode: systemCode, Status: 'Active', }, }); if (!system) { throw new Error('Access denied: invalid or unauthorized system.'); } const passwordHashService = new password_hash_service_1.PasswordHashService(); const isPasswordValid = yield passwordHashService.verify(password, this.Password); if (!isPasswordValid) { console.error('Invalid password for user:', this.UserId); throw new Error('Invalid credentials.'); } yield this.checkSystemAccess(this.UserId, system.SystemCode, dbTransaction); if (this.Status === enum_1.UserStatus.LOCKED) { const isReleaseLock = User.shouldReleaseLock(this.LastFailedLoginAt); if (isReleaseLock) { yield User.releaseLock(this.UserId, dbTransaction); this.Status = enum_1.UserStatus.ACTIVE; } else { throw new Error('Your account has been locked. Please contact the administrator for assistance.'); } } } catch (error) { yield this.incrementFailedLoginAttemptCount(dbTransaction); throw error; } const system = yield User._SystemRepository.findOne({ where: { SystemCode: systemCode, }, }); yield this.alertNewLogin(this.ObjectId, system.SystemCode, ipAddress); this.FailedLoginAttemptCount = 0; this.LastLoginAt = new Date(); if (!this.FirstLoginAt) { this.FirstLoginAt = new Date(); } yield User._Repository.update({ FullName: this.FullName, UserName: this.UserName, IDNo: this.IDNo, Email: this.Email, ContactNo: this.ContactNo, Password: this.Password, Status: this.Status, DefaultPasswordChangedYN: this.DefaultPasswordChangedYN, FirstLoginAt: this.FirstLoginAt, LastLoginAt: this.LastLoginAt, MFAEnabled: this.MFAEnabled, MFAConfig: this.MFAConfig, RecoveryEmail: this.RecoveryEmail, FailedLoginAttemptCount: this.FailedLoginAttemptCount, LastFailedLoginAt: this.LastFailedLoginAt, LastPasswordChangedAt: this.LastPasswordChangedAt, NeedToChangePasswordYN: this.NeedToChangePasswordYN, }, { where: { UserId: this.UserId, }, transaction: dbTransaction, }); const sessionName = config_1.ApplicationConfig.getComponentConfigValue('sessionName'); if (!sessionName) { console.error('Session name is not set in the configuration'); throw new Error('Session name is not set in the configuration'); } const userSession = yield this._SessionService.retrieveUserSession(this.ObjectId, sessionName); const systemLogin = userSession.systemLogins.find((system) => system.code === systemCode); const sessionId = (0, crypto_1.randomUUID)(); if (systemLogin) { const privileges = yield this.getPrivileges(system.SystemCode, dbTransaction); systemLogin.sessionId = sessionId; systemLogin.privileges = privileges; userSession.systemLogins.map((system) => system.code === systemCode ? systemLogin : system); } else { const newLogin = { id: system.SystemCode, code: system.SystemCode, sessionId: sessionId, privileges: yield this.getPrivileges(system.SystemCode, dbTransaction), }; userSession.systemLogins.push(newLogin); } this._SessionService.setUserSession(this.ObjectId, userSession, sessionName); yield User._LoginHistoryRepository.create({ UserId: this.UserId, SystemCode: system.SystemCode, OriginIp: ipAddress, CreatedAt: new Date(), LoginStatus: login_status_enum_1.LoginStatusEnum.SUCCESS, }, { transaction: dbTransaction, }); const is2FAEnabledYN = config_1.ComponentConfig.getComponentConfigValue('@tomei/sso', 'is2FAEnabledYN'); const loginUser = yield login_user_1.LoginUser.init(this.SessionService, this.UserId, dbTransaction); if (is2FAEnabledYN === 'Y') { loginUser.session.Id = `${this.UserId}:`; } else { loginUser.session.Id = `${this.UserId}:${sessionId}`; } return loginUser; } catch (error) { if (this.ObjectId) { yield User._LoginHistoryRepository.create({ UserId: this.UserId, SystemCode: systemCode, OriginIp: ipAddress, LoginStatus: login_status_enum_1.LoginStatusEnum.FAILURE, CreatedAt: new Date(), }, { transaction: dbTransaction, }); } console.error('Login failed:', error); throw error; } }); } checkSystemAccess(userId, systemCode, dbTransaction) { return __awaiter(this, void 0, void 0, function* () { try { let isUserHaveAccess = false; const systemAccess = yield User._UserSystemAccessRepo.findOne({ where: { UserId: userId, SystemCode: systemCode, Status: 'Active', }, dbTransaction, }); if (systemAccess) { isUserHaveAccess = true; } else { const userGroups = yield User._UserGroupRepo.findAll({ where: { UserId: userId, InheritGroupSystemAccessYN: 'Y', Status: 'Active', }, include: [ { model: group_entity_1.default, }, ], dbTransaction, }); outer: for (const usergroup of userGroups) { const group = usergroup.Group; const groupSystemAccess = yield User.getInheritedSystemAccess(dbTransaction, group); for (const system of groupSystemAccess) { if (system.SystemCode === systemCode) { isUserHaveAccess = true; break outer; } } } } if (!isUserHaveAccess) { throw new Error("User don't have access to the system."); } } catch (error) { console.error('Error checking system access:', error); throw error; } }); } checkPrivileges(systemCode, privilegeName) { return __awaiter(this, void 0, void 0, function* () { try { if (!this.ObjectId) { throw new Error('ObjectId(UserId) is not set'); } const sessionName = config_1.ApplicationConfig.getComponentConfigValue('sessionName'); if (!sessionName) { throw new Error('Session name is not set in the configuration'); } const userSession = yield this._SessionService.retrieveUserSession(this.ObjectId, sessionName); const systemLogin = userSession.systemLogins.find((system) => system.code === systemCode); if (!systemLogin) { return false; } const privileges = systemLogin.privileges; const hasPrivilege = privileges.includes(privilegeName); return hasPrivilege; } catch (error) { throw error; } }); } alertNewLogin(userId, systemCode, ipAddress) { return __awaiter(this, void 0, void 0, function* () { try { const userLogins = yield User._LoginHistoryRepository.findAll({ where: { UserId: userId, SystemCode: systemCode, }, }); const gotPreviousLogins = (userLogins === null || userLogins === void 0 ? void 0 : userLogins.length) !== 0; let ipFound = undefined; if (gotPreviousLogins) { ipFound = userLogins.find((item) => item.OriginIp === ipAddress); } } catch (error) { throw error; } }); } getPrivileges(systemCode, dbTransaction) { return __awaiter(this, void 0, void 0, function* () { try { const system = yield User._SystemRepository.findOne({ where: { SystemCode: systemCode, }, transaction: dbTransaction, }); if (!system) { throw new Error('Invalid system code.'); } const userPrivileges = yield this.getUserPersonalPrivileges(systemCode, dbTransaction); const objectPrivileges = yield this.getObjectPrivileges(systemCode, dbTransaction); const userGroupOwnByUser = yield User._UserGroupRepo.findAll({ where: { UserId: this.UserId, InheritGroupSystemAccessYN: 'Y', InheritGroupPrivilegeYN: 'Y', Status: 'Active', }, include: [ { model: group_entity_1.default, where: { Status: 'Active', }, include: [ { model: group_system_access_entity_1.default, where: { SystemCode: systemCode, }, }, ], }, ], transaction: dbTransaction, }); let groupsPrivileges = []; for (const userGroup of userGroupOwnByUser) { const gp = yield this.getInheritedPrivileges(userGroup.GroupCode, systemCode, dbTransaction); groupsPrivileges = [...groupsPrivileges, ...gp]; } const privileges = [ ...userPrivileges, ...objectPrivileges, ...groupsPrivileges, ]; return privileges; } catch (error) { throw error; } }); } getInheritedPrivileges(groupCode, systemCode, dbTransaction) { return __awaiter(this, void 0, void 0, function* () { try { const group = yield User._GroupRepo.findOne({ where: { GroupCode: groupCode, Status: 'Active', }, include: [ { model: group_privilege_entity_1.default, where: { Status: 'Active', }, include: [ { model: system_privilege_entity_1.default, where: { SystemCode: systemCode, Status: 'Active', }, }, ], }, ], transaction: dbTransaction, }); const objectPrivileges = yield User._GroupObjectPrivilegeRepo.findAll({ where: { GroupCode: groupCode, }, include: { model: system_privilege_entity_1.default, where: { SystemCode: systemCode, Status: 'Active', }, }, transaction: dbTransaction, }); const gp = (group === null || group === void 0 ? void 0 : group.GroupPrivileges) || []; const op = objectPrivileges || []; let privileges = []; const groupPrivileges = []; for (const groupPrivilege of gp) { groupPrivileges.push(groupPrivilege.Privilege.PrivilegeCode); } const ops = []; for (const objectPrivilege of op) { ops.push(objectPrivilege.Privilege.PrivilegeCode); } privileges = [...privileges, ...groupPrivileges, ...ops]; if ((group === null || group === void 0 ? void 0 : group.ParentGroupCode) && (group === null || group === void 0 ? void 0 : group.InheritParentPrivilegeYN) === 'Y') { const parentGroupPrivileges = yield this.getInheritedPrivileges(group.ParentGroupCode, systemCode, dbTransaction); privileges = [...privileges, ...parentGroupPrivileges]; } return privileges; } catch (error) { throw error; } }); } getUserPersonalPrivileges(systemCode, dbTransaction) { return __awaiter(this, void 0, void 0, function* () { try { const userPrivileges = (yield User._UserPrivilegeRepo.findAll({ where: { UserId: this.UserId, Status: 'Active', }, include: { model: system_privilege_entity_1.default, where: { SystemCode: systemCode, Status: 'Active', }, }, transaction: dbTransaction, })) || []; const privileges = userPrivileges.map((u) => u.Privilege.PrivilegeCode); return privileges; } catch (error) { throw error; } }); } getObjectPrivileges(systemCode, dbTransaction) { return __awaiter(this, void 0, void 0, function* () { try { const userObjectPrivileges = (yield User._UserObjectPrivilegeRepo.findAll({ where: { UserId: this.UserId, }, include: { model: system_privilege_entity_1.default, where: { SystemCode: systemCode, Status: 'Active', }, }, transaction: dbTransaction, })) || []; const privilegesCodes = userObjectPrivileges.map((u) => u.Privilege.PrivilegeCode); return privilegesCodes; } catch (error) { throw error; } }); } static checkUserInfoDuplicated(dbTransaction, query) { return __awaiter(this, void 0, void 0, function* () { try { const { Email, UserName, IdType, IdNo, ContactNo } = query; const where = { [sequelize_1.Op.or]: {}, }; if (Email) { where[sequelize_1.Op.or]['Email'] = Email; } if (UserName) { where[sequelize_1.Op.or]['UserName'] = UserName; } if (IdType && IdNo) { where[sequelize_1.Op.and] = [{ IdType: IdType }, { IdNo: IdNo }]; } if (ContactNo) { where[sequelize_1.Op.or]['ContactNo'] = ContactNo; } const user = yield User._Repository.findAll({ where, transaction: dbTransaction, }); if (user && user.length > 0) { throw new general_1.ClassError('LoginUser', 'LoginUserErrMsg0X', 'User info already exists'); } } catch (error) { throw error; } }); } static generateDefaultPassword() { try { const passwordPolicy = config_1.ComponentConfig.getComponentConfigValue('@tomei/sso', 'passwordPolicy'); if (!passwordPolicy || !passwordPolicy.maxLen || !passwordPolicy.minLen || !passwordPolicy.nonAcceptableChar || !passwordPolicy.numOfCapitalLetters || !passwordPolicy.numOfNumbers || !passwordPolicy.numOfSpecialChars) { throw new general_1.ClassError('LoginUser', 'LoginUserErrMsg0X', 'Missing password policy. Please set in config file.'); } if (passwordPolicy.numOfCapitalLetters + passwordPolicy.numOfNumbers + passwordPolicy.numOfSpecialChars > passwordPolicy.maxLen) { throw new general_1.ClassError('LoginUser', 'LoginUserErrMsg0X', 'Password policy is invalid. Please set in config file.'); } const { maxLen, minLen, nonAcceptableChar, numOfCapitalLetters, numOfNumbers, numOfSpecialChars, } = passwordPolicy; const passwordLength = Math.floor(Math.random() * (maxLen - minLen + 1)) + minLen; const words = 'abcdefghijklmnopqrstuvwxyz'; const capitalLetters = words.toUpperCase(); const numbers = '0123456789'; const specialChars = '!@#$%^&*()_+-={}[]|:;"<>,.?/~`'; const nonAcceptableChars = nonAcceptableChar.split(','); const filteredWords = words .split('') .filter((word) => !nonAcceptableChars.includes(word)); const filteredCapitalLetters = capitalLetters .split('') .filter((word) => !nonAcceptableChars.includes(word)); const filteredNumbers = numbers .split('') .filter((word) => !nonAcceptableChars.includes(word)); const filteredSpecialChars = specialChars .split('') .filter((word) => !nonAcceptableChars.includes(word)); const generatedCapitalLetters = []; const generatedNumbers = []; const generatedSpecialChars = []; const generatedWords = []; for (let i = 0; i < numOfCapitalLetters; i++) { const randomIndex = Math.floor(Math.random() * filteredCapitalLetters.length); generatedCapitalLetters.push(filteredCapitalLetters[randomIndex]); } for (let i = 0; i < numOfNumbers; i++) { const randomIndex = Math.floor(Math.random() * filteredNumbers.length); generatedNumbers.push(filteredNumbers[randomIndex]); } for (let i = 0; i < numOfSpecialChars; i++) { const randomIndex = Math.floor(Math.random() * filteredSpecialChars.length); generatedSpecialChars.push(filteredSpecialChars[randomIndex]); } for (let i = 0; i < passwordLength - (numOfCapitalLetters + numOfNumbers + numOfSpecialChars); i++) { const randomIndex = Math.floor(Math.random() * filteredWords.length); generatedWords.push(filteredWords[randomIndex]); } let generatedPassword = ''; const allGeneratedChars = generatedCapitalLetters.concat(generatedNumbers, generatedSpecialChars, generatedWords); allGeneratedChars.sort(() => Math.random() - 0.5); generatedPassword = allGeneratedChars.join(''); return generatedPassword; } catch (error) { throw error; } } static setPassword(dbTransaction, user, password) { return __awaiter(this, void 0, void 0, function* () { try { const passwordPolicy = config_1.ComponentConfig.getComponentConfigValue('@tomei/sso', 'passwordPolicy'); if (!passwordPolicy || !passwordPolicy.maxLen || !passwordPolicy.minLen || !passwordPolicy.nonAcceptableChar || !passwordPolicy.numOfCapitalLetters || !passwordPolicy.numOfNumbers || !passwordPolicy.numOfSpecialChars) { throw new general_1.ClassError('LoginUser', 'LoginUserErrMsg0X', 'Missing password policy. Please set in config file.'); } try { if (password.length < passwordPolicy.minLen) { throw Error('Password is too short'); } if (password.length > passwordPolicy.maxLen) { throw Error('Password is too long'); } const nonAcceptableChars = passwordPolicy.nonAcceptableChar.split(','); const nonAcceptableCharsFound = nonAcceptableChars.some((char) => password.includes(char)); if (nonAcceptableCharsFound) { throw Error('Password contains unacceptable characters'); } const capitalLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; const numOfCapitalLetters = passwordPolicy.numOfCapitalLetters; const capitalLettersFound = capitalLetters .split('') .filter((char) => password.includes(char)).length; if (capitalLettersFound < numOfCapitalLetters) { throw Error('Password does not contain enough capital letters'); } const numbers = '0123456789'; const numOfNumbers = passwordPolicy.numOfNumbers; const numbersFound = numbers .split('') .filter((char) => password.includes(char)).length; if (numbersFound < numOfNumbers) { throw Error('Password does not contain enough numbers'); } const specialChars = '!@#$%^&*()_+-={}[]|:;"<>,.?/~`'; const numOfSpecialChars = passwordPolicy.numOfSpecialChars; const specialCharsFound = specialChars .split('') .filter((char) => password.includes(char)).length; if (specialCharsFound < numOfSpecialChars) { throw Error('Password does not contain enough special characters'); } } catch (error) { throw new general_1.ClassError('LoginUser', 'LoginUserErrMsg0X', "Your password doesn't meet security requirements. Try using a mix of uppercase and lowercase letters, numbers, and symbols."); } const passwordHashService = new password_hash_service_1.PasswordHashService(); const hashedPassword = yield passwordHashService.hashPassword(password); user._Password = hashedPassword; return user; } catch (error) { throw error; } }); } generateAuthorizationToken() { return __awaiter(this, void 0, void 0, function* () { const plaintextToken = (0, crypto_1.randomBytes)(32).toString('hex'); const hashedToken = (0, crypto_1.createHash)('sha256') .update(plaintextToken) .digest('hex'); this._SessionService.setAuthorizationCode(hashedToken, this.ObjectId, 60 * 60 * 24); return { plaintextToken, hashedToken }; }); } validateAuthorizationToken(autorizationToken) { return __awaiter(this, void 0, void 0, function* () { try { const hashedSubmittedToken = (0, crypto_1.createHash)('sha256') .update(autorizationToken) .digest('hex'); const userId = yield this._SessionService.retrieveAuthorizationCode(hashedSubmittedToken); if (!userId) { return null; } yield this._SessionService.deleteAuthorizationCode(hashedSubmittedToken); return userId; } catch (error) { throw error; } }); } static resetPassword(sessionService, autorizationToken, password, dbTransaction) { return __awaiter(this, void 0, void 0, function* () { try { const hashedSubmittedToken = (0, crypto_1.createHash)('sha256') .update(autorizationToken) .digest('hex'); const userId = yield sessionService.retrieveAuthorizationCode(hashedSubmittedToken); if (!userId) { throw new general_1.ClassError('LoginUser', 'LoginUserErrMsg0X', 'Invalid token', 'setupFirstPassword', 401); } yield sessionService.deleteAuthorizationCode(hashedSubmittedToken); console.log(`Token verified for user: ${userId}`); const passwordHashService = new password_hash_service_1.PasswordHashService(); yield user_password_history_1.UserPasswordHistory.validate(dbTransaction, parseInt(userId), password, passwordHashService); const user = yield User.init(sessionService, parseInt(userId), dbTransaction); yield User.setPassword(dbTransaction, user, password); let userData = yield User._Repository.update({ Password: user._Password, DefaultPasswordChangedYN: yn_enum_1.YN.Yes, NeedToChangePasswordYN: yn_enum_1.YN.No, }, { where: { UserId: user.UserId, }, transaction: dbTransaction, }); yield user_password_history_1.UserPasswordHistory.create(dbTransaction, parseInt(userId), user._Password); } catch (error) { throw error; } }); } static create(loginUser, dbTransaction, user) { return __awaiter(this, void 0, void 0, function* () { try { const systemCode = config_1.ApplicationConfig.getComponentConfigValue('system-code'); const isPrivileged = yield loginUser.checkPrivileges(systemCode, 'User - Create'); if (!isPrivileged) { throw new general_1.ClassError('LoginUser', 'LoginUserErrMsg0X', 'You do not have the privilege to create user'); } if (!user.Email && !user.UserName) { throw new general_1.ClassError('LoginUser', 'LoginUserErrMsg0X', 'Email and Username is required'); } yield User.checkUserInfoDuplicated(dbTransaction, { Email: user.Email, UserName: user.UserName, IdType: user.IDType, IdNo: user.IDNo, ContactNo: user.ContactNo, }); const defaultPassword = User.generateDefaultPassword(); user = yield User.setPassword(dbTransaction, user, defaultPassword); const userInfo = { UserName: user.UserName, FullName: user.FullName, IDNo: user