bugie.cz

Sniperův blog o věcech webových i newebových

23 března

Autentizace a autorizace v Zend Frameworku

Velmi častým požadavkem při výrobě webových stránek je schopnost odlišit jednotlivé uživatele, ať již jde o jednoduchou administraci, kde jde pouze o odlišení normálního návštěvníka od administrátora nebo o složitou sociální síť, ve které má každý uživatel svůj profil a své nastavení. A právě tímto se zabývá autentizace a autorizace. Dnes si povíme, jak tyto úkoly řešit pomocí ZF.

Nejdříve trochu teorie

Autentizace je proces, ve kterém na základě informací od uživatele ověřujeme jeho identitu, čili „Jsem ten, kdo tvrdím, že jsem“. Ověřovací informace mohou být třech typů – Znám (např. heslo), Mám (např. fyzickou platební kartu nebo generátor jednorázových hesel) a Jsem (autentizace pomocí biometrických údajů).Autentizace neřeší, zda má daný uživatel dostatečná práva.

Autorizace je naproti tomu řeší, zda daný uživatel má dostatečná práva k vykonání dané akce, čili „Na základě toho, kdo jsem, mohu vykonat danou akci“. Autorizace neřeší, zda je uživatel systému známý nebo ne, v tomto ohledu plně spoléhá na autentizaci.

Zend_Auth

Zend_Auth je Zendí knihovna poskytující služby autentizace. Využívá k tomu jednolivé adaptéry, o kterých se zmíním později. Zend_Auth implementuje návrhový vzor Singleton pomocí metody Zend_Auth::getInstance();, díky které si nemusíme držet v session objekt Zend_Auth konkrétního uživatele, stačí nám si pouze vyžádat objekt Zend_Auth.

Autentizační adaptéry

Autentizační adaptéry jsou ty objekty, které provádí samotné ověření uživatele. Jsou vytvářeny pro konkrétní služby a Zend_Auth pouze využívá jejich metody. Každý adaptér musí implementovat Zend_Auth_Adapter_Interface, který zaručuje minimální společné rozhraní – metodu autenticate(). Jakékoliv další metody jsou záležitostí čistě konkrétního adaptéru. Já si vytvořil takový sice v praxi asi zbytečný, nicméně v rámci výuky určitě užitečný adaptér pro ověřování uživatele oproti CSV souboru.

<?php 
/**  
 * Adapter pro autorizaci oproti CSV souboru 
 * 
 * Adapter pro Zend_Auth, ktery pro autorizaci vyuziva CSV soubory. 
 * Kazdy soubor obsahuje id, login a heslo. Heslo muze byt zasifrovano 
 * libovolnou funkci. 
 * 
 * LICENCE: Delejte si s timhle souborem co chcete, jenom zde zachovejte oznaceni 
 * mojeho autorstvi. Adapter je dodavan jak je, nenesu za nej zadnou odpovednost. 
 * 
 * @link        http://blog.snipers-softworks.net 
 * @author      Ondrej Flidr, ondrej.flidr@seznam.cz 
 * @package     L1 
 * @subpackage  L1_Auth 
 * @version     1.0 
 */ 
 
/** 
 * @see Zend_Auth_Adapter_Interface 
 */ 
require_once("Zend/Auth/Adapter/Interface.php"); 
  
/** 
 * @see Zend_Auth_Result 
 */ 
require_once("Zend/Auth/Result.php"); 
 
/** 
 * Autorizace uzivatele oproti CSV souboru 
 * 
 * @author Ondrej Flidr, Sniper's Softworks 
 * @version 1.0 
 * @package L1 
 * @subpackage L1_Auth 
 * @see Zend_Auth_Adapter_Interface 
 */  
class L1_Auth_Adapter_Csv implements Zend_Auth_Adapter_Interface { 
 
    /** 
     * Soubor s ucty 
     * @var string 
     */ 
    protected $_datafile = 'accounts.csv'; 
  
    /** 
     * Oddelovac poli 
     * @var string 
     */ 
    protected $_delimiter = ','; 
  
    /** 
     * Mapa poradi atributu CSV souboru 
     * @var array 
     */ 
    protected $_mapAttribs = array(); 
  
