Files
worldshine-redesign/app/controllers/auth.controller.js
Lixian Zhou c779f9383e
All checks were successful
Build And Deploy Main / build-and-deploy (push) Successful in 35s
fix
2026-03-17 10:32:20 -04:00

298 lines
10 KiB
JavaScript

const db = require("../models");
const Employee = db.employee;
const ExtUserPermission = db.ext_usr_perm;
const config = require("../config/auth.config");
const axios = require("axios");
const https = require("https");
var jwt = require("jsonwebtoken");
var bcrypt = require("bcryptjs");
const { splitSite } = require("../middlewares");
const HR_AUTH_LOGIN_ENDPOINT = "https://ws-hr.mayosolution.com/api/auth/login";
const SYSTEM_ACCESS_PERMISSION = "System Access";
const HR_INSECURE_HTTPS_AGENT = new https.Agent({ rejectUnauthorized: false });
const ALL_PERMISSIONS = [
'Dashboard',
'Admin View',
'View_Info Screen',
'Edit_Info Screen',
'View_Customer Info _Personal Info',
'View_Customer Info _Care & Services',
'View_Customer Info _Medical & Insurance',
'View_Customer Info _Confidential Details',
'View_Customer Info _Form Submission',
'Edit_Customer Info _ Personal Info',
'Edit_Customer Info _ Care & Services',
'Edit_Customer Info _ Medical & Insurance',
'Edit_Customer Info _ Confidential Details',
'Edit_Customer Info _ Form Submission',
'Discharge_Customer',
'Reactivate_Customer',
'Create_Customer',
'Export_Customer Report',
'View _Calendar _Medical Appointment',
'View _Calendar _Activities',
'View _Calendar _Attendance Notes',
'View _Calendar _Meal Plan',
'View _Calendar _Important Dates',
'Edit&Create _Calendar _Medical Appointment',
'Edit&Create _Calendar _Activities',
'Edit&Create _Calendar _Attendance Notes',
'Edit&Create _Calendar _Meal Plan',
'Edit&Create _Calendar _Important Dates',
'View_Messaging',
'Sent_Messaging',
'View_Messaging Template',
'Create&Edit_Messaging Template',
'View_Vehicle info_Basic Info',
'View_Vehicle info_Documents',
'View_Vehicle info_Repair Records',
'Edit_Vehicle info_Basic Info',
'Edit_Vehicle info_Documents',
'Edit_Vehicle info_Repair Records',
'Add_New Vehicle',
'Archive_Vehicle',
'Export_Vehicle Report',
'View_Transportation Schedule_Route Overview',
'Create&Edit_Transportation Schedule',
'Export_Transportation Schedule Report',
'View_Route Template',
'Create&Edit_Route Template',
'View_Driver Assignment for Appointment',
'Edit_Driver Assignment for Appointment',
'isDriver',
'View_Provider Info',
'Create & Edit _Provider Info',
'View_Appointment Request',
'Edit & Create_Appointment Request',
'View_Appointment Calendar',
'Edit & Create_Appointment Calendar',
'Medical Template',
'View_Meal Status',
'Edit_Meal Status',
'View_Seating Chart',
'Edit_Seating Chart',
'Employee page',
'Set Permission for Employee'
];
const getEffectivePermissions = (employeeDoc) => {
const username = `${employeeDoc?.username || ''}`.trim().toLowerCase();
const roles = Array.isArray(employeeDoc?.roles) ? employeeDoc.roles : [];
const permissions = Array.isArray(employeeDoc?.permissions) ? employeeDoc.permissions : [];
// Keep hardcoded full permission override.
if (username === 'testadmin03') return ALL_PERMISSIONS;
// Backward-compatible fallback for old admin records with no permissions assigned yet.
if (permissions.length === 0 && roles.includes('admin')) return ALL_PERMISSIONS;
return permissions;
};
const isEmployeeActive = (employeeDoc) => {
const rawStatus = employeeDoc?.status;
// Backward compatibility: older records may not have status set.
if (rawStatus === undefined || rawStatus === null || `${rawStatus}`.trim() === "") {
return true;
}
return `${rawStatus}`.trim().toLowerCase() === "active";
};
const parseExternalTokenId = (value = "") => {
const token = `${value || ""}`;
if (!token.startsWith("ext:")) return null;
const chunks = token.split(":");
if (chunks.length < 3) return null;
return {
externalUserId: chunks[1],
site: Number(chunks[2]) || null
};
};
const getExternalPermissionBySite = async (externalUserId, site) => {
if (!externalUserId || !site) return null;
return ExtUserPermission.findOne({
external_user_id: `${externalUserId}`,
allow_site: Number(site)
}).sort({ updatedAt: -1 });
};
const buildExternalAuthResponse = async (externalUser, site) => {
const permissionDoc = await getExternalPermissionBySite(externalUser?.id, site);
const permissions = Array.isArray(permissionDoc?.permissions) ? permissionDoc.permissions : [];
const title = permissionDoc?.title || "";
const tokenPayload = {
id: `ext:${externalUser?.id}:${site}`,
authSource: "external",
externalUserId: externalUser?.id,
site: Number(site),
username: externalUser?.username || "",
email: externalUser?.email || "",
name: externalUser?.name || "",
firstname: externalUser?.firstname || "",
lastname: externalUser?.lastname || "",
status: externalUser?.status || "",
title
};
const accessToken = jwt.sign(tokenPayload, config.secret, {
expiresIn: 86400
});
return {
accessToken,
username: externalUser?.username || "",
email: externalUser?.email || "",
roles: [],
permissions,
id: externalUser?.id,
name: externalUser?.name || "",
firstname: externalUser?.firstname || "",
lastname: externalUser?.lastname || "",
title,
site: Number(site),
status: externalUser?.status || "",
auth_source: "external"
};
};
// Create and Save a new User
exports.login = async (req, res) => {
const emailUsername = req.body.emailUsername;
const password = req.body.password;
const requestedSite = Number(req.body.site);
const site = Number.isInteger(requestedSite) && requestedSite > 0 ? requestedSite : splitSite.findSiteNumber(req);
if (!emailUsername) {
return res.status(400).send({ message: "email or username is required" });
}
try {
let condition = {
$or: [{ email: emailUsername }, { username: emailUsername }]
};
condition = splitSite.splitSiteGet(req, condition);
const localEmployees = await Employee.find(condition);
if (localEmployees && localEmployees.length > 0) {
const localEmployee = localEmployees[0];
const activeEmployee = isEmployeeActive(localEmployee);
const isPasswordCorrect = bcrypt.compareSync(password, localEmployee.password || "");
if (localEmployees.length === 1 && isPasswordCorrect && activeEmployee) {
const token = jwt.sign({ id: localEmployee.id }, config.secret, {
expiresIn: 86400 // 24 hours
});
return res.send({
accessToken: token,
username: localEmployee.username,
email: localEmployee.email,
roles: localEmployee.roles,
permissions: getEffectivePermissions(localEmployee),
id: localEmployee.id,
name: localEmployee.name,
name_cn: localEmployee.name_cn,
title: localEmployee.title || ""
});
}
if (!activeEmployee && isPasswordCorrect) {
throw Error("User is not activated");
}
// Local account exists but credential doesn't match. Continue to external auth fallback.
}
try {
const externalAuthResponse = await axios.post(
HR_AUTH_LOGIN_ENDPOINT,
{
emailUsername,
password,
site
},
{
timeout: 15000,
httpsAgent: HR_INSECURE_HTTPS_AGENT
}
);
const externalUser = externalAuthResponse?.data;
const isExternalUserValid =
externalUser &&
externalUser.id &&
`${externalUser?.status || ""}`.trim().toLowerCase() === "active";
if (isExternalUserValid) {
const loginPayload = await buildExternalAuthResponse(externalUser, site);
return res.send(loginPayload);
}
} catch (_externalError) {
// Fall through to existing invalid-login response.
}
throw Error("Email or Password Is Invalid");
} catch (err) {
return res.status(500).send({
message: err.message || "Email Or Password Invalid"
});
}
};
exports.me = (req, res) => {
const authPayload = req.authPayload || {};
const parsedExternalToken = parseExternalTokenId(req.userId);
const isExternalAuth =
authPayload?.authSource === "external" || !!parsedExternalToken?.externalUserId;
if (isExternalAuth) {
const site = Number(authPayload?.site) || parsedExternalToken?.site || splitSite.findSiteNumber(req);
const externalUserId = authPayload?.externalUserId || parsedExternalToken?.externalUserId;
return getExternalPermissionBySite(externalUserId, site)
.then((permissionDoc) => {
let permissions = Array.isArray(permissionDoc?.permissions) ? permissionDoc.permissions : [];
if (permissions.includes(SYSTEM_ACCESS_PERMISSION) === false) {
// Keep login valid; UI permission gate will lock down non-system users.
permissions = permissions.filter(Boolean);
}
return res.send({
username: authPayload?.username || "",
email: authPayload?.email || "",
roles: [],
permissions,
id: externalUserId,
name: authPayload?.name || "",
firstname: authPayload?.firstname || "",
lastname: authPayload?.lastname || "",
title: permissionDoc?.title || authPayload?.title || "",
site,
status: authPayload?.status || "",
auth_source: "external"
});
})
.catch((err) => {
return res.status(500).send({
message: err.message || "Failed to fetch current user"
});
});
}
const condition = splitSite.splitSiteGet(req, { _id: req.userId });
Employee.findOne(condition)
.then((employee) => {
if (!employee) {
return res.status(404).send({ message: "User not found" });
}
if (!isEmployeeActive(employee)) {
return res.status(403).send({ message: "User is not activated" });
}
return res.send({
username: employee.username,
email: employee.email,
roles: employee.roles,
permissions: getEffectivePermissions(employee),
id: employee.id,
name: employee.name,
name_cn: employee.name_cn,
title: employee.title || ""
});
})
.catch((err) => {
res.status(500).send({
message: err.message || "Failed to fetch current user"
});
});
};