Berechtigungen

Um Datensätze nur einer bestimmten Entity (Benutzer, Gruppen oder Rollen) zugänglich zu machen, kann man Berechtigungen auf Record-Ebene vergeben. Dazu muss das zu speichernde Objekt das \Alvine\Security\Authorization\AccessControlList-Interface implementieren.

uml diagram

Mit dem \Alvine\Security\Authorization\AccessControlListImplementation Trait steht hierfür bereits eine fertige Implementierung zur Verfügung.

Klassenstruktur

Benutzer, Gruppen und Rollen

Alle Identitäten sind von dem zentralen Interface \Alvine\Security\Authentication\Entity abgeleitet. Einer Entität können über eine Zugriffsliste verschiedene Rechte zugeordnet werden.

Ist kein Benutzer bekannt oder ist der Benutzer noch nicht identifiziert, so kommt das \Alvine\Security\Authentication\Anonymous Objekt zum Einsatz. Nach der Anmeldung wird der Benutzer zum authentifizierten Benutzer und wird über ein \Alvine\Security\Authentication\User Objekt abgebildet.

uml diagram

Zugriffsrechte

Rechte werden ebsonso wie Entitäten durch Klassen ausgedrückt. Im Bereich der Persitenz sind die Rechte \Alvine\Persistence\Permission\Write, \Alvine\Persistence\Permission\Read und \Alvine\Persistence\Permission\Delete definiert.

uml diagram

Jedes dieser Rechte steht für eine Operation. Benutzer die nur über das Recht Read verfügen, können das Objekt nicht ändern oder löschen.

AccessControlList

Zugriffsrechte und Entitäten werden in einer AccessControlList zusammengefasst. Das Interface \Alvine\Security\Authorization\AccessControlList definiert die notwenidigen Funktionen und mit dem \Alvine\Security\Authorization\AccessControlListImplementation Trait steht eine fertige Implementierung zur Verfügung.

uml diagram

In dem folgenden Beispiel wird eine Zugriffsliste definiert. Hier sieht man, dass der der Benutzer $user alle drei Rechte hat. $object ist in diesem Fall das zu speichernde Datenobjekt.

// Zugriffsrecte auf ein Objekt
$accessControl=new \Alvine\Security\Authorization\DefaultAccessControl();
// User darf lesen, schreiben und löschen
$accessControl->addEntity($user);

// Wenn diese Rechte eingeschränkt werden, wird weiter unten eine Exception geworfen.
$accessControl->addPermission(new Alvine\Persistence\Permission\Read());
$accessControl->addPermission(new Alvine\Persistence\Permission\Write());
$accessControl->addPermission(new Alvine\Persistence\Permission\Delete());

// Zugriffskontrolle in $object setzen
$object->addAccessControl($accessControl);

Implementierungsdetail

In der Standard-Implementierung der Zugriffsliste AccessControlListImplementation wird die Berechtigung über eine Assoziation in einem eigenen Objekt und nicht direkt im Datenobjekt gespeichert. Damit werden pro Speichervorgang zwei Objekte geschrieben bzw. geladen. Je nach Konfiguration können die Objekte aus unterschiedlichen Storages kommen.

Beispiel

Das folgende Beispiel zeigt eine Implementierung der Zugriffsrechte.

/**
 * Stringklasse mit Zugangsberechtigung
 */
class MyString extends Alvine\Types\StringType implements \Alvine\Security\Authorization\AccessControlList {

    /**
     * Standardimplementierung des \Alvine\Security\Authorization\AccessControlList 
     * Interfaces für die Zugangsberechtigung.
     */
    use \Alvine\Security\Authorization\AccessControlListImplementation;

    public function __construct($value=null) {
        parent::__construct($value);
        $this->initAccessControlList();
    }

}