    /** 
     * Pole s identitou  
     * @var string  
     */  
    protected $_identityField;  
 
    /** 
     * Pole s heslem 
     * @var string 
     */ 
    protected $_credentialField; 
 
    /** 
     * Nazev sifrovaci funkce. null znamena nesifrovano 
     * @var string|null 
     */ 
    protected $_crypt = null; 
  
    /** 
     * Uzivatelske jmeno 
     * @var string 
     */ 
    protected $_username; 
 
    /** 
     * Heslo 
     * @var string 
     */ 
    protected $_password; 
 
    /** 
     * konstruktor nastavuje soubor s daty  
     * @param string|null $datafile  
     */  
    public function __contruct($datafile = null)  
    {  
        $this->setDatafile($datafile);  
    }  
  
    /**  
     * Nastavuje cestu k souboru s nastavenim uctu  
     * @param string|null $datafile  
     * @return L1_Auth_Adapter_Csv  
     */  
    public function setDatafile($datafile = null)  
    {  
        $datafile = (string)$datafile;  
        if (!empty($datafile)) { // nastavi datafile pouze pokud je nejaky predan  
            if (file_exists($datafile)) $this->_datafile = $datafile;  
            else {  
                $incDir = explode(PATH_SEPARATOR, get_include_path());  
                foreach ($incDir as $includepath) {  
                    $fullpath = $includepath . "/" . $datafile;  
                    if (file_exists($fullpath)) {  
                        $this->_datafile = $fullpath;  
                    }  
                }  
            }  
        }  
        return $this;  
    }  
  
    /**  
     * Vraci cestu k souboru s nastavenim uctu  
     * @return stirng  
     */  
    public function getDatafile()  
    {  
        return $this->_datafile;  
    }  
  
    /**  
     * Nastavuje mapu prvku v souboru  
     * @param stirng|array $map vyznam jednotlivych atributu CSV souboru.  
     * @param string pouzity oddelovac, pokud je mapa predana jako retezec  
     * @return L1_Auth_Adapter_Csv  
     */  
    public function setMapAttribs($map = array("id", "login", "password"), $delimiter = ',')  
    {  
        if (!is_array($map)) { // pokud nedostanu mapu jako pole, rozsekam ji dellimiterem  
            $map = explode($delimiter, (string)$map);  
        }  
  
        $this->_mapAttribs = array_values($map);  
        return $this;  
    }  
  
    /**  
     * Vraci mapu atributu CSV souboru  
     * @param string|null $glue Pokud se ma mapa vratit jako retezec, zde uvedte spojeni  
     * @return string|array  
     */  
    public function getMapAttribs($glue = null)  
    {  
        if (!is_null($glue)) {  
            return implode((string)$glue, $this->_mapAttribs);  
        }  
        return $this->_mapAttribs;  
    }  
  
    /**  
     * Nastavi pole s identitou  
     * @param string $field  
     * @return L1_Auth_Adapter_Csv  
     */  
    public function setIdentityField($field)  
    {  
        $this->_identityField = (string)$field;  
        return $this;  
    }  
  
    /**  
     * Vraci pole s identitou  
     * @return string  
     */  
    public function getIdentityField()  
    {  
       return $this->_identityField;  
    }  
  
    /**  
     * nastavi pole s heslem  
     * @param string $field  
     * @return L1_Auth_Adapter_Csv  
     */  
    public function setCredentialField($field)  
    {  
        $this->_credentialField = (string)$field;  
        return $this;  
    }  
  
    /**  
     * Vraci pole s heslem  
     * @return string  
     */  
    public function getCredentialField()  
    {  
        return $this->_credentialField;  
    }  
  
    /**  
     * Nastavuje funkci pouzitou pro sifrovani hesla. Null znamena heslo se nesifruje  
     * @param string|null $alg  
     * @return L1_Auth_Adapter_Csv  
     * @throws Zend_Auth_Exception Byla zadana neexistujici funkce  
     */  
    public function setCrypt($alg)  
    {  
        if (function_exists((string)$alg) || is_null($alg)) {  
            $this->_crypt = $alg;  
            return $this;  
        }  
        require_once('Zend/Auth/Exception.php');  
        throw new Zend_Auth_Exception('Dana sifrovaci funkce neni k dispozici');  
    }  
  
