<?xml version="1.0" encoding="UTF-8"?>
<!-- EN-Revision: 24249 -->
<!-- Reviewed: no -->
<sect1 id="learning.multiuser.authorization">
    <title>Erstellung eines Authorisations Systems in Zend Framework</title>

    <sect2 id="learning.multiuser.authorization.intro">
        <title>Einführung in Authorisation</title>

        <para>
            Nachdem ein Benutzer als authentisch identifiziert wurde, kann eine Anwendung mit Ihrem
            Geschäft weitermachen und einige nützliche und gewünschte Ressourcen an den Konsumenten
            liefern. In vielen Fällen können Anwendungen andere Ressource Typen enthalten, wobei
            einige Ressourcen auch striktere Regeln betreffend Zugriff haben können. Dieser Prozess
            der Erkennung, wer Zugriff zu welchen Ressourcen hat, ist der Prozess der
            "Authorisierung". Authorisierung in seiner einfachsten Form ist die Komposition der
            folgenden Elemente:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    die Identität von dem der Zugriff wünscht
                </para>
            </listitem>

            <listitem>
                <para>
                    die Ressource für welche die Identität den Zugriff anfragt um Sie zu konsumieren
                </para>
            </listitem>

            <listitem>
                <para>
                    und optional, was der Identität erlaubt ist mit der Ressource zu tun
                </para>
            </listitem>
        </itemizedlist>

        <para>
            Im Zend Framework behandelt die Komponente <classname>Zend_Acl</classname> die Arbeit
            der Erstellung eines Baums von Rollen, Ressourcen und Privilegien um
            Authorisationsanfragen zu managen und abzufragen.
        </para>
    </sect2>

    <sect2 id="learning.multiuser.authorization.basic-usage">
        <title>Grundsätzliche Verwendung von Zend_Acl</title>

