2012-04-17 7 views
8

Nel mio Symfony 2 app che ho 3 diversi ruoli utente che possono avere accesso a una parte di amministrazione backend:Associare una via a diversi controller a seconda dei ruoli utente

role_hierarchy: 
    ROLE_STAFF:  ROLE_USER 
    ROLE_MODERATOR: ROLE_STAFF 
    ROLE_ADMIN:  ROLE_MODERATOR 

Per un percorso come http://example.org/admin/post/, mi piacerebbe come la mia app per visualizzare informazioni diverse a seconda del ruolo dell'utente, che significa 3 controller vincolante su un solo route.

Qual è il modo migliore per gestire questo?

Stavo pensando di alcune soluzioni, ma nessuno sembra essere buono per me:

  1. Un controller, e in ogni azione Ho appena prova ruolo utente:

    <?php 
    
    /** 
    * @Route("/admin/post") 
    */ 
    class PostController extends Controller 
    { 
        /** 
        * Lists all post entities. 
        * 
        * @Route("/", name="post_index") 
        * @Template() 
        * @Secure(roles="ROLE_STAFF") 
        */ 
        public function indexAction() 
        { 
         $user = $this->get('security.context')->getToken()->getUser(); 
    
         if ($this->get('security.context')->isGranted('ROLE_STAFF')) { 
          // Do ROLE_STAFF related stuff 
         } else if ($this->get('security.context')->isGranted('ROLE_MODERATOR')) { 
          // Do ROLE_MODERATOR related stuff 
         } else if ($this->get('security.context')->isGranted('ROLE_ADMIN')) { 
          // Do ROLE_ADMIN related stuff 
         } 
    
         return array('posts' => $posts); 
        } 
    } 
    

    Anche se che fa il lavoro, IMO ovviamente non è un buon progetto.

  2. Uno BackendController che la spedizione a 3 diversi controller:

    <?php 
    
    /** 
    * @Route("/admin/post") 
    */ 
    class PostBackendController extends Controller 
    { 
        /** 
        * Lists all post entities. 
        * 
        * @Route("", name="admin_post_index") 
        * @Template("AcmeBlogBundle:PostAdmin:index.html.twig") 
        * @Secure(roles="ROLE_STAFF") 
        */ 
        public function indexAction() 
        { 
         if ($this->get('security.context')->isGranted('ROLE_STAFF')) { 
          $response = $this->forward('AcmeBlogBundle:PostStaff:index'); 
         } else if ($this->get('security.context')->isGranted('ROLE_MODERATOR')) { 
          $response = $this->forward('AcmeBlogBundle:PostModerator:index'); 
         } else if ($this->get('security.context')->isGranted('ROLE_ADMIN')) { 
          $response = $this->forward('AcmeBlogBundle:PostAdmin:index'); 
         } 
    
         return $response; 
        } 
    } 
    

    Uguale al numero uno.

  3. Ho provato a fare i controllori si estende ogni altri:

    <?php 
    
    /** 
    * @Route("/admin/post") 
    */ 
    class PostStaffController extends Controller 
    { 
        /** 
        * Lists all post entities. 
        * 
        * @Route("/", name="post_index") 
        * @Template() 
        * @Secure(roles="ROLE_STAFF") 
        */ 
        public function indexAction() 
        { 
         $user = $this->get('security.context')->getToken()->getUser(); 
    
         // Do ROLE_STAFF related stuff 
    
         return array('posts' => $posts); 
        } 
    } 
    
    <?php 
    
    /** 
    * @Route("/admin/post") 
    */ 
    class PostModeratorController extends PostStaffController 
    { 
        /** 
        * Lists all post entities. 
        * 
        * @Route("/", name="post_index") 
        * @Template() 
        * @Secure(roles="ROLE_MODERATOR") 
        */ 
        public function indexAction() 
        { 
         $user = $this->get('security.context')->getToken()->getUser(); 
    
         // As PostModeratorController extends PostStaffController, 
         // I can either use parent action or redefine it here 
    
         return array('posts' => $posts); 
        } 
    } 
    
    <?php 
    
    /** 
    * @Route("/admin/post") 
    */ 
    class PostAdminController extends PostModeratorController 
    { 
        /** 
        * Lists all post entities. 
        * 
        * @Route("/", name="post_index") 
        * @Template() 
        * @Secure(roles="ROLE_ADMIN") 
        */ 
        public function indexAction() 
        { 
         $user = $this->get('security.context')->getToken()->getUser(); 
    
         // Same applies here 
    
         return array('posts' => $posts); 
        } 
    } 
    

    IMO è un disegno migliore, ma non riesco a fare funziona. Il sistema di routing si ferma sul primo controller che corrisponde. Mi piacerebbe farlo agire come re dello stile a cascata automaticamente (ad esempio se l'utente è personale, allora vai a PostStaffController, altrimenti se l'utente è moderatore vai su PostModeratorController, altrimenti vai su PostAdminController).

  4. Aggiungi un listener a kernel.controller nel mio BlogBundle che farà lo stesso lavoro del numero 2?