    /**  
     * vraci sifrovaci funkci  
     * @return string  
     */  
    public function getCrypt()  
    {  
        return $this->_crypt;  
    }  
  
    /**  
     * Nastavuje oddelovac poli  
     * @param string $delimiter  
     * @return L1_Auth_Adapter_Csv  
     */  
    public function setDelimiter($delimiter)  
    {  
        $this->_delimiter = (string)$delimiter;  
        return $this;  
    }  
      
    /**  
     * Vraci oddelovac poli  
     * @return string  
     */  
    public function getDelimiter()  
    {  
        return $this->_delimiter;  
    }  
  
    /**  
     * Nastavi uzivatelske jmeno  
     * @param string $username  
     * @return L1_Auth_Adapter_Csv  
     */  
    public function setUsername($username)  
    {  
        $this->_username = (string)$username;  
        return $this;  
    }  
  
    /**  
     * Vraci uzivatelske jmeno  
     * @return string  
     */  
    public function getUsername()  
    {  
        return $this->_username;  
    }  
  
    /**  
     * Nastavi heslo k overeni  
     * @param string $password  
     * @return L1_Auth_Adapter_Csv  
     */  
    public function setPassword($password)  
    {  
        $this->_password = $password;  
        return $this;  
    }  
  
    /**  
     * Samotna autentizace uzivatele oproti zaznamum v CSV  
     * @return Zend_Auth_Rersult  
     */  
    public function authenticate()  
    {  
        $fp = fopen($this->getDatafile(), 'r'); // otevru si soubor CSV ke cteni  
        if ($fp === false) {  
            require_once('Zend/Auth/Exception.php');  
            throw new Zend_Auth_Exception('Soubor s ucty nelze otevrit');  
        }  
  
        if (!is_null($this->_crypt)) { // pokud je heslo ulozeno sifrovane, zasifruju heslo zadane v prihlasovani  
            $heslo = call_user_func($this->_crypt, $this->_password);  
        } else {  
            $heslo = $this->_password;  
        }  
  
        $mapovani = array(); // upravim mapovaci pole pro potreby CSV  
        foreach ($this->_mapAttribs as $klic => $pole) {  
            $mapovani[$pole] = $klic;  
        }  
        while ($line = fgetcsv($fp, 4096, $this->_delimiter)) { // prochazi soubor a parsuje radky jako CSV  
            if ($line[$mapovani[$this->_identityField]] != $this->_username) continue; // neshoduji se uzivatelska jmena, beru dalsi  
            if ($line[$mapovani[$this->_credentialField]] != $heslo)  
                return new Zend_Auth_Result(Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID, $this->_username); // neshoduji se hesla, vracim objekt chybeho hesla  
            return new Zend_Auth_Result(Zend_Auth_Result::SUCCESS, $this->_username); // vse se shoduje, vracim uspesny objekt  
        }  
        return new Zend_Auth_Result(Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND, $this->_username);  
  
    }  
}

Adaptér musí výsledek autentizace vracet jako objekt Zend_Auth_Result. Tento objekt obsahuje několik metod pro práci s výsledkem autentizace. Jako nejdůležitější bych označil metodu isValid(), která vrací true v případě, kdy přihlášení proběhlo úspěšně. Pokud dojde k jakékoliv chybě (chybné údaje nebo nedostupnost služby, vůči které se autentizuje), vrací false. Adaptéry také umí rozlišovat typ chyby (chybný login, chybné heslo, ne jednoznačný login – více účtů se stejným loginem apod.) a tyto stavy oznamovat rozdílným kódem. Kódy jsou celočíselné konstanty definované v třídě Zend_Auth_Result.

Storage

