2012-04-17 20 views
8

W mojej aplikacji Symfony 2 mam 3 różne role użytkowników, które mogą mieć dostęp do części administracyjnej backend:związania jednej trasy do różnych sterowników w zależności od ról użytkowników

role_hierarchy: 
    ROLE_STAFF:  ROLE_USER 
    ROLE_MODERATOR: ROLE_STAFF 
    ROLE_ADMIN:  ROLE_MODERATOR 

Na trasie jak http://example.org/admin/post/, ja podobnie jak moja aplikacja do wyświetlania różnych informacji w zależności od roli użytkownika, czyli 3 kontrolerów powiązanie z tylko trasą.

Jaki jest najlepszy sposób, aby sobie z tym poradzić?

Myślałam o pewnych rozwiązań, ale nikt zdaje się być dobre dla mnie:

  1. Jeden sterownik, iw każdym działaniu Właśnie testy rola użytkownika:

    <?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); 
        } 
    } 
    

    Nawet jeśli to robi praca, IMO oczywiście to nie jest dobry projekt.

  2. Jeden BackendController że wysyła do 3 różnych regulatorów:

    <?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; 
        } 
    } 
    

    samo jak numer jeden.

  3. Starałem się uczynić kontrolery rozciąga się nawzajem:

    <?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 jest to lepszy projekt, ale nie uda się zrobić to działa. System routingu zatrzymuje się na pierwszym kontrolowanym kontrolerze. Chciałbym, aby działał automatycznie w stylu kaskadowym automatycznie (tj. Jeśli użytkownik jest personelem, wówczas należy przejść do PostStaffController, w przeciwnym razie, jeśli użytkownik jest moderatorem, przejdź do PostModeratorController, w przeciwnym razie przejdź do PostAdminController).

  4. Dodaj detektora do kernel.controller w moim BlogBundle, który wykona tę samą pracę co numer 2?

Szukam najlepiej zaprojektowanego i bardziej elastycznego rozwiązania, ponieważ istnieje szansa, że ​​dodamy więcej ról w przyszłości.

+0

Mam do czynienia z dokładnie taką samą sytuacją, czy znalazłeś dobre rozwiązanie? –

+0

Wszystkie rozwiązania są dobre, ale są źle zaprojektowane. Jeśli napotkasz ten sam problem, najpierw upewnij się, że to nie jest zła koncepcja twojej aplikacji. W moim przypadku zamiast tego zrobiłem 2 różne formy dla mojej jednostki: "konfiguracja" i "personalizacja". Dzięki temu administrator ma dostęp do kontrolerów "konfiguracyjnych" i "dostosowawczych", a personel i moderator może uzyskać dostęp tylko do "dostosowywania". Nie jestem pewien, czy jest jasne. Być może powinienem zrobić to kompletną odpowiedź? – iamdto

+0

Masz rację, to był zły projekt. Moim rozwiązaniem było rozdzielenie różnych obszarów aplikacji w różnych pakietach, w których mogę odpowiednio zarządzać rolami. Dzięki. –

Odpowiedz

0

Co powiesz na automatyczną wersję drugiego rozwiązania? Podobnych:

// 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 ... 

Ponieważ nie mam konfiguracji, nie przetestowałem powyższego kodu. Btw: jaka jest różnica między różnymi metodami publikowania czegoś?

+0

Załóżmy, że chcesz zobaczyć wpis w obszarze administracyjnym: przechodzisz do 'http://example.org/admin/post/ {id}'.Moderatorzy będą mieli lżejszą wersję widoku, natomiast administrator zobaczy ** wszystkie ** informacje o encji (moderatorzy to klienci, administratorzy to programiści i ludzie z naszej firmy). Nie chcę przeciążać moich widoków, typów formularzy, kontrolerów itp. Z dużą ilością 'if'' else', dlatego właśnie potrzebuję tego typu dispatchera. Nie jestem pewien, czy to dobra decyzja dotycząca projektu, ale to jedyne rozwiązanie, jakie znalazłem w tej chwili. – iamdto

0

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

Istnieje możliwość zastąpienia matcher_class co powinno być możliwe w config.yml.

Jeśli podklasa UrlMatcher i overRide matchRequest ma pierwszeństwo przed dopasowaniem do ścieżki (tylko adres URL).

matchRequest przyjmuje parametr $ request (Request Object)

Przedmiotem Żądanie powinno zawierać informacje o użytkowniku przewidzianego słuchacza dostawca zabezpieczeń biegnie przed słuchaczem routera i pozwala wybrać trasę przez łączenie URL i User Role. Trasy są przechowywane w tablicy indeksowanej według nazwy, więc nazwy muszą być różne.

Można ewentualnie użyć nazwy jak post_index[USER]post_index[STAFF]post_index[MODERATOR]

W celu generowania adresów URL zawierających {{ path('post_index', {...}) }} będzie trzeba także wymienić podklasy URLGenerator i wstrzyknąć że do routera z opcją generator_class.

1

IMHO, Nie możesz rozpalać różnych kontrolerów na tę samą trasę w zależności od ról. To tylko różne obowiązki. Trasy są dla kontrolera select, role dla uprawnień. Po roku nie będziesz pamiętał lewy, czyli. kiedy spróbujesz dodać nową rolę.

Oczywiście problem różnej zawartości dla różnych ról jest dość często, więc moje ulubione rozwiązania w tym przypadku to:

  1. Gdy sterownik dla różnych ról różni się zbytnio, używam różnych tras z przekierowaniem kiedy potrzebne.
  2. Gdy kontroler jest podobny, ale zawartość jest inna, tj. w różnych warunkach zapytania do bazy danych używam rozwiązania podobnego do twojego 2., ale zamiast forwadingu, użyj prywatnych/chronionych metod z tego samego kontrolera, aby wykonać zadanie. Jest jeden hack - musisz sprawdzić rolę od góry do dołu, czyli. najpierw sprawdź ROLE_ADMIN, następna ROLE_OPERATOR i ostatnia ROLE_STAFF, ponieważ gdy twoja rola ROLE_ADMIN dziedziczy po ROLE_STAFF, blokuj, aby użytkownik ją złapał.
  3. Gdy różnica dotyczy tylko niektórych bloków informacji, które powinny być pokazywane/ukrywane dla różnych ról, pozostaje mi jeden kontroler i sprawdzam rolę w szablonie, aby określić, który blok jest renderowany lub nie.
Powiązane problemy