Sto cercando il miglior design e la soluzione più flessibile ci sono possibilità che aggiungiamo più ruoli in futuro.

+0

Sto affrontando la stessa identica situazione, hai trovato una buona soluzione? –

+0

Tutte le soluzioni sono buone ma progettate male. Se affronti lo stesso problema, assicurati che non sia una cattiva idea della tua applicazione. Nel mio caso invece di fare questo ho creato 2 tipi di moduli diversi per la mia entità: "configurazione" e "personalizzazione". Quindi l'amministratore può accedere ai controller "di configurazione" e "personalizzazione" mentre lo staff e il moderatore possono accedere solo alla "personalizzazione". Non sono sicuro se è chiaro. Forse dovrei renderlo una risposta completa? – iamdto

+0

Hai ragione, era un cattivo design. La mia soluzione era di separare le diverse aree dell'applicazione in diversi pacchetti in cui posso gestire i ruoli di conseguenza. Grazie. –

risposta

0

Che ne dici di una versione automatizzata della seconda soluzione? Ad esempio:

// Roles ordered from most to least significant (ROLE_ADMIN -> ROLE_MODERATOR -> etc) 
    $roles = $myUserProvider->getRoles(); 
    foreach ($roles as $role) { 
     // add a check to test, if the function you're calling really exists 
     $roleName = ucfirst(strtolower(mb_substr($role, 0, 5))); 
     $response = $this->forward(sprintf('AcmeBlogBundle:Post%s:index', $roleName)) 

     break; 
    } 

    // Check that $response is not null and do something with it ... 

Poiché non ho la configurazione, non ho testato il codice sopra. A proposito: qual è la differenza tra il diverso metodo per pubblicare qualcosa?

+0

Supponiamo che tu voglia visualizzare un post nell'area di amministrazione: vai a 'http://example.org/admin/post/ {id}'.I moderatori avranno una versione più leggera della vista, mentre l'amministratore vedrà ** tutte ** le informazioni sull'entità (i moderatori sono clienti, gli amministratori sono sviluppatori e le persone della nostra azienda). Non voglio sovraccaricare le mie viste, i tipi di moduli, i controller ecc. Con un sacco di 'if'' else', ecco perché ho bisogno di un dispatcher di questo tipo. Non sono sicuro che sia una buona decisione di progettazione, ma questa è l'unica soluzione che ho trovato al momento. – iamdto

0

in vendor/symfony/symfony/src/Symfony/Component/Routing/Router.php

V'è la possibilità di sostituire il matcher_class che dovrebbe essere possibile in config.yml.

Se la sottoclasse UrlMatcher e override matchRequest avrà la precedenza sulla corrispondenza Percorso (solo url).

matchRequest prende una richiesta parametro $ (Request oggetto)

L'oggetto richiesta deve contenere le informazioni utente fornita l'ascoltatore fornitore di sicurezza viene eseguito prima l'ascoltatore router e consentono di selezionare il percorso unendo l'URL e l'utente ruolo. I percorsi sono memorizzati in una matrice indicizzata per nome, quindi i nomi dovranno essere diversi.

Si potrebbe utilizzare nomi come post_index[USER]post_index[STAFF]post_index[MODERATOR]

Al fine di generare gli URL con {{ path('post_index', {...}) }} sarà anche necessario per sostituire la sottoclasse il URLGenerator e iniettare che nel router con l'opzione generator_class.

1

IMHO, non si spengono diversi controller per lo stesso percorso in base ai ruoli. Sono solo diverse responsabilità. Le rotte sono per il controller selezionato, il ruolo per i privilegi. Dopo un anno non ricorderai il trucco, ad es. quando proverai ad aggiungere un nuovo ruolo.

Naturalmente il problema dei contenuti diversi per ruoli diversi è abbastanza spesso, quindi le mie soluzioni preferite in questo caso sono:

  1. Quando il controller per diversi ruoli è molto diverso, io uso percorsi diversi con reindirizzamento quando necessario.
  2. Quando il controller è simile ma il contenuto è diverso, es. Diverse condizioni di interrogazione del database, utilizzo una soluzione simile alla tua 2. ma, invece, preferisco utilizzare metodi privati ​​/ protetti dallo stesso controller per realizzare il Job. C'è un trucco: è necessario controllare il ruolo dall'alto verso il basso, ad es. prima controlla ROLE_ADMIN, il prossimo ROLE_OPERATOR e l'ultimo ROLE_STAFF, perché quando il tuo ROLE_ADMIN eredita da ROLE_STAFF, quindi blocca per l'utente catturarlo.
  3. Quando la differenza è solo in alcuni blocchi di informazioni che devono essere mostrati/nascosti per ruoli diversi, resto con un controller e controllo il ruolo nel modello per determinare quale blocco esegua o meno.
Problemi correlati