CreaRack-SL

staff.ts — Mapeo de identidad CF Access → TeamMember

staff.ts — Mapeo de identidad CF Access → TeamMember

Descripción

functions/_lib/staff.ts es la fuente de verdad de identidad del workspace de CreaRack. Su responsabilidad es resolver el email que Cloudflare Access inyecta en la cabecera Cf-Access-Jwt-Assertion (o el claim email del JWT validado) hacia el tipo nominal TeamMember ('Edu' | 'Dani' | 'Txell').

Este módulo es importado por todos los handlers de la API que necesitan saber quién hace la petición: /api/me, /api/zoho/*, /api/tasks/links/*, /api/oauth/zoho/start, y cualquier handler que derive actor del contexto de sesión — incluido, desde s293, el registro de auditoría de notas/noticias/alertas/proyectos/tareas/adjuntos (ver auditActor más abajo).


Entidades exportadas

TeamMember

export type TeamMember = 'Edu' | 'Dani' | 'Txell';
export const TEAM_MEMBERS: readonly TeamMember[] = ['Edu', 'Dani', 'Txell'] as const;

Tipo nominal que representa a un miembro activo del equipo. Unico tipo permitido como actor en logs y operaciones con trazabilidad.

STAFF_EMAIL_TO_NAME (privado)

const STAFF_EMAIL_TO_NAME: Record<string, TeamMember> = {
  'eesquembri@esfericlabs.com': 'Edu',
  'dfuentes@esfericlabs.com':   'Dani',   // login Google real en CF Access
  'tfuentes@esfericlabs.com':   'Txell',  // login Google real en CF Access
};

Invariante crítica: los emails aquí deben coincidir con:

  1. La allowlist de CF Access (política Google OAuth, dominio esfericlabs.com).
  2. La allowlist de Resend en functions/api/mcp/handlers/email.ts.
  3. Los usernames de Zoho OAuth configurados para cada persona.

Si un email en esta tabla no coincide exactamente con el que CF Access pone en el JWT, el lookup devuelve undefined, el actor se resuelve como null, y todos los endpoints protegidos rompen para esa persona.

resolveStaffEmail(email: string): TeamMember | null

Función pública que hace el lookup. Devuelve null si el email no está en la tabla. Los handlers deben tratar null como no autenticado / no autorizado.

auditActor(request: Request): string (añadida en s293)

Deriva el actor que se anota en el registro de auditoría de una request HTTP, con esta prioridad estricta: resolveTeamMember(request) ?? resolveActorEmail(request) ?? 'web'. Nunca lee el body ni la query string.

Por qué existe: la Auditoría Suprema del workspace (hallazgos WS5-M10 / WS6-B14) detectó que varios endpoints de escritura (notes, news, alerts, projects, tasks, attachments) anotaban un 'web' anónimo en su log de actividad, y dos de ellos tomaban el actor de un campo que controla el propio cliente (author en el body de news, ?actor= en el borrado de attachments) — la firma del audit-log era falsificable.

Decisión de la casa (equipo plano de 3, confianza total — “acceso democrático”): que Edu edite o borre una nota de Dani sigue sin restringirse; no se añade ningún gate ni 403 nuevo. Lo único que cambia es que ahora se puede responder con certeza quién hizo cada cambio, porque el actor sale siempre de CF Access. En los PUT se anota además qué campos se tocaron; en los DELETE, quién era el autor original y si el borrado fue de otro miembro (cross_author). El registro de cada acción destructiva se espera (await) antes de responder, en vez de dispararse en fire-and-forget.

Consumidores: functions/api/notes/[id].ts, functions/api/news/[id].ts, functions/api/alerts/[id].ts, functions/api/projects/[id]/index.ts (+ index.ts), functions/api/tasks/[id]/index.ts (+ index.ts), functions/api/attachments/[id].ts.


Historial de cambios relevantes

SesiónFechaCambio
s6817-05-2026Migración a dominio esfericlabs.com. Emails anteriores (edudomo2@gmail.com, dfuentes@edomo.net, tfuentes@edomo.net) retirados de la política CF Access.
s7620-05-2026Fix crítico: dani@esfericlabs.com y txell@esfericlabs.com eran aliases incorrectos. Las cuentas reales de login Google que CF Access autentica son dfuentes@ y tfuentes@. Detectado en auditoría pre-onboarding Txell. Sin el fix, login de Dani/Txell resolvía actor=null.
s29329-08-2026Nuevo helper auditActor(request): cierra el agujero de firma falsificable en el audit-log de notes/news/alerts/projects/tasks/attachments (WS5-M10/WS6-B14), sin restringir permisos entre miembros del equipo.

Endpoints afectados si STAFF_EMAIL_TO_NAME es incorrecto

  • GET /api/me — devuelve 401 o actor incorrecto
  • GET|POST /api/zoho/* — todas las operaciones Zoho CRM
  • GET|POST /api/tasks/links/* — gestión de tareas
  • GET /api/oauth/zoho/start — inicio del flujo OAuth Zoho
  • PUT|DELETE de notes, news, alerts, projects, tasks, attachments — el audit-log resuelve actor a través de auditActor, que depende del mismo lookup

Mantenimiento

Cada vez que se añade o cambia una cuenta de login de un miembro del equipo:

  1. Actualizar STAFF_EMAIL_TO_NAME en este fichero.
  2. Sincronizar la allowlist en functions/api/mcp/handlers/email.ts.
  3. Verificar que la política CF Access (panel Cloudflare → Access → Applications) incluye el nuevo email.
  4. Verificar el username Zoho OAuth correspondiente.
  5. Probar /api/me con la sesión del miembro afectado antes de cerrar la sesión de trabajo.

Véase también

  • [[feature—workspace—quick-links]]
  • [[workspace—guias—team-processes]]
  • [[workspace—onboarding—onboarding-txell]]
  • [[workspace—que-es-workspace]]
  • [[workspace—informes]]