Dove dovrebbe essere chiamato il gestore ACL in un'architettura MVC?

3

Pensando alle liste di controllo degli accessi legate a un'architettura MVC web, ho dei dubbi su dove dovrebbe essere eseguita la verifica.

All'interno dei modelli? Controllori? FrontController?

Immagina un'applicazione web con più moduli (come un sito Web). Ho un sito pubblico e una zona amministrativa riservata.

  • Tutte le pagine del sito pubblico sono accessibili da chiunque.
  • L'area di amministrazione riservata è accessibile solo da un utente autenticato.
  • Ci sono alcuni livelli di accesso per gli utenti, come root, admin e utente regolare.

Ho dato questo un pensiero durante il mio pranzo oggi e alcune idee sono uscite.

Per prima cosa, dovevo pormi questa domanda:

Is access control part of business logic?

Dopo qualche tempo, ho capito che la risposta dovrebbe essere no per la maggior parte dei casi. Normalmente, per la situazione che ho descritto, la logica commerciale dell'area pubblica è circa la stessa di quella ristretta. Quando non è esatto lo stesso, quello che faccio normalmente è creare un superset di entrambe le regole della logica di business, che contenga tutta la logica necessaria. Quindi, anche se nella mia applicazione ci sono diversi moduli, normalmente ho solo un livello di modello.

Fare l'ACL all'interno dei miei controller porta alla duplicazione del codice, poiché dovrei controllarlo ogni volta che mi serve. Inoltre, otterrei un elevato accoppiamento tra i controller e il componente ACL.

Pensare di metterlo dentro FrontController sembra essere l'approccio giusto. In effetti, il mio primo pensiero era di mettere il gestore ACL come dipendenza per il FrontController.

Questo approccio avrebbe metodi di controllo (azioni) come risorse dell'ACL, creando ruoli per i miei utenti.

Alcuni pseudo-codice (combinazione di Java, C #, PHP):

aclHandler = new DefaultAcl();
aclHandler.addRole('none'); // not authenticated
aclHandler.addRole('user');
aclHandler.addRole('admin', extend='user');
aclHandler.addRole('root', extend='admin');

aclHandler.addUser('john', 'root');
aclHandler.addUser('mary', 'admin');
aclHandler.addUser('jane', 'user');

aclHandler.addResource('admin.content.create'); // MODULE.CONTROLLER.ACTION
aclHandler.addResource('admin.content.view');
aclHandler.addResource('admin.content.edit');
aclHandler.addResource('admin.content.remove');

aclHandler.addResource('public.content.view');

aclHandler.allow('*', 'public.*');

aclHandler.deny('*', 'admin.*'); // deny from all
aclHandler.allow('user', 'admin.content.view'); // allow user to view content
aclHandler.allow('admin', 'admin.content.{edit,create}'); // allow admin to edit and create content
aclHandler.allow('root', '*'); // root can do anything

In questo modo, il mio FrontController avrebbe qualcosa del tipo:

class DefaultFrontController implements FrontControllerInterface {
    AclInterface acl {get};
    User currentUser {get;set};
    public DefaultFrontController(AclInterface acl, User user) {
        this.acl = acl;
        this.currentUser = user;
        // ...
    }

    public void dispatch(ControllerRequest request, ControllerResponse response) {
        try {
            this.acl.checkPrivileges(request.resource, this.currentUser);
        } catch (AclException e) {
            this.forbiddenAccessCallback(request, response, this.acl, this.currentUser);
            return;
        }

        // continue impl...
    }
}

fc = new FrontController(aclHandler, new User(name='John', role='root'));
fc.dispatch(...);  // ...

Questa implementazione rende un componente ACL un parametro obbligatorio per il costruttore di FrontController, quindi, ora fa parte del suo stato. Questo potrebbe essere un problema quando non ho bisogno di un ACL, ma può essere superato creando un'implementazione fittizia del componente ACL, come AllowFromAllAcl , che consentirebbe a qualsiasi utente di fare qualsiasi cosa.

Qualche idea? Correzioni? Suggerimenti?

    
posta Henrique Barcelos 19.02.2014 - 18:02
fonte

2 risposte

6

Il controllo degli accessi fa parte della logica aziendale. Se osservi casi d'uso, o anche storie di utenti, vedrai che ci sono attori coinvolti in qualsiasi processo aziendale. Le liste di controllo degli accessi sono un metodo per garantire che tutti e solo gli attori possano essere coinvolti nel processo di business.

Il controllo degli accessi deve essere supportato nel modello, nella vista e nel controller.

Il controllo degli accessi fa parte del modello: limitando la possibilità di modificare o visualizzare colonne o record in base alla logica aziendale, è necessario evitare molti errori. Se solo gli amministratori di sistema possono modificare un campo chiave secondario, non verrà modificato per errore perché qualcuno ha implementato un modulo in modo errato. Ora, suggerisco anche di non specificare la restrizione come basata sul privilegio "system_administrator" ma piuttosto sul privilegio "change_secondary_key", che system_adminstrators ha in base alla progettazione. Questo separa il ruolo dalla funzione.

Il controllo degli accessi fa parte della vista. Non abilitando i campi del modulo per modificare i dati per i quali la persona non ha i privilegi, previeni sia la confusione che gli errori.

Il controllo accessi fa parte del controller. Il controller regola il flusso della pagina per presentare solo le informazioni appropriate all'utente in un momento e in un luogo appropriati.

Certamente, si desidera avere una gestione centralizzata dei controlli di accesso, sul principio di DNRY (Non ripetere inutilmente te stesso). Il controllo dell'accesso è una preoccupazione trasversale.

    
risposta data 19.02.2014 - 22:09
fonte
-1

In generale, questo genere di cose è incorporato in MVC.

L'aggiunta di un attributo "Autorizza" a una classe controller, limita l'accesso a tutti i metodi del controller a "Deve essere registrato". L'aggiunta di Autorizza (Ruoli="Amministratore") a specifici metodi di controller limita ulteriormente l'accesso a tale percorso a un utente autenticato con i ruoli di amministratore assegnati ad essi.

Ci sono un paio di meccanismi di autenticazione incorporati in MVC, inclusi Windows Forms, Basic, ecc., e alcuni che possono essere facilmente abilitati con pacchetti di nuget come Oauth, ecc.

Se è necessario un metodo di autenticazione personalizzato, è possibile derivare dall'attributo Authorize e aggiungerlo alla configurazione globale. Un altro metodo che ho usato in passato quando si lavora con uno scenario di tipo api REST personalizzato, consiste nell'aggiungere un "ActionFilter" che viene eseguito prima di chiamare la rotta sul controller.

Altre informazioni: link link

Overflow dello stack correlato: link

Un sacco di altre informazioni là fuori, basta fare qualche ricerca su google.

Intorno a dove mettere la tua reale logica di autenticazione, generalmente dovrai interrogare utenti e ruoli da qualche parte, se uno scenario personalizzato, aggiungi questa logica al tuo livello di accesso ai dati, aggiungi una sorta di "AuthorizationService" alla tua logica aziendale, e consumarlo tramite il tuo attributo / filtro di autorizzazione personalizzato.

    
risposta data 19.02.2014 - 20:17
fonte