Każda aplikacja webowa, tak jak każdy plik na dysku, musi mieć ustawione uprawnienia dla konkretnych użytkowników, lub grup użytkowników. O listach kontroli dostępu ( ang. Access Control List) zapewne słyszał każdy. Jedną z prostszych implementacji takiej listy jest zwykła baza loginów i haseł dostępowych. Co zrobić, gdy jednak potrzebujemy bardziej zaawansowanego systemu kontroli dostępu? Możemy wykorzystać jedną z gotowych bibliotek, np. PHP Generic Access Control List, ale możemy także sami napisać taki system - dobrze jest wtedy wykorzystać częściowo napisane mechanizmy, na przykład pochodzący z Zend Frameworka - Zend_Acl.
Omówmy powyższą tabelę. Zapewne zastanawiacie się, dlaczego "Administrator" nie ma uprawnień prywatnych i dziedziczonych? Z prostego powodu, kontroler dostępu będzie identyfikował rolę. Jeśli rolą będzie "Administrator", to nie sprawdzamy wtedy uprawnień. Jeśli byśmy chcieli utworzyć listę ról korzystając z biblioteki Zend_Acl, powinniśmy napisać takikod:
<?php $acl = new Zend_Acl(); // Dodajemy rolę - Gość $acl->addRole(new Zend_Acl_Role("Guest")); // Dodajemy rolę - Użytkownik - i dziedziczymy z Gościa $acl->addRole(new Zend_Acl_Role("User"), "Guest"); // Dodajemy rolę - Moderator - i dziedziczymy z Użytkownika $acl->addRole(new Zend_Acl_Role("Moderator"), "User"); // Dodajemy rolę - Administrator - nie dziedziczymy $acl->addRole(new Zend_Acl_Role("Administrator")); ?>
Bardzo dobrym rozwiązaniem, wykorzystanym w Zend_Acl, jest możliwość ustawienia globalnych uprawnień dla roli. Przykładowo, jeśli chcielibyśmy aby Gość miał globalne uprawnienia na przeglądanie, wystarczy to po prostu ustalić przy tworzeniu uprawnienia. Metoda Zend_Acl::allow() przyjmuje cztery parametry: identyfikator roli, identyfikator zasobu, tablicę z uprawnieniami oraz obiekt typu Zend_Acl_Assert_Interface, czyli obiekt precyzujący dostęp. Dzięki temu, możemy ustalić, że Gość ma prawo do czytania artykułu, chyba, że jego adres IP jest zapisany w tzw. blacklist, czyli liście adresów zablokowanych. Do kwestii precyzowania wrócę w późniejszym etapie. Zajmijmy się teraz utworzeniem zasobu oraz podstawowych uprawnień dostępu do niego.
<?php // Dodaj zasob - Artykul $acl->add(new Zend_Acl_Resource("Article")); // Dodaj uprawnienia Gosciom do przegladania wszystkich zasobow $acl->allow("Guest", null, "view"); // Dodaj uprawnienia Uzytkownikowi do oceniania artykulu $acl->allow("User", "Article", "rate"); // Dodaj uprawnienia Moderatorowi do edycji, publikacji i ukrycia artykulu $acl->allow("Moderator", "Article", array("edit", "public", "hide")); // Dodaj role Administrator, z maksymalnymi uprawnieniami $acl->allow("Administrator"); ?>
Samo sprawdzanie dostępu powinno działać w bardzo prosty sposób. Żądamy informacji na temat tego, czy dana rola ma dostęp do konkretnego uprawnienia zasobu. Wykorzystujemy metodę isAllowed() i mamy wszystko, jak na tacy:
<?php // Zalozmy, ze uzytkownik ma role Moderator $userRole = "Moderator"; if ( $acl->isAllowed($userRole, "Article", "edit") ) { echo "You are allowed to edit this article."; } else { echo "You aren’t allowed to edit this article."; } ?>
Bardzo proste, prawda? Spróbujmy teraz sprecyzować, z jakiego adresu IP musi łączyć się moderator, by miał dostęp do moderacji artykułu. Zatem, utwórzmy obiekt implementujący Zend_Acl_Assert_Interface:
<?php class AllowedIPAssertion implements Zend_Acl_Assert_Interface { private $allowedIps; public function assert(Zend_Acl $acl, Zend_Acl_Role_Interface $role=null, Zend_Acl_Resource_Interface $resource=null, $privilege = null) { $this->allowedIps = array("127.0.0.1", "192.168.0.1", "83.27.252.11"); return $this->_isIPAllowed($_SERVER["REMOTE_ADDR"]); } private function _isIPAllowed($ip_address) { if ( in_array($ip_address, $this->allowedIps) ) { return true; } return false; } } ?>
Wróćmy teraz do tworzenia uprawnień i poprawmy uprawnienia moderatora:
<?php // Dodaj uprawnienia Moderatorowi do edycji, publikacji i ukrycia artykulu // ale tylko wtedy, gdy spelnia kryteria AllowedIPAssertion $acl->allow( "Moderator", "Article", array("edit", "public", "hide"), new AllowedIPAssertion() ); ?>
Polecam artykuł pana Tomasza Jędrzejewskiego na temat budowy systemu uprawnień http://artykuly.zyxist.com/czytaj.php/system_uprawnien_w_php
Chętnie poczytałbym o propozycjach przechowywania ACL w bazie.
Przedstawiony kod to Role Based Access Control, czyli bazuje na rolach. Uprawnienia przypisuje się roli a nie użytkownikowi.
Acces Control List jest drobnoziarniste, tzn. można przyznać jednemu użytkownikowi prawo do edycji danego obiektu (artykuł, nowość).
Coś to słabo widzę :)
Rola powinna chyba kolekcjonować uprawnienia, a grupa kolekcję użytkowników, w tym przypadku mamy trochę pomieszanie odpowiedzialności, ale jeśli takie były założenia autora to nie a się co czepiać. Implementacji ACL jest tyle, ile implementacji MVC ;)
artykul, slaby. takich powierzchownych opisow acl jest w sieci mnostwo. a co gorsza, wiecej jest w dokumentacji zenda. [jest tez przetlumaczona wersja dokumentacji]
Dobry, bo poruszający konkretny temat z bardziej zaawansowanej dziedziny. Oby takie artykuły też były promowane, nie tylko wyjaśnienia OOP i tym podobne.