@tomei/sso
Version:
Tomei SSO Package
457 lines (416 loc) • 13.9 kB
text/typescript
import { ClassError, HashTable, ObjectBase } from '@tomei/general';
import { SystemRepository } from './system.repository';
import { ISystemAttr } from '../../interfaces/system.interface';
import { LoginUser } from '../login-user/login-user';
import { ApplicationConfig, ComponentConfig } from '@tomei/config';
import { ActionEnum, Activity } from '@tomei/activity-history';
import { ISystemSearchAttr } from '../../interfaces/system-search-attr.interface';
import { Op } from 'sequelize';
import { v4 as uuidv4 } from 'uuid';
export class System extends ObjectBase {
ObjectId: string;
ObjectName: string;
TableName = 'sso_System';
ObjectType = 'System';
private static _htSystem: HashTable;
SystemCode: string;
Name: string;
Description: string;
AccessURL: string;
GooglePlayURL: string;
AppleStoreURL: string;
APIKey: string;
APISecret: string;
Status: string;
private _CreatedById: number;
private _CreatedAt: Date;
private _UpdatedById: number;
private _UpdatedAt: Date;
private static _Repo = new SystemRepository();
get CreatedById(): number {
return this._CreatedById;
}
get CreatedAt(): Date {
return this._CreatedAt;
}
get UpdatedById(): number {
return this._UpdatedById;
}
get UpdatedAt(): Date {
return this._UpdatedAt;
}
private constructor(systemAttr?: ISystemAttr) {
super();
if (systemAttr) {
this.SystemCode = systemAttr.SystemCode;
this.Name = systemAttr.Name;
this.Description = systemAttr?.Description;
this.AccessURL = systemAttr?.AccessURL;
this.GooglePlayURL = systemAttr?.GooglePlayURL;
this.AppleStoreURL = systemAttr?.AppleStoreURL;
this.APIKey = systemAttr?.APIKey;
this.APISecret = systemAttr?.APISecret;
this.Status = systemAttr?.Status;
this._CreatedById = systemAttr.CreatedById;
this._CreatedAt = systemAttr.CreatedAt;
this._UpdatedById = systemAttr.UpdatedById;
this._UpdatedAt = systemAttr.UpdatedAt;
}
}
public static async init(dbTransaction: any, SystemCode?: string) {
try {
if (SystemCode) {
const system = await System._Repo.findByPk(SystemCode, {
transaction: dbTransaction,
});
if (system) {
return new System(system);
} else {
throw new ClassError('System', 'SystemErrMsg00', 'System Not Found');
}
}
return new System();
} catch (error) {
throw new ClassError(
'System',
'SystemErrMsg01',
'Failed To Initialize System',
);
}
}
public async createSystem(
loginUser: LoginUser,
dbTransaction: any,
): Promise<void> {
try {
//Creates a new system in the database.
//Part 1: Check Privilege
//Call loginUser.checkPrivilege() method to check if the user has the privilege to create a system.
const systemCode =
ApplicationConfig.getComponentConfigValue('system-code');
const isPrivileged = await loginUser.checkPrivileges(
systemCode,
'System - Create',
);
if (!isPrivileged) {
throw new Error('You do not have permission to list UserGroup.');
}
//Part 2: Validate Input Params
//If this._SystemCode is missing, throw ClassError
if (!this.SystemCode) {
throw new ClassError(
'System',
'SystemErrMsg02',
'SystemCode must have value.',
);
}
//If this._Name missing, throw ClassError
if (!this.Name) {
throw new ClassError(
'System',
'SystemErrMsg03',
'Name must have value.',
);
}
//If this._Description missing, throw ClassError
if (!this.Description) {
throw new ClassError(
'System',
'SystemErrMsg04',
'Description must have value.',
);
}
//Part 3: Prepare to Insert Data
//Set private properties
this._CreatedById = loginUser.UserId;
this._CreatedAt = new Date();
this._UpdatedById = loginUser.UserId;
this._UpdatedAt = new Date();
//Call System._Repo create method
await System._Repo.create(
{
SystemCode: this.SystemCode,
Name: this.Name,
Description: this.Description,
AccessURL: this.AccessURL,
GooglePlayURL: this.GooglePlayURL,
AppleStoreURL: this.AppleStoreURL,
APIKey: this.APIKey,
APISecret: this.APISecret,
Status: this.Status,
CreatedById: this._CreatedById,
CreatedAt: this._CreatedAt,
UpdatedById: this._UpdatedById,
UpdatedAt: this._UpdatedAt,
},
{
transaction: dbTransaction,
},
);
//Part 4: Record Create System Activity
//Initialise EntityValueAfter variable and set to this class.
const entityValueAfter = {
SystemCode: this.SystemCode,
Name: this.Name,
Description: this.Description,
AccessURL: this.AccessURL,
GooglePlayURL: this.GooglePlayURL,
AppleStoreURL: this.AppleStoreURL,
APIKey: this.APIKey,
APISecret: this.APISecret,
Status: this.Status,
};
//Instantiate new activity from Activity class, call createId() method, then set the properties.
const activity = new Activity();
activity.ActivityId = activity.createId();
activity.Action = ActionEnum.CREATE;
activity.Description = 'Add System';
activity.EntityType = 'System';
activity.EntityId = this.SystemCode;
activity.EntityValueBefore = JSON.stringify({});
activity.EntityValueAfter = JSON.stringify(entityValueAfter);
} catch (error) {
throw error;
}
}
protected static async checkDuplicateSystemCode(
dbTransaction: any,
systemCode: string,
): Promise<void> {
//This method will make sure there is no duplicate system code.
try {
//Call System._Repo findOne() method by passing Params.SystemCode and dbTransaction, if SystemCode already exists, throw ClassError.
const system = await System._Repo.findOne({
where: {
SystemCode: systemCode,
},
transaction: dbTransaction,
});
if (system) {
throw new ClassError(
'System',
'SystemErrMsg05',
'System Code already exists.',
);
}
} catch (error) {
throw error;
}
}
public async setSystemCode(
dbTransaction: any,
systemCode: string,
): Promise<void> {
//Custom setter method for SystemCode
try {
//Call checkDuplicateSystemCode() method to make sure there is no duplicate system code.
await System.checkDuplicateSystemCode(dbTransaction, systemCode);
//Set this._SystemCode to Params.SystemCode
this.SystemCode = systemCode;
} catch (error) {
throw error;
}
}
public static async findAll(
dbTransaction: any,
loginUser: LoginUser,
page?: number,
rows?: number,
search?: ISystemSearchAttr,
): Promise<{ count: number; systems: System[] }> {
//This method list all system records based on filter.
try {
//Part 1: Retrieve listing
const queryObj: any = {};
const whereObj: any = {};
if (search) {
Object.entries(search).forEach(([key, value]) => {
if (value) {
queryObj[key] = {
[Op.substring]: value,
};
}
});
}
if (page && rows) {
whereObj.offset = (page - 1) * rows;
whereObj.limit = rows;
}
//Call System._Repo findAll() method by passing queryObj and whereObj
const result = await System._Repo.findAllWithPagination({
distinct: true,
where: queryObj,
...whereObj,
order: [['CreatedAt', 'DESC']],
transaction: dbTransaction,
});
//Return result
const systems = result.rows.map((system) => new System(system));
return { count: result.count, systems };
} catch (error) {
throw error;
}
}
public static async renewApiKeyAndSecret(
loginUser: LoginUser,
dbTransaction: any,
systemCode: string,
) {
try {
//Part 1: Privilege Checking
//Call loginUser.checkPrivilege() method to check if the user has the privilege to renew API Key and Secret.
const sc = ApplicationConfig.getComponentConfigValue('system-code');
const isPrivileged = await loginUser.checkPrivileges(sc, 'SYSTEM_UPDATE');
if (!isPrivileged) {
throw new ClassError(
'System',
'SystemErrMsg06',
'You do not have permission to renew API Key and Secret.',
);
}
//Part 2: Validation
//Instantiate existing System
const system = await System.init(dbTransaction, systemCode);
//Check if system.AccessURL got value. If not, throw new ClassError
if (!system.AccessURL) {
throw new ClassError(
'System',
'SystemErrMsg07',
'AccessURL is required for callback',
);
}
//Check if system.Status is "Active". If not, throw new ClassError
if (system.Status !== 'Active') {
throw new ClassError(
'System',
'SystemErrMsg08',
'Cannot do this operation on inactive system.',
);
}
//Set EntityValueBefore to system instance.
const entityValueBefore = {
SystemCode: system.SystemCode,
Name: system.Name,
Description: system.Description,
AccessURL: system.AccessURL,
GooglePlayURL: system.GooglePlayURL,
AppleStoreURL: system.AppleStoreURL,
APIKey: system.APIKey,
APISecret: system.APISecret,
Status: system.Status,
};
//Part 3: Generate API key and secret
//Use https://www.npmjs.com/package/uuid package to generate both the api key and api secret.
const apiKey = uuidv4();
const apiSecret = uuidv4();
//Update the system instance with new API key and secret.
system.APIKey = apiKey;
system.APISecret = apiSecret;
system._UpdatedById = loginUser.UserId;
system._UpdatedAt = new Date();
//Call System._Repo update() method to update the system record.
await System._Repo.update(
{
APIKey: apiKey,
APISecret: apiSecret,
UpdatedById: system._UpdatedById,
UpdatedAt: system._UpdatedAt,
},
{
where: {
SystemCode: systemCode,
},
transaction: dbTransaction,
},
);
//Part 4: Record Renew API Key and Secret Activity
//Set EntityValueAfter to system instance.
const entityValueAfter = {
SystemCode: system.SystemCode,
Name: system.Name,
Description: system.Description,
AccessURL: system.AccessURL,
GooglePlayURL: system.GooglePlayURL,
AppleStoreURL: system.AppleStoreURL,
APIKey: system.APIKey,
APISecret: system.APISecret,
Status: system.Status,
};
//Instantiate new activity from Activity class, call createId() method, then set the properties.
const activity = new Activity();
activity.ActivityId = activity.createId();
activity.Action = ActionEnum.UPDATE;
activity.Description = 'Renew API key and secret for a system';
activity.EntityType = 'System';
activity.EntityId = system.SystemCode;
activity.EntityValueBefore = JSON.stringify(entityValueBefore);
activity.EntityValueAfter = JSON.stringify(entityValueAfter);
await activity.create(loginUser.ObjectId, dbTransaction);
//Return the updated system instance.
return system;
} catch (error) {
throw error;
}
}
public static async loadSystem(dbTransaction): Promise<string> {
try {
// Part 1: Retrieve System Info
// Load sso component config.loadComponentConfig Call Config. by passing:
// filepath: '/component-config/sso-config.json'
ComponentConfig.loadComponentConfig('./component-config/sso-config.json');
const config: {
name: string;
code: string;
description: string;
userId: string;
} = ComponentConfig.getComponentConfigValue('@tomei/sso', 'system');
// Make sure all required fields are provided in the config file.
if (
!config.name ||
!config.code ||
!config.description ||
!config.userId
) {
throw new Error('Missing required fields in the config file.');
}
// Retrieve existing System. Call System._Repo findByPk method by passing:
// SystemCode: system.code
// dbTransaction
const system = await System._Repo.findByPk(config.code, {
transaction: dbTransaction,
});
// If system already exists, skip all steps below and return "System loaded."
if (system) {
return 'System loaded.';
}
//if system not exists. Call System._Repo create method by passing:
// SystemCode: system.code,
// Name: system.name
// Description: system.description,
// Status: 'Active',
// CreatedById: system.userId
// CreatedAt: current date & time
// UpdatedById: system.userId
// UpdatedAt: current date & time
await System._Repo.create(
{
SystemCode: config.code,
Name: config.name,
Description: config.description,
Status: 'Active',
CreatedById: config.userId,
CreatedAt: new Date(),
UpdatedById: config.userId,
UpdatedAt: new Date(),
},
{
transaction: dbTransaction,
},
);
// Return "System loaded."
return 'System loaded.';
} catch (error) {
throw error;
}
}
}