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:
- Modelli di Autorizzazione — definiscono i tipi di oggetto, le loro relazioni e le regole di riscrittura usando un DSL compatibile con OpenFGA.
- Tuple di Relazione — sono i fatti del sistema. Ogni tupla afferma che un soggetto ha una relazione con un oggetto.
- 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 ownerTipi di regole di riscrittura:
| Regola | Sintassi | Descrizione |
|---|---|---|
Diretta (this) | [user] | Il soggetto deve essere assegnato direttamente tramite una tupla |
| Unione | A or B | Il soggetto deve soddisfare almeno una delle relazioni |
| Intersezione | A and B | Il soggetto deve soddisfare tutte le relazioni |
| Esclusione | A but not B | Il soggetto deve soddisfare A e non deve soddisfare B |
| Userset calcolato | owner | Eredita da un’altra relazione sullo stesso oggetto |
| Tuple-to-userset | group#member | Segue 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
/api/fga/modelsRequires: view:fga_modelsElenca 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
| Parametro | Tipo | Descrizione |
|---|---|---|
page | integer | Numero di pagina (default: 1) |
limit | integer | Elementi 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
/api/fga/modelsRequires: manage:fga_modelsCrea 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
| Codice | HTTP | Descrizione |
|---|---|---|
DSL_PARSE_ERROR | 400 | La sintassi DSL non è valida. Il campo message include il numero di riga e la descrizione dell’errore |
DSL_VALIDATION_ERROR | 400 | Il DSL è sintatticamente valido ma fa riferimento a tipi, relazioni non definiti o contiene cicli |
VALIDATION_ERROR | 400 | Campi obbligatori mancanti (name o dsl) |
Recupera Modello
/api/fga/models/[id]Requires: view:fga_modelsRecupera 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
| Codice | HTTP | Descrizione |
|---|---|---|
NOT_FOUND | 404 | Il modello non esiste o appartiene a un tenant diverso |
Aggiorna Modello
/api/fga/models/[id]Requires: manage:fga_modelsAggiorna 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
| Codice | HTTP | Descrizione |
|---|---|---|
NOT_FOUND | 404 | Il modello non esiste |
DSL_PARSE_ERROR | 400 | Il DSL aggiornato contiene errori di sintassi |
DSL_VALIDATION_ERROR | 400 | Il DSL aggiornato fa riferimento a tipi o relazioni non definiti |
Elimina Modello
/api/fga/models/[id]Requires: manage:fga_modelsElimina 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
| Codice | HTTP | Descrizione |
|---|---|---|
NOT_FOUND | 404 | Il modello non esiste |
MODEL_IS_ACTIVE | 400 | Impossibile eliminare il modello attualmente attivo |
Attiva Modello
/api/fga/models/[id]/activateRequires: manage:fga_modelsImposta 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
| Codice | HTTP | Descrizione |
|---|---|---|
NOT_FOUND | 404 | Il modello non esiste |
ALREADY_ACTIVE | 400 | Questo 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
/api/fga/tuplesRequires: view:fga_tuplesElenca 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
| Parametro | Tipo | Descrizione |
|---|---|---|
objectType | string | Filtra per tipo di oggetto (es. document) |
objectId | string | Filtra per ID oggetto |
relation | string | Filtra per nome relazione |
subjectType | string | Filtra per tipo di soggetto |
subjectId | string | Filtra per ID soggetto |
page | integer | Numero di pagina (default: 1) |
limit | integer | Elementi per pagina (default: 20, max: 100) |
Scrivi Tupla
/api/fga/tuplesRequires: manage:fga_tuplesScrive 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
| Codice | HTTP | Descrizione |
|---|---|---|
NO_ACTIVE_MODEL | 400 | Nessun modello di autorizzazione attivo per questo tenant |
INVALID_TYPE | 400 | Il tipo di oggetto non esiste nel modello attivo |
INVALID_RELATION | 400 | La relazione non esiste su questo tipo di oggetto nel modello attivo |
INVALID_SUBJECT_TYPE | 400 | La relazione non accetta questo tipo di soggetto come target valido |
TUPLE_EXISTS | 409 | Una tupla identica esiste già |
VALIDATION_ERROR | 400 | Campi obbligatori mancanti |
Elimina Tupla
/api/fga/tuplesRequires: manage:fga_tuplesElimina una tupla di relazione specifica. Restituisce successo anche se la tupla non esiste (eliminazione idempotente).
Scrittura/Eliminazione Bulk di Tuple
/api/fga/tuples/bulkRequires: manage:fga_tuplesScrive 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
/api/fga/checkRequires: debug:fgaVerifica 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
}| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
objectType | string | Sì | Il tipo dell’oggetto target |
objectId | string | Sì | L’ID dell’oggetto target |
relation | string | Sì | La relazione da verificare |
subjectType | string | Sì | Il tipo del soggetto (tipicamente user) |
subjectId | string | Sì | L’ID del soggetto |
explain | boolean | No | Se 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
| Codice | HTTP | Descrizione |
|---|---|---|
NO_ACTIVE_MODEL | 400 | Nessun modello di autorizzazione attivo |
INVALID_TYPE | 400 | Il tipo di oggetto non esiste nel modello attivo |
INVALID_RELATION | 400 | La relazione non esiste sul tipo specificato |
MAX_DEPTH_EXCEEDED | 400 | La valutazione ha superato la profondità massima di ricorsione di 25 |
VALIDATION_ERROR | 400 | Campi obbligatori mancanti |
Expand
/api/fga/expandRequires: debug:fgaEspande 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
/api/fga/list-objectsRequires: debug:fgaElenca 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
| Permesso | Descrizione |
|---|---|
view:fga_models | Visualizza modelli di autorizzazione e le loro definizioni DSL |
manage:fga_models | Crea, aggiorna, elimina e attiva modelli di autorizzazione |
view:fga_tuples | Elenca e legge le tuple di relazione |
manage:fga_tuples | Scrive ed elimina tuple di relazione (singole e bulk) |
debug:fga | Esegue 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}/activatePasso 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 editorCorrelati
- Modello FGA / Zanzibar — Come funzionano il modello di autorizzazione e le regole di riscrittura
- Guida Fine-Grained Authorization — Guida pratica all’implementazione FGA
- FGA Debugger — Testa le check e ispeziona gli alberi di risoluzione dalla Console
- API Ruoli e Permessi — Endpoint RBAC che completano FGA