// Datenprovider & Storage-Objekt erstellen
// $mongoURL = 'mongodb://mongo.example.com/test/collection';
$provider=new Alvine\Persistence\Provider\MongoDB\DataObject(new Alvine\Net\Resource\URI($mongoURL));
$storage=new \Alvine\Persistence\ObjectStorage($provider);


// Handler mit SecurityContext ausstatten, da sonst der 
// Manager davon ausgeht, dass der Handler keine Sicherheit
// unterstützt.
// Das kann mit einem Anonymen Benutzer erfolgen, da der
// Manager das später überschreibt.
$context=new Alvine\Persistence\SecurityContext(new \Alvine\Security\Authentication\Anonymous);
$storage->setSecurityContext($context);

// Der Manager sorgt für die Verwaltung der Objekte
// Der Manager kann auch über eine Eigenschaftsdatei initialisiert werden
// $manager->initPersitenceFromProperties()
$manager=\Alvine\Persistence\Manager::getInstance();
$manager->registerDefaultStorage($storage);

// Aktueller User
$user=new Alvine\Security\Authentication\User('me', '1425-0001');



// Der Manager muss über die entsprechende Identität verfügen.
// Damit kann er in den Storage-Objekten den SecurityIndex setzen.
$manager->setIdentity($user);

// Das eigentliche Datenobjekt
$string=new MyString();

// Zugriffsrecte auf dieses Objekt
$accessControl=new \Alvine\Security\Authorization\DefaultAccessControl();
// User darf lesen, schreiben und löschen
$accessControl->addEntity($user);

// Wenn diese Rechte eingeschränkt werden, wird weiter unten eine Exception geworfen.
$accessControl->addPermission(new Alvine\Persistence\Permission\Read());
$accessControl->addPermission(new Alvine\Persistence\Permission\Write());
$accessControl->addPermission(new Alvine\Persistence\Permission\Delete());

// Zugriffskontrolle setzen
$string->addAccessControl($accessControl);

$id=$string->getID();

// ID ausgeben
echo $id.Alvine\Types\Character::CRLF;

// Inhalt setzen
$string->string='Ich esse gerne Bananen!';
//
// Objekt in das Default-Objekt schreiben
try {
    $result=$manager->writeObject($string);
} catch(Alvine\Persistence\Permission\ForbiddenException $ex) {
    // Fallback, wird hier nicht aufgerufen.
    echo "write not allowed".Alvine\Types\Character::CRLF;
    exit(1);
}

// ... und im Anschluß Objekt im Speicher löschen
unset($string);

// Abfrage im Kontext eines Annoymen Benutzers.
$manager->setIdentity(new \Alvine\Security\Authentication\Anonymous());

// Objekt mit Anonymus versuchen zu laden
// sollte \Alvine\Persistence\Permission\ReadForbiddenException
// werfen.
try {
    $obj=$manager->getObject(MyString::class, $id);
} catch(\Alvine\Persistence\ObjectNotFoundException $ex) {
    echo "object not found".Alvine\Types\Character::CRLF;
    exit(1);
} catch(\Alvine\Persistence\Permission\ReadForbiddenException $ex) {
    echo "read not allowed".Alvine\Types\Character::CRLF;
    // -> read not allowed
}


// Zweiter Versuch mit dem User der das Objekt geschrieben hat
$manager->setIdentity($user);

try {
    $obj=$manager->getObject(MyString::class, $id);
} catch(\Alvine\Persistence\ObjectNotFoundException $ex) {
    echo "object not found".Alvine\Types\Character::CRLF;
    exit(1);
} catch(\Alvine\Persistence\Permission\ReadForbiddenException $ex) {
    echo "read not allowed".Alvine\Types\Character::CRLF;
    exit(1);
}

// ... und ausgeben der Zeichenkette
echo (string) $obj.Alvine\Types\Character::CRLF;
// -> Ich esse gerne Bananen!


// Objekt löschen
try {
    $manager->deleteObject($obj);
} catch(\Alvine\Persistence\Permission\ForbiddenException $ex) {

}