Pakiet w teorii zakłada, że programista wczytuje do pamięci skryptu PHP wszystkie reguły uprawnień dla wszystkich grup, jakie tylko występują w aplikacji i dla wszystkich definiuje zasady dostępu do określonych zasobów. O ile w przypadku niewielkiego lub banalnie prostego mechanizmu nie jest to szczególnie wielki problem, to przy pobieraniu odpowiednich danych z bazy przeważnie pobiera się tylko niewielki wycinek listy uprawnień, najczęściej dotyczący aktualnie zalogowanego użytkownika lub grupy w przypadku wizyt anonimowych. W efekcie, korzystając ze standardowego kodu ZF-a, musimy używać bardziej rozbudowanego API oraz z bardziej wymagających algorytmów sprawdzających.
Napisanie własnej obsługi ACL odpowiednio przykrojonej na potrzeby optymalnego wykorzystania bazy danych nie okazało się dużym problemem (hehe, wiem - niedługo z Zend Frameworka w moim silniku nie zostanie nic, poza ideą, ale trudno :D). Należało jednak samodzielnie zaprogramować obsługę drzewiastej struktury listy uprawnień, najlepiej na pojedynczej tablicy, aby uniknąć zabawy z referencjami i wielowymiarowymi potworkami.
<?php public function isAllowed($rule) { switch($this -> state) { case ACL_CHECK: $items = explode('/', $rule); $id = 0; $x = NULL; $i = 0; $cnt = sizeof($items); foreach($items as $item) { if(is_null($x = $this -> findNode($id, $item))) { return false; } elseif($i == $cnt - 1) { return $this -> rules[$x][1]; } $id = $x; $i++; } return false; case ACL_ALLOW_ALL: return true; case ACL_DENY_ALL: return false; } } // end isAllowed(); public function insertRule($resource, $state) { $items = explode('/', $resource); $id = 0; $x = NULL; $i = 0; $cnt = sizeof($items); foreach($items as $item) { if(is_null($x = $this -> findNode($id, $item))) { $x = $this -> insertNode($id, $item, $i != $cnt - 1 ? false : $state); } elseif($i == $cnt - 1) { $this -> rules[$x][1] = $state; } $id = $x; $i++; } } // end insertRule(); private function insertNode($parent, $name, $state) { $this -> rules[$this->size] = array( 0 => $name, // Name 1 => $state, // State 2 => $parent, // Parent 3 => -1, // Next 4 => -1, // First Child 5 => -1 // Last Child ); if($parent >= 0) { if($this -> rules[$parent][5] != -1) { $this -> rules[$this->rules[$parent][5]][3] = $this->size; $this->rules[$parent][5] = $this -> size; } else { $this->rules[$parent][5] = this->rules[$parent][4] = $this -> size; } } $this -> size++; return $this -> size - 1; } // end insertNode(); private function findNode($parent, $name) { if(isset($this -> rules[$parent])) { $id = $this -> rules[$parent][4]; while($id != -1) { if($this -> rules[$id][0] == $name) { return $id; } $id = $this -> rules[$id][3]; } } return NULL; } // end findNode(); ?>
Nowy ACL nie jest przystosowany do jednej, konkretnej reprezentacji uprawnień w bazie danych. W przyszłych projektach budowanych na bazie tego silnika na pewno pojawią się różne specyficzne wymagania, których nie będzie dało się uwzględnić w pojedynczej, uniwersalnej tabeli uprawnień. Dlatego do kompletu dorzuciłem interfejs ładowarki uprawnień, którego implementacja da nam obsługę pojedynczego źródła listy uprawnień. W trakcie przetwarzania skryptu uprawnienia można ładować z kilku takich źródeł, po prostu przekazując odpowiednie obiekty w miarę potrzeb:
$acl = new Terenzzia_Acl; $generalAclSource = new Terenzzia_Acl_General; $generalAclSource -> setOptions($session -> get('Auth', 'userId'), $config -> terenzzia -> primaryRole); $acl -> addRules($generalAclSource);
Aktualnie brakuje jeszcze dziedziczenia uprawnień po innej grupie i jeszcze się nie zastanawiałem, w jaki sposób to zrealizować. Są dwie możliwości:
- Dziedziczenie statyczne - w momencie tworzenia w panelu administracyjnym nowej grupy i ustawieniu dziedziczenia, w bazie danych powstają kopie rekordów. Zalety: prostsze zapytania :). Wady: zmiana uprawnień w grupie nadrzędnej nie jest widoczna w grupach potomnych.
- Dziedziczenie dynamiczne - w bazie danych zapisane są tylko informacje, która grupa po której dziedziczy i mechanizm ACL na ich podstawie pobiera odpowiednie uprawnienia. Tu z kolei mamy przeciwieństwo, tzn. większe możliwości za cenę wydajności i stopnia komplikacji problemu.
Trzeba się teraz zastanowić, na czym mi bardziej zależy, ew. co mi się bardziej chce zaprogramować :).







Napisał MiB w niedzielę, 13 maja 2007 o 17:20
Witam
Implementacja uprawnień grupowych zależy również od potrzeb. W mniejszym serwisie (o mniejszej ilości przewidywanych odwiedzin) można sobie pozwolić na metodę drugą. W większym - na pierwszą.
Gdyby metodą pierwszą wzbogacić o aktualizowanie potomków w momencie edycji przodka, byłoby całkiem sympatycznie :)
(a aby nie napisać się zbyt wiele, można po prostu czyścić tabele uprawnień i wpisywać na nowo - przecież zmiany w uprawnieniach nie będą następować co 10 minut...).
Pozdrawiam