@tomei/sso
Version:
Tomei SSO Package
188 lines (168 loc) • 6.1 kB
text/typescript
import { ClassError, ObjectBase } from '@tomei/general';
import { ComponentConfig } from '@tomei/config';
import { IUserPasswordHistoryAttr } from '../../interfaces/user-password-history.interface';
import { UserPasswordHistoryRepository } from './user-password-history.repository';
import { PasswordHashService } from '../../components/password-hash';
export class UserPasswordHistory
extends ObjectBase
implements IUserPasswordHistoryAttr
{
ObjectId: string;
ObjectName: string;
ObjectType = 'UserPasswordHistory';
TableName = 'sso_UserPasswordHistory';
UserId: number;
PasswordHash: string;
private _CreatedAt: Date;
private static _Repo = new UserPasswordHistoryRepository();
get HistoryId(): string {
return this.ObjectId;
}
set HistoryId(value: string) {
this.ObjectId = value;
}
get CreatedAt(): Date {
return this._CreatedAt;
}
private constructor(params?: IUserPasswordHistoryAttr) {
super();
if (params) {
this.ObjectId = params.HistoryId;
this.UserId = params.UserId;
this.PasswordHash = params.PasswordHash;
this._CreatedAt = params.CreatedAt;
}
}
public static async init(
historyId?: number,
dbTransaction?: any,
): Promise<UserPasswordHistory> {
try {
if (historyId) {
const data = await UserPasswordHistory._Repo.findByPk(
historyId.toString(),
dbTransaction,
);
if (!data) {
throw new ClassError(
'UserPasswordHistory',
'UserPasswordHistoryErrMsg01',
'UserPasswordHistory not found',
'init',
400,
);
}
return new UserPasswordHistory(data.get({ plain: true }));
}
return new UserPasswordHistory();
} catch (error) {
throw error;
}
}
public static async validate(
dbTransaction: any,
UserId: number,
Password: string,
passwordHashService: PasswordHashService,
): Promise<void> {
// This method used to check if password entered is valid by checking previous password history
try {
// Part 1-2: Retrieve password history policy by using component config, call ComponentConfig. by passing:
// - ComponentName: "@tomei/sso"
// - ConfigKey: "passwordHistory"
// If no password history set, use default value 3
const passwordHistoryPolicy =
ComponentConfig.getComponentConfigValue(
'@tomei/sso',
'passwordHistory',
) || 3;
// Part 3-4: Retrieve records from the table by using class._repo findAll() by passing:
// where: { UserId: params.UserId }
// order: [['CreatedAt', 'DESC']]
// limit: passwordHistory count above.
// If no record found, return null.
let passwordHistory = await UserPasswordHistory._Repo.findAll({
where: { UserId: UserId },
order: [['CreatedAt', 'DESC']],
limit: passwordHistoryPolicy,
transaction: dbTransaction,
});
if (passwordHistory?.length < 1) {
return null;
} else {
// Part 5: If record found, map each record to compare params.Password and record.PasswordHash using the params.passwordHashService. If match, stop the mapping, and return ClassError:
// ClassName: "UserPasswordHistory"
// MethodName: "validate"
// MessageCode: "UserPasswordHistory01"
// Message: You cannot reuse your last ${passwordHistory} passwords. Please choose a new and unique password.
for (let index = 0; index < passwordHistory.length; index++) {
const isPasswordSame = await passwordHashService.verify(
Password,
passwordHistory[index].PasswordHash,
);
if (isPasswordSame) {
throw new ClassError(
'UserPasswordHistory',
'UserPasswordHistory01',
`You cannot reuse your last ${passwordHistoryPolicy} passwords. Please choose a new and unique password.`,
'validate',
403,
);
}
}
}
} catch (error) {
throw error;
}
}
public static async create(
dbTransaction: any,
UserId: number,
PasswordHash: string,
): Promise<void> {
// This method used to check if password entered is valid by checking previous password history
try {
// Part 1-2: Retrieve password history policy by using component config, call ComponentConfig. by passing:
// - ComponentName: "@tomei/sso"
// - ConfigKey: "passwordHistory"
// If no password history set, use default value 3
const passwordHistoryPolicy =
ComponentConfig.getComponentConfigValue(
'@tomei/sso',
'passwordHistory',
) || 3;
// Part 3: Insert new password history by calling class _repo create() method.
const userPasswordHistory = new UserPasswordHistory();
let passwordHistory = await UserPasswordHistory._Repo.create(
{
HistoryId: userPasswordHistory.createId(),
UserId: UserId,
PasswordHash: PasswordHash,
},
{
transaction: dbTransaction,
},
);
// Part 3: When inserted successfully, retrieve all the password history for the user to check
// how many previous password records. If records more than the passwordHistory count from
// config. Remove the oldest record.
if (passwordHistory) {
let passwordHistoryList = await UserPasswordHistory._Repo.findAll({
where: { UserId: UserId },
order: [['CreatedAt', 'DESC']],
transaction: dbTransaction,
});
if (passwordHistoryList.length > passwordHistoryPolicy) {
let deleteList = passwordHistoryList.slice(passwordHistoryPolicy);
let historyIdList = deleteList.map((record) => record.HistoryId);
await UserPasswordHistory._Repo.destroyMultiple(
historyIdList,
dbTransaction,
);
}
}
} catch (error) {
throw error;
}
}
}