Files
sap-erp/middleware/auth.js
T
John 69b4e68baf
SAP-ERP Portal CI/CD / build (push) Failing after 5m20s
first commit
2026-09-23 17:31:02 +05:30

106 lines
4.7 KiB
JavaScript

// backend/middleware/auth.js
const jwt = require('jsonwebtoken');
const SECRET = process.env.JWT_SECRET || 'sap-portal-secret';
function verifyToken(req, res, next) {
const auth = req.headers.authorization || '';
const token = auth.startsWith('Bearer ') ? auth.slice(7) : auth;
if (!token) return res.status(401).json({ success: false, message: 'No token provided' });
try {
req.user = jwt.verify(token, SECRET);
next();
} catch {
res.status(401).json({ success: false, message: 'Invalid or expired token' });
}
}
// Only sap_adder can approve → push to SAP B1
function verifyAdmin(req, res, next) {
if (req.user?.role === 'admin' || req.user?.role === 'sap_adder')
return next();
return res.status(403).json({ success: false, message: 'SAP Adder or Admin role required' });
}
// User-administration gate: full Admin, SAP Adder (historically also manages
// users) and the System Admin role (user management ONLY — deliberately NOT part
// of verifyAdmin, so sub-admins can't touch system settings / approvals).
function verifyUserAdmin(req, res, next) {
if (['admin', 'sap_adder', 'system_admin'].includes(req.user?.role))
return next();
return res.status(403).json({ success: false, message: 'Admin, SAP Adder or System Admin role required' });
}
// Every step assignment grants a set of perms. Legacy entries are plain
// strings ("work_order:prepared_qa") from before per-step perms existed —
// normalize them to the full perm set so nobody's access silently shrinks.
// 'approve' is distinct from 'edit': performing the sign-off action at a
// stage (e.g. "did QC check it, yes/no") is NOT the same capability as
// editing the record's own fields (product/batch/materials/etc.) — someone
// can be trusted to check a stage off without being able to alter the data.
const ALL_PERMS = ['view', 'add', 'edit', 'approve', 'delete'];
function normalizeSteps(raw) {
const arr = Array.isArray(raw) ? raw : [];
return arr.map(s => {
if (typeof s === 'string') return { step: s, perms: ALL_PERMS.slice() };
if (s && typeof s === 'object' && s.step) return { step: s.step, perms: Array.isArray(s.perms) ? s.perms : ALL_PERMS.slice() };
return null;
}).filter(Boolean);
}
// Does this user hold `perm` on the exact step `fullKey` ("workflow:key")?
function hasStepPerm(user, fullKey, perm) {
if (user?.role === 'admin') return true;
const steps = normalizeSteps(user?.approvalSteps);
const entry = steps.find(s => s.step === fullKey);
return !!entry && entry.perms.includes(perm);
}
// Does this user hold `perm` on ANY step within `workflow`? Used for
// workflow-wide actions like viewing a list, where the specific stage
// doesn't matter — holding the perm on one step is enough.
function hasWorkflowPerm(user, workflow, perm) {
if (user?.role === 'admin') return true;
const steps = normalizeSteps(user?.approvalSteps);
return steps.some(s => s.step.startsWith(`${workflow}:`) && s.perms.includes(perm));
}
// Generic Approval Steps gate — 'workflow:key' (see services/approvalStepsStore.js).
// Admin always passes. Everyone else needs `perm` (default 'view') on this
// exact step in req.user.approvalSteps (baked into the JWT at login).
function requireApprovalStep(fullKey, perm = 'view') {
return (req, res, next) => {
if (hasStepPerm(req.user, fullKey, perm)) return next();
res.status(403).json({ success: false, message: `You are not assigned "${perm}" on approval step: ${fullKey}` });
};
}
// Same as above but workflow-wide (any step in the workflow grants access) —
// for actions like listing/viewing that aren't tied to one specific stage.
function requireWorkflowPerm(workflow, perm = 'view') {
return (req, res, next) => {
if (hasWorkflowPerm(req.user, workflow, perm)) return next();
res.status(403).json({ success: false, message: `You are not assigned "${perm}" on workflow: ${workflow}` });
};
}
// "Is this step assigned to the user at all?" — true if the user holds the
// step with ANY permission (view/add/edit/approve/delete). Used for actions
// (Issuance, Receipt, Close…) where being assigned the step means the user
// may perform it, regardless of which specific perm boxes were ticked.
function hasStepAssigned(user, fullKey) {
if (user && user.role === 'admin') return true;
return ALL_PERMS.some(p => hasStepPerm(user, fullKey, p));
}
function requireStepAssigned(fullKey) {
return (req, res, next) => {
if (hasStepAssigned(req.user, fullKey)) return next();
res.status(403).json({ success: false, message: `You are not assigned to approval step: ${fullKey}` });
};
}
module.exports = {
verifyToken, verifyAdmin, verifyUserAdmin, requireApprovalStep, requireWorkflowPerm,
requireStepAssigned, hasStepAssigned,
normalizeSteps, hasStepPerm, hasWorkflowPerm, ALL_PERMS,
};