Autorizzazione dello storage
Le collection sono protette dalla sicurezza a livello di riga; un bucket non ha un equivalente per singolo oggetto, quindi lo storage si autorizza qui. La configurazione dello storage tratta i backend veri e propri.
La forma dichiarativa. Un elenco di pattern di percorso, letto senza eseguire nulla:
storagePolicies: [ { path: "public/**", operations: ["read"], allow: "public" }, { path: "users/:uid/**", allow: ({ params, user }) => user?.uid === params.uid }]Una chiave che nessuna policy riconosce viene rifiutata, a meno che un hook storageAuthorize non la consenta: l’hook viene interpellato solo quando nessuna policy ha trovato corrispondenza per quella chiave e operazione, e nessuno dei due può restringere l’altro. Una policy che ha trovato corrispondenza e non ha concesso è definitiva. Ogni ampliamento è una riga esplicita, e un errore nega anziché concedere.
I pattern corrispondono per segmento, mai per sottostringa — public/** non corrisponde a
publicity/secret.png:
| Pattern | Corrisponde a |
|---|---|
avatars/logo.png |
esattamente quella chiave |
users/*/avatar.png |
esattamente un segmento dove si trova * |
users/:uid/** |
un segmento catturato, poi il resto — anche nulla |
** è ammesso solo come ultimo segmento. :name cattura un segmento e non
attraversa mai un /; le catture arrivano come params al predicato.
allow è "public" (chiunque la route lasci passare), "authenticated" (qualsiasi chiamante con un uid) oppure un
predicato che riceve i parametri catturati, l’utente, l’operazione e il bucket.
Una policy viene eseguita dopo l’autenticazione della route, quindi "public" da solo non apre un percorso ai chiamanti anonimi. Con requireAuth attivo raggiungono solo le letture di chiavi public/, o tutte le letture con storagePublicRead; /upload, /list, le eliminazioni e gli upload riprendibili richiedono sempre un chiamante. Altrove la risposta è 401.
operations per impostazione predefinita le comprende tutte e quattro — read, write, delete, list.
Le policy soddisfano da sole la protezione all’avvio in produzione, e un pattern malformato fa fallire l’avvio anziché il primo upload.
requireAuth e publicRead sono interruttori globali: decidono se un chiamante deve essere autenticato, non cosa quel chiamante possa toccare. Senza un hook di autorizzazione, qualsiasi utente autenticato può leggere qualsiasi chiave che sappia nominare — l’unica cosa che separa i file di due tenant è l’impossibilità di indovinare la chiave, che non è un modello di controllo degli accessi. Peggio ancora, può prima eseguire GET /storage/list?prefix=, quindi le chiavi non devono nemmeno essere indovinate.
storageAuthorize è l’analogo per lo storage delle regole di sicurezza di una collection, e viene eseguito dopo l’autenticazione su ogni route dello storage:
await initializeRebaseBackend({ storage: { type: "s3", bucket: "app-files", /* ... */ }, storageAuthorize: async ({ key, bucket, operation, user }) => { if (!user) return false; // Keys are laid out as `{teamId}/{docId}/...` const [teamId] = key.split("/"); return isTeamMember(user.uid, teamId); }});| Campo | Descrizione |
|---|---|
key |
Chiave dell’oggetto, privata del prefisso del bucket e ripulita da ogni tentativo di attraversamento dei percorsi |
bucket |
Bucket risolto ("default" se non specificato) |
operation |
"read", "write", "delete" o "list" |
user |
{ uid, email?, roles? }, oppure null dove la route consente l’accesso anonimo |
storageId |
Il backend con nome, quando la richiesta ne ha indicato uno — la sua chiave canonica (storageId=%20media è "media"). Assente per la sorgente predefinita, comunque la richiesta l’abbia indicata (nessun parametro, vuoto, o (default)) |
data |
Accesso in lettura attendibile che aggira l’RLS — data.collection(slug).find(query) / .findById(id). La proprietà risiede in una riga, non in un prefisso della chiave, quindi l’hook ha bisogno di un lettore per rispondere a “chi possiede questo oggetto?”. Aggira deliberatamente la sicurezza a livello di riga: questo hook è la decisione di autorizzazione, e prenderla attraverso un lettore già ristretto dai permessi del chiamante sarebbe circolare. Di sola lettura per progettazione. |
Restituisci false per negare con un 403. Anche un’eccezione nega — una ricerca di proprietà che fallisce non lascia passare.
Da sapere:
- La route dei metadati è dove si decide davvero l’accesso in lettura. Emette il token di download di breve durata, limitato al percorso, di cui si fida la route del file, quindi l’hook la controlla lì. Due letture saltano l’hook, di proposito. Una che porta un tale token: l’hook l’ha approvata quando il token è stato emesso, solo per quel percorso e quella sorgente, quindi un hook che ora la rifiuterebbe (un’appartenenza revocata da allora) non la ferma prima che il token scada —
storageDownloadTokenTtl, 300 secondi per impostazione predefinita. E la lettura di una chiave sottopublic/da parte di un chiamante senza credenziali: il prefisso è la dichiarazione che chiunque può leggerla, visitatori anonimi e CDN inclusi. La lettura da parte di un chiamante autenticato viene comunque sottoposta all’hook, ma quella anonima no, quindi un hook non può rendere privata una chiavepublic/; salva invece il file fuori dapublic/. Solo quelle due letture lo saltano: un utente che il tuo auth adapter chiamapublicviene sottoposto all’hook come chiunque altro. - Un bucket, un nome. Su S3 e GCS il
bucketdell’hook è sempre"default", anche quando una richiesta indica il bucket del provider, che raggiunge gli stessi oggetti. - Un percorso è pubblico quando lo è la chiave che indica. Il prefisso
public/viene verificato sulla chiave canonica servita dalla route — dopo che un segmentodefault/iniziale del bucket è stato letto e che//,./e un/iniziale sono stati normalizzati — mai sull’URL così com’è scritto./api/storage/file/notes://public/x.txtindica la chiave privatanotes:/public/x.txt, e la chiavedefault/public/x.txt(raggiunta come/api/storage/file/default/default/public/x.txt) si trova in una cartella chiamatadefault; nessuna delle due è pubblica. In un hook, verifica lakeyche ti viene passata conisPublicStorageKeyda@rebasepro/types—isPublicStoragePathlegge un percorso o un URL lato client, e considera pubblici entrambi quei casi. listè controllato sul prefisso. Elencare è il modo per scoprire chiavi di cui nessuno ti ha parlato.- Gli upload ripristinabili (TUS) sono controllati al momento della creazione, così un upload negato non lascia alcun file temporaneo.
- Omettere l’hook (e
storagePolicies) non lascia alcun controllo per singolo oggetto, quindi la produzione rifiuta di avviarsi a meno che non sia impostatostoragePublicReadostorageInsecureAllowAnyAuthenticated.
Chiavi API e token
Sezione intitolata “Chiavi API e token”Una persona autenticata possiede ogni scope di storage, quindi decidono per lei l’hook e le policy
descritti sopra. Una chiave API ha bisogno anche dello scope di
storage per l’operazione, sulla sorgente che la richiesta indica: storage:read per
download, metadati ed elenchi, storage:write per upload, cartelle e ogni
passaggio di un upload ripristinabile, storage:delete per le eliminazioni. Il target è
l’id della sorgente, e quello della sorgente predefinita è (default): storage:read:(default),
storage:write:avatars. Senza di esso la risposta è 403 SCOPE_MISSING. Con esso,
l’hook viene comunque eseguito, come chi agisce dietro la chiave.
Passaggi successivi
Sezione intitolata “Passaggi successivi”- Configurazione dello storage — backend, endpoint e limiti
- Chiavi API — gli scope che una chiave possiede