Domanda

Sto costruendo un sito basato sulla comunità in Rails per i membri di un'organizzazione del mondo reale. Sto cercando di aderire alle migliori pratiche del design RESTful, e la maggior parte è più o meno nel libro. Il problema che mi fa correre il cervello in ambienti RESTful è l'autorizzazione. Autenticazione è un problema facile e risolto da tempo con soluzioni RESTful ampiamente accettate, ma l'autorizzazione RESTful sembra essere un po 'un'arte nera. Sto cercando di trovare l'approccio che fornirà il quadro più generale e flessibile per il controllo dell'accesso alle risorse pur essendo il più semplice possibile, il tutto conforme a un'architettura RESTful. (Inoltre, un pony.)

Considerazioni:

  1. Devo controllare l'accesso a una varietà di risorse, come Utenti, Pagine, Post, eccetera.
  2. L'autorizzazione per una determinata risorsa deve essere più precisa rispetto al semplice CRUD.
  3. Desidero consentire a me e agli altri di modificare le regole di autorizzazione dall'interno dell'applicazione.
  4. Le regole di autorizzazione dovrebbero poter dipendere da predicati, come (concettualmente) Proprietario (Utente, Risorsa) o Bloccato (Argomento)

Consideration (2) è quello che mi preoccupa di più. Sembra che ci sia una discrepanza di impedenza tra la mia concezione delle autorizzazioni e la concezione RESTful delle azioni. Ad esempio, accetta Messaggi (come in una bacheca). REST determina l'esistenza di quattro operazioni sulla risorsa Posta: Crea, Leggi, Aggiorna ed Elimina. È semplice affermare che un utente dovrebbe essere in grado di aggiornare i propri post, ma solo determinati utenti (o ruoli o gruppi) dovrebbero essere autorizzati a bloccarli. Il modo tradizionale di rappresentare il blocco è all'interno dello stato della Posta, ma ciò porta all'odore che un Utente nelle stesse condizioni possa o meno essere in grado di aggiornare una Posta a seconda dei valori (completamente validi) che fornisce. Mi sembra chiaro che ci sono davvero due diverse azioni per cambiare lo stato della Posta, e calunniarli significa semplicemente mascherare una violazione dei principi RESTful.

