node-user-profiling
node-user-profiling
In sintesi
Servizio di autorizzazione enterprise: risponde alle domande "chi è questo utente?" e "cosa può fare?" per l'intero portale ditta. Mantiene la proiezione locale dell'identità applicativa — ruoli, permessi granulari, gruppi, team, policy ABAC dinamiche — e ne pubblica il risultato come decisione autorizzativa cache-friendly. Si distingue dal complementare node-user-auth, che gestisce come l'utente si autentica (cookie nginx, JWT, OAuth M2M): qui si concentra esclusivamente la logica RBAC/ABAC e l'analisi del rischio. Le route REST sono il canale primario; NATS è usato in modo puntuale per restare allineato a eventi di ciclo vita utente.
Funzionalità principali
- RBAC gerarchico su cinque livelli (
superadmin > admin > manager > operatore > viewer) con ereditarietà permessi e prevenzione cicli a livello DB - ABAC engine con policy
allow/deny/conditional, condizioni runtime su orari, dipartimenti e attributi della richiesta, modalitàfail_open/fail_closed - Gestione gruppi e team: CRUD completo, membership, ruoli e permessi collettivi su
auth.gruppi(tipogruppoprogettooorganizzativo), invalidazione cache transitiva - Check autorizzazione singolo e batch (
POST /api/v1/authorization/checke/bulk-check) con cache distribuita Redis e TTL distinti per esiti positivi, sensibili e negativi - Endpoint nginx
auth_request: verifica l'autorizzazione di una richiesta a partire dagli header inoltrati dal reverse proxy (/nginx-auth) - Anomaly detection con risk scoring 0-100, finestre di osservazione configurabili, alert su soglia, indagine pattern per singolo utente
- Audit trail delle decisioni autorizzative tramite event bus interno (audit, metrics, security alert) con correlation ID propagato
- Reconciliation utenti orfani (
/api/v1/admin/users/exists) e endpoint internal S2S (/api/v1/internal/*) per fallback hard-delete e operazioni batch protette daX-Internal-Token - Migrazioni SQL idempotenti con checksum tracking eseguite all'avvio del processo
Architettura
Stack: Fastify v5 · Inversify (DI con @injectable / @inject e profili default / high-performance / high-security / development) · PostgreSQL via driver pg (schema auth) · Redis opzionale per cache decisioni · NATS opzionale per un singolo subject di sincronizzazione · @pzeta/fastify-utils (authPlugin, securityPlugin, errorHandlerPlugin, openapiPlugin, healthcheckPlugin) · @pzeta/authorization (libreria interna che fornisce AuthorizationOrchestrator, PermissionEvaluationService, BusinessPolicyService, SecurityAnalyticsService) · @pzeta/log per logging strutturato · Zod per validazione input.
Layout DDD (src/):
| Layer | Contenuto |
|---|---|
domain/authorization/ | Motori RBACEngine e ABACEngine (con OPAPolicyEngine), entità Role, Permission, Policy, PermissionConflict, AuthorizationDecision |
domain/entities/ | Group e Team con value object di membership, ruoli e permessi |
domain/events/ | Eventi di dominio PermissionGrantedEvent, PermissionDeniedEvent, PolicyEvaluatedEvent, AnomalyDetectedEvent, CacheInvalidatedEvent + DomainEventPublisher |
domain/services/ | UserBehaviorAnalysisService per analisi pattern accessi e timing profile |
application/services/ | AuthorizationAppService — orchestra i use case combinando l'orchestrator della libreria con CRUD policy/group/team e mapping HTTP↔domain |
infrastructure/database/ | PostgresConnectionPool (pool tunato), MigrationRunner con checksum |
infrastructure/repositories/ | RBACRepository e OptimizedRBACRepository (variante con prepared statement per profilo high-performance) |
infrastructure/authorization/ | AuthorizationFactory, PostgresABACRepository, GroupRepository, TeamRepository |
infrastructure/cache/ | PermissionCache Redis-backed e NullPermissionCache per esecuzione senza cache |
infrastructure/messaging/ | NatsEventBus (wrapper riconnessione automatica) e UserDeletedSubscriber |
infrastructure/di/ | AuthorizationContainer — wiring Inversify e registrazione subscriber dell'event bus in-memory |
presentation/routes/ | 13 moduli route (RoleRoutes, PermissionRoutes, UserRoleRoutes, UserPermissionRoutes, AuthorizationCheckRoutes, PolicyRoutes, GroupRoutes, TeamRoutes, StatsRoutes, AnomalyRoutes, NginxAuthRoutes, AdminRoutes, InternalRoutes) |
presentation/middleware/ | Rate limiting per-user/IP, RequirePermission per protezione granulare |
Pattern adottati: Clean Architecture + DDD, Dependency Injection (Inversify), Repository, Factory, Strategy (profili AUTH_SERVICE_PROFILE), in-memory event bus per side-effects post-decisione (audit / metrics / security alert), Result monad per error handling funzionale nei servizi di dominio.
RBAC e ABAC: le route applicano requirePermission() sulla coppia resource:action derivata dal contesto. Il check finale è risolto dall'AuthorizationOrchestrator che combina decisione RBAC (materializzata dai ruoli effettivi dell'utente: diretti + ereditati + da gruppi/team), valutazione ABAC della policy applicabile e politica di fallback fail_open / fail_closed per ogni policy.
Casi d'uso
- Gestione ruoli da UI admin: il frontend Vue (sezione amministrativa) crea un nuovo ruolo via
POST /api/v1/roles, ne dichiara ilparentRoleId, associa permessiresource:actione lo assegna a gruppi/team o singoli utenti con eventuale scadenza - Check permessi runtime da altri microservizi: un servizio applicativo (es.
node-orchestrator) inviaPOST /api/v1/authorization/checkcon{ userId, resource, action, context }per decidere se eseguire un task sensibile; le decisioni sono cacheable con TTL configurabili - Autorizzazione del reverse proxy: nginx invoca
/nginx-authcon headerx-user-ide path target; il servizio risponde 200/403 e popola gli header inoltrati alla risorsa protetta - Sincronizzazione utenti da provider esterno:
node-user-auth, dopo aver registrato un nuovo utente nell'identity provider, può triggerare la reconciliation viaGET /api/v1/admin/users/existso pubblicare un evento di cancellazione su NATS (vedi sezione eventing) per propagare l'hard-delete GDPR - Investigazione security: l'operatore SOC consulta
/api/v1/anomaliesper identificare utenti con risk score elevato, ricostruisce la timeline dei tentativi negati tramite gli aggregati esposti da/api/v1/statse disabilita ruoli compromessi - Operazioni batch internal: pipeline di sistema chiamano
/api/v1/internal/users/:userId/roles/batch(protette daX-Internal-Token) per riassegnare massivamente ruoli a fronte di una riorganizzazione
Identità & esposizione
| Campo | Valore |
|---|---|
| Categoria | identity |
| Versione cluster | 1.0.0 |
| Image | gitea.pzetatouch.it/pzeta_touch/node-user-profiling:1.0.14 |
| URL pubblico | https://ditta.pzeta.it/user-profiling |
| Path regex ingress | `/user-profiling(/ |
| Rewrite a backend | /$2 |
| DNS interno | node-user-profiling-ditta.ditta.svc.cluster.local:3000 |
| Auth nginx | auth_request → node-user-auth |
| Repository | node-user-profiling |
| Endpoint REST | 64 (vedi sezione "API reference") |
Endpoint operazionali
Endpoint convenzionali esposti da tutti i microservizi PZeta basati su @pzeta/fastify-utils:
| Path pubblico | Scopo |
|---|---|
https://ditta.pzeta.it/user-profiling/health | liveness probe |
https://ditta.pzeta.it/user-profiling/ready | readiness probe |
https://ditta.pzeta.it/user-profiling/metrics | metriche Prometheus |
https://ditta.pzeta.it/user-profiling/api-docs.json | spec OpenAPI runtime (richiede OPENAPI_EXPOSE_IN_PRODUCTION=true) |
https://ditta.pzeta.it/user-profiling/api-docs | Swagger UI (solo in NODE_ENV !== production) |
Configurazione
Variabili d'ambiente che un integratore deve conoscere (per la lista completa vedi .env.example del repo):
| Variabile | Ruolo |
|---|---|
DATABASE_HOST / _PORT / _USER / _PASSWORD / _NAME | Connessione PostgreSQL — ruoli, permessi, gruppi, team, policy, audit |
DATABASE_SCHEMA | Default auth; isola gli oggetti di profilazione dal resto del DB applicativo |
DATABASE_MAX_CONNECTIONS | Pool size del driver pg; alzare nei profili high-performance |
AUTH_SERVICE_PROFILE | Profilo di runtime: default, high-performance (repository ottimizzati + Redis aggressivo), high-security (policy fail_closed, soglie anomaly stringenti), development (Swagger UI esposto, error verbose) |
REDIS_ENABLED / REDIS_HOST / _PORT / _PASSWORD / _DB | Cache distribuita delle decisioni autorizzative; off in dev |
CACHE_STANDARD_TTL / CACHE_SENSITIVE_TTL / CACHE_DENY_TTL | TTL in secondi per esiti standard, sensibili e di rifiuto |
NGINX_AUTH_ENABLED / NGINX_AUTH_STRICT | Abilita l'endpoint /nginx-auth e ne governa la modalità strict (rifiuta header malformati) |
TRUST_X_USER_ID_HEADER | Forza il rispetto di x-user-id solo se ricevuto da nginx upstream; false in produzione esposta |
SECURITY_RISK_THRESHOLD / SECURITY_ANALYSIS_WINDOW_HOURS | Soglia di rischio e finestra di analisi per anomaly detection |
RATE_LIMIT_ENABLED / RATE_LIMIT_MAX_REQUESTS | Rate limit sliding window per utente/IP via @pzeta/fastify-utils |
MIGRATION_STRICT_CHECKSUM | Se true, una migrazione modificata rispetto al checksum registrato blocca l'avvio |
INTERNAL_API_TOKEN | Token (min 16 char) per le route /api/v1/internal/* di operazioni S2S |
NATS_URL / NATS_USER / NATS_PASSWORD / NATS_NAMESPACE | Connessione NATS opzionale; se assente, il servizio gira in modalità solo-HTTP |
USER_DELETED_SUBJECT | Override del subject NATS per eventi di cancellazione utente; default ${NATS_NAMESPACE}.authz.user.deleted |
Swagger UI e api-docs.json runtime sono esposti solo quando AUTH_SERVICE_PROFILE=development o, in produzione, se OPENAPI_EXPOSE_IN_PRODUCTION=true.
Note eventing NATS
Il servizio è principalmente HTTP-driven e usa NATS come canale secondario di sincronizzazione, non come trasporto primario delle decisioni autorizzative. La connessione NATS è opzionale: se NATS_URL non è configurato o non raggiungibile, il servizio prosegue in modalità degradata mantenendo intatte tutte le funzionalità REST.
Il subscriber NATS effettivamente registrato è uno solo: UserDeletedSubscriber ascolta ${NATS_NAMESPACE}.authz.user.deleted (override via USER_DELETED_SUBJECT) per ricevere notifiche di hard-delete GDPR pubblicate da node-user-auth. Alla ricezione invoca AuthorizationAppService.cleanupUser, idempotente per costruzione (DELETE su righe assenti = no-op). Le semantiche di delivery sono at-most-once (Core NATS senza JetStream): un errore nell'handler scarta il messaggio; il recupero avviene via endpoint HTTP /api/v1/internal/users/:userId/cleanup (manuale) o reconciliation batch /api/v1/admin/users/exists. L'upgrade a JetStream a livello infrastruttura aggiunge at-least-once senza modifiche al codice.
I dieci subject che lo scanner statico rileva nel file nats.json (permission.granted, permission.denied, anomaly.detected, policy.evaluated, cache.invalidated) non sono subject NATS: sono sottoscrizioni a un InMemoryEventPublisher interno usato per pipeline di side-effect (audit, metrics, security alert) generate dalle decisioni autorizzative. Vengono restituite dallo scanner perché l'API subscribe() è omonima a quella NATS, ma il dispatch avviene in-process. In altre parole, il servizio mantiene una proiezione locale dell'identità ma la mantiene per pull (HTTP) o per evento puntuale di lifecycle (cancellazione utente), non per stream continuo.
Dipendenze e dipendenti
Dipende da (servizi che questo servizio chiama):
- Nessuna dipendenza applicativa diretta.
Consumato da (chi chiama questo servizio):
node-user-authfrontend Vue
Infrastruttura (PostgreSQL, NATS, Redis, MinIO) non è elencata qui — vedi sezione Architettura del singolo servizio.