{"files": {"main.ts": "import {run} from './core.ts';\nimport {POLICY} from './policy.ts';\nexport function evaluate(payload: any): any { return run(payload, POLICY); }\n", "core.ts": "import {asciiLower} from './helpers.ts';\nfunction result(allowed: boolean, reason: string, matched: number[], policy: any): any { return {allowed: allowed, reason: reason, matched: matched, audit: (!allowed && policy.audit_denials) ? [reason] : []}; }\nfunction grantEffect(g: any): string { if (g == null) return 'allow'; if (typeof g.effect === 'string') return asciiLower(g.effect); if (typeof g.type === 'string') { const t = asciiLower(g.type); if (t === 'allow' || t === 'deny') return t; } if (typeof g.allow === 'boolean') return g.allow ? 'allow' : 'deny'; if (typeof g.allowed === 'boolean') return g.allowed ? 'allow' : 'deny'; if (g.deny === true) return 'deny'; return 'allow'; }\nfunction grantScope(g: any): any { if (g == null) return undefined; if (g.scope !== undefined) return g.scope; if (g.resource !== undefined) return g.resource; if (g.resource_id !== undefined) return g.resource_id; if (g.resourceId !== undefined) return g.resourceId; return undefined; }\nfunction grantExpires(g: any): any { if (g == null) return null; if (g.expires !== undefined) return g.expires; if (g.expires_at !== undefined) return g.expires_at; if (g.expiry !== undefined) return g.expiry; return null; }\nexport function run(payload: any, policy: any): any { const subject: any = (payload && payload.subject) ? payload.subject : {}; const resource: any = (payload && payload.resource) ? payload.resource : {}; const now: any = payload ? payload.now : undefined; const issuedAt: any = payload ? payload.issued_at : undefined; const action: any = payload ? payload.action : undefined; if (subject.tenant !== undefined && resource.tenant !== undefined && subject.tenant !== resource.tenant) return result(false, 'tenant', [], policy); if (typeof now === 'number' && typeof issuedAt === 'number' && typeof policy.token_ttl === 'number' && now - issuedAt > policy.token_ttl) return result(false, 'token', [], policy); if (policy.suspended_denies && subject.suspended) return result(false, 'suspended', [], policy); let isOwner: boolean = false; if (subject.owner === true) isOwner = true; else if (resource.owner !== undefined && subject.id !== undefined && resource.owner === subject.id) isOwner = true; else if (resource.owner_id !== undefined && subject.id !== undefined && resource.owner_id === subject.id) isOwner = true; if (policy.owner_bypass && isOwner) return result(true, 'owner', [], policy); const roles = new Set<string>(); const addRole = (r: any) => { if (typeof r === 'string') roles.add(r); };\n if (Array.isArray(subject.roles)) { for (const r of subject.roles) addRole(r); }\n if (policy.group_union && Array.isArray(subject.groups)) { for (const g of subject.groups) { if (typeof g === 'string') roles.add(g); else if (g && typeof g === 'object') { if (Array.isArray(g.roles)) { for (const r of g.roles) addRole(r); } if (typeof g.name === 'string') roles.add(g.name); else if (typeof g.id === 'string') roles.add(g.id); else if (typeof g.role === 'string') roles.add(g.role); } } }\n if (policy.inherit_roles) { const parents: any = (payload && payload.role_parents) ? payload.role_parents : {}; const visited = new Set<string>(); for (const r of roles) visited.add(r); const queue: string[] = Array.from(roles); let qi = 0; while (qi < queue.length) { const cur = queue[qi++]; const ps: any = parents ? parents[cur] : undefined; if (Array.isArray(ps)) { for (const p of ps) { if (typeof p === 'string' && !visited.has(p)) { visited.add(p); roles.add(p); queue.push(p); } } } } }\n const grants: any[] = (payload && Array.isArray(payload.grants)) ? payload.grants : []; const matched: number[] = []; const effects: string[] = []; const resourceId: any = resource.id; for (let i = 0; i < grants.length; i++) { const g: any = grants[i]; if (!g || typeof g !== 'object') continue; const grole: any = (g.role !== undefined) ? g.role : g.role_id; if (typeof grole !== 'string' || !roles.has(grole)) continue; const gaction: any = g.action; if (typeof gaction !== 'string' || typeof action !== 'string') continue; let actionOk = false; if (policy.wildcard_action && gaction === '*') actionOk = true; else if (policy.action_casefold) actionOk = asciiLower(gaction) === asciiLower(action); else actionOk = gaction === action; if (!actionOk) continue; const scope: any = grantScope(g); if (typeof scope !== 'string' || typeof resourceId !== 'string') continue; let scopeOk = false; if (policy.resource_scope === 'prefix') scopeOk = resourceId.startsWith(scope); else scopeOk = scope === resourceId; if (!scopeOk) continue; const exp: any = grantExpires(g); let expOk = false; if (exp === null || exp === undefined) expOk = true; else if (policy.expiry_inclusive) expOk = now <= exp; else expOk = now < exp; if (!expOk) continue; matched.push(i); effects.push(grantEffect(g)); }\n if (matched.length === 0) return result(!!policy.default_allow, 'default', [], policy); const hasAllow = effects.indexOf('allow') >= 0; const hasDeny = effects.indexOf('deny') >= 0; let allowed: boolean; if (policy.deny_wins) allowed = hasAllow && !hasDeny; else allowed = hasAllow; return result(allowed, 'grants', matched, policy); }\n", "policy.ts": "export const POLICY: any = {\"deny_wins\": true, \"expiry_inclusive\": true, \"inherit_roles\": true, \"owner_bypass\": true, \"action_casefold\": true, \"group_union\": true, \"resource_scope\": \"exact\", \"suspended_denies\": true, \"token_ttl\": 100, \"wildcard_action\": true, \"default_allow\": true, \"audit_denials\": true};\n", "helpers.ts": "export function asciiLower(s: string): string { let out = ''; for (let i = 0; i < s.length; i++) { const c = s.charCodeAt(i); if (c >= 65 && c <= 90) out += String.fromCharCode(c + 32); else out += s[i]; } return out; }\n"}}