Ein einfaches ACL System über Zend_Config_Ini
Ich will hier ein knappes und einfaches ACL und Auth System vorstellen, das ich mir gebastelt habe. Natürlich ist es hier sehr viel vereinfachter dargestellt, aber es funktioniert ;)
Erstmal die, welche in meinem Beispiel in /application/configs/ lagert. Dort dürfte es nichts zu erklären geben.
; Die existierenden Rollen angeben role.0 = "guest" role.1 = "user" role.2 = "admin" ; Die existierenden Resourcen angeben res.0 = "index" res.1 = "error" res.2 = "user" res.3 = "test" ; Nun die genehmigungen für die einzelnen ROllen access.guest.0 = "index" access.guest.1 = "error" access.guest.2 = "user" access.user.0 = "index" access.user.1 = "error" access.user.2 = "user" access.user.3 = "test" access.admin.0 = "index" access.admin.1 = "error" access.admin.2 = "user" access.admin.3 = "test"
Dazu gibt es eine Model Datei. Man kann diese natürlich auch anderweitig lagern. Ich habe es aber vorerst so gelöst. Die Klasse erweitert Zend_Acl. Als Parameter kann man den Standort einer Konfigurationsdatei angeben. Standard ist aber der o.g. Pfad. Es wird dann die Konfiguration geladen und in ein Array umgewandelt. Dieses Array wird nun durchgegangen. Die Resourcen angelegt und die Rollen. Wenn es nun für eine Rolle auch Einstellungen gibt, dann werden diese ebenso angelegt. Da Zend_Acl grundsätzlich erstmal alle Resourcen verbietet werden wir auch all jene erlauben, die erlaubt sein dürfen.
Wenn keine passende Konfiguration vorliegt, dann wird eine Zend_Acl_Exception geworfen.
<?php
/**
* Festlegen der ACL Strukturen
*/
class Model_Acl extends Zend_Acl{
private $_config;
public function __construct($config = null){
if($config != null)
{
$this->_config = (string) $config;
}
else
{
$this->_config = (string) APPLICATION_PATH . "/configs/acl.ini";
}
# Ini Datei muss im Construct mitgegebenw erden
if(file_exists($this->_config))
{
# Konfig einlesen und in ein Array wandeln
$config = new Zend_Config_Ini($this->_config);
$config = $config->toArray();
# Die Ressourcen dem ACL Syste4m hinzugüen
foreach ($config["res"] as $res)
{
$this->add(new Zend_Acl_Resource($res));
}
# DIe Rollen dem ACL System hinzufügen
foreach($config["role"] as $role)
{
$this->addRole(new Zend_Acl_Role($role));
# Wenn es zu der Rolle auch was in der Accesslist gibt, dann füge das hinzu
if(isset($config["access"][$role]))
{
foreach($config["access"][$role] as $access)
{
$this->allow($role, $access);
}
}
}
}
else
{
throw Zend_Acl_Exception('Keine gültige Konfig angegeben!');
}
}
}
Irgendwo muss das Acl System aber auch initialisiert werden. Dafür habe ich ein Auth Plugin. In diesem Plugin nutze ich öfters ein Log, das ich mit “firelog” in der Registry benannt habe. Entweder man ändert dies in sein eigenes Log oder nimmt das Logging gänzlich raus. Die ganze Routine läuft nach dem Routing und vor dem Dispatch, damit Controller und Action noch beeinflusst werden können.
Eigentlich sollte das ganze Selbsterklärend sein für jemanden, der zumindest schon ein wenig gelesen hat. Ich empfehle hier das Kapitel Zend_Auth im Reference Guide.
Ansonsten sei nur gesagt, dass zu anlässen wie der nicht funktionierende Login oder das erfolgte einloggen Zielcontroller und Zielaction geändert werden, damit andere Seiten erscheinen. So braucht man sich in den Controllern selbst keine Gedanken mehr über das Auth System machen.
<?php
class Zorta_Plugin_Auth extends Zend_Controller_Plugin_Abstract {
/**
* Nachdem das Routing durchgeführt wurde geht es los mit dem Auth System
* @uses Zend_Controller_Request_Abstract
*/
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
{
# Lade eine Instanz von Zend_Auth
$auth = Zend_Auth::getInstance();
# Wenn der Request ein POST ist und die Action Login aufgerufen wurde, dann versuche den Login
if($request->isPost() && $request->getActionName() == "login"){
# POST Daten checken
$username = $request->getParam('username');
if($username == "")
$username = "Dummy";
$userpass = $request->getParam('userpass');
if($userpass == "")
$userpass = "Dummy";
# Zend_DB_Table aus der Registry holen als Datenbankadapter für die Authorisation
$db = Zend_Registry::get('Zend_Db_Table');
$adapter = new Zend_Auth_Adapter_DbTable($db,'user','user_name','user_pass','MD5(?)');
# Username und Passwort setzen
$adapter->setCredential($userpass);
$adapter->setIdentity($username);
# Wenn die Nutzerdaten Ok waren, dann schreibe das Auth in den Storage
if($auth->authenticate($adapter) && $auth->getIdentity() != false){
# Schreibe die Identität in die Session
$auth->getStorage()->write($adapter->getResultRowObject(null, 'password'));
# Log, dass der Login durchgeführt wurde
Zend_Registry::get('firelog')->log('Login accomplished!', Zend_Log::INFO);
# Ab zum Index (Willkommensseite)
$request->setControllerName('index');
$request->setActionName('index');
}
else
{
# Wenn es nicht ok war, log und ab zurück zum Login
Zend_Registry::get('firelog')->log('Login failed!', Zend_Log::INFO);
$request->setControllerName('user');
$request->setActionName('login');
}
}
# Wenn keine Identität vorliegt
if(!$auth->hasIdentity())
{
# Keine Identität, loggen und auf Zugriff prüfen mit der Gruppe "Gast"
Zend_Registry::get('firelog')->log('User is not logged in!', Zend_Log::INFO);
$this->testAcl($request, "guest");
}
else
{
# Authorisiert, log und Zugriffsprüfung
Zend_Registry::get('firelog')->log('User is logged in!', Zend_Log::INFO);
$this->testAcl($request, $auth->getIdentity()->user_group);
}
}
/**
* Methode zum prüfen der Authorisation mittels ACL
* @param Zend_Controller_Request_Abstract $request
* @param $group Benutzergruppe
*/
public function testAcl(Zend_Controller_Request_Abstract $request, $group = null)
{
# Laden der ACL Struktur und erstellen einer Instanz
require_once APPLICATION_PATH . '/model/Acl.php';
$acl = new Model_Acl();
# Prüfe den Status der Zugriffsberechtigung
if($acl->isAllowed($group,$request->getControllerName()))
{
# Wenn die Ressource erlaubt ist
Zend_Registry::get('firelog')->log('User has Auth for: '. $request->getControllerName(), Zend_Log::INFO);
}
else
{
# Wenn die Resource nicht erlaubt ist
Zend_Registry::get('firelog')->log('User has no Auth for: '. $request->getControllerName(), Zend_Log::ALERT);
$request->setControllerName('user');
$request->setActionName('noauth');
}
}
}