Informaci o přihlášení musíme logicky někam ukládat. K tomu slouží třídy Zend_Auth_Storage. V distribuci ZF jsou přítomny 2 typy - Zend_Auth_Storage_NonPersistent a Zend_Auth_Storage_Session. První z nich se používá pro http přihlašování, kdy se informace o přihlášení neukládá na serveru ale pamatuje si ji prohlížeč (proto se také nelze odhlásit jinak než ukončením prohlížeče), druhý se používá pro ostatní možnosti a ukládá objekt Zend_Auth do session. Pokud si chcete implementovat vlastní storage, je připraven Zend_Auth_Storage_Interface, který zajišťuje minimální společné metody read, write, clear a isEmpty. Já si napsal storage pro ukládání informací do databáze.

<?php  
/**  
 * Uuklada informaci o prihlasenem uzivateli do DB  
 *  
 * Uloziste pro Zend_Auth, ktere jako storage pouziva databazi. U kazdeho zaznamu  
 * se uklada uzivatel, IP adresa, datum a cas prihlaseni a klic, ktery se zaroven  
 * uklada do session. Zaznam se vyhledava podle klice a IP adresy.  
 *  
 * LICENCE: Delejte si s timhle souborem co chcete, jenom zde zachovejte oznaceni  
 * mojeho autorstvi. Adapter je dodavan jak je, nenesu za nej zadnou odpovednost.  
 *  
 * @link        http://blog.snipers-softworks.net  
 * @author      Ondrej Flidr, ondrej.flidr@seznam.cz  
 * @package     L1  
 * @subpackage  L1_Auth  
 * @version     1.0  
 */  
  
/**  
 * @see Zend_Auth_Storage_Interface  
 */  
require_once("Zend/Auth/Storage/Interface.php");  
  
/**  
 * @see Zend_Session  
 */  
require_once("Zend/Session.php");  
  
/**  
 * Ukladani informaci o prihlasenem uzivateli do databaze.  
 *  
 * @author Ondrej Flidr, Sniper's Softworks  
 * @version 1.0  
 * @package L1  
 * @subpackage L1_Auth  
 * @see Zend_Auth_Adapter_Interface  
 */  
class L1_Auth_Storage_DbTable implements Zend_Auth_Storage_Interface {  
  
    /**  
     * Namespace pro session pro ulozeni  
     */  
    const DEFAULT_NAMESPACE = "Zend_Auth";  
  
    /**  
     * Nazev clena v session pro ulozeni informaci  
     */  
    const DEFAULT_MEMBER = 'key';  
  
    /**  
     * Pouzivany nazev namespace  
     * @var string  
     */  
    protected $_namespace;  
    /**  
     * Objekt pro praci se session  
     * @var Zend_Session_Namespace  
     */  
    protected $_session;  
    /**  
     * DB pristup k tabulce pro ukladani udaju  
     * @var Zend_Db_Table_Abstract  
     */  
    protected $_dbModel;  
    /**  
     * Aktualne pouzivany nazev clena v session  
     * @var string  
     */  
    protected $_member;  
  
    /**  
     * Konstruktor nastavi vychozi informace  
     * @param Zend_Db_Table_Abstract $dbModel  
     * @param string $namespace  
     * @param string $member  
     */  
    public function __construct(Zend_Db_Table_Abstract $dbModel,  
                                $namespace = self::DEFAULT_NAMESPACE,  
                                $member = self::DEFAULT_MEMBER)  
    {  
        $this->_namespace = $namespace;  
        $this->_session = new Zend_Session_Namespace($namespace);  
        $this->setDbTable($dbModel);  
        $this->_member = $member;  
    }  
  
    /**  
     * Nastavuje pristup k tabulce v db  
     * @param Zend_Db_Table_Abstract $dbTable  
     * @return L1_Auth_Storage_DbTable  
     */  
    public function setDbTable(Zend_Db_Table_Abstract $dbTable)  
    {  
       $this->_dbModel = $dbTable;  
       return $this;  
    }  
  
    /**  
     * Vraci objekt  pro pristup k tabulce v db  
     * @return Zend_Db_Table_Abstract  
     */  
    public function getDbTable()  
    {  
       return $this->_dbModel;  
    }  
  