<!-- explain the interaction with a User object, how -->

        <para>
            Wenn <classname>Zend_Acl</classname> verwendet wird können beliebige Modelle als Rollen
            oder Ressourcen fungieren indem einfach das richtige Interface implementiert wird. Um
            als Rolle verwendet zu werden muss die Klasse
            <classname>Zend_Acl_Role_Interface</classname> implementieren, welche nur
            <methodname>getRoleId()</methodname> benötigt. Um als Ressource zu verwenden muss eine
            Klasse <classname>Zend_Acl_Resource_Interface</classname> implementieren wofür die
            Klasse so ähnlich die <methodname>getResourceId()</methodname> Methode implementieren
            muss.
        </para>

        <para>
            Anbei wird ein einfaches Benutzermodell demonstriert. Dieses Modell kann einen Teil in
            unserem <acronym>ACL</acronym> System übernehmen indem einfach
            <classname>Zend_Acl_Role_Interface</classname> implementiert wird. Die Methode
            <methodname>getRoleId()</methodname> gibt die Id "guest" zurück wenn die Id nicht
            bekannt ist, oder die Id der Rolle welche mit dem aktuellen Benutzerobjekt verknüpft
            ist. Dieser Wert kann effektiv von überall kommen, eine statische Definition oder
            vielleicht dynamisch von der Datenbankrolle des Benutzers selbst.
        </para>

        <programlisting language="php"><![CDATA[
class Default_Model_User implements Zend_Acl_Role_Interface
{
    protected $_aclRoleId = null;

    public function getRoleId()
    {
        if ($this->_aclRoleId == null) {
            return 'guest';
        }

        return $this->_aclRoleId;
    }
}
]]></programlisting>

        <para>
            Wärend das Konzept eines Benutzers als Rolle recht gerade heraus ist, könnte die
            Anwendung ein anderes Modell wählen welches im eigenen System als potentielle
            "Ressource" im <acronym>ACL</acronym> System verwendet wird. Der Einfachheit halber
            verwenden wir das Beispiel eines Blog Posts. Da der Typ der Ressource mit dem Typ des
            Objekts verknüpft ist, gibt diese Klasse nur 'blogPost' als Ressource ID im System
            zurück. Natürlich kann dieser Wert dynamisch sein wenn das System dies benötigt.
        </para>

        <programlisting language="php"><![CDATA[
class Default_Model_BlogPost implements Zend_Acl_Resource_Interface
{
    public function getResourceId()
    {
        return 'blogPost';
    }
}
]]></programlisting>

        <para>
            Jetzt da wir zumindest eine Rolle und eine Ressource haben können wir mit der Definition
            der Regeln des <acronym>ACL</acronym> Systems weitermachen. Diese Regeln werden
            konsultiert wenn das System eine Abfrage darüber erhält was möglicherweise eine
            bestimmte Rolle, eine Ressource und optional ein Privileg ist.
        </para>

        <para>
            Nehmen wir die folgenden Regeln an:
        </para>

        <programlisting language="php"><![CDATA[
$acl = new Zend_Acl();

// Die verschiedenen Rollen im System einstellen
$acl->addRole('guest');
// Der Eigentümer erbt alle Regeln vom Gast
$acl->addRole('owner', 'guest');

// Die Ressource hinzufügen
$acl->addResource('blogPost');

// Die Privilegien den Rollen- und Ressourcekombinationen hinzufügen
$acl->allow('guest', 'blogPost', 'view');
$acl->allow('owner', 'blogPost', 'post');
$acl->allow('owner', 'blogPost', 'publish');
]]></programlisting>

        <para>
            Die oben stehenden Regeln sund recht einfach: eine Gastrolle und eine Eigentümerrolle
            existieren; sowie ein blogPost Ressourcetyp. Gästen ist es erlaubt Blogposts anzusehen,
            und Eigentümern ist es erlaubt zu posten und Blogposts zu veröffentlichen. Um dieses
            System abzufragen könnte man das folgende machen:
        </para>

        <programlisting language="php"><![CDATA[
// Wir nehmen an dass das Benutzermodell vom Ressourcetyp Gast ist
$guestUser = new Default_Model_User();
$ownerUser = new Default_Model_Owner('OwnersUsername');

$post = new Default_Model_BlogPost();

$acl->isAllowed($guestUser, $post, 'view'); // true
$acl->isAllowed($ownerUser, $post, 'view'); // true
$acl->isAllowed($guestUser, $post, 'post'); // false
$acl->isAllowed($ownerUser, $post, 'post'); // true
]]></programlisting>

        <para>
            Wie man sieht können bei Ausführung der obigen Regeln entweder Eigentümer und Gäste
            Posts ansehen, oder neue Posts erstellen, was Eigentümer können und Gäste nicht. Aber
            wie man erwarten kann ist diese Art von System nicht so dynamisch wie man es wünschen
            könnte. Was, wenn wir sicherstellen wollen das einem spezifischen Benutzer ein sehr
            spezifischer Blogpost gehört bevor Ihm erlaubt wird Ihn zu veröffentlichen? In anderen
            Worten wollen wir sicherstellen das nur Blogpost Eigentümer nur die Möglichkeit haben
            Ihre eigenen Posts zu veröffentlichen.
        </para>

        <para>
            Hier kommen Annahmen zum Einsatz. Annahmen sind Methoden welche aufgerufen werden wenn
            das prüfen einer statischen Regel einfach nicht genug ist. Wenn ein Annahmeobjekt
            registriert wird, dann wird dieses Objekt konsultiert um, typischerweise dynamisch, zu
            ermitteln ob einige Rollen Zugriff auf einige Ressourcen, mit einigen optionalen
            Privilegien haben was nur durch die Logik in der Annahme beantwortet werden kann.
            Für dieses Beispiel verwenden wir die folgende Annahme:
        </para>

        <programlisting language="php"><![CDATA[
class OwnerCanPublishBlogPostAssertion implements Zend_Acl_Assert_Interface
{
    /**
     * Diese Annahme sollte die aktuellen Benutzer und BlogPost Objekte
     * empfangen
     *
     * @param Zend_Acl $acl
     * @param Zend_Acl_Role_Interface $user
     * @param Zend_Acl_Resource_Interface $blogPost
     * @param $privilege
     * @return bool
     */
    public function assert(Zend_Acl $acl,
                           Zend_Acl_Role_Interface $user = null,
                           Zend_Acl_Resource_Interface $blogPost = null,
                           $privilege = null)
    {
        if (!$user instanceof Default_Model_User) {
            throw new Exception(__CLASS__
                              . '::'
                              . __METHOD__
                              . ' erwartet das die Rolle eine'
                              . ' Instanz von user ist');
        }

        if (!$blogPost instanceof Default_Model_BlogPost) {
            throw new Exception(__CLASS__
                              . '::'
                              . __METHOD__
                              . ' erwartet das die Ressource eine'
                              . ' Instanz von BlogPost ist');
        }

        // Wenn die Rolle ein publisher ist kann Sie einen Post immer verändern
        if ($user->getRoleId() == 'publisher') {
            return true;
        }

        // Prüfen um sicherzustellen das alle anderen nur deren eigene Posts
        // verändern
        if ($user->id != null && $blogPost->ownerUserId == $user->id) {
            return true;
        } else {
            return false;
        }
    }
}
]]></programlisting>

        <para>
            Um dies mit unserem <acronym>ACL</acronym> System zu verknüpfen würden wir das folgende
            tun:
        </para>

        <programlisting language="php"><![CDATA[
// Dies ersetzen:
//   $acl->allow('owner', 'blogPost', 'publish');
// Mit diesem:
$acl->allow('owner',
            'blogPost',
            'publish',
            new OwnerCanPublishBlogPostAssertion());

// Auch die Rolle"publisher" hinzufügen der auf alles Zugriff hat
$acl->allow('publisher', 'blogPost', 'publish');
]]></programlisting>

        <para>
            Jetzt wird jedesmal wenn <acronym>ACL</acronym> darüber konsultiert wird ob ein
            Benutzer einen spezifischen Blogpost veröffentlichen kann diese Annahme ausgeführt.
            Diese Annahme stellt sicher dass, solange der Rollentyp nicht 'publisher' ist, die
            Benutzerrolle der Anfrage logisch mit dem Blogpost verbunden sein muss. In diesem
            Beispiel haben wir geprüft das die Eigenschaft <property>ownerUserId</property> des
            Blogposts mit der übergebenen Id des Benutzers übereinstimmt.
        </para>
    </sect2>
</sect1>
