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

141 lines
7.5 KiB
JavaScript

// Render-blocking auth gate — included as the FIRST <head> element on every
// protected page so unauthenticated visitors are redirected before any markup
// paints (prevents pages like /gstr1 from being viewable by direct URL access).
(function(){
try{
// Session-only auth: login is mirrored into a SESSION COOKIE (no
// expires/max-age) at login time — cookies, unlike sessionStorage,
// are shared across every tab of the same browser, so Ctrl+Click /
// "open in new tab" doesn't force a fresh login. A true session cookie
// is still wiped by the browser itself when it fully closes (not just a
// tab), so closing the browser still always ends the session — same
// guarantee as before, just no longer a per-tab-only one.
// Defensive cleanup: remove any leftover localStorage entries from an
// even older version of this scheme, so a lingering copy can't grant access.
try{ localStorage.removeItem('token'); localStorage.removeItem('portal_user'); }catch(e){}
function getCookie(name){
var m=document.cookie.match(new RegExp('(?:^|; )'+name+'=([^;]*)'));
return m?decodeURIComponent(m[1]):null;
}
if(!sessionStorage.getItem('portal_token')){
var cTok=getCookie('portal_token'), cUser=getCookie('portal_user');
if(cTok){
sessionStorage.setItem('portal_token',cTok);
if(cUser) sessionStorage.setItem('portal_user',cUser);
}
}
var tok=sessionStorage.getItem('portal_token');
if(!tok){ location.replace('/'); return; }
// Token PRESENCE alone isn't enough — a token issued at login lives in
// storage indefinitely even after its 12h JWT expiry (nothing clears it
// on its own), so a tab left open/idle past that would otherwise render
// the full page and let the user act right up until an API call 401s.
// Decode the JWT payload (no verification needed client-side, just the
// exp claim) and bounce immediately if it's already expired.
function jwtExpired(t){
try{
var payload=JSON.parse(atob(t.split('.')[1].replace(/-/g,'+').replace(/_/g,'/')));
return !payload.exp || (Date.now()>=payload.exp*1000);
}catch(e){ return true; }
}
function killSession(){
sessionStorage.clear();document.cookie='portal_token=;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT';document.cookie='portal_user=;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT';;
location.replace('/');
}
if(jwtExpired(tok)){ killSession(); return; }
// Re-check periodically so a tab left open through expiry gets logged
// out on its own, instead of only discovering it on the next API call.
setInterval(function(){
var t=sessionStorage.getItem('portal_token');
if(!t||jwtExpired(t)) killSession();
}, 60000);
// Page → required module key. Pages not listed here are accessible to any
// authenticated user (e.g. the dashboard itself).
var PAGE_MODULES={
'/gstr1':'gstr1', '/gstr2':'gstr2', '/itc04':'itc04', '/business-master':'business-master', '/admin':'admin',
'/bom':'bom', '/items':'items',
'/production':'production-create', '/issue-production':'production-issue', '/receipt-production':'production-receipt', '/close-production':'production-close',
'/requirements':'production-requirements', '/batch-issuance':'production-batch-issuance', '/work-order':'production-work-order',
'/purchase-request':'purchase-request', '/purchase-quotation':'purchase-quotation', '/purchase-order':'purchase-order', '/grpo':'purchase-grpo',
'/approvals':'approvals', '/sap-approvals':'sap-approvals',
'/vendor-register':'vendors', '/register':'customers',
'/documents':'documents', '/reports':'reports',
'/project-form':['projects-new','projects-view'], '/project-approvals':'projects-approvals',
'/costing-pl':'finance-monthly', '/costing-comparison':'finance-comparison', '/cost-sheet':'finance-costsheet', '/balance-sheet':'finance-balancesheet', '/cash-flow':'finance-cashflow',
'/salary':'salary',
};
// Backward-compat: a user granted the OLD broad key (e.g. 'production',
// before it was split into per-page sub-modules) still gets every new
// sub-key under it, so nobody silently loses access when this ships.
var LEGACY_BROAD_MODULES={production:'production-',purchase:'purchase-',costing:'finance-',projects:'projects-'};
var path=location.pathname.replace(/\/$/,'')||'/';
var reqRaw=PAGE_MODULES[path];
if(reqRaw){
var required=Array.isArray(reqRaw)?reqRaw:[reqRaw]; // any ONE of these keys is enough
var raw=sessionStorage.getItem('portal_user');
var user=raw?JSON.parse(raw):null;
var role=user&&user.role;
var isAdmin=role==='admin'||role==='sap_adder';
var mods=user&&Array.isArray(user.modules)?user.modules:null;
var allowed=isAdmin; // no modules explicitly granted → no access (was: unchecked = full access)
if(!allowed&&mods){
for(var i=0;i<required.length&&!allowed;i++){
var req=required[i];
if(mods.indexOf(req)>-1){ allowed=true; break; }
for(var broad in LEGACY_BROAD_MODULES){
if(mods.indexOf(broad)>-1&&req.indexOf(LEGACY_BROAD_MODULES[broad])===0){ allowed=true; break; }
}
}
}
// '/production' also opens the Pre-PWO Store Review screen — a pure
// Store reviewer has no reason to hold the broad 'production-create'
// module (that would ALSO grant full Production Order creation), so
// let anyone holding EITHER Pre-PWO approval step in (any perm) reach
// the page even without the module — production.html's own per-tab
// permission checks (CAN_SHARE_PREPWO/CAN_REVIEW_PREPWO) still gate
// what they can actually see/do once there.
if(!allowed&&path==='/production'){
var steps=user&&user.approvalSteps;
var prepwoKeys=['production_order:prepwo_share','production_order:prepwo_review'];
for(var pi=0;pi<prepwoKeys.length&&!allowed;pi++){
var pk=prepwoKeys[pi];
if(Array.isArray(steps)){
for(var si=0;si<steps.length&&!allowed;si++){
var s=steps[si];
if(typeof s==='string'){ if(s===pk)allowed=true; }
else if(s&&s.step===pk&&Array.isArray(s.perms)&&s.perms.length>0){ allowed=true; }
}
}
}
}
if(!allowed){ location.replace('/'); }
}
// '/wo-header-profiles' has no module of its own (it's gated purely by
// the 'work_order:approved_mgr_qa' approval step — the same step that
// already represents "QA Manager" for Work Order approval — so it isn't
// in PAGE_MODULES above). Admin always passes; everyone else needs any
// perm on that step. wo-header-profiles.html's own CAN_EDIT check
// further gates whether they can actually add/edit/delete once there.
if(path==='/wo-header-profiles'){
var raw2=sessionStorage.getItem('portal_user');
var user2=raw2?JSON.parse(raw2):null;
var isAdmin2=user2&&(user2.role==='admin'||user2.role==='sap_adder');
var allowed2=!!isAdmin2;
if(!allowed2){
var steps2=user2&&user2.approvalSteps;
if(Array.isArray(steps2)){
for(var qi=0;qi<steps2.length&&!allowed2;qi++){
var qs=steps2[qi];
if(typeof qs==='string'){ if(qs==='work_order:approved_mgr_qa')allowed2=true; }
else if(qs&&qs.step==='work_order:approved_mgr_qa'&&Array.isArray(qs.perms)&&qs.perms.length>0){ allowed2=true; }
}
}
}
if(!allowed2){ location.replace('/'); }
}
}catch(e){ location.replace('/'); }
})();