    /**  
     * Vraci true, pokud je uloziste prazdne  
     * @return boolean  
     */  
    public function isEmpty()  
    {  
        $klic = $this->_session->{$this->_member};  
        $radek = $this->_dbModel->fetchRow(array(   'key = ?' => $klic,  
                                                    'ip = ?' => $_SERVER["REMOTE_ADDR"]),  
                                                    'datetime desc');  
        return is_null($radek);  
    }  
  
    /**  
     * Vraci informace o prihlaseni uzivatele. pokud prihlasen uzivatel neni, vracim null  
     * @return array|null  
     */  
    public function read()  
    {  
        $klic = $this->_session->{$this->_member};  
        $data = $this->_dbModel->fetchRow(array(   'key = ?' => $klic,  
                                                    'ip = ?' => $_SERVER["REMOTE_ADDR"]),  
                                                    'datetime desc');  
        if (is_null($data)) return null; else return $data->toArray();  
    }  
  
    /**  
     * Zapise informaci do databaze  
     * @param string $identity  
     * @throws Zend_Auth_Storage_Exception Pri ukladani doslo k chybe  
     */  
    public function write($identity)  
    {  
        $date = new Zend_Db_Expr('now()');  
        $klic = $this->_getRandomKey();  
        try {  
            $this->_dbModel->createRow(array(   "ip" => $_SERVER["REMOTE_ADDR"],  
                                                "datetime" => $date,  
                                                "key" => $klic,  
                                                "user" => $identity))->save();  
        } catch (Exception $ex) {  
            require_once("Zend/Auth/Storage/Exception.php");  
            throw new Zend_Auth_Storage_Exception($ex->getMessage());  
        }  
        $this->_session->{$this->_member} = $klic; // ulozim klic do session  
    }  
  
    /**  
     * Odstraneni pristupu k prihlaseni smazanim klice ze session  
     */  
    public function clear()  
    {  
        $this->_session->{$this->_member} = "";  
    }  
  
    /**  
     * Vraci nazev namespace  
     * @return string  
     */  
    public function getNamespace()  
    {  
        return $this->_namespace;  
    }  
  
    /**  
     * Generuje nahodny unikatni klic o zadane delce  
     * @param integer $lenght  
     * @return string  
     */  
    protected function _getRandomKey($lenght = 10)  
    {  
        $unique = false;  
        do {  
            $key = "";  
            for ($i = 0; $i < (int)$lenght; $i++) {  
                $key .= chr(rand(32, 126));  
            }  
            if (is_null($this->_dbModel->fetchAll(array('key = ?' => $key)))) {  
                $unique = true;  
            }  
        } while ($unique);  
        return $key;  
    }  
  
}

A nakonec prezentace připravená pro naše firemní vývojové oddělení.

Zend_Acl

Třída Zend_Acl zajišťuje autorizaci. Využívá k tomu role, zdroje a asserty.

Role

Role reprezentují jednotlivé uživatele a skupiny. K jejich tvorbě použijte třídy Zend_Acl_Role, případně vlastní třídu implementující rozhraní Zend_Acl_Role_Interface. Role má u sebe jedinečné ID, což je řetězec, kterým se identifikuje ve stromu. Obvykle se jedná uživatelské jméno nebo uživatelovo ID. Role se uchovávají v tzv. Registrech, což je vnitřní struktura Zend_Acl, poopisující vztahy mezi rolemi. Role může mít několik rodičů, ze kterých dědí práva ke zdrojům (viz. Dále).

Zdroje

Zdroje představují prostředky, které se role hodlají využít. Může se jednat o databázovou tabulku, sekci webu nebo třeba využití vzdálené služby. Resource je identifikován jednoduchým řetězcem a 1e reprezentován objektem Zend_Acl_Resource. Pokud si budete chtít připravit vlastní třídu pro reprezentaci resource, stačí, když implementujete Zend_Acl_Resource_Interface a metodu getResourceId(); Resource podporuje dědičnost, na rozdíl od rolí ale může jeden resource dědit maximálně od jednoho rodiče.

Asserty

