Door
Ozzie PHP
op 24-02-2014 08:21
gewijzigd op 24-02-2014 08:22
2.780 views
Hallo,
Gisteren wees Wouter mij hier op het Strategy pattern. Ik gebruik dit zelf nooit, dus ik ben er wat over gaan lezen. Zelf blijk ik vaak het template pattern te gebruiken. Nu vraag ik me af wanneer je wat gebruikt. Ik vind ze heel erg op elkaar lijken namelijk. Ik heb op Google gezocht, en blijkbaar hebben veel mensen dezelfde vraag gesteld, maar er komen zeer veel verschillende antwoorden op.
Ik heb zelf een abstracte autoloader class waarvan de loadClass method abstract is. De child class extend de abstracte class en moet deze loadClass method dan zelf invullen. Is dat correct? Of moet je hier ook een strategy pattern gebruiken?
Bij het inloggen controleer je het "wie" en injecteer je de groep-ID in de user. Bij het starten van een taak controleer je het "wat": welke rechten zijn voor deze taak vereist en heeft de groep-ID die rechten. Een applicatie of module daarbinnen kent geen gebruikers of groepen, alleen rechten.
De analogie van het rijbewijs gaat niet helemaal op, want je mag pas instappen in de auto-applicatie als je autorijden-rechten hebt die zijn toegekend aan de rijbewijsbezitters-groep.
Ward, hoe jij het nu uitlegt lijkt het alsof je gebruik maakt van 1 user (de "wie"). Eerder zei je dat er onderscheid is tussen een user en admin. Ik volg je dus nog niet helemaal. Kun je een stukje pseudo-code geven wellicht?
>> De analogie van het rijbewijs gaat niet helemaal op, want je mag pas instappen in de auto-applicatie als je autorijden-rechten hebt die zijn toegekend aan de rijbewijsbezitters-groep.
Oké, laten we dan zeggen dat de politie aan een jongen van 10 gaat vragen of hij mag rijden met een caravan. Anders gesteld, je gaat aan iemand (een user) iets vragen, waarvoor hij in basis geen bevoegdheid heeft. Ik heb geen vliegbewijs. Het zou dan heel raar zijn als iemand aan mij gaat vragen of ik een vluchtschema in elkaar wil zetten.
Ward zegt al 10x dat het afhangt van de situatie :)
<?php
$securityContext = new SecurityContext(); // de registry voor de security, houdt de current user vast
$authenticator = new RequestAuthentication($request, $securityContext);
$authorizator = new AccessDecissionManager($securityContext);
$authorizator->addVoter(new FormPostVoter()); // voters bepalen of een role iets mag of niet
$user = new AnonymousUser();
$authenticator->authenticate($user);
// $user in context wordt nu als het goed is een AuthenticatedUser, of anders
// nog steeds een AnonymousUser (als hij niet is ingelogd)
// ergens in je code
if ($authorizator->isGranted('EDIT_OWN', $formPost) {
$formPost->setContent($newContent);
In mijn voorbeeld is "admin" geen user maar een groep. Zo kun je, conform jouw eigen opzet, dus ook een extra groep "super-admin" hebben.
- Applicatie "auto" vereist het recht "autorijden".
- Het recht "autorijden" is toegekend aan de groep "rijbewijs B".
Op het moment dat de jongen van 10 instapt in de auto-applicatie, controleer je niet of hij mag autorijden. Je controleert alleen of hij tot een groep behoort die het voor die applicatie vereiste recht "autorijden" heeft. Al in een eerder stadium, bij het inloggen, had de user daarvoor in de groep "rijbewijs B" moeten worden geplaatst — wat niet is gebeurd bij deze jongen van 10, want die is geen lid van deze groep.
Maar je kunt het dus inderdaad op tig manieren oplossen. Bijvoorbeeld door lidmaatschap van de groep "admin" via de configuratie verplicht te stellen voor alle applicaties in de HTTP-route /admin/.
Ah, kijk... nu snap ik wat je bedoelt. Je bedoelt dus dat admin geen user is, maar een groep, en binnen die groep heb je dan weer rechten om bijv. een bericht te editen, verwijderen enz. Correct tot nu toe?
En iemand met de groep "admin" wordt dus in de back-end toegelaten, en binnen de back-end wordt dan weer gekeken of hij de juiste rol heeft. Klopt het nog wat ik tot nu toe zeg?
En stel nu dat je een groep "admin" hebt en een groep "super-admin" (waarbij deze laatste alles mag) en je wilt ergens controleren of een user een bericht mag aanpassen. Krijg je dan zoiets?
<?php
if ($user->getGroup() == 'super_admin' || ($user->getGroup() == 'admin' && $user->hasRight('edit'))) {
// bericht aanpassen
}
?>
Of kan zo'n controle ook efficiënter?
Zo'n controller zou ik dus laten uitvoeren in een andere klasse (in mijn voorbeeld de AccessDecisionManager). Deze heeft een heleboel voters die kijken of het user object mag wat je wil dat het mag. Die voeren dus de check uit, het enige wat jij dan nog hoeft te doen is de AccessDecisionManager aanroepen om de check uit te voeren.
Hierdoor kun je je voters ook heel snel aanpassen om van de ene methode (rechten door extenden) naar de andere (rechten door roles/groups) te gaan.
Hmmm.. ik snap niet echt hoe dat werkt... gaat ff boven m'n pet geloof ik :-/
Maar als ik je enigszins begrijp, wordt er op het punt waar je (in dit voorbeeld) het bericht kunt editen slechts 1 controle uitgevoerd:
<?php
if ($authorizator->isGranted('EDIT_OWN')) {
// bericht aanpassen
}
?>
En die authorizator class, die kijkt dan weer of de user tot de super-admin groep behoort, of indien dit niet het geval is, hij de juiste rechten, in dit geval 'EDIT_OWN', heeft. Zoiets bedoel je?
>> En die authorizator class, die kijkt dan weer of de user tot de super-admin groep behoort, of indien dit niet het geval is, hij de juiste rechten, in dit geval 'EDIT_OWN', heeft. Zoiets bedoel je?
Niet helemaal. Wat de authorizator#isGranted doet is dit:
<?php
public function isGranted($token, $object = null) {
foreach ($this->securityVoters as $voter) {
if (!$voter->accepts($token, $object)) {
continue;
}
Een voorbeeld van een Voter:
<?php
interface Voter
{
/**
* Checks if it can handle the requested token.
*
* @param string $token The requested token
* @param null|object $object The current object
*
* @return boolean
*/
public function accepts($token, $object = null);
/**
* Checks if it grants access.
*
* @param string $token The requested token
* @param User $user The current user
* @param null|object $object The current object
*
* @return boolean
*/
public function vote($token, User $user, $object = null);
}
class ForumPostVoter implements Voter
{
/**
* {@inheritDoc}
*/
public function accepts($token, $object = null)
{
return $object instanceof Post;
}
/**
* {@inheritDoc}
*/
public function vote($token, User $user, $object = null)
{
switch ($token) {
case 'DELETE':
// als de user de DELETE_POSTS role heeft mag hij forum posts verwijderen
return $user->getRoles()->has('DELETE_POSTS');
case 'EDIT':
// als de post minder dan 7 dagen oud is en de user is gelijk aan de schrijver
// óf de user heeft de EDIT_POSTS role mag de user de post bewerken
return ($object->publishTime() > strtotime('7 days ago')
&& $object->getAuthor() === $user)
|| $user->getRoles()->has('EDIT_POSTS');
case 'CREATE':
// als de user is ingelogd mag hij berichten bewerken
return $user->getRoles()->has('CREATE_POSTS');
}
}
}
?>
In gebruik:
<?php
if ($authorizator->isGranted('DELETE', $forumPost)) {
// ja, hij mag de forum post verwijderen!
$forumPost->delete();
} else {
// nee, hij mag het niet
throw new InvalidActionException('Je hebt niet genoeg rechten om een bericht te verwijderen');
}
?>
Pfff, pretty ingewikkeld :)
Maar ik snap (enigszins) het principe... met de nadruk op enigszins :)
Waar staat voter eigenlijk voor? Het heeft toch niks met stemmen te maken?