napisałem sobie prościutki moduł do obsługi uprawnień i chciałbym się nim z Wami podzielić. Poniżej przedstawiam wszystkie wymagane pliki oraz przykład użycia z wykorzystaniem kontroli dostępu do akcji kontrolera. Równie dobrze moduł może zostać wykorzystany do kontroli dostępu do innych zasobów.
application/modules/authorization/libraries/Authorization.php
<?php /** * Klasa realizująca obsługę uprawnień w systemie. * * @package authorization */ class Authorization { /** * Instancja klasy Authorization. * * @var Authorization */ /** * Nazwa roli aktualnego użytkownika. * * @var string */ private $role; /** * Tablica uprawnień. * * @var array */ /** * Domyślna wartość uprawnień. * * @var boolean */ private $default; /** * Zwraca instancję klasy Authorization * * @return Authorization */ self::$instance = new Authorization(); } return self::$instance; } /** * Wczytuje konfigurację uprawnień z pliku zewnętrznego. * * Dodatkowo ustawia domyślną wartość o ile została ustalona w pliku konfiguracyjnym. */ public function loadConfig() { $this->default = (bool)$this->credentials[self::DEFAULT_KEY]; } else { $this->default = self::DEFAULT_VALUE; } } /** * Dodaje uprawnienia do danego zasobu. * * @param string $resource Nazwa zasobu. */ public function allow($resource) { $this->credentials[$resource] = true; } /** * Odbiera uprawnienia do danego zasobu. * * @param string $resource Nazwa zasobu. */ public function deny($resource) { $this->credentials[$resource] = false; } /** * Sprawdza, czy użytkownik ma prawo dostępu do danego zasobu. * * W przypadku podania drugiego parametru ($method) zasób traktowany jest jako żądanie akcji kontrolera. * * Sprawdzanie odbywa się w kilku etapach: * - przypisanie wartości domyślnej, * - sprawdzenie globalnego ustawienia dla kontrolera (w przypadku akcji), * - sprawdzenie konkretnego zasobu. * * Zwracana wartość jest nadpisywana nową wartością w kolejnych etapach sprawdzania. * * @param string $resource Nazwa zasobu. * @param string $method Opcjonalna nazwa akcji kontrolera. * @return boolean */ public function isAllowed($resource, $method = null) { $return = $this->default; $return = (bool)$this->credentials[$resource.self::SEPARATOR.self::DEFAULT_KEY]; } $resource .= self::SEPARATOR.$method; } $return = (bool)$this->credentials[$resource]; } return $return; } public function setRole($v) { $this->role = $v; } public function getRole() { return $this->role; } $this->credentials = $v; } public function getCredentials() { return $this->credentials; } /** * Wartość domyślna dla uprawnień. * * @var boolean */ const DEFAULT_VALUE = false; /** * Klucz oznaczający elementy domyślne. * * @var string */ const DEFAULT_KEY = '*'; /** * Separator kontrolera oraz akcji. * * @var string */ const SEPARATOR = '/'; } ?>
application/modules/authorization/hooks/authorization_hook.php
<?php /** * Hook realizujący zabezpieczenie autoryzacji dostępu. * * @package authorization */ class Authorization_Hook { /** * Sprawdza czy dany użytkownik ma prawo dostępu do danej akcji kontrolera. * * W przypadku braku uprawnień wyświetla stronę z odpowiednim komunikatem. * */ public function check() { $acl = Authorization::instance(); $acl->setRole('user'); // docelowo pobierane z sesji $acl->loadConfig(); if (!$acl->isAllowed(Router::$controller, Router::$method)) { Event::run('system.401'); } } /** * Przekierowuje żądanie do strony z błędem braku dostępu. * */ public function error() { Router::$controller = 'authorization'; Router::$method = 'error_401'; } } ?>
application/modules/authorization/controllers/authorization.php
<?php /** * @package authorization */ class Authorization_Controller extends Main_Controller_Core { public function error_401() { $this->template->content = new View('authorization/error_401'); } } ?>
W tym momencie warto zauwazyć, iż kontroler rozszerza moją klasę Main_Controller_Core, która natomiast rozszerza Template_Core.
application/modules/authorization/views/authorization/error_401.php
<?php <h3>Brak uprawnień!</h3> ?>
Całą konfigurację należy zawrzeć w konfigu dla aplikacji tj. application/config/authorization.php
<?php '*' => false, 'authentication/*' => true, 'authentication/logout' => false ); '*' => true ); ?>
Powyższy kod definiuje 2 typy użytkowników: administratora oraz użytkownika zwykłego. Administrator ma wszelkie uprawnienia ('*' => true), natomiast w przypadku użytkownika sprawa wygląda inaczej. Początkowo odbierane są mu wszystkie uprawnienia. Następnie nadawane są uprawnienia dla wszystkich akcji kontrolera "authentication", po czym odbierana jest mu możliwość wykonania akcji "logout". W efekcie użytkownik będzie mógł się jedynie zalogować

Poza powyższymi krokami należy również aktywować hooki oraz dodać moduł autoryzacji w konfigu aplikacji.
Od razu uprzedzam, że skrypt nie obsługuje dziedziczenia uprawnień. Można to jednak osiągnąć np. poprzez array_merge() podczas definiowania uprawnień.
Zademonstrowałem wykorzystanie klasy na przykładzie kontroli dostępu do akcji. Jednak równie dobrze można ją wykorzystać do kontroli wewnątrz samych akcji. Przykładowo:
application/config/authorization.php
<?php '*' => true, 'cos' => false ); ?>
application/controllers/jakiskontroler.php
<?php public function index() { if (Authorization::instance()->isAllowed('cos')) { // dodatkowy kod } } ?>
Zaletą korzystania z instance() jest to, że zwraca ona główny obiekt Authorization. Główny czyli ten dotyczący aktualnie zalogowanego użytkownika. Można jednak, o czym pisałem wcześniej, na bieżąco tworzyć obiekty autoryzacyjne:
application/controllers/jakiskontroler.php
<?php public function index() { // utworzenie nowego obiektu $acl = new Authorization(); // przypisanie roli $acl->setRole('mietek'); // zezwolenie na dostęp do zasobu "lalala" $acl->allow('lalala'); if ($acl->isAllowed('lalala')) { } else { } // odebranie dostępu do zasobu "lalala" $acl->deny('lalala'); if ($acl->isAllowed('lalala')) { } else { } } ?>
Proszę o opinie.
pion