Asserty jsou docela zajímavá, nicméně jak tak koukám na web tak velmi málo používaná schopnost Zend_Acl. Pokud přiřazujete práva rolím, standardně nelze omezit přiřazení vnějšími podmínkami, např. časem nebo zdrojovou IP. A právě kvůli tomu jsou tu asserty, které toto umožňují. Přímo v ZF je k dispozici pouze Zend_Acl_Assert_Interface, společný interface pro asserty, s metodou assert. Celý koncept assertů funguje tak, že dané nastavení práva se aplikuje pouze v případě, kdy tato metoda vrátí true. Bohužel, v distribuci ZF nejsou k dispozici nějaké ukázkové asserty, takže jejich psaní je hodně o schopnostech programátora. Já si napsal jednoduchý assert pro omezení práv v závislosti na čase. Jeho použití je sice spíše ve vnitropodnikových systémech, nicméně i tak ho může ledaskdo shledat užitečným.

<php  
/**  
 * Assert umoznujici povoleni a zakazani pristup na zaklade aktualniho data a casu  
 *  
 * Klasicky ACL umoznuje pouze nastavit, jestli dana role ma k danemu resource pristup nebo ne.  
 * Tento assert umoznuje vstahnout dane pravo i vzhledem k aktualnimu casu  
 *  
 * LICENCE: Delejte si s timhle souborem co chcete, jenom zde zachovejte oznaceni  
 * mojeho autorstvi. Assert je dodavan jak je, nenesu za nej zadnou odpovednost.  
 *  
 * @link        http://blog.snipers-softworks.net  
 * @author      Ondrej Flidr, ondrej.flidr@seznam.cz  
 * @package     L2  
 * @subpackage  L2_Acl  
 * @version     1.0  
 *  
 */  
  
/**  
 * @see Zend_Acl_Assert_Interface  
 */  
require_once "Zend/Acl/Assert/Interface.php";  
  
/**  
 * Assert pro ovlivneni prava na zaklade casu  
 *  
 * @author Ondrej Flidr, Sniper's Softworks  
 * @version 1.0  
 * @package L2  
 * @subpackage L2_Acl  
 * @see Zend_Acl_Assert_Interface  
 */  
class L2_Acl_Assert_Time implements Zend_Acl_Assert_Interface  
{  
    /**  
     * Cas, od ktereho je assert uspesny. Vyhozi nastaveni je 0:00:00  
     * @var array [h] - hodiny, [m] - minuty, [s] - sekundy  
     */  
    protected $_from = array("h" => 0, "m" => 0, "s" => 0);  
    /**  
     * Cas, do ktereho je assert uspesny. Musim zohlednit i pripadne prestupnou vterinu,  
     * takze vychozi cas je 23:59:60  
     * @var array [h] - hodiny, [m] - minuty, [s] - sekundy  
     */  
    protected $_to = array("h" => 23, "m" => 59, "s" => 60);  
  
    /**  
     * Konstruktor nastavuje hranicni casy assertu pomoci poli. Kazde pole ma indexy h, m, s  
     * reprezentujici hodiny, minuty a vteriny. System je optimalizovan i pro prestupne vteriny  
     * @param array $from  
     * @param array $to  
     */  
    public function __construct(array $from = array("h" => 0, "m" => 0, "s" => 0),  
                                array $to = array("h" => 23, "m" => 59, "s" => 60) )  
    {  
          
        $this->setTimeFrom($from);  
        $this->setTimeTo($to);  
    }  
  
    /**  
     * Nastavuje cas, od ktereho je povoleno prihlaseni  
     * @param array $from  
     */  
    public function setTimeFrom(array $from)  
    {  
        if (!isset($from["h"])) $from["h"] = 0;  
        if (!isset($from["m"])) $from["m"] = 0;  
        if (!isset($from["s"])) $from["s"] = 0;  
  
        if ($from["h"] > 23 || $from["h"] < 0 || !ctype_digit($from["h"])) $from["h"] = 0;  
        if ($from["m"] > 59 || $from["m"] < 0 || !ctype_digit($from["m"])) $from["m"] = 0;  
        if ($from["s"] > 60 || $from["s"] < 0 || !ctype_digit($from["s"])) $from["s"] = 0;  
  
        $this->_from = $from;  
    }  
  
