Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [Algorytm][Funkcje] do ACL i nawigacji
Forum PHP.pl > Forum > Gotowe rozwiązania > Algorytmy, klasy, funkcje
cve
Witam. Jestem bardzo początkujący w temacie php, ale duużżżooo czytam i zacząłem od jakiegoś czasu rozwijać swój framework i tutaj na forum chciałbym zaprezentować swoje przemyślenia na temat ACL jakie zastosowałem. Otóż w żadnym znanym mi frameworku (Zend, Cake, Symfony, Code Igniter, Kohana) nie umiałem łatwo i szybko zaimplementować takiej jakby automatycznej autoryzacji użytkownika chodziło mi o sprawdzenie praw dostępu do zasobu już na poziomie front-controllera, który zobaczy czy w Liście Kontroli Dostępu znajduje się żądany zasób dla obecnej roli użytkownika i albo 'wpuszcza' go i włącza odpowiednią akcje controllera albo nie 'wpuszcza' i kieruje na wiadomość o braku dostępu. Wydaje mi się to lepszym rozwiązaniem niż pisanie w controllerach instrukcji if i else za każdym razem. Oto jak to jest zaimplementowane:
We folderze config trzymam sobie prosty plik xml, który wgląda następująco:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <acl>
  3. <guest>
  4. <resource name="/" label="Strona główna" />
  5. <resource name="/user/login" label="Zaloguj się" />
  6. <resource name="/user/register" label="Zarejestruj się" />
  7. </guest>
  8.  
  9. <user>
  10. <resource name="/user/logout" label="Wyloguj się" />
  11. <resource name="/user/login" label="" />
  12. <resource name="/user/register" label="" />
  13. </user>
  14.  
  15. <moderator>
  16. </moderator>
  17.  
  18. <admin>
  19. </admin>
  20. </acl>

jak widać chyba nic nie trzeba tutaj tłumaczyć, jedyne co może wzbudzać zainteresowanie to dlaczego w roli user zasoby /user/login i user/register maja pusty label, ale to później wytłumaczę, bo jest jeszcze jedna funkcjonalność tego podejścia, o której za chwile napisze.

A tak wygląda kod:
  1. //najpierw potrzebujemy role, jest ona brana z bazy, a jeśli nie to domyślną jest guest
  2.  
  3. $rola = (isset($_SESSION['user']['rola'])) ? $_SESSION['user']['rola'] : 'guest';
  4.  
  5. //teraz w obiekt acl pobieramy wcześniejszy plik xml
  6.  
  7. $acl = simplexml_load_file('../config/acl.xml');
  8.  
  9. //budujemy tablice dwuwymiarową z rolami i odpowiadającymi im zasobami z pliku xml
  10.  
  11. foreach($acl as $role) {
  12. foreach($role as $res) {
  13. $label = (string)$res['label'];
  14. $ACL[$role->getName()][(string)$res['name']] = ($label == '') ? null : $label;
  15. }
  16. }
  17.  
  18. //teraz czas na funkcje, która tworzy powiązania (dziedziczenie) ról jakie chcemy ze sobą
  19. //oraz tworzy nam na podstawie powiązań nawigacje gotową dla obecnie zalogowanego użytkownika z odpowiednią rolą
  20. //tutaj właśnie są nam potrzebne te puste atrybuty 'label' w zasobach, bo po co mamy wyświetlać zalogowanemu użytkownikowi
  21. //linki do zalogowania albo do zarejestrowania się, skoro on już to zrobił :) Dajemy mu tylko możliwość do wylogowania. :)
  22. //funkcja po prostu wyrzuca sobie takie powtarzające się zasoby z pustymi labelami i daje Nam ładną tablicę z nawigacją.
  23.  
  24. function setInheritAndNav () {
  25.  
  26. $args = func_get_args();
  27.  
  28. foreach($args as $key => $val) {
  29. if($val == null) {
  30. unset($args[$key]);
  31. }
  32.  
  33. }
  34.  
  35. foreach($args as $tab) {
  36. $newArgs[] = $tab;
  37. }
  38.  
  39. $nav = array_reverse($newArgs[0]);
  40.  
  41. foreach($newArgs as $tab) {
  42. if($tab != null) {
  43.  
  44. $nav += array_reverse($tab);
  45. }
  46. }
  47.  
  48. foreach($nav as $key => $val) {
  49. if($val == null) {
  50. unset($nav[$key]);
  51. }
  52. }
  53.  
  54. $nav = array_reverse($nav);
  55.  
  56. return $nav;
  57.  
  58. }
  59.  
  60. //tutaj w prosty sposób definiujemy sobie jakie role jak mają zostać dziedziczone
  61. //korzystając z wyżej wymienionej funkcji, dziedziczenie działa od lewej strony, czyli
  62. //tak jak np. dla admina: admin -> moderator -> user -> guest ('->' oznacza 'dziedziczy po'),
  63. //a nawet mozemy zrobic tak guest -> user -> moderator -> admin :) wtedy guest dziedziczy po naszych wszystkich rolach :)
  64.  
  65. $guest = setInheritAndNav($ACL['guest']);
  66. $user = setInheritAndNav($ACL['user'], $ACL['guest']);
  67. $admin = setInheritAndNav($ACL['admin'], $ACL['moderator'], $ACL['user'], $ACL['guest']);
  68.  
  69. //tworzymy sobie dwuwymiarową tablicę z nawigacjami dla ról
  70.  
  71. $NAV = array('guest' => $guest, 'user' => $user, 'admin' => $admin);
  72.  
  73. //żeby w prosty sposób sprawdzić sobie właśnie czy żądany zasób znajduje się ACL:
  74.  
  75. $resources = array_keys($NAV[$rola]);
  76.  
  77. //i działanie prostego front-controllera:
  78. //w adresie trzymam zmienną url, która przechowuje mi kombinację czystych urli np. '/index/index/' lub '/user/login' albo nic czyli '/' itp.
  79.  
  80. $_url = strip_tags($_GET['url']);
  81.  
  82. $urlArray = array();
  83. $urlArray = explode('/', $_url);
  84.  
  85. $_controller = $urlArray[0] ? $urlArray[0] : 'index';
  86. $_action = $urlArray[1] ? $urlArray[1] : 'index';
  87. $_parameter = $urlArray[2] ? $urlArray[2] : '1';
  88.  
  89. if(in_array('/'.$_controller.'/'.$_action, $resources) || in_array('/', $resources)) {
  90.  
  91. $controllerName = ucfirst($_controller).'Controller';
  92.  
  93. $run = new $controllerName($_controller, $_action, $NAV[$rola]);
  94.  
  95. $run->$_action($_parameter);
  96.  
  97. }
  98. else
  99. {
  100. URL::_redirect();
  101. }


