@tomei/sso
Version:
Tomei SSO Package
765 lines (725 loc) • 26.9 kB
text/typescript
import { ClassError, ObjectBase } from '@tomei/general';
import { UserGroupRepository } from './user-group.repository';
import { IUserGroupAttr } from '../../interfaces/user-group.interface';
import { LoginUser, User } from '../../components/login-user';
import { Group } from '../../components/group';
import { ApplicationConfig } from '@tomei/config';
import { ActionEnum, Activity } from '@tomei/activity-history';
import GroupSystemAccessModel from '../../models/group-system-access.entity';
import GroupModel from '../../models/group.entity';
import SystemModel from '../../models/system.entity';
import UserModel from '../../models/user.entity';
import { Transaction } from 'sequelize';
export class UserGroup extends ObjectBase {
ObjectType = 'UserGroup';
TableName = 'sso_UserGroup';
ObjectName: string;
ObjectId: string;
UserGroupId: number;
UserId: number;
GroupCode: string;
InheritGroupPrivilegeYN = 'Y';
InheritGroupSystemAccessYN = 'Y';
Status: string;
private _CreatedAt: Date;
private _UpdatedAt: Date;
private _CreatedById: number;
private _UpdatedById: number;
protected static _Repository = new UserGroupRepository();
get CreatedAt() {
return this._CreatedAt;
}
get UpdatedAt() {
return this._UpdatedAt;
}
get CreatedById() {
return this._CreatedById;
}
get UpdatedById() {
return this._UpdatedById;
}
private constructor(userGroupAttr?: IUserGroupAttr) {
super();
if (userGroupAttr) {
this.UserGroupId = userGroupAttr.UserGroupId;
this.UserId = userGroupAttr.UserId;
this.GroupCode = userGroupAttr.GroupCode;
this.Status = userGroupAttr.Status;
this.InheritGroupPrivilegeYN = userGroupAttr.InheritGroupPrivilegeYN;
this.InheritGroupSystemAccessYN =
userGroupAttr.InheritGroupSystemAccessYN;
this._CreatedById = userGroupAttr.CreatedById;
this._CreatedAt = userGroupAttr.CreatedAt;
this._UpdatedById = userGroupAttr.UpdatedById;
this._UpdatedAt = userGroupAttr.UpdatedAt;
}
}
static async init(dbTransaction: any, UserGroupId?: number) {
try {
const userGroup = new UserGroup();
if (UserGroupId) {
const userGroupAttr = await this._Repository.findOne({
where: { UserGroupId },
transaction: dbTransaction,
});
if (userGroupAttr) {
userGroup.UserGroupId = userGroupAttr.UserGroupId;
userGroup.UserId = userGroupAttr.UserId;
userGroup.GroupCode = userGroupAttr.GroupCode;
userGroup.Status = userGroupAttr.Status;
userGroup.InheritGroupPrivilegeYN =
userGroupAttr.InheritGroupPrivilegeYN;
userGroup.InheritGroupSystemAccessYN =
userGroupAttr.InheritGroupSystemAccessYN;
userGroup._CreatedById = userGroupAttr.CreatedById;
userGroup._CreatedAt = userGroupAttr.CreatedAt;
userGroup._UpdatedById = userGroupAttr.UpdatedById;
userGroup._UpdatedAt = userGroupAttr.UpdatedAt;
} else {
throw new ClassError(
'UserGroup',
'UserGroupErrMsg00',
'UserGroup Not Found',
);
}
}
return userGroup;
} catch (error) {
throw error;
}
}
async create(
loginUser: LoginUser,
dbTransaction: any,
group: Group,
user: User,
) {
//This method will create a user group record.
try {
// Part 1: Privilege Checking
// Call loginUser.checkPrivileges() by passing:
// SystemCode: "<get_from_app_config>"
// PrivilegeCode: "USER_GROUP_CREATE"
const systemCode =
ApplicationConfig.getComponentConfigValue('system-code');
const isPrivileged = await loginUser.checkPrivileges(
systemCode,
'USER_GROUP_CREATE',
);
// If user does not have privilege to update user, throw a ClassError
if (!isPrivileged) {
throw new ClassError(
'UserGroup',
'UserGroupErrMsg0X',
'User does not have privilege to create user group.',
);
}
// Part 2: Validation
// Make sure group.GroupCode exists, if not throw new ClassError by passing:
// Classname: "UserGroup"
// MethodName: "create"
// MessageCode: "UserGroupErrMsg02"
// Message: "GroupCode is required."
if (!group.GroupCode) {
throw new ClassError(
'UserGroup',
'UserGroupErrMsg02',
'GroupCode is required.',
);
}
// Make sure user.UserId exists, if not throw new ClassError by passing:
// Classname: "UserGroup"
// MethodName: "create"
// MessageCode: "UserGroupErrMsg03"
// Message: "UserId is required."
if (!user.UserId) {
throw new ClassError(
'UserGroup',
'UserGroupErrMsg03',
'UserId is required.',
);
}
// Call UserGroup.findOne static method by passing:
// loginUser
// dbTransaction
// GroupCode: group.GroupCode
// UserId: user.UserId
const userGroup = await UserGroup.findOne(
dbTransaction,
loginUser,
group.GroupCode,
user.UserId,
);
if (userGroup) {
return userGroup;
}
// Part 3: Create
// Set below attributes:
// UserGroupId: this.createId()
// UserId: Params.user.UserId
// GroupCode: Params.group.GroupCode
// Status: "Active"
// CreatedById: loginUser.ObjectId
// CreatedAt: current timestamp
// UpdatedById: loginUser.ObjectId
// UpdatedAt: current timestamp
this.UserId = user.UserId;
this.GroupCode = group.GroupCode;
this.Status = 'Active';
this._CreatedById = loginUser.UserId;
this._CreatedAt = new Date();
this._UpdatedById = loginUser.UserId;
this._UpdatedAt = new Date();
// Call UserGroup._Repo create() method by passing:
// populate this instance attributes
// dbTransaction
const userData = await UserGroup._Repository.create(
{
UserId: this.UserId,
GroupCode: this.GroupCode,
Status: this.Status,
CreatedById: this._CreatedById,
CreatedAt: this._CreatedAt,
UpdatedById: this._UpdatedById,
UpdatedAt: this._UpdatedAt,
InheritGroupPrivilegeYN: this.InheritGroupPrivilegeYN,
InheritGroupSystemAccessYN: this.InheritGroupSystemAccessYN,
},
{
transaction: dbTransaction,
},
);
this.UserGroupId = userData.UserGroupId;
// Part 4: Record Create UserGroup Activity
// Initialise EntityValueAfter variable and set to this instance
const EntityValueAfter = {
UserGroupId: this.UserGroupId,
UserId: this.UserId,
GroupCode: this.GroupCode,
Status: this.Status,
CreatedById: this._CreatedById,
CreatedAt: this._CreatedAt,
UpdatedById: this._UpdatedById,
UpdatedAt: this._UpdatedAt,
InheritGroupPrivilegeYN: this.InheritGroupPrivilegeYN,
InheritGroupSystemAccessYN: this.InheritGroupSystemAccessYN,
};
// Instantiate new activity from Activity class, call createId() method, then set:
// Action: ActionEnum.Create
// Description: Assign user to group.
// EntityType: "UserGroup"
// EntityId: this.UserGroupId
// EntityValueBefore: <stringify of empty object>
// EntityValueAfter: EntityValueAfter
const activity = new Activity();
activity.ActivityId = activity.createId();
activity.Action = ActionEnum.CREATE;
activity.Description = 'Assign user to group.';
activity.EntityType = 'UserGroup';
activity.EntityId = this.UserGroupId.toString();
activity.EntityValueBefore = JSON.stringify({});
activity.EntityValueAfter = JSON.stringify(EntityValueAfter);
// Call new activity create method by passing:
// dbTransaction
// userId: loginUser.ObjectId
// return this instance
await activity.create(loginUser.ObjectId, dbTransaction);
return this;
} catch (error) {
throw error;
}
}
public static async findOne(
dbTransaction: any,
loginUser: LoginUser,
GroupCode: string,
UserId: number,
): Promise<UserGroup> {
try {
// Part 1: Privilege Checking
// Call loginUser.checkPrivileges() by passing:
// SystemCode: "<get_from_app_config>"
// PrivilegeCode: "USER_GROUP_VIEW"
const systemCode =
ApplicationConfig.getComponentConfigValue('system-code');
const isPrivileged = await loginUser.checkPrivileges(
systemCode,
'USER_GROUP_VIEW',
);
// If user does not have privilege to view user group, throw a ClassError
if (!isPrivileged) {
throw new ClassError(
'UserGroup',
'UserGroupErrMsg0X',
'User does not have privilege to view user group.',
);
}
// Part 2: Retrieve Record
// Call UserGroup._Repo findOne method by passing:
// where:
// [Op.AND]:
// UserId: Params.UserId
// GroupCode: Params.GroupCode
// dbTransaction
const userGroupAttr = await UserGroup._Repository.findOne({
where: {
UserId,
GroupCode,
},
transaction: dbTransaction,
});
// If record exists, instantiate UserGroup by calling the private constructor and passing the attributes. Then, returns the instance
if (userGroupAttr) {
return new UserGroup(userGroupAttr.get({ plain: true }));
}
// If record not exists, return null.
return null;
} catch (error) {
throw error;
}
}
public static async getUser(
dbTransaction: any,
loginUser: LoginUser,
GroupCode: string,
) {
try {
// Part 1: Privilege Checking
// Call loginUser.checkPrivileges() by passing:
// SystemCode: "<get_from_app_config>"
// PrivilegeCode: "USER_GROUP_VIEW"
const systemCode =
ApplicationConfig.getComponentConfigValue('system-code');
const isPrivileged = await loginUser.checkPrivileges(
systemCode,
'USER_GROUP_VIEW',
);
// If user does not have privilege to view user group, throw a ClassError
if (!isPrivileged) {
throw new ClassError(
'UserGroup',
'UserGroupErrMsg0X',
'User does not have privilege to view user group.',
);
}
// Part 2: Retrieve Record
// Call UserGroup._Repo findAll method by passing:
// where:
// GroupCode: Params.GroupCode
// dbTransaction
const userGroup = await UserGroup._Repository.findAll({
where: {
GroupCode,
},
include: [
{
model: UserModel,
as: 'User',
attributes: ['UserId', 'FullName', 'Email'],
},
],
transaction: dbTransaction,
});
// If record exists, instantiate UserGroup by calling the private constructor and passing the attributes. Then, returns the instance
return userGroup;
// If record not exists, return null.
return null;
} catch (error) {
throw error;
}
}
static async findAllInheritedSystemAccesses(
UserId: number,
loginUser: User,
dbTransaction: any,
): Promise<
{
UserGroupId: number;
GroupCode: string;
GroupName: string;
InheritGroupSystemAccessYN: string;
CreatedAt: Date;
UpdatedAt: Date;
Systems: {
SystemCode: string;
SystemName: string;
AccessStatus: string;
CreatedAt: Date;
UpdatedAt: Date;
}[];
}[]
> {
try {
// Part 1: Privilege Checking
// Call loginUser.checkPrivileges() to ensure the user has permission to retrieve system access information.
// SystemCode: Retrieve from app config.
// PrivilegeCode: 'USER_SYSTEM_ACCESS_LIST'.
// If the privilege check fails, throw an error with a 403 Forbidden status.
const systemCode =
ApplicationConfig.getComponentConfigValue('system-code');
const isPrivileged = await loginUser.checkPrivileges(
systemCode,
'USER_SYSTEM_ACCESS_LIST',
);
if (!isPrivileged) {
throw new ClassError(
'UserGroup',
'UserGroupErrMsg0X',
'User does not have privilege to view user system access.',
'findAllInheritedSystemAccesses',
403,
);
}
// Part 2: Retrieve User Groups
// Query the sso_UserGroup table to find all active groups the user belongs to.
// Join with the sso_Group table to retrieve the GroupCode, GroupName, and InheritGroupSystemAccessYNfields.
// Ensure that the value of InheritGroupSystemAccessYN is explicitly 'Y' or 'N' for each group.
// If InheritGroupSystemAccessYN is not set, default it to 'N'.
// Return only active groups (based on Status field).
// The query should return the following fields for each group:
// GroupCode
// GroupName
// InheritGroupSystemAccessYN
const userGroups = await UserGroup._Repository.findAll({
where: {
UserId,
Status: 'Active',
},
include: [
{
model: GroupModel,
required: true,
where: {
Status: 'Active',
},
include: [
{
model: GroupSystemAccessModel,
where: {
Status: 'Active',
},
include: [
{
model: SystemModel,
},
],
},
],
},
],
transaction: dbTransaction,
});
const result: {
UserGroupId: number;
GroupCode: string;
GroupName: string;
InheritGroupSystemAccessYN: string;
CreatedAt: Date;
UpdatedAt: Date;
Systems: {
SystemCode: string;
SystemName: string;
AccessStatus: string;
CreatedAt: Date;
UpdatedAt: Date;
}[];
}[] = [];
for (const userGroup of userGroups) {
// Part 3: Retrieve System Access for Groups with Inheritance
// For each group where InheritGroupSystemAccessYN = 'Y', query the sso_GroupSystemAccess table to retrieve system access details.
// Join with the sso_System table to fetch system details (SystemName, SystemCode).
// Ensure only active system accesses (AccessStatus = 'Active') are included.
// For each system access, retrieve the following fields:
// SystemName (from sso_System.Name)
// SystemCode (from sso_System.SystemCode)
// AccessStatus (from sso_GroupSystemAccess.Status)
// CreatedAt (from sso_GroupSystemAccess.CreatedAt)
// UpdatedAt (from sso_GroupSystemAccess.UpdatedAt)
// Part 4: Handling Non-Inherited Groups
// For groups where InheritGroupSystemAccessYN = 'N', return the group details without system access records.
// Set the Systems field to an empty array or null to indicate no inherited access for those groups.
// Part 5: Grouping Results
// Group the results by GroupCode and GroupName.
// For each group, create an object with the following structure:
// GroupCode: Code of the group.
// GroupName: Name of the group.
// InheritGroupSystemAccessYN: 'Y' or 'N', indicating whether the user inherits system access from the group.
// Systems: An array of system access objects (for groups where InheritGroupSystemAccessYN = 'Y'), each including:
// SystemName
// SystemCode
// AccessStatus
// CreatedAt
// UpdatedAt
// For groups where InheritGroupSystemAccessYN = 'N', Systems will be an empty array.
const groupData = {
UserGroupId: userGroup.UserGroupId,
GroupCode: userGroup.GroupCode,
GroupName: userGroup.Group.Name,
InheritGroupSystemAccessYN: userGroup.InheritGroupSystemAccessYN,
CreatedAt: userGroup.CreatedAt,
UpdatedAt: userGroup.UpdatedAt,
Systems: [],
};
if (userGroup.InheritGroupSystemAccessYN === 'Y') {
groupData.Systems = userGroup.Group.GroupSystemAccesses.map(
(groupSystemAccess) => {
return {
SystemCode: groupSystemAccess.System.SystemCode,
SystemName: groupSystemAccess.System.Name,
AccessStatus: groupSystemAccess.Status,
CreatedAt: groupSystemAccess.CreatedAt,
UpdatedAt: groupSystemAccess.UpdatedAt,
};
},
);
}
result.push(groupData);
}
// Part 6: Return Grouped Data
// Return the array of grouped system accesses for the user's groups, including both inherited ('Y') and non-inherited ('N') system accesses.
return result;
} catch (error) {
throw error;
}
}
public async update(
loginUser: LoginUser,
dbTransaction: Transaction,
UpdatedProperties: {
InheritGroupPrivilegeYN?: string;
InheritGroupSystemAccessYN?: string;
},
): Promise<UserGroup> {
try {
// Part 1: Privilege Checking
// Call loginUser.checkPrivileges() to ensure the user has permission to retrieve system access information.
// SystemCode: Retrieve from app config.
// PrivilegeCode: 'USER_GROUP_UPDATE'.
const systemCode =
ApplicationConfig.getComponentConfigValue('system-code');
const isPrivileged = await loginUser.checkPrivileges(
systemCode,
'USER_GROUP_UPDATE',
);
// If the privilege check fails, throw an error with a 403 Forbidden status.
if (!isPrivileged) {
throw new ClassError(
'UserGroup',
'UserGroupErrMsg0X',
'User does not have privilege to update user group.',
'update',
403,
);
}
// Part 2: Validation
// Check to make sure that at least one of the UpdatedProperties is exist if not throw error.
if (
!UpdatedProperties.InheritGroupPrivilegeYN &&
!UpdatedProperties.InheritGroupSystemAccessYN
) {
throw new ClassError(
'UserGroup',
'UserGroupErrMsg04',
'At least one of the properties to update is required.',
'update',
400,
);
}
// Part 3: Update User Group
// Call the UserGroup._Repo.update() method to perform the update operation, passing:
// InheritGroupPrivilegeYN (if exist): updatedProperties.InheritGroupPrivilegeYN
// InheritGroupSystemAccessYN (if exist): updatedProperties.InheritGroupSystemAccessYN
// UpdatedById: loginUser.UserId (to indicate who updated the record).
// UpdatedAt: Set to the current date and time.
// dbTransaction: The database transaction instance.
const entityValueBefore = {
UserGroupId: this.UserGroupId,
UserId: this.UserId,
GroupCode: this.GroupCode,
Status: this.Status,
CreatedById: this._CreatedById,
CreatedAt: this._CreatedAt,
UpdatedById: this._UpdatedById,
UpdatedAt: this._UpdatedAt,
InheritGroupPrivilegeYN: this.InheritGroupPrivilegeYN,
InheritGroupSystemAccessYN: this.InheritGroupSystemAccessYN,
};
this._UpdatedById = loginUser.UserId;
this._UpdatedAt = new Date();
if (UpdatedProperties.InheritGroupPrivilegeYN) {
this.InheritGroupPrivilegeYN =
UpdatedProperties.InheritGroupPrivilegeYN;
}
if (UpdatedProperties.InheritGroupSystemAccessYN) {
this.InheritGroupSystemAccessYN =
UpdatedProperties.InheritGroupSystemAccessYN;
}
await UserGroup._Repository.update(
{
InheritGroupPrivilegeYN: this.InheritGroupPrivilegeYN,
InheritGroupSystemAccessYN: this.InheritGroupSystemAccessYN,
UpdatedById: this._UpdatedById,
UpdatedAt: this._UpdatedAt,
},
{
where: {
UserGroupId: this.UserGroupId,
},
transaction: dbTransaction,
},
);
// Part 2: Record Activity History
// Initialize a variable entityValueBefore to store the current state of the record before the update.
const entityValueAfter = {
UserGroupId: this.UserGroupId,
UserId: this.UserId,
GroupCode: this.GroupCode,
Status: this.Status,
CreatedById: this._CreatedById,
CreatedAt: this._CreatedAt,
UpdatedById: this._UpdatedById,
UpdatedAt: this._UpdatedAt,
InheritGroupPrivilegeYN: this.InheritGroupPrivilegeYN,
InheritGroupSystemAccessYN: this.InheritGroupSystemAccessYN,
};
// Create an instance of the Activity class and set the following properties:
// ActivityId: Call activity.createId().
// Action: Set to ActionEnum.Update.
// Description: Set to Update User Group.
// EntityType: Set to UserGroup.
// EntityId: Use the ID of the updated record.
// EntityValueBefore: Stringify entityValueBefore to capture the state before the update.
// EntityValueAfter: Stringify the updated record to capture the new state after the update.
const activity = new Activity();
activity.ActivityId = activity.createId();
activity.Action = ActionEnum.UPDATE;
activity.Description = 'Update User Group';
activity.EntityType = 'UserGroup';
activity.EntityId = this.UserGroupId.toString();
activity.EntityValueBefore = JSON.stringify(entityValueBefore);
activity.EntityValueAfter = JSON.stringify(entityValueAfter);
// Call the activity create a method with the following parameters:
// dbTransaction
// userId: loginUser.UserId
// Part 3: Return Updated Record
await activity.create(loginUser.ObjectId, dbTransaction);
// Retrieve the updated user group record from the database or return the updated instance as needed.
return this;
} catch (error) {
throw error;
}
}
public static async isUserMemberOfGroup(
dbTransaction: any,
loginUser: LoginUser,
UserId: number,
GroupCode: string,
): Promise<boolean> {
try {
// Part 1: Privilege Checking
// Call loginUser.checkPrivileges() to ensure the user has permission to retrieve system access information.
// SystemCode: Retrieve from app config.
// PrivilegeCode: 'USER_GROUP_VIEW'.
const systemCode =
ApplicationConfig.getComponentConfigValue('system-code');
const isPrivileged = await loginUser.checkPrivileges(
systemCode,
'USER_GROUP_VIEW',
);
// If the privilege check fails, throw an error with a 403 Forbidden status.
if (!isPrivileged) {
throw new ClassError(
'UserGroup',
'UserGroupErrMsg0X',
'User does not have privilege to view user group.',
'isUserMemberOfGroup',
403,
);
}
// Part 2: Retrieve User Group
// Query the sso_UserGroup table to find the user group record with the given UserId and GroupCode.
// If the record exists, return true; otherwise, return false.
const userGroup = await UserGroup.findOne(
dbTransaction,
loginUser,
GroupCode,
UserId,
);
return !!userGroup;
} catch (error) {
throw error;
}
}
public async delete(
loginUser: LoginUser,
dbTransaction: Transaction,
): Promise<void> {
try {
// Part 1: Privilege Checking
// Call loginUser.checkPrivileges() to ensure the user has permission to delete user group records.
// SystemCode: Retrieve from app config.
// PrivilegeCode: 'USER_GROUP_DELETE'.
const systemCode =
ApplicationConfig.getComponentConfigValue('system-code');
const isPrivileged = await loginUser.checkPrivileges(
systemCode,
'USER_GROUP_DELETE',
);
// If the privilege check fails, throw an error with a 403 Forbidden status.
if (!isPrivileged) {
throw new ClassError(
'UserGroup',
'UserGroupErrMsg0X',
'User does not have privilege to delete user group.',
'delete',
403,
);
}
// Part 2: Delete User Group
// Call the UserGroup._Repo.destroy() method to delete the user group record with the given UserGroupId.
// Pass the dbTransaction parameter to ensure the operation is part of the current transaction.
await UserGroup._Repository.delete({
where: {
UserGroupId: this.UserGroupId,
},
transaction: dbTransaction,
});
// Part 3: Record Activity History
// Initialize a variable entityValueBefore to store the current state of the record before the update.
const entityValueBefore = {
UserGroupId: this.UserGroupId,
UserId: this.UserId,
GroupCode: this.GroupCode,
Status: this.Status,
CreatedById: this._CreatedById,
CreatedAt: this._CreatedAt,
UpdatedById: this._UpdatedById,
UpdatedAt: this._UpdatedAt,
InheritGroupPrivilegeYN: this.InheritGroupPrivilegeYN,
InheritGroupSystemAccessYN: this.InheritGroupSystemAccessYN,
};
// Create an instance of the Activity class and set the following properties:
// ActivityId: Call activity.createId().
// Action: Set to ActionEnum.Delete.
// Description: Set to Delete User Group.
// EntityType: Set to UserGroup.
// EntityId: Use the ID of the deleted record.
// EntityValueBefore: Stringify entityValueBefore to capture the state before the delete.
// EntityValueAfter: Set to an empty string to indicate the record has been deleted.
const activity = new Activity();
activity.ActivityId = activity.createId();
activity.Action = ActionEnum.DELETE;
activity.Description = `Delete User Group ${this.UserGroupId}`;
activity.EntityType = 'UserGroup';
activity.EntityId = this.UserGroupId.toString();
activity.EntityValueBefore = JSON.stringify(entityValueBefore);
activity.EntityValueAfter = JSON.stringify({});
// Call the activity create method with the following parameters:
// dbTransaction
// userId: loginUser.UserId
await activity.create(loginUser.ObjectId, dbTransaction);
} catch (error) {
throw error;
}
}
}