    /**  
     * Vraci cas, od ktereho je mozne se prihlasit  
     * @return array  
     */  
    public function getTimeFrom()  
    {  
        return $this->_from;  
    }  
  
    /**  
     * Nastavuje cas, do ktereho je mozne se prihlasit  
     * @param array $to  
     */  
    public function setTimeTo(array $to)  
    {  
        // pokud nemam dobre nastavenej cas do, prenastavim  
        if (!isset($to["h"])) $to["h"] = 23;  
        if (!isset($to["m"])) $to["m"] = 59;  
        if (!isset($to["s"])) $to["s"] = 60;  
  
        if ($to["h"] > 23 || $to["h"] < 0 || !ctype_digit($to["h"])) $to["h"] = 23;  
        if ($to["m"] > 59 || $to["m"] < 0 || !ctype_digit($to["m"])) $to["m"] = 59;  
        if ($to["s"] > 60 || $to["s"] < 0 || !ctype_digit($to["s"])) $to["s"] = 60;  
  
        $this->_to = $to;  
    }  
  
    /**  
     * Vraci cas, do ktereho je mozne se prihlasit  
     * @return array  
     */  
    public function getTimeTo()  
    {  
        return $this->_to;  
    }  
  
    /**  
     * Samotna porovnavaci metoda. Vraci true v pripade, ze uzivatel ma  
     * moznost v tento okamzik toto pravo aplikovat. Jinak vraci false  
     *  
     * @param Zend_Acl $acl  
     * @param Zend_Acl_Role_Interface $role  
     * @param Zend_Acl_Resource_Interface $resource  
     * @param string|null $privilege  
     * @return boolean  
     */  
    public function assert(Zend_Acl $acl, Zend_Acl_Role_Interface $role, Zend_Acl_Resource_Interface $resource,  
                            $privilege = null )  
    {  
        $aktTs = mktime();  
        $fromTs = mktime($this->_from["h"], $this->_from["m"], $this->_from["s"]);  
        $toTs = mktime($this->_to["h"], $this->_to["m"], $this->_to["s"]);  
        if (!$fromTs || !$toTs || $aktTs > $toTs || $aktTs < $fromTs) return false;  
        return true;  
    }  
}  

Práva a jejich aplikace

A teď už k tomu, proč celé ACL stavíme – přiřazení práv. Pokud jste si již se Zend_Acl hráli, jistě jste zjistili, že nikde není žádná třída Zend_Acl_Rule. Je to proto, že právo je definováno jako název vstahu mezi zdrojem a rolí. Můžu mít např. roli uzivatel a resource clanek. Uzivatel patrně bude mít povoleno právo číst, ale už ne editovat.

Pro přiřazení práv nejprve musíme do ACL přidat role a resource. K tomu slouží objekt Zend_Acl a konkrétně metody ::add() pro přidání resoure a ::addRole() pro přidání role. Pamatujte, že práva můžeme nastavovat pouze mezi rolemi a resource, které máme v ACL.

Na samotné přiřazení práv máme dvojici metod Zend_Acl::allow() a Zend_Acl::deny() pro povolení, resp. odepření práv. Syntaxe parametrů je jednoduchá: role (komu), resource (k čemu), právo (co), assert (za jakých podmínek). Assert a právo je nepovinné, pokud neuvedete assert, platí přiřazení vždy, pokud neuvedete právo, platí pro všechna práva. Role a resource může být buďto objekt příslušného typu nebo jeho ID.

K vyhodnocení práv se používají dvě další metody Zend_Acl, a to ::isAllowed() a ::isDenied() pro zjištění, zda je dané právo dané roli pro daný resource poskytnuto či nikoliv. Pořadí parametrů je stejné jako u přiřazování, tedy role (kdo), resource (k čemu) a nakonec právo (co). Výchozím nastavením ACL je všechno všem zakázáno.

Na závěr zase jedna prezentačka. Budou nás provázet po celou dobu mojich pokusů o certifikaci.

Tak, to by bylo něco o autentizaci a autorizaci v ZF, doufám, že Vám to aspoň něco málo dalo. Příště si povíme něco málo o Zend Framework Coding Standard, tedy o tom, jak psát kód.