Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Kohana - ACL czyli kontrola uprawnień
Forum PHP.pl > Inne > Oceny
phpion
Witam,
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
  1. <?php
  2. /**
  3.  * Klasa realizująca obsługę uprawnień w systemie.
  4.  *
  5.  * @package authorization
  6.  */
  7. class Authorization {
  8.    /**
  9.      * Instancja klasy Authorization.
  10.      *
  11.      * @var Authorization
  12.      */
  13.    private static $instance = null;
  14.    
  15.    /**
  16.      * Nazwa roli aktualnego użytkownika.
  17.      *
  18.      * @var string
  19.      */
  20.    private $role;
  21.    
  22.    /**
  23.      * Tablica uprawnień.
  24.      *
  25.      * @var array
  26.      */
  27.    private $credentials = array();
  28.    
  29.    /**
  30.      * Domyślna wartość uprawnień.
  31.      *
  32.      * @var boolean
  33.      */
  34.    private $default;
  35.    
  36.    /**
  37.      * Zwraca instancję klasy Authorization
  38.      *
  39.      * @return Authorization
  40.      */
  41.    public static function instance() {
  42.        if (is_null(self::$instance)) {
  43.            self::$instance = new Authorization();
  44.        }
  45.        
  46.        return self::$instance;
  47.    }
  48.    
  49.    /**
  50.      * Wczytuje konfigurację uprawnień z pliku zewnętrznego.
  51.      *
  52.      * Dodatkowo ustawia domyślną wartość o ile została ustalona w pliku konfiguracyjnym.
  53.      */
  54.    public function loadConfig() {
  55.        $this->credentials = (array)Kohana::config('authorization.'.$this->role);
  56.        
  57.        if (isset($this->credentials[self::DEFAULT_KEY])) {
  58.            $this->default = (bool)$this->credentials[self::DEFAULT_KEY];
  59.            unset($this->credentials[self::DEFAULT_KEY]);
  60.        }
  61.        else {
  62.            $this->default = self::DEFAULT_VALUE;
  63.        }
  64.    }
  65.    
  66.    /**
  67.      * Dodaje uprawnienia do danego zasobu.
  68.      *
  69.      * @param string $resource Nazwa zasobu.
  70.      */
  71.    public function allow($resource) {
  72.        $this->credentials[$resource] = true;
  73.    }
  74.    
  75.    /**
  76.      * Odbiera uprawnienia do danego zasobu.
  77.      *
  78.      * @param string $resource Nazwa zasobu.
  79.      */
  80.    public function deny($resource) {
  81.        $this->credentials[$resource] = false;
  82.    }
  83.    
  84.    /**
  85.      * Sprawdza, czy użytkownik ma prawo dostępu do danego zasobu.
  86.      *
  87.      * W przypadku podania drugiego parametru ($method) zasób traktowany jest jako żądanie akcji kontrolera.
  88.      *
  89.      * Sprawdzanie odbywa się w kilku etapach:
  90.      * - przypisanie wartości domyślnej,
  91.      * - sprawdzenie globalnego ustawienia dla kontrolera (w przypadku akcji),
  92.      * - sprawdzenie konkretnego zasobu.
  93.      *
  94.      * Zwracana wartość jest nadpisywana nową wartością w kolejnych etapach sprawdzania.
  95.      *
  96.      * @param string $resource Nazwa zasobu.
  97.      * @param string $method Opcjonalna nazwa akcji kontrolera.
  98.      * @return boolean
  99.      */
  100.    public function isAllowed($resource, $method = null) {
  101.        $return = $this->default;
  102.        
  103.        if (!is_null($method)) {
  104.            if (isset($this->credentials[$resource.self::SEPARATOR.self::DEFAULT_KEY])) {
  105.                $return = (bool)$this->credentials[$resource.self::SEPARATOR.self::DEFAULT_KEY];
  106.            }
  107.            
  108.            $resource .= self::SEPARATOR.$method;
  109.        }
  110.        
  111.        if (isset($this->credentials[$resource])) {
  112.            $return = (bool)$this->credentials[$resource];
  113.        }
  114.        
  115.        return $return;
  116.    }
  117.    
  118.    public function setRole($v) {
  119.        $this->role = $v;
  120.    }
  121.    
  122.    public function getRole() {
  123.        return $this->role;
  124.    }
  125.    
  126.    public function setCredentials(array $v = array()) {
  127.        $this->credentials = $v;
  128.    }
  129.    
  130.    public function getCredentials() {
  131.        return $this->credentials;
  132.    }
  133.    
  134.    /**
  135.      * Wartość domyślna dla uprawnień.
  136.      *
  137.      * @var boolean
  138.      */
  139.    const DEFAULT_VALUE = false;
  140.    
  141.    /**
  142.      * Klucz oznaczający elementy domyślne.
  143.      *
  144.      * @var string
  145.      */
  146.    const DEFAULT_KEY = '*';
  147.    
  148.    /**
  149.      * Separator kontrolera oraz akcji.
  150.      *
  151.      * @var string
  152.      */
  153.    const SEPARATOR = '/';
  154. }
  155. ?>


