Preuve · code, extrait

backend/commercial-supervision-session.mjs

Dépôt jeremy-flexylead/flexylead-crm · commit ccbbaa07870e0f786729003dd6f8862e4bcc8256 · dossier de production synchronisé le 14 septembre 2026 à 08 h 46

Extrait tel qu'à ce commit : lignes 1–60, sur 620 (numérotation d'origine, 30 lignes de contexte autour de chaque citation) · fichier intégral sur GitHub (dépôt privé, accès requis)

60 lignes reproduites · ← retour à l'audit · catalogue des preuves

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",