(Devo notare che questo problema è abbastanza distinto dal problema di un aggiornamento non riuscito a causa di dati non validi o incoerenti & # 8212; una richiesta di blocco da un utente non privilegiato è in linea di principio abbastanza valida, semplicemente non consentito.)

  

La decomposizione non è un'altra parola per marcire?

Ciò può essere risolto decomprimendo il Post: un Lock è una fonte secondaria di un particolare post, e creare o distruggere uno può quindi avere autorizzazioni separate. Questa soluzione ha l'anello di REST, ma comporta difficoltà sia teoriche che pratiche. Se fattorizzo i blocchi, che dire degli altri attributi? Supponiamo che io decida, in una forma di capriccio, che solo un membro dell'Amministratore dovrebbe essere autorizzato a modificare il titolo della Posta? Una semplice modifica dell'autorizzazione richiederebbe quindi una ristrutturazione del database per adattarlo! Questa non è una soluzione. Per consentire questo tipo di flessibilità nell'ambito di una strategia di decomposizione richiederebbe che ogni attributo sia una risorsa. Questo presenta un po 'un dilemma. La mia ipotesi implicita è stata che una risorsa è rappresentata nel database come una tabella. In base a questo presupposto, una risorsa per ogni attributo indica una tabella per ogni attributo. Chiaramente, questo non è pratico. Tuttavia, per rimuovere questo presupposto si presenta una discrepanza di impedenza tra tabelle e risorse, che potrebbe aprire la propria lattina di worm. Usare questo approccio richiederebbe una considerazione molto più approfondita di quanto io abbia dato. Per prima cosa, gli utenti si aspettano ragionevolmente di essere in grado di modificare più attributi contemporaneamente. Dove va la richiesta? Alla risorsa più piccola che contiene tutti gli attributi? Ad ogni singola risorsa in parallelo? Alla luna?

  

Alcune di queste cose non sono come le altre & # 8230;

Supponiamo quindi che non decomponga gli attributi. L'alternativa sembra quindi definire un insieme di privilegi per ogni risorsa. A questo punto, tuttavia, l'omogeneità di REST è persa. Per definire le regole di accesso per una risorsa, il sistema deve avere una conoscenza specifica delle capacità di quella risorsa. Inoltre, ora è impossibile propagare genericamente le autorizzazioni alle risorse discendenti & # 8212; anche se una risorsa figlio aveva un privilegio con lo stesso nome, non esiste una connessione semantica intrinseca tra i privilegi. Definire un insieme di privilegi standard simili a REST mi sembra il peggiore di entrambi i mondi, quindi sono bloccato con una gerarchia di autorizzazioni separata per ogni tipo di risorsa.

  

Bene, abbiamo fatto il naso. E il cappello. Ma è una risorsa!

Un suggerimento che ho visto che mitiga alcuni degli svantaggi dell'approccio sopra è quello di definire le autorizzazioni come creare / eliminare su risorse e leggere / scrivere su attributi . Questo sistema è un compromesso tra attributi come risorse e privilegi per risorsa: uno è ancora lasciato solo con CRUD, ma ai fini dell'autorizzazione, Read e Update riguardano gli attributi, che potrebbero essere considerati pseudo-risorse. Ciò fornisce molti dei vantaggi pratici dell'approccio degli attributi come risorse, sebbene l'integrità concettuale sia, in una certa misura, compromessa. Le autorizzazioni potrebbero comunque propagarsi da risorsa a risorsa e da risorsa a pseudo-risorsa, ma mai da una pseudo-risorsa. Non ho esplorato completamente le ramificazioni di questa strategia, ma sembra che possa essere promettente. Mi viene in mente che un tale sistema funzionerebbe al meglio come parte integrante del Modello. In Rails, ad esempio, potrebbe essere una modifica di ActiveRecord . Questo mi sembra piuttosto drastico, ma l'autorizzazione è una preoccupazione trasversale fondamentale che può essere giustificata.

  

Oh, e non dimenticare il pony

Tutto ciò ignora il problema delle autorizzazioni predicative. Ovviamente, un utente dovrebbe essere in grado di modificare i propri post, ma nessun altro. Allo stesso modo, ovviamente, la tabella delle autorizzazioni scritte dall'amministratore non dovrebbe avere record separati per ciascun utente. Questo non è un requisito insolito & # 8212; il trucco sta rendendolo generico. Penso che tutte le funzionalità di cui ho bisogno possano essere acquisite rendendo solo le regole predicative, in modo che l'applicabilità della regola possa essere decisa rapidamente e immediatamente. Una regola " consente all'utente di scrivere post dove autore (utente, post) " si tradurrebbe in " per tutti gli utenti, in modo tale che l'autore (utente, post), consentisse all'utente di scrivere post " ;, e " rifiutare tutti i post di scrittura in cui bloccato (post) " a " per tutti i Post in modo tale che Locked (Post), neghi tutto scrivi Post " ;. (Sarebbe grandioso se tutti questi predicati potessero essere espressi in termini di un utente e una risorsa.) Il concettualmente risultante "finale" le regole sarebbero non predicative. Ciò solleva la questione di come implementare un tale sistema. I predicati dovrebbero essere membri delle classi Model, ma non sono sicuro di come si possa fare riferimento a loro con garbo nel contesto delle regole. Farlo in modo sicuro richiederebbe una sorta di riflessione. Anche in questo caso ho la sensazione che ciò richiederebbe un aggiornamento dell'attuazione del modello.

  

Come si scrive di nuovo?

L'ultima domanda è come rappresentare al meglio queste regole di autorizzazione come dati. Una tabella di database potrebbe fare il trucco, con colonne enum per allow / deny e C / R / U / D (o forse bit CRUD? O forse {C, R, U, D} & # 215; {consentire, negare, ereditare }?) e una colonna di risorse con un percorso. Forse, per comodità, un "ereditare" po. Sono in perdita per quanto riguarda i predicati. Tavolo separato? Certamente molta cache per impedirgli di essere troppo ungodly slow.


Immagino che questo sia molto da chiedere. Ho provato a fare i compiti prima di porre la domanda, ma a questo punto ho davvero bisogno di una prospettiva esterna. Gradirei qualsiasi input che qualcuno di voi potrebbe avere sul problema.

È stato utile?

Soluzione

Mi dispiace non ho tempo per rendere giustizia a questa domanda, ma è bello vedere alcune domande ben ponderate su SO. Ecco alcuni commenti:

Non cadere nella trappola della mappatura dei verbi HTTP su CRUD. Sì OTTIENI e ELIMINA la mappa in modo abbastanza chiaro, ma PUT può creare e aggiornare (ma solo la sostituzione completa) e POST è un verbo jolly. POST è davvero quello di gestire tutto ciò che non rientra in GET, PUT e ELIMINA.

L'uso degli attributi per rappresentare lo stato di un oggetto è solo un approccio alla gestione dello stato. Immagino che tu possa immaginare cosa potrebbe fare la seguente richiesta:

POST /LockedPosts?url=/Post/2010

Una risorsa secondaria è anche un approccio valido per gestire lo stato corrente di una risorsa. Non mi sentirei obbligato a trattare lo "stato" di una risorsa attributi e i suoi "dati" attributi in modo coerente.

Tentare di mappare le risorse direttamente alle tabelle ti ostacolerà seriamente. Non dimenticare che quando segui i vincoli REST improvvisamente sei molto limitato nei verbi che hai a disposizione. Devi essere in grado di compensare quello di essere creativo nelle risorse che usi. Limitare te stesso a una risorsa equivale a una tabella limiterà gravemente la funzionalità dell'applicazione finale.

Vediamo regolarmente gli utenti di Rails, ASP.NET MVC e WCF Rest che postano qui domande su StackOverflow su come fare determinate cose all'interno dei vincoli di REST. Il problema spesso non è un vincolo di REST ma nei limiti del framework nel suo supporto per le applicazioni RESTful. Penso che sia essenziale prima trovare una soluzione RESTful a un problema e poi vedere se può essere ricondotta al tuo framework di scelta.

Per quanto riguarda la creazione di un modello di autorizzazioni che esiste a un livello più fine rispetto alla risorsa stessa. Ricorda che uno dei principali vincoli REST è l'ipermedia. Hypermedia può essere utilizzato non solo per trovare entità correlate, ma anche per rappresentare transizioni di stato valide / consentite. Se si restituisce una rappresentazione che contiene collegamenti incorporati, in base alle autorizzazioni, è possibile controllare quali azioni possono essere eseguite da chi. ovvero se un utente dispone delle autorizzazioni per sbloccare POST 342, è possibile restituire il seguente collegamento incorporato nella rappresentazione:

<Link href="/UnlockedPosts?url=/Post/342" method="POST"/>

Se non dispongono di tale autorizzazione, non restituire il collegamento.

Penso che una delle tue difficoltà qui sia che stai provando a masticare un problema troppo grande in una volta. Penso che sia necessario esaminare l'interfaccia RESTful che si sta tentando di esporre al client come un problema distinto da come si gestiranno le autorizzazioni e i predicati al fine di gestire l'autorizzazione nel modello di dominio.

Mi rendo conto di non aver risposto direttamente a nessuna delle tue domande, ma spero di aver fornito alcuni punti di vista che potrebbero aiutare in qualche modo.

Altri suggerimenti

Di recente ho scoperto una soluzione di autenticazione che sembra rispondere alla maggior parte delle mie preoccupazioni. Se hai preferito questa domanda, potrebbe interessarti:

https://github.com/stffn/declarative_authorization

Come ha sottolineato Darrel, REST non è CRUD. Se scopri che le tue risorse identificate sono troppo grossolane e che l'interfaccia uniforme non fornisce un controllo sufficiente, allora dividi la tua risorsa in sotto-risorse e usa la risorsa originale come una 'raccolta' di collegamenti ipertestuali ai suoi componenti.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top