application/modules/authorization/hooks/authorization_hook.php
  1. <?php
  2. /**
  3.  * Hook realizujący zabezpieczenie autoryzacji dostępu.
  4.  *
  5.  * @package authorization
  6.  */
  7. class Authorization_Hook {
  8.    /**
  9.      * Sprawdza czy dany użytkownik ma prawo dostępu do danej akcji kontrolera.
  10.      *
  11.      * W przypadku braku uprawnień wyświetla stronę z odpowiednim komunikatem.
  12.      *
  13.      */
  14.    public function check() {
  15.        $acl = Authorization::instance();
  16.        $acl->setRole('user'); // docelowo pobierane z sesji
  17.        $acl->loadConfig();
  18.        
  19.        if (!$acl->isAllowed(Router::$controller, Router::$method)) {
  20.            Event::add('system.401', array('Authorization_Hook', 'error'));
  21.            Event::run('system.401');
  22.        }
  23.    }
  24.    
  25.    /**
  26.      * Przekierowuje żądanie do strony z błędem braku dostępu.
  27.      *
  28.      */
  29.    public function error() {
  30.        Router::$controller = 'authorization'; 
  31.        Router::$method = 'error_401'; 
  32.        Router::$arguments  = array();
  33.    }
  34. }
  35.  
  36. Event::add('system.routing', array('Authorization_Hook', 'check'));
  37. ?>


application/modules/authorization/controllers/authorization.php
  1. <?php
  2. /**
  3.  * @package authorization
  4.  */
  5. class Authorization_Controller extends Main_Controller_Core {
  6.    public function error_401() {
  7.        $this->template->content = new View('authorization/error_401');
  8.    }
  9. }
  10. ?>

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
  1. <?php
  2. <h3>Brak uprawnień!</h3>
  3. ?>


Całą konfigurację należy zawrzeć w konfigu dla aplikacji tj. application/config/authorization.php
  1. <?php
  2. $config = array();
  3.  
  4. $config['user'] = array(
  5.    '*' => false,
  6.    'authentication/*' => true,
  7.    'authentication/logout' => false
  8. );
  9.  
  10. $config['admin'] = array(
  11.    '*' => true
  12. );
  13. ?>

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ć smile.gif Operacja wylogowania (oraz wszelkie pozostałe) zostały zablokowane.

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
  1. <?php
  2. $config = array();
  3.  
  4. $config['user'] = array(
  5.    '*' => true,
  6.    'cos' => false
  7. );
  8. ?>

application/controllers/jakiskontroler.php
  1. <?php
  2. public function index() {
  3.    if (Authorization::instance()->isAllowed('cos')) {
  4.        // dodatkowy kod
  5.    }
  6. }
  7. ?>


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
  1. <?php
  2. public function index() {
  3.    // utworzenie nowego obiektu
  4.    $acl = new Authorization();
  5.    // przypisanie roli
  6.    $acl->setRole('mietek');
  7.    // zezwolenie na dostęp do zasobu "lalala"
  8.    $acl->allow('lalala');
  9.  
  10.    if ($acl->isAllowed('lalala')) {
  11.        echo 'lalala tak';
  12.    }
  13.    else {
  14.        echo 'lalala nie';
  15.    }
  16.  
  17.    // odebranie dostępu do zasobu "lalala"
  18.    $acl->deny('lalala');
  19.  
  20.    if ($acl->isAllowed('lalala')) {
  21.        echo 'lalala tak';
  22.    }
  23.    else {
  24.        echo 'lalala nie';
  25.    }
  26. }
  27. ?>


Proszę o opinie.

pion
bim2
moim zdaniem troche źle to jest rozwiązane. Nie znam kohany, ale ja sprawdzałbym uprawnienia na poziomie wczytywania akcji (kontrolera). Jeśli nie ma uprawnień przekierowujesz ruch do akcji (kontrolera) Error_403 smile.gif Nie trzeba wtedy co chwilę wpisywac ifki. :]
phpion
Cytat(bim2 @ 1.12.2008, 19:05:46 ) *
moim zdaniem troche źle to jest rozwiązane. Nie znam kohany, ale ja sprawdzałbym uprawnienia na poziomie wczytywania akcji (kontrolera). Jeśli nie ma uprawnień przekierowujesz ruch do akcji (kontrolera) Error_403 smile.gif Nie trzeba wtedy co chwilę wpisywac ifki. :]

Hmmm, no przecież jest to zautomatyzowane poprzez użycie hook'a.
  1. <?php
  2. Event::add('system.routing', array('Authorization_Hook', 'check'));
  3. ?>
kbsucha
Bardzo fajna i w miare prosta do zastosowania biblioteka, wlasnie nie dawno pisałem podobny modul. Dla mnie np brakowałoby rozdzielenia rodzaju dostępu, chodzi mi o: read, write, del.
Fajnie, że tak szczegółowo to opisales. Może zyskasz kilku zwolenników Kohany biggrin.gif

pozdr

EDIT: Istotnie mozna i tak.
phpion
Cytat(kbsucha @ 1.12.2008, 19:11:21 ) *
Bardzo fajna i w miare prosta do zastosowania biblioteka, wlasnie nie dawno pisałem podobny modul. Dla mnie np brakowałoby rozdzielenia rodzaju dostępu, chodzi mi o: read, write, del.
Fajnie, że tak szczegółowo to opisales. Może zyskasz kilku zwolenników Kohany biggrin.gif

pozdr

Dzięki za przychylną opinię. Nie do końca rozumiem o co Ci chodzi z tym "rozdzieleniem rodzaju dostępu". Możesz przecież zrobić tak:
  1. <?php
  2. $config['user'] = array(
  3.   'resource/read' => true,
  4.   'resource/write' => true,
  5.   'resource/delete' => false
  6. );
  7. ?>

lub po prostu:
  1. <?php
  2. $config['user'] = array(
  3.   '*' => true,
  4.   'resource/delete' => false
  5. );
  6. ?>
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-2025 Invision Power Services, Inc.