1const __authParityEvent = Symbol("verified-invocation-event"); 2// Flexylead authorization adapter for Functions' reserved invocation context. 3// Business identity is never inferred from event.auth, actor flags or headers. 4function requireTrustedPrincipal(event, context) { 5 const principal = event?.requestContext?.authorizer?.principal; 6 const identity = context?.invocation; 7 const tenant = String(process.env.FLEXYLEAD_AUTH_ORGANIZATION_SLUG || 'default'); 8 if (!principal || identity?.kind !== 'authenticated' 9 || principal.issuer !== `apteva:auth:${tenant}` 10 || principal.claims?.tenant_id !== tenant 11 || !principal.project_id || principal.project_id !== identity.project_id 12 || principal.subject !== identity.subject || principal.issuer !== identity.issuer 13 || !/^[1-9]\d*$/.test(String(principal.subject))) { 14 throw __authParityDenied('Authenticated Function context required.', 401); 15 } 16 return principal; 17} 18 19async function trustedActor(event, context, claimed = {}, { required = false } = {}) { 20 if (!event?.requestContext?.authorizer && context?.invocation?.kind !== 'authenticated') { 21 if (required) throw __authParityDenied('Authenticated Function context required.', 401); 22 return null; // Existing explicitly authenticated service workflows remain separate. 23 } 24 const principal = requireTrustedPrincipal(event, context); 25 const organizationId = Number(process.env.FLEXYLEAD_AUTH_ORGANIZATION_ID || 1); 26 const payload = await context.call('auth', 'auth_users_get_context', { 27 organization_id: organizationId, id: Number(principal.subject), 28 }); 29 const user = payload?.user || {}; 30 const id = String(user.id ?? ''); 31 const email = String(user.email ?? '').trim().toLowerCase(); 32 if (id !== String(principal.subject) || !email || user.status !== 'active' 33 || (user.organization_id != null && Number(user.organization_id) !== organizationId)) { 34 throw __authParityDenied('User is inactive or outside the authenticated organization.'); 35 } 36 if ((claimed?.auth_user_id && String(claimed.auth_user_id).trim() !== id) 37 || (claimed?.email && String(claimed.email).trim().toLowerCase() !== email)) { 38 throw __authParityDenied('Command actor does not match the authenticated user.'); 39 } 40 const names = value => (Array.isArray(value) ? value : []).map(String); 41 const roles = names(payload?.authorization?.roles); 42 return { id, auth_user_id: id, email, organization_id: String(organizationId), 43 roles, role: roles[0] || '', permissions: names(payload?.authorization?.permissions) }; 44} 45 46function __authParityDenied(message, statusCode = 403) { 47 const error = new Error(message); error.statusCode = statusCode; return error; 48} 49 50import crypto from "node:crypto"; 51 52const AUDIT_TABLE = "commercial_supervision_sessions"; 53const PERMISSION = "commercial:supervise"; 54const ACCESS_PERMISSION = "commercial:team:supervise"; 55const ALL_CENTRES_PERMISSION = "commercial:all_centres"; 56const DEFAULT_TTL_SECONDS = 10 * 60; 57const MAX_TTL_SECONDS = 15 * 60; 58const ADMIN_ROLES = new Set(["admin", "owner", "super_admin", "project_admin"]); 59const REPORT_COLUMNS = [ 60 "id", "local_id", "appel_id", "appel_ids", "vente_ids", "centre_appel_id",