Hallo Denis,
ein großes Lob und herzlichen für Deine Mühe!
Ich Sachen Zend bin ich noch Anfänger – beschäftige mich erst seit 2 Monaten damit – und komme mit Hilfe Deiner “HowTo”s ein gutes Stück weiter.
Mach weiter so – Du bist einer der Wenigen, die auch mal die 1.8er Features anwenden. Das ist meiner Meinung nach nämlich noch ein großes Manko bei den Tutorials von 2007 ;)
Beste Grüße aus dem Süden,
Florian
Hallo,
ich bin seit längerem auf der Suche nach einer Lösung. Vielleicht kannst Du mir ja weiterhelfen? Ich würde gerne in der INI-Datei auch die “Vererbung” der einzelnen Rollen realisieren – geht das?
Gruß
Piccolino81
@Piccolino81
Es ist möglich. Du kannst deine ACL.ini genauso wie die application.ini aufbauen. Minimalistisches Beispiel:
[user1]
…
[user2 : user1]
vG René
stimme Rene zu. Folgende ini-Struktur ist auch denkbar:
[role1]
{|*}.{|*}.{|*} = {allow|deny}
Sind bestimmte Ressourcen nicht definiert, werden sie standardmäßig als “deny” gewertet.
Bei diesem Schema ist allerdings etwas zusätzliche Arbeit nötig, um zu gewährleisten, dass das Überschreiben bestehender ‘*’-Regeln mittels neuer ‘*’-Regeln konsistent bleibt. Beispielsweise:
[visitor]
default.index.* = allow
*.*.index = deny
Je nach Reihenfolge, in der die Regeln interpretiert werden, ist der Zugriff auf default.index.index erlaubt oder nicht erlaubt. Hier müssen zusätzliche Maßnahmen getroffen werden (vorzugsweise überschreiben der Zend_Acl-Klasse).
Hey evilmonkey,
danke für deinen Kommentar. Es gibt nun einen neuen Artikel in dem ich deinen Vorschlag verarbeitet habe. Du bist da aber bestimmt schon weiter :)