prawie wszystko dzieje się metodą strukturalną, bo po co obciążać cały system obiektówką, tam gdzie nie jest ona potrzebna smile.gif.

Chciałbym usłyszeć jakieś opinie lub uwagi co można jeszcze poprawić, usprawnić lub zmienić.
darko
Cytat(cve @ 5.01.2010, 14:07:46 ) *
Witam. Jestem bardzo początkujący w temacie php, ale duużżżooo czytam i zacząłem od jakiegoś czasu rozwijać swój framework i tutaj na forum chciałbym zaprezentować swoje przemyślenia na temat ACL jakie zastosowałem. Otóż w żadnym znanym mi frameworku (Zend, Cake, Symfony, Code Igniter, Kohana) nie umiałem łatwo i szybko zaimplementować takiej jakby automatycznej autoryzacji użytkownika chodziło mi o sprawdzenie praw dostępu do zasobu już na poziomie front-controllera, który zobaczy czy w Liście Kontroli Dostępu znajduje się żądany zasób dla obecnej roli użytkownika i albo 'wpuszcza' go i włącza odpowiednią akcje controllera albo nie 'wpuszcza' i kieruje na wiadomość o braku dostępu.

To coś słabo szukałeś, w ZF można sobie napisać plugin i zarejestrować w Bootstrap, coś np. takiego:

  1. class Plugin_AccessCheck extends Zend_Controller_Plugin_Abstract {
  2.  
  3. private $_acl = null;
  4.  
  5. public function __construct(Zend_Acl $acl) {
  6. $this->_acl = $acl;
  7. }
  8.  
  9. public function preDispatch(Zend_Controller_Request_Abstract $request) {
  10.  
  11. $module = $request->getModuleName();
  12. $resource = $request->getControllerName();
  13. $action = $request->getActionName();
  14.  
  15. if(!$this->_acl->isAllowed(Zend_Registry::get("role"), $module. ':' . $resource, $action)) {
  16. // tutaj przekierowujemy jeśli user nie jest zalogowany
  17. $request//->setModuleName("NAZWA_MODUŁU")
  18. ->setControllerName("NAZWA_KONTROLERA")
  19. ->setActionName("NAZWA_AKCJI");
  20. }
  21.  
  22. }
  23.  
  24. }


zapisujemy do folderu plugins plik o nazwie AccessCheck.php i dalej w Bootstrap.php rejestrujemy nasz plugin:
  1. class Bootstrap extends Zend_Application_Bootstrap_Bootstrap {
  2. // (...)
  3. private $_acl = null;
  4. // (...)
  5. protected function _initAutoload() {
  6. // (...)
  7. //$this->_acl = new Model_Acl();
  8. $fc = Zend_Controller_Front::getInstance();
  9. $fc->registerPlugin(new Plugin_AccessCheck($this->_acl));
  10. return $autoloader;
  11. }
  12. // (...)
  13. }



Co do Twojego kodu to mam trzy uwagi:
1. w tym przypadku kod proceduralny będzie niezauważalnie szybszy czy wydajniejszy od obiektowego, ale kodem obiektowym łatwiej zarządzać i modyfikować
2. chyba zapomniałeś, jak wygląda Acl i navigation.xml w przypadku, jeśli wprowadzimy do aplikacji podział na moduły
3. widzę, że jak na razie nie przewidujesz komunikacji z bazą w celu pobrania ról, zasobów i uprawnień, a szkoda...
Pozdrawiam
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2024 Invision Power Services, Inc.