UNPKG

@tomei/sso

Version:
364 lines (329 loc) 10.8 kB
import { ILoginUser } from '@tomei/general'; import { User } from './user'; import { ISystemLogin } from '../../../src/interfaces/system-login.interface'; import GroupModel from '../../models/group.entity'; import { ISessionService } from '../../session/interfaces/session-service.interface'; import { RedisService } from '../../redis-client/redis.service'; import { UserRepository } from './user.repository'; import { IUserAttr, IUserInfo } from './interfaces/user-info.interface'; import Staff from '../../models/staff.entity'; import UserModel from '../../models/user.entity'; import { createHash, randomBytes, randomUUID } from 'crypto'; import { UserGroupRepository } from '../user-group/user-group.repository'; import GroupSystemAccessModel from '../../models/group-system-access.entity'; import SystemModel from '../../models/system.entity'; import { ApplicationConfig } from '@tomei/config'; export class LoginUser extends User implements ILoginUser { session = { Id: null, }; static async init( sessionService: ISessionService, userId?: number, dbTransaction = null, ): Promise<LoginUser> { User._RedisService = await RedisService.init(); if (userId) { if (dbTransaction) { User._Repository = new UserRepository(); } const user = await User._Repository.findOne({ where: { UserId: userId, }, include: [ { model: Staff, }, ], transaction: dbTransaction, }); if (!user) { throw new Error('Invalid credentials.'); } if (user) { const userAttr: IUserAttr = { UserId: user.UserId, UserName: user.UserName, FullName: user?.FullName || null, IDNo: user?.IdNo || null, IDType: user?.IdType || null, ContactNo: 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?.Staff, }; return new LoginUser(sessionService, dbTransaction, userAttr); } else { throw new Error('User not found'); } } return new LoginUser(sessionService, dbTransaction); } async checkPrivileges( systemCode: string, privilegeName: string, ): Promise<boolean> { try { if (!this.ObjectId) { throw new Error('ObjectId(UserId) is not set'); } const sessionName = ApplicationConfig.getComponentConfigValue('sessionName'); if (!sessionName) { throw new Error('Session name is not set'); } const userSession = await 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; } } async checkSession( systemCode: string, sessionId: string, userId: string, ): Promise<ISystemLogin> { try { const sessionName = ApplicationConfig.getComponentConfigValue('sessionName'); if (!sessionName) { throw new Error('Session name is not set'); } const userSession = await this._SessionService.retrieveUserSession( userId, sessionName, ); if (userSession.systemLogins.length === 0) { throw new Error('Session expired.'); } const systemLogin = userSession.systemLogins.find( (sl) => sl.code === systemCode, ); if (!systemLogin) { throw new Error('Session expired.'); } if (systemLogin.sessionId !== sessionId) { throw new Error('Session expired.'); } await this._SessionService.refreshDuration(userId, sessionName); return systemLogin; } catch (error) { throw error; } } async logout(systemCode: string) { try { if (!this.ObjectId) { throw new Error('ObjectId(UserId) is not set'); } const sessionName = ApplicationConfig.getComponentConfigValue('sessionName'); if (!sessionName) { throw new Error('Session name is not set'); } const userSession = await this._SessionService.retrieveUserSession( this.ObjectId, sessionName, ); const index = userSession.systemLogins.findIndex( (system) => system.code === systemCode, ); userSession.systemLogins.splice(index, 1); this._SessionService.setUserSession( this.ObjectId, userSession, sessionName, ); } catch (error) { throw error; } } async getProfile(dbTransaction: any) { const user = await User._Repository.findOne({ where: { UserId: this.UserId, Status: 'Active', }, include: [ { model: Staff, }, ], transaction: dbTransaction, }); return user; } public static async getGroups(loginUser: User, dbTransaction: any) { // This method will retrieve all user groups. // Part 2: Retrieve User Groups & Returns const userGroups = await User._UserGroupRepo.findAll({ where: { UserId: loginUser.ObjectId, Status: 'Active', }, include: [{ model: UserModel, as: 'User' }, { model: GroupModel }], transaction: dbTransaction, }); return userGroups; } public static async getSystems(loginUser: User, dbTransaction: any) { // This method will retrieve all system records which user has accessed to. // Part 2: Retrieve System Access const groups = await this.getGroups(loginUser, dbTransaction); const systemAccess = await User.combineSystemAccess( loginUser, dbTransaction, groups, ); const output = []; if (systemAccess) { for (let i = 0; i < systemAccess.length; i++) { const system = await User._SystemRepository.findOne({ where: { SystemCode: systemAccess[i].SystemCode, Status: 'Active', }, }); output.push({ UserSystemAccessId: systemAccess[i].UserSystemAccessId, UserId: systemAccess[i].UserId, SystemCode: systemAccess[i].SystemCode, Status: systemAccess[i].Status, CreatedById: systemAccess[i].CreatedById, UpdatedById: systemAccess[i].UpdatedById, CreatedAt: systemAccess[i].CreatedAt, UpdatedAt: systemAccess[i].UpdatedAt, inheritedBy: ['OWN'], System: system, }); } } let userGroupRepository = new UserGroupRepository(); const userGroups = await userGroupRepository.findAll({ where: { UserId: loginUser.UserId, Status: 'Active', }, include: [ { model: GroupModel, required: true, where: { Status: 'Active', }, include: [ { model: GroupSystemAccessModel, where: { Status: 'Active', }, include: [ { model: SystemModel, }, ], }, ], }, ], transaction: dbTransaction, }); if (userGroups) { for (let i = 0; i < userGroups.length; i++) { let systemAccessList = userGroups[i].Group.GroupSystemAccesses; for (let j = 0; j < systemAccessList.length; j++) { let systemDetails = systemAccessList[j]; let isFound = output.findIndex( (e) => e.SystemCode === systemDetails.SystemCode, ); if (isFound > -1) { output[isFound].inheritedBy.push(userGroups[i].GroupCode); } else { output.push({ UserSystemAccessId: systemDetails.GroupSystemAccessId, UserId: systemDetails.GroupSystemAccessId, SystemCode: systemDetails.SystemCode, Status: systemDetails.Status, CreatedById: systemDetails.CreatedById, UpdatedById: systemDetails.UpdatedById, CreatedAt: systemDetails.CreatedAt, UpdatedAt: systemDetails.UpdatedAt, inheritedBy: [userGroups[i].GroupCode], System: systemDetails.System, }); } } } } // Part 3: Map Result to System Object return output; } async setSession(systemCode: string, sessionId: string, dbTransaction: any) { // fetch user session if exists const sessionName = ApplicationConfig.getComponentConfigValue('sessionName'); if (!sessionName) { throw new Error('Session name is not set in the configuration'); } const userSession = await this._SessionService.retrieveUserSession( this.ObjectId, sessionName, ); const systemLogin = userSession.systemLogins.find( (system) => system.code === systemCode, ); if (systemLogin) { const privileges = await this.getPrivileges(systemCode, dbTransaction); systemLogin.sessionId = sessionId; systemLogin.privileges = privileges; userSession.systemLogins.map((system) => system.code === systemCode ? systemLogin : system, ); } else { // if not, add new system login into the userSession const newLogin = { id: systemCode, code: systemCode, sessionId: sessionId, privileges: await this.getPrivileges(systemCode, dbTransaction), }; userSession.systemLogins.push(newLogin); } // then update userSession inside the redis storage with 1 day duration of time-to-live this._SessionService.setUserSession( this.ObjectId, userSession, sessionName, ); } }