@tomei/sso
Version:
Tomei SSO Package
364 lines (329 loc) • 10.8 kB
text/typescript
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,
);
}
}