Instant ACLs with Zend Framework 2 Zend Framework Day – Turin, Italy – 07/02/2014
@stefanovalle
http://www.mvlabs.it/
http://friuli.grusp.org/
AUTHENTICATION AUTHORIZATION 5
Two step process
Two step process WHO WHAT
Two step process WHO Authentication “a process that ensures and confirms a user’s identity” WHAT Definitions from http://www.techopedia.com
Two step process WHO WHAT Authentication “a process that ensures and confirms a user’s identity” Authorization “a security mechanism used to determine user/client privileges or access levels related to system resources” Definitions from http://www.techopedia.com
11
In ZF2 WHO Zend/Authentication
In ZF2 WHO Zend/Authentication Authenticate against: • DB table • LDAP • HTTP • And more…
In ZF2 WHAT Zend/Permissions/Acl Zend/Permissions/Rbac
In ZF2 WHAT Zend/Permissions/Acl Zend/Permissions/Rbac Role Permission Identity Resource
SEEMS TOUGH…
JUST AS IT SHOULD!
CONSEQUENCES COULD BE UNDERESTIMATED
How does ZF2 help? 19
‘NUFF TALK. TIME FOR ACTION…
We need to add/edit conferences through a restricted area 1ST NEED
THE ADMINISTRATOR NEEDS TO BE RECOGNIZED
THE ADMINISTRATOR NEEDS TO BE RECOGNIZED IDENTIFIED
HEAD TO OFFICIAL MODULES’ WEBSITE
OH, LOOK WHAT WE JUST GOT!
Installing and enabling ZfcUser // composer.json "require": { "zf-commons/zfc-user-doctrine-orm": "0.1.*" } 28
Installing and enabling ZfcUser // composer.json "require": { "zf-commons/zfc-user-doctrine-orm": "0.1.*" } let’s suppose we use the Doctrine ORM 29
Installing and enabling ZfcUser // composer.json "require": { "zf-commons/zfc-user-doctrine-orm": "0.1.*" } // config/application.config.php <?php return array( 'modules' => array( // ... 'ZfcBase', 'ZfcUser', 'ZfcUserDoctrineORM', ), ); 30
Back to our user… 31
How shall we represent him?
We need a class class Systemuser { private $id; private $name; private $city; private $birthday; private $username; private $password; }
With some mandatory fields class Systemuser { private $id; private $name; private $city; private $birthday; private $username; private $password; }
Implementing an interface class Systemuser implements ZfcUserEntityUserInterface { private $id; private $name; private $city; private $birthday; private $username; private $password; }
Let’s configure ZfcUser // config/autoload/zfcuser.global.php /** ZfcUser Configuration */ $settings = array( /** User Model Entity Class */ 'user_entity_class' => 'ApplicationEntitySystemuser', /** Start configuration for ZfcUserDoctrineORM */ 'enable_default_entities' => false, ); 36
Yay, here’s our working login form! 37
Yay, here’s our working login form! Available at: http://myaddress/user/login 38
Yay, it works! 39
ZfcUser also allows to: • • • • • 40 Customize login form Customize User entity fields Quickly implement a registration form Interact with either Zend/DB or Doctrine out of the box Do much more stuff…
ZfcUser also allows to: • • • • • 41 Customize login form Customize User entity fields Quickly implement a registration form Interact with either Zend/DB or Doctrine out of the box Do much more stuff…
Remember the two steps? WHO WHAT
Remember the two steps? WHO WHAT
We need an admin panel! 44
We need an admin panel! Welcome ZfcAdmin! provides a ready to use /admin route 45
hubme.in has an admin panel!
hubme.in has an admin panel!
Are we done yet? 48
What if a malicious user…
What if a malicious user…
What if a malicious user… …hits this url: http://myawesomewebsite/admin/conferences
What if a malicious user… …hits this url: http://myawesomewebsite/admin/conferences accessible to everyone!
What if a malicious user… …hits this url: http://myawesomewebsite/admin/conferences nothing’s protecting our private area
What if a malicious user… …hits this url: http://myawesomewebsite/admin/conferences nothing’s protecting our private area Login form could be bypassed!
No worries! /* * On each action */ <?php public function indexAction() { if (!$this->zfcUserAuthentication()->hasIdentity()) { return $this->redirect()->toRoute('home'); } } 55
No worries! /* * On each action */ <?php public function indexAction() { if (!$this->zfcUserAuthentication()->hasIdentity()) { return $this->redirect()->toRoute('home'); } } 56 in EACH action of EACH controller
WHAAAT?
IN EACH ACTION???
SOMEONE HELP US!
ZENDPERMISSIONSACL
Remember? There were two steps… WHO WHAT
Using Zend/Permissions/Acl <?php use ZendPermissionsAclAcl; use ZendPermissionsAclRoleGenericRole as Role; use ZendPermissionsAclResourceGenericResource as Resource; $acl = new Acl(); $acl->addRole(new Role('guest')) ->addRole(new Role('admin')); $acl->addResource(new $acl->addResource(new $acl->addResource(new $acl->addResource(new $acl->allow('guest', $acl->allow('admin', $acl->allow('admin', $acl->allow('admin', 62 Resource('someResource')); Resource('adminarea')); Resource('adminconferencearea')); Resource('adminsettingsarea')); 'someResource'); 'adminarea'); 'adminconferencearea '); 'adminsettingsarea ');
Welcome BjyAuthorize! … a facade for ZendPermissionsAcl that will ease its usage with modules and applications … From https://github.com/bjyoungblood/BjyAuthorize 63
Welcome BjyAuthorize! … a facade for ZendPermissionsAcl that will ease its usage with modules and applications … From https://github.com/bjyoungblood/BjyAuthorize 64
OUR EASIER WAY
How does it work? 66
Standard ZendMvc app workflow From https://github.com/bjyoungblood/BjyAuthorize 67
With BjyAuthorize enabled From https://github.com/bjyoungblood/BjyAuthorize 68
With BjyAuthorize enabled From https://github.com/bjyoungblood/BjyAuthorize 69
With BjyAuthorize enabled From https://github.com/bjyoungblood/BjyAuthorize 70
With BjyAuthorize enabled + control over resources From https://github.com/bjyoungblood/BjyAuthorize 71
Installing and enabling BjyAuthorize // composer.json "require": { "bjyoungblood/bjy-authorize": "1.4.*" } // config/application.config.php <?php return array( 'modules' => array( // ... 'BjyAuthorize', ), ); 72
Configuring BjyAuthorize // config/autoload/bjyauthorize.global return array( 'bjyauthorize' => array( 'default_role' => 'guest', 'identity_provider' => 'BjyAuthorizeProviderIdentityAuthenticationIdentityProvider', 'role_providers' => array( 'BjyAuthorizeProviderRoleConfig' => array( 'guest' => array(), 'admin' => array(), ), ), ), ); 73
Configuring BjyAuthorize // config/autoload/bjyauthorize.global return array( 'bjyauthorize' => array( A new concept: the Role 'default_role' => 'guest', 'identity_provider' => 'BjyAuthorizeProviderIdentityAuthenticationIdentityProvider', 'role_providers' => array( 'BjyAuthorizeProviderRoleConfig' => array( 'guest' => array(), 'admin' => array(), ), ), ), ); 74
Guards on routes http://myawesomewebsite/ Allowed to all users 75
Guards on routes http://myawesomewebsite/ Allowed to all users http://myawesomewebsite/admin/... Restricted area! For admins only 76
Guards on controller actions class ConferencesController { public function listAction() { // code... } public function manageAction() { // code... } } 77
Guards on controller actions class ConferencesController { public function listAction() { // code... } Allowed public function manageAction() { // code... } } 78 to all users
Guards on controller actions class ConferencesController { public function listAction() { // code... } Allowed to all users public function manageAction() { // code... } } 79 Restricted area! For admins only
Guards on controller actions array( 'controller' => 'ZfcAdminControllerAdminController', 'roles' => array('admin') ) 80
Where should guards be placed? 81
Inside each module configuration // module/Conferences/config/module.config.php return array( 'bjyauthorize' => array( 'guards' => array( 'BjyAuthorizeGuardController' => array( //... ), ), ), 82
Inside each module configuration // module/Conferences/config/module.config.php return array( 'bjyauthorize' => array( Taking advantage of ZF2 configuration merge 'BjyAuthorizeGuardController' => array( 'guards' => array( //... ), ), ), 83
It works!
It works! User could be redirected to whatever url we want
Dude, forgot to tell ya! …we got 2 fellas! 2ND NEED
Two different roles The reader 87
Two different roles The reader 88
Two different roles The reader 89 The editor
Two different roles Can only view conference info The reader 90 Can view conferences + create, edit and delete info The editor
What we want Only editor should see these icons
Until now… 'bjyauthorize' => array( // ... 'role_providers' => array( 'BjyAuthorizeProviderRoleConfig' => array( 'guest' => array(), 'admin' => array(), ), ), ) Static role list 93
Until now… 'bjyauthorize' => array( // ... 'role_providers' => array( 'BjyAuthorizeProviderRoleConfig' => array( 'guest' => array(), 'admin' => array(), ), ), ) More flexibility wouldn’t hurt… 94
BjyAuthorize config changes // config/autoload/bjyauthorize.global return array( 'bjyauthorize' => array( 'role_providers' => array( 'BjyAuthorizeProviderRoleObjectRepositoryProvider' => array( 'role_entity_class' => 'ApplicationEntityRole', 'object_manager' => 'doctrine.entity_manager.orm_default', ), ), ), ); From array to class (persisted on db) 95
Let’s map the actions
New concept: the Resource
something
something upon which someone
something upon which someone could perform an action
ENTITY IDENTITY / ROLE PRIVILEGE
On BjyAuthorize… 'resource_providers' => array( 'BjyAuthorizeProviderResourceConfig' => array( 'Conference' => array(), ), ), 'rule_providers' => array( 'BjyAuthorizeProviderRuleConfig' => array( 'allow' => array( // allow editors to edit conferences array(array('editor'), 'Conference', array('edit')), ), ), 102
On views… //Conferences/view/…/index.phtml <?php if ($this->isAllowed($event, 'edit')) { ?> <a href="someurl">Remove</a><br /> <a href="someurl">Edit</a> <?php } ?> 103
Views, routes and controllers are safe 104
Views, routes and controllers are safe Is this enough? 105
Another controller, another action //Conferences/Controller/AnotherAdminController.php class AnotherAdminController extends AbstractActionController { public function someCrazyAction() { //... $this->conferenceService->updateConference($myConference); } } What prevents this? 106
107 SERVICE CONTROLLER ROUTE Choose your protection level
Conference service //Conferences/Service/ConferenceService.php namespace ConferencesService; class ConferenceService { public function getConference($id) { ... } public function getConferenceList($someCriteria) { ... } public function updateConference($myConf) { ... } public function deleteConference($myConf) { ... } } 108
Conference service //Conferences/Service/ConferenceService.php namespace ConferencesService; class ConferenceService { public function getConference($id) { ... } public function getConferenceList($someCriteria) { ... } public function updateConference($myConf) { ... } public function deleteConference($myConf) { ... } } Only to allowed users! 109
Let’s inject the Authorize class //Conferences/Service/ConferenceServiceFactory.php namespace ConferencesService; class ConferenceServiceFactory implements FactoryInterface { public function createService(ServiceLocatorInterface $serviceLocator) { //... $authorize = $serviceLocator->get('BjyAuthorizeServiceAuthorize'); return new ConferenceService(..., $authorize); } } 110
Updated conference service //Conferences/Service/ConferenceService.php namespace ConferencesService; class ConferenceService { //... public function updateConference($myConf) { if (!$this->authorize->isAllowed($myConf, 'edit')) { throw new UnAuthorizedException(); } // other code... } // the same for deleteConference method } 111
112 SERVICE CONTROLLER ROUTE Now our service is secured
We’ll outsource the management of foreign conferences 3RD NEED
Based on their country
How database changes class Systemuser { //... private $country; } class Conference { //... private $country; } 116
Create an Assertion use ZendPermissionsAclAssertionAssertionInterface; class CheckUserCountry implements AssertionInterface { // ... public function assert(Acl $acl, RoleInterface $role = null, ResourceInterface $resource = null, $privilege = null) { // ... } } 117
Create an Assertion use ZendPermissionsAclAssertionAssertionInterface; class CheckUserCountry implements AssertionInterface { // ... public function assert(Acl $acl, RoleInterface $role = null, ResourceInterface $resource = null, $privilege = null) { return $resource->getCountry() == $this->loggedUser->getCountry(); } } 118
Create an Assertion use ZendPermissionsAclAssertionAssertionInterface; class CheckUserCountry implements AssertionInterface { // ... Injected through constructor public function assert(Acl $acl, RoleInterface $role = null, ResourceInterface $resource = null, $privilege = null) { return $resource->getCountry() == $this->loggedUser->getCountry(); } } 119
Create an Assertion use ZendPermissionsAclAssertionAssertionInterface; class CheckUserCountry implements AssertionInterface { // ... public function assert(Acl $acl, RoleInterface $role = null, ResourceInterface $resource = null, $privilege = null) { return $resource->getCountry() == $this->loggedUser->getCountry(); } } 120 1 LoC… AWESOME!!
Update rule with the new assertion 'rule_providers' => array( 'BjyAuthorizeProviderRuleConfig' => array( 'allow' => array( // role check through assertion array(array('editor'), 'Conference', array('edit'), 'assertion.CheckUserCountry'), ), ), 121
The reader 122
The reader The editor 123
In the same way we could: • • • 124 Restrict access to user owned onferences only or conferences owned by a group the user is belonging to …and much more!
Cool. How'bout the admin menu though? 4TH NEED
Navigation menu 126
Configure Zend/Navigation // module/Conferences/config/module.config.php return array( 'navigation' => array( 'admin' => array( 'conferences' => array( 'label' => 'Conferences', 'route' => 'zfcadmin/conferences', 'resource' => 'Conference', 'privilege' => 'view', ), ), ), ), ); 127
Configure Zend/Navigation // module/Settings/config/module.config.php return array( 'navigation' => array( 'admin' => array( 'settings' => array( 'label' => 'Settings', 'route' => 'zfcadmin/settings', 'resource' => 'Setting', 'privilege' => 'view', ), ), ), ), ); 128
How menu looks like for admins
How menu looks like for other users
FINAL NOTES
PLUGGABLE COMPONENTS
PLUGGABLE COMPONENTS CLEAN ARCHITECTURE
PLUGGABLE COMPONENTS CLEAN ARCHITECTURE COMPLEX ACL LOGIC IN A FEW MINUTES
Thank you for your attention! Stefano Valle @stefanovalle s.valle@mvassociati.it
Questions? Stefano Valle @stefanovalle s.valle@mvassociati.it
Photo Credits From Flickr: http://www.flickr.com/photos/cbpphotos/8652042987 http://www.flickr.com/photos/disa4ever/9409743179 http://www.flickr.com/photos/ben_salter/6169305845 http://www.flickr.com/photos/elzey/3481161467 http://www.flickr.com/photos/morris278/8022505933 A-Team members’ photos: http://5gta.com/gta-5-info/gta-5-the-a-team-similarities.html/ http://www.legendarytv.com/the_a-team/the_a-team_lance_legault.asp http://www.fanpop.com/clubs/the-a-team/images http://dwightschultz.freeforums.org/dwight-photo-s-t8.html http://docmanhattan.blogspot.it/2010/10/vita-mort-immortalita-e-miracoli-di-mr.html http://www.starsky-iom.com/forum/viewtopic.php?f=8&t=58 http://www.thea-teamonline.com/ And others form iStockPhoto 137

Instant ACLs with Zend Framework 2