Skip to Content

API FGA (Fine-Grained Authorization)

Il motore di Fine-Grained Authorization fornisce controllo degli accessi basato sulle relazioni (ReBAC) in stile Zanzibar. Estende il livello RBAC con autorizzazione a livello di oggetto: invece di “l’utente ha il permesso X globalmente,” FGA risponde “Alice ha la relazione viewer sul documento readme?”

Il sistema si basa su tre concetti:

  1. Modelli di Autorizzazione — definiscono i tipi di oggetto, le loro relazioni e le regole di riscrittura usando un DSL compatibile con OpenFGA.
  2. Tuple di Relazione — sono i fatti del sistema. Ogni tupla afferma che un soggetto ha una relazione con un oggetto.
  3. Query di Autorizzazione (check, expand, list-objects) — valutano le tuple rispetto alle regole di riscrittura del modello per rispondere a domande di accesso.

Tutti gli endpoint FGA richiedono l’intestazione x-tenant e un access token valido.

DSL del Modello di Autorizzazione

Il DSL definisce i tipi e le loro relazioni. Ogni relazione può avere regole di riscrittura che compongono l’accesso da altre relazioni o relazioni indirette.

type user type group relations define member: [user] type document relations define owner: [user] define editor: [user, group#member] define viewer: [user, group#member] or editor or owner

Tipi di regole di riscrittura:

RegolaSintassiDescrizione
Diretta (this)[user]Il soggetto deve essere assegnato direttamente tramite una tupla
UnioneA or BIl soggetto deve soddisfare almeno una delle relazioni
IntersezioneA and BIl soggetto deve soddisfare tutte le relazioni
EsclusioneA but not BIl soggetto deve soddisfare A e non deve soddisfare B
Userset calcolatoownerEredita da un’altra relazione sullo stesso oggetto
Tuple-to-usersetgroup#memberSegue una relazione su un oggetto correlato (indiretto)

Il motore FGA usa la valutazione ricorsiva con una profondità massima di 25 e rilevamento dei cicli tramite un set di visitati. Questo previene i loop infiniti nelle definizioni di relazioni circolari.

Modelli di Autorizzazione

Elenca Modelli

GET/api/fga/modelsRequires: view:fga_models

Elenca tutti i modelli di autorizzazione per il tenant, ordinati per versione decrescente. Solo un modello può essere attivo alla volta. Il modello attivo viene usato per tutta la validazione delle tuple e le query di autorizzazione.

Parametri di query

ParametroTipoDescrizione
pageintegerNumero di pagina (default: 1)
limitintegerElementi per pagina (default: 20)

Risposta di successo

{ "ok": true, "data": [ { "id": "model_abc123", "name": "SaaS Authorization Model", "version": 3, "isActive": true, "createdAt": "2025-02-10T00:00:00Z", "updatedAt": "2025-02-12T08:30:00Z" }, { "id": "model_def456", "name": "SaaS Authorization Model", "version": 2, "isActive": false, "createdAt": "2025-02-05T00:00:00Z", "updatedAt": "2025-02-05T00:00:00Z" } ] }

Crea Modello

POST/api/fga/modelsRequires: manage:fga_models

Crea un nuovo modello di autorizzazione fornendo un nome e una definizione DSL. Il DSL viene analizzato riga per riga e validato prima della memorizzazione. Se il DSL contiene errori di sintassi o riferimenti a tipi o relazioni non definiti, la richiesta viene rifiutata con un messaggio di errore dettagliato che include il numero di riga.

Corpo della richiesta

{ "name": "Document Access Model", "dsl": "type user\n\ntype group\n relations\n define member: [user]\n\ntype document\n relations\n define owner: [user]\n define editor: [user, group#member]\n define viewer: [user, group#member] or editor or owner\n" }

Risposta di successo

{ "ok": true, "data": { "id": "model_ghi789", "name": "Document Access Model", "version": 1, "isActive": false, "dsl": "type user\n\ntype group\n relations\n define member: [user]\n\ntype document\n relations\n define owner: [user]\n define editor: [user, group#member]\n define viewer: [user, group#member] or editor or owner\n", "schema": { "typeDefinitions": [ { "type": "user", "relations": {} }, { "type": "group", "relations": { "member": { "this": {} } } }, { "type": "document", "relations": { "owner": { "this": {} }, "editor": { "this": {} }, "viewer": { "union": { "children": [ { "this": {} }, { "computedUserset": { "relation": "editor" } }, { "computedUserset": { "relation": "owner" } } ] } } } } ] }, "createdAt": "2025-02-18T10:00:00Z" } }

Codici di errore

CodiceHTTPDescrizione
DSL_PARSE_ERROR400La sintassi DSL non è valida. Il campo message include il numero di riga e la descrizione dell’errore
DSL_VALIDATION_ERROR400Il DSL è sintatticamente valido ma fa riferimento a tipi, relazioni non definiti o contiene cicli
VALIDATION_ERROR400Campi obbligatori mancanti (name o dsl)

Recupera Modello

GET/api/fga/models/[id]Requires: view:fga_models

Recupera un modello di autorizzazione specifico tramite il suo ID. Restituisce il modello completo incluso il testo DSL grezzo e l’oggetto schema analizzato.

Codici di errore

CodiceHTTPDescrizione
NOT_FOUND404Il modello non esiste o appartiene a un tenant diverso

Aggiorna Modello

PUT/api/fga/models/[id]Requires: manage:fga_models

Aggiorna il nome o la definizione DSL di un modello. Quando il DSL viene aggiornato, viene ri-analizzato e ri-validato. La versione del modello non viene incrementata automaticamente in caso di aggiornamento — crea un nuovo modello per modifiche con versioning.

Corpo della richiesta

{ "name": "Document Access Model v2", "dsl": "type user\n\ntype document\n relations\n define owner: [user]\n define viewer: [user] or owner\n" }

Entrambi name e dsl sono opzionali. Solo i campi forniti vengono aggiornati.

Codici di errore

CodiceHTTPDescrizione
NOT_FOUND404Il modello non esiste
DSL_PARSE_ERROR400Il DSL aggiornato contiene errori di sintassi
DSL_VALIDATION_ERROR400Il DSL aggiornato fa riferimento a tipi o relazioni non definiti

Elimina Modello

DELETE/api/fga/models/[id]Requires: manage:fga_models

Elimina un modello di autorizzazione. I modelli attivi non possono essere eliminati — è necessario prima attivare un modello diverso.

L’eliminazione di un modello non elimina automaticamente le tuple scritte su di esso. Le tuple orfane vengono ignorate dalle query di autorizzazione ma rimangono nel database fino alla pulizia manuale.

Codici di errore

CodiceHTTPDescrizione
NOT_FOUND404Il modello non esiste
MODEL_IS_ACTIVE400Impossibile eliminare il modello attualmente attivo

Attiva Modello

POST/api/fga/models/[id]/activateRequires: manage:fga_models

Imposta un modello come modello di autorizzazione attivo per il tenant. Qualsiasi modello precedentemente attivo viene automaticamente disattivato. Tutte le query successive di check, expand e list-objects utilizzeranno le regole di riscrittura del modello appena attivato. La cache del modello viene invalidata immediatamente.

Risposta di successo

{ "ok": true, "data": { "activated": true, "modelId": "model_ghi789" } }

Codici di errore

CodiceHTTPDescrizione
NOT_FOUND404Il modello non esiste
ALREADY_ACTIVE400Questo modello è già il modello attivo

Il motore FGA memorizza il modello attivo in memoria con un TTL di 5 minuti. Dopo l’attivazione di un nuovo modello, le query possono usare il vecchio modello per un massimo di 5 minuti su altre istanze del server.

Tuple di Relazione

Le tuple sono i fatti di autorizzazione. Ogni tupla afferma che un soggetto ha una relazione con un oggetto.

Formato tupla: objectType:objectId#relation@subjectType:subjectId

Esempi:

  • document:readme#viewer@user:alice — Alice è un viewer del documento “readme”
  • document:readme#editor@group:engineering#member — i membri del gruppo “engineering” sono editor del documento “readme”
  • folder:projects#owner@user:bob — Bob è il proprietario della cartella “projects”

Elenca Tuple

GET/api/fga/tuplesRequires: view:fga_tuples

Elenca le tuple di relazione con filtraggio opzionale. Si raccomanda almeno un parametro di filtro per evitare di restituire l’intero tuple store. Supporta la paginazione.

Parametri di query

ParametroTipoDescrizione
objectTypestringFiltra per tipo di oggetto (es. document)
objectIdstringFiltra per ID oggetto
relationstringFiltra per nome relazione
subjectTypestringFiltra per tipo di soggetto
subjectIdstringFiltra per ID soggetto
pageintegerNumero di pagina (default: 1)
limitintegerElementi per pagina (default: 20, max: 100)

Scrivi Tupla

POST/api/fga/tuplesRequires: manage:fga_tuples

Scrive una singola tupla di relazione. La tupla viene validata rispetto al modello di autorizzazione attivo prima della memorizzazione.

Corpo della richiesta — assegnazione diretta utente

{ "objectType": "document", "objectId": "readme", "relation": "viewer", "subjectType": "user", "subjectId": "alice" }

Corpo della richiesta — subject set (indiretto/appartenenza al gruppo)

{ "objectType": "document", "objectId": "readme", "relation": "editor", "subjectType": "group", "subjectId": "engineering", "subjectRelation": "member" }

Questo significa: “tutte le entità che hanno la relazione member su group:engineering sono anche editor su document:readme.”

Codici di errore

CodiceHTTPDescrizione
NO_ACTIVE_MODEL400Nessun modello di autorizzazione attivo per questo tenant
INVALID_TYPE400Il tipo di oggetto non esiste nel modello attivo
INVALID_RELATION400La relazione non esiste su questo tipo di oggetto nel modello attivo
INVALID_SUBJECT_TYPE400La relazione non accetta questo tipo di soggetto come target valido
TUPLE_EXISTS409Una tupla identica esiste già
VALIDATION_ERROR400Campi obbligatori mancanti

Elimina Tupla

DELETE/api/fga/tuplesRequires: manage:fga_tuples

Elimina una tupla di relazione specifica. Restituisce successo anche se la tupla non esiste (eliminazione idempotente).

Scrittura/Eliminazione Bulk di Tuple

POST/api/fga/tuples/bulkRequires: manage:fga_tuples

Scrive e/o elimina più tuple in una singola richiesta atomica. Tutte le operazioni nel batch vengono validate rispetto al modello attivo prima di qualsiasi scrittura. Se anche una sola tupla fallisce la validazione, l’intero batch viene rifiutato.

Corpo della richiesta

{ "writes": [ { "objectType": "document", "objectId": "api-spec", "relation": "owner", "subjectType": "user", "subjectId": "bob" } ], "deletes": [ { "objectType": "document", "objectId": "readme", "relation": "viewer", "subjectType": "user", "subjectId": "alice" } ] }

Entrambi writes e deletes sono opzionali, ma almeno uno deve essere presente. Ogni array può contenere fino a 100 tuple.

Risposta di successo

{ "ok": true, "data": { "written": 2, "deleted": 1 } }

Le operazioni bulk sono atomiche. Se la tupla #47 di 100 fallisce la validazione, nessuna delle 100 tuple viene scritta o eliminata.

Query di Autorizzazione

Check

POST/api/fga/checkRequires: debug:fga

Verifica se un soggetto ha una relazione specifica con un oggetto. Il motore valuta le regole di riscrittura del modello attivo ricorsivamente. Opzionalmente restituisce un albero di risoluzione che mostra esattamente come è stata presa la decisione.

Corpo della richiesta

{ "objectType": "document", "objectId": "readme", "relation": "viewer", "subjectType": "user", "subjectId": "alice", "explain": false }
CampoTipoObbligatorioDescrizione
objectTypestringSìIl tipo dell’oggetto target
objectIdstringSìL’ID dell’oggetto target
relationstringSìLa relazione da verificare
subjectTypestringSìIl tipo del soggetto (tipicamente user)
subjectIdstringSìL’ID del soggetto
explainbooleanNoSe true, include l’albero di risoluzione (default: false)

Risposta di successo (senza explain)

{ "ok": true, "data": { "allowed": true } }

Risposta di successo (con explain)

{ "ok": true, "data": { "allowed": true, "resolution": { "type": "union", "relation": "viewer", "result": true, "children": [ { "type": "this", "relation": "viewer", "result": false }, { "type": "computedUserset", "relation": "editor", "result": false }, { "type": "computedUserset", "relation": "owner", "result": true, "children": [ { "type": "this", "relation": "owner", "result": true, "tupleFound": "document:readme#owner@user:alice" } ] } ] } } }

Codici di errore

CodiceHTTPDescrizione
NO_ACTIVE_MODEL400Nessun modello di autorizzazione attivo
INVALID_TYPE400Il tipo di oggetto non esiste nel modello attivo
INVALID_RELATION400La relazione non esiste sul tipo specificato
MAX_DEPTH_EXCEEDED400La valutazione ha superato la profondità massima di ricorsione di 25
VALIDATION_ERROR400Campi obbligatori mancanti

Expand

POST/api/fga/expandRequires: debug:fga

Espande una relazione su un oggetto per scoprire tutti i soggetti che hanno quella relazione. Restituisce una struttura ad albero che segue le regole di riscrittura.

Corpo della richiesta

{ "objectType": "document", "objectId": "readme", "relation": "viewer" }

List Objects

POST/api/fga/list-objectsRequires: debug:fga

Elenca tutti gli oggetti di un tipo dato a cui un soggetto può accedere tramite una relazione specifica. Esegue una ricerca inversa attraverso il tuple store e le regole di riscrittura. Utile per costruire viste filtrate come “mostrami tutti i documenti che questo utente può vedere.”

Corpo della richiesta

{ "objectType": "document", "relation": "viewer", "subjectType": "user", "subjectId": "alice" }

Risposta di successo

{ "ok": true, "data": { "objectIds": ["readme", "api-spec", "changelog", "roadmap"], "total": 4 } }

La query list-objects può essere costosa per tuple store di grandi dimensioni. Usa la paginazione e considera la memorizzazione nella cache dei risultati per i pattern frequentemente acceduti.

Riferimento Permessi

PermessoDescrizione
view:fga_modelsVisualizza modelli di autorizzazione e le loro definizioni DSL
manage:fga_modelsCrea, aggiorna, elimina e attiva modelli di autorizzazione
view:fga_tuplesElenca e legge le tuple di relazione
manage:fga_tuplesScrive ed elimina tuple di relazione (singole e bulk)
debug:fgaEsegue query check, expand e list-objects

Esempio Completo

Questo esempio dimostra un pattern di autorizzazione comune: un sistema di accesso ai documenti basato sui team.

Passo 1: Crea il modello

POST /api/fga/models { "name": "Team Documents", "dsl": "type user\n\ntype team\n relations\n define member: [user]\n define admin: [user]\n\ntype document\n relations\n define owner: [user]\n define team: [team]\n define editor: [user, team#admin]\n define viewer: [user, team#member] or editor or owner\n" }

Passo 2: Attiva il modello

POST /api/fga/models/{modelId}/activate

Passo 3: Scrivi le tuple

POST /api/fga/tuples/bulk { "writes": [ { "objectType": "team", "objectId": "engineering", "relation": "member", "subjectType": "user", "subjectId": "alice" }, { "objectType": "team", "objectId": "engineering", "relation": "admin", "subjectType": "user", "subjectId": "alice" }, { "objectType": "document", "objectId": "arch-doc", "relation": "editor", "subjectType": "team", "subjectId": "engineering", "subjectRelation": "admin" }, { "objectType": "document", "objectId": "arch-doc", "relation": "viewer", "subjectType": "team", "subjectId": "engineering", "subjectRelation": "member" } ] }

Passo 4: Verifica l’accesso

POST /api/fga/check { "objectType": "document", "objectId": "arch-doc", "relation": "editor", "subjectType": "user", "subjectId": "alice", "explain": true } // Risultato: { "allowed": true } — Alice è admin del team engineering, che concede editor

Correlati