Creating fast, dynamic ACLs in Zend Framework Wim Godden Cu.be Solutions
Who am I ? <ul><li>Wim Godden (@wimgtr)
Owner of Cu.be Solutions (http://cu.be)
PHP developer since 1997
Developer of OpenX
Zend Certified Engineer
Zend Framework Certified Engineer
MySQL Certified Developer </li></ul>
Talking about... <ul><li>Authentication </li><ul><li>-> Zend_Auth </li></ul><li>Auditing </li><ul><li>-> Zend_Log </li></u...
Authorization <ul>Wikipedia : &quot;the function of specifying access rights to resources&quot; </ul>
What's a resource ? <ul><li>Object (Article, Invoice, Document, …)
Webpage
Database / table / row
... </li></ul>
Standard ACL <ul><li>Access to  resources  is defined in  privileges
Privileges are grouped together in  roles
2 types of  roles  : </li><ul><li>Anonymous / Unknown
Registered / Known </li></ul></ul>
Zend_Acl : the good <ul><li>Flexible
Uses standard role / resource principles </li><ul><li>Recognizable -> easy to get started </li></ul><li>No link to specifi...
Allow + deny
Proven, tested </li></ul>
Zend_Acl : the bad & ugly <ul><li>Complexity of rules rises quickly
Performance issues
All rules are in-code
-> maintainability becomes an issue </li></ul>
Evolution of a portal $acl =  new  Zend_Acl(); $acl->addRole( new  Zend_Acl_Role( 'guest' )); $acl->addRole( new  Zend_Acl...
Evolution of a portal $acl =  new  Zend_Acl(); $acl->addRole( new  Zend_Acl_Role( 'guest' )); $acl->addRole( new  Zend_Acl...
Evolution of a portal $acl =  new  Zend_Acl(); $acl->addRole( new  Zend_Acl_Role( 'guest' )); $acl->addRole( new  Zend_Acl...
Evolution of a portal $acl =  new  Zend_Acl(); $acl->addRole( new  Zend_Acl_Role( 'guest' )); $acl->addRole( new  Zend_Acl...
Evolution of a portal $acl =  new  Zend_Acl(); $acl->addRole( new  Zend_Acl_Role( 'guest' )); $acl->addRole( new  Zend_Acl...
Hard to ... <ul><li>maintain all rules
keep track of the rules
debug the rules </li></ul>
Possible solution : database <ul><li>Extend Zend_Acl to database driven design
Good : no code changes required
Bad : more load on DB </li></ul>
A different approach <ul><li>Not  THE  solution, merely  A  solution
Uses database, but...
Additional caching layer
ZF Conventional Modular Directory Structure
Backend interface for easy management </li><ul><li>with a twist ! </li></ul></ul>
Upcoming SlideShare
Loading in...5
×

Creating fast, dynamic ACLs in Zend Framework

10,080

Published on

How do you create ACLs in a dynamic and flexible way, while keeping the solution very fast and scalable ?

Published in: Technology, Health & Medicine
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
10,080
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
88
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Creating fast, dynamic ACLs in Zend Framework

  1. 1. Creating fast, dynamic ACLs in Zend Framework Wim Godden Cu.be Solutions
  2. 2. Who am I ? <ul><li>Wim Godden (@wimgtr)
  3. 3. Owner of Cu.be Solutions (http://cu.be)
  4. 4. PHP developer since 1997
  5. 5. Developer of OpenX
  6. 6. Zend Certified Engineer
  7. 7. Zend Framework Certified Engineer
  8. 8. MySQL Certified Developer </li></ul>
  9. 9. Talking about... <ul><li>Authentication </li><ul><li>-> Zend_Auth </li></ul><li>Auditing </li><ul><li>-> Zend_Log </li></ul><li>Authorization </li><ul><li>-> Zend_Acl </li></ul></ul>
  10. 10. Authorization <ul>Wikipedia : &quot;the function of specifying access rights to resources&quot; </ul>
  11. 11. What's a resource ? <ul><li>Object (Article, Invoice, Document, …)
  12. 12. Webpage
  13. 13. Database / table / row
  14. 14. ... </li></ul>
  15. 15. Standard ACL <ul><li>Access to resources is defined in privileges
  16. 16. Privileges are grouped together in roles
  17. 17. 2 types of roles : </li><ul><li>Anonymous / Unknown
  18. 18. Registered / Known </li></ul></ul>
  19. 19. Zend_Acl : the good <ul><li>Flexible
  20. 20. Uses standard role / resource principles </li><ul><li>Recognizable -> easy to get started </li></ul><li>No link to specific backend
  21. 21. Allow + deny
  22. 22. Proven, tested </li></ul>
  23. 23. Zend_Acl : the bad & ugly <ul><li>Complexity of rules rises quickly
  24. 24. Performance issues
  25. 25. All rules are in-code
  26. 26. -> maintainability becomes an issue </li></ul>
  27. 27. Evolution of a portal $acl = new Zend_Acl(); $acl->addRole( new Zend_Acl_Role( 'guest' )); $acl->addRole( new Zend_Acl_Role( 'member' ), 'guest' ); $acl->addRole( new Zend_Acl_Role( 'admin' ), 'member' ); $acl->addResource( new Zend_Acl_Resource( 'cms' )); $acl->addResource( new Zend_Acl_Resource( 'report' )); $acl->allow( 'guest' , 'cms' , 'view' ); $acl->allow( 'admin' , 'cms' , 'edit' ); $acl->deny( 'guest' , 'report' ); $acl->allow( 'member' , 'report' );
  28. 28. Evolution of a portal $acl = new Zend_Acl(); $acl->addRole( new Zend_Acl_Role( 'guest' )); $acl->addRole( new Zend_Acl_Role( 'departmentA' ), 'guest' ); $acl->addRole( new Zend_Acl_Role( 'departmentB' ), 'guest' ); $acl->addRole( new Zend_Acl_Role( 'admin' ), 'member' ); $acl->addResource( new Zend_Acl_Resource( 'cms' )); $acl->addResource( new Zend_Acl_Resource( 'report' )); $acl->allow( 'guest' , 'cms' , 'view' ); $acl->allow( 'admin' , 'cms' , 'edit' ); $acl->deny( 'guest' , 'report' ); $acl->allow( 'departmentA' , 'report' );
  29. 29. Evolution of a portal $acl = new Zend_Acl(); $acl->addRole( new Zend_Acl_Role( 'guest' )); $acl->addRole( new Zend_Acl_Role( 'departmentA' ), 'guest' ); $acl->addRole( new Zend_Acl_Role( 'departmentB' ), 'guest' ); $acl->addRole( new Zend_Acl_Role( 'departmentC_senior_staff' ), 'guest' ); $acl->addRole( new Zend_Acl_Role( 'departmentC_marketing' ), 'guest' ); $acl->addRole( new Zend_Acl_Role( 'admin' ), 'member' ); $acl->addResource( new Zend_Acl_Resource( 'cms' )); $acl->addResource( new Zend_Acl_Resource( 'report' )); $acl->addResource( new Zend_Acl_Resource( 'newsletter' )); $acl->addResource( new Zend_Acl_Resource( 'photo' )); $acl->addResource( new Zend_Acl_Resource( 'faq' )); $acl->allow( 'guest' , 'cms' , 'view' ); $acl->allow( 'admin' , 'cms' , 'edit' ); $acl->deny( 'guest' , 'report' ); $acl->allow( 'departmentA' , 'report' ); $acl->deny('departmentC_senior_staff', 'newsletter'); $acl->allow('departmentC_marketing', 'newsletter'); $acl->allow('member', 'photo', 'view'); $acl->allow('departmentC_marketing', 'photo', 'upload'); $acl->allow('admin', 'photo', 'delete'); $acl->allow('guest', 'faq', 'view'); $acl->allow('member', 'faq', 'comment'); $acl->allow('departmentA', 'faq', 'edit'); $acl->allow('departmentC_senior_staff', 'faq', 'edit'); $acl->allow('admin', 'faq', 'edit');
  30. 30. Evolution of a portal $acl = new Zend_Acl(); $acl->addRole( new Zend_Acl_Role( 'guest' )); $acl->addRole( new Zend_Acl_Role( 'departmentA' ), 'guest' ); $acl->addRole( new Zend_Acl_Role( 'departmentB' ), 'guest' ); $acl->addRole( new Zend_Acl_Role( 'departmentC_senior_staff' ), 'guest' ); $acl->addRole( new Zend_Acl_Role( 'departmentC_marketing' ), 'guest' ); $acl->addRole( new Zend_Acl_Role( 'cook' ), 'guest' ); $acl->addRole( new Zend_Acl_Role( 'admin' ), 'member' ); $acl->addResource( new Zend_Acl_Resource( 'cms' )); $acl->addResource( new Zend_Acl_Resource( 'report' )); $acl->addResource( new Zend_Acl_Resource( 'newsletter' )); $acl->addResource( new Zend_Acl_Resource( 'photo' )); $acl->addResource( new Zend_Acl_Resource( 'faq' )); $acl->addResource( new Zend_Acl_Resource( 'invoicing' )); $acl->addResource( new Zend_Acl_Resource( 'stats' )); $acl->addResource( new Zend_Acl_Resource( 'lunchmenu' )); $acl->allow( 'guest' , 'cms' , 'view' ); $acl->allow( 'admin' , 'cms' , 'edit' ); $acl->deny( 'guest' , 'report' ); $acl->allow( 'departmentA' , 'report' ); $acl->deny('departmentC_senior_staff', 'newsletter'); $acl->allow('departmentC_marketing', 'newsletter'); $acl->allow('member', 'photo', 'view'); $acl->allow('departmentC_marketing', 'photo', 'upload'); $acl->allow('admin', 'photo', 'delete'); $acl->allow('guest', 'faq', 'view'); $acl->allow('member', 'faq', 'comment'); $acl->allow('departmentA', 'faq', 'edit'); $acl->allow('departmentC_senior_staff', 'faq', 'edit'); $acl->allow('admin', 'faq', 'edit'); $acl->allow('admin', 'photo', 'delete'); $acl->allow('guest', 'faq', 'view'); $acl->allow('member', 'faq', 'comment'); $acl->allow('departmentA', 'faq', 'edit'); $acl->allow('departmentC_senior_staff', 'faq', 'edit'); $acl->allow('admin', 'faq', 'edit'); $acl->allow('cook', 'lunchmenu', 'edit'); $acl->allow('member', 'lunchmenu', 'view'); $acl->allow('accounting', 'invoicing', 'edit'); $acl->allow('admin', 'invoicing', 'edit'); $acl->allow('departmentC_senior_staff', 'invoicing', 'report');
  31. 31. Evolution of a portal $acl = new Zend_Acl(); $acl->addRole( new Zend_Acl_Role( 'guest' )); $acl->addRole( new Zend_Acl_Role( 'departmentA' ), 'guest' ); $acl->addRole( new Zend_Acl_Role( 'departmentB' ), 'guest' ); $acl->addRole( new Zend_Acl_Role( 'departmentC_senior_staff' ), 'guest' ); $acl->addRole( new Zend_Acl_Role( 'departmentC_marketing' ), 'guest' ); $acl->addRole( new Zend_Acl_Role( 'cook' ), 'guest' ); $acl->addRole( new Zend_Acl_Role( 'admin' ), 'member' ); $acl->addResource( new Zend_Acl_Resource( 'cms' )); $acl->addResource( new Zend_Acl_Resource( 'report' )); $acl->addResource( new Zend_Acl_Resource( 'newsletter' )); $acl->addResource( new Zend_Acl_Resource( 'photo' )); $acl->addResource( new Zend_Acl_Resource( 'faq' )); $acl->addResource( new Zend_Acl_Resource( 'invoicing' )); $acl->addResource( new Zend_Acl_Resource( 'stats' )); $acl->addResource( new Zend_Acl_Resource( 'lunchmenu' )); $acl->allow( 'guest' , 'cms' , 'view' ); $acl->allow( 'admin' , 'cms' , 'edit' ); $acl->deny( 'guest' , 'report' ); $acl->allow( 'departmentA' , 'report' ); $acl->deny('departmentC_senior_staff', 'newsletter'); $acl->allow('departmentC_marketing', 'newsletter'); $acl->allow('member', 'photo', 'view'); $acl->allow('departmentC_marketing', 'photo', 'upload'); $acl->allow('admin', 'photo', 'delete'); $acl->allow('guest', 'faq', 'view'); $acl->allow('member', 'faq', 'comment'); $acl->allow('departmentA', 'faq', 'edit'); $acl->allow('departmentC_senior_staff', 'faq', 'edit'); $acl->allow('admin', 'faq', 'edit'); $acl->allow('admin', 'photo', 'delete'); $acl->allow('guest', 'faq', 'view'); $acl->allow('member', 'faq', 'comment'); $acl->allow('departmentA', 'faq', 'edit'); $acl->allow('departmentC_senior_staff', 'faq', 'edit'); $acl->allow('admin', 'faq', 'edit'); $acl->allow('cook', 'lunchmenu', 'edit'); $acl->allow('member', 'lunchmenu', 'view'); $acl->allow('accounting', 'invoicing', 'edit'); $acl->allow('admin', 'invoicing', 'edit'); $acl->allow('departmentC_senior_staff', 'invoicing', 'report');
  32. 32. Hard to ... <ul><li>maintain all rules
  33. 33. keep track of the rules
  34. 34. debug the rules </li></ul>
  35. 35. Possible solution : database <ul><li>Extend Zend_Acl to database driven design
  36. 36. Good : no code changes required
  37. 37. Bad : more load on DB </li></ul>
  38. 38. A different approach <ul><li>Not THE solution, merely A solution
  39. 39. Uses database, but...
  40. 40. Additional caching layer
  41. 41. ZF Conventional Modular Directory Structure
  42. 42. Backend interface for easy management </li><ul><li>with a twist ! </li></ul></ul>
  43. 43. Different resources <ul><li>Zend_ACL : </li></ul>$acl->addResource( new Zend_Acl_Resource( 'cms' )); $acl->allow( 'guest' , 'cms' , 'view' ); $acl->allow( 'admin' , 'cms' , 'edit' ); <ul><li>Access to : </li><ul><li>Controller : cms
  44. 44. Action : view / edit </li></ul><li>Why not integrate with the request itself ? </li></ul>
  45. 45. Controller plugins
  46. 46. Zend_Acl as a controller plugin <?php class My_Plugin_Acl extends Zend_Controller_Plugin_Abstract { private $_acl = null; public function __construct(Zend_Acl $acl ) { $this ->_acl = $acl ; } public function preDispatch(Zend_Controller_Request_Abstract $request ) { $role = (Zend_Auth::getInstance()->hasIdentity()) ? 'user' : 'guest' ; //For this example, we will use the controller as the resource: $resource = $request ->getControllerName(); if (! $this ->_acl->isAllowed( $role , $resource , 'view' )) { //If the user has no access we send him elsewhere by changing the request and resetting the dispatch $request ->setModuleName( 'auth' ) ->setControllerName( 'auth' ) ->setActionName( 'login' ) ->setDispatched(false); return false; } } }
  47. 47. Initializing the ACL Let's have a look
  48. 48. Zend_Acl manual rules <?php class My_Acl extends Zend_Acl { public function __construct() { //Add a new role called &quot;guest&quot; $this ->addRole( new Zend_Acl_Role( 'guest' )); //Add a role called user, which inherits from guest $this ->addRole( new Zend_Acl_Role( 'user' ), 'guest' ); //Add a resource called page $this ->add( new Zend_Acl_Resource( 'page' )); //Add a resource called news, which inherits page $this ->add( new Zend_Acl_Resource( 'news' ), 'page' ); //Finally, we want to allow guests to view pages $this ->allow( 'guest' , 'page' , 'view' ); //and users can comment news $this ->allow( 'user' , 'news' , 'comment' ); } }
  49. 49. Our ACL id role_id module controller action 1 2 newsletter send index 2 1 cms article edit 3 3 % % % id name email pw 1 Chris [email_address] ******* 2 Jake [email_address] ******* 3 Jeniffer [email_address] ******* id name 1 webmaster 2 marketeer 3 admin user_id role_id 1 2 2 3 3 1
  50. 50. Application_Acl class Application_Acl { public function isAllowed($user = null , $request = null ) { if (is_null($user) === false && $user !== false && $user instanceof User) { $userId = $user-> id ; } else { $userId = 0; } $db = Zend_Db_Table:: getDefaultAdapter (); $stmt = $db->query( ' select module_name, controller_name, action_name from privilege join role on role.id = privilege.role_id join userRole on userRole.role_id = role.role_id where userRole.user_id = ? and ( module_name = &quot;%&quot; or ( module_name = ? and ( controller_name = &quot;%&quot; or ( controller_name = ? and ( action_name = &quot;%&quot; or action_name = ? ) ) ) ) ) ' , array ( $userId, $request->getModuleName(), $request->getControllerName(), $request->getActionName() ) ); $stmt->execute(); $row = $stmt->fetch(); // Returns a row or false if ($row !== false ) { return true ; } else { return false ; } } }
  51. 51. To cache or not to cache - Option 1 (no cache) <ul><li>1 query per ACL request
  52. 52. = 1 query per pageview / ajax request
  53. 53. 99.99% will be identical
  54. 54. -> &quot;Just rely on MySQL query cache !&quot;
  55. 55. FAIL !
  56. 56. Even for cached queries, MySQL connections use memory, I/O, CPU, ... </li></ul>
  57. 57. Caching - Option 2 (cache the main query) <ul><li>1 DB query per user for each unique ACL request
  58. 58. User with 20 privileges -> 20 possible requests
  59. 59. All subsequent pageviews : 1 cache request </li></ul>
  60. 60. What's in the cache ? Entry Data acl_user_3_%_%_% 1 acl_user_1_cms_article_edit 1 acl_user_1_admin_destroy_planet 0 <ul>Problem : what if we add a privilege to a role ? <ul>-> All cached entries for all users should be refreshed (ouch !) </ul></ul>
  61. 61. Caching - Option 3 - denormalize in cache
  62. 62. What's in the cache ? Entry Data acl_user_3 3, 1, 4 acl_user_1 3, 1, 2 acl_role_1 a:3:{i:0;s:45:&quot;a:2:{i:0;s:5:&quot;%_%_%&quot;;}&quot;;i:1;i:1308106740;i:2;s:6:&quot;604800&quot;;} User's roles Privileges listed in a role
  63. 63. Caching - Option 3 - denormalize in cache <ul><li>1 DB query per user at login (retrieve user's roles)
  64. 64. User with 20 privileges -> just 1 DB query
  65. 65. Per pageview : 1 cache query + 1 cache query per role
  66. 66. Good : </li><ul><li>Less queries on DB
  67. 67. Less data in the cache (only roles, not full privileges of each user)
  68. 68. Add a privilege to a role -> update the role only </li></ul><li>Bad : </li><ul><li>More queries on cache </li></ul><li>Choice : depends on where your highest load is </li><ul><li>(but memory is cheap and Memcache is fast !) </li></ul></ul>
  69. 69. Caching - let's have a look
  70. 70. Managing the roles / privileges <ul><li>Zend_Acl : manual typ(o)ing
  71. 71. Goals : </li><ul><li>automation
  72. 72. easy management </li></ul><li>-> Reflection </li></ul>
  73. 73. Reflection ? <ul><li>Used to inspect objects during run-time
  74. 74. Available since PHP 5.0
  75. 75. Can be applied to : </li><ul><li>Classes
  76. 76. Objects
  77. 77. Methods
  78. 78. Functions
  79. 79. Properties
  80. 80. Extensions </li></ul></ul>
  81. 81. Reflection - example <?php class Test { static public function testMe ($reason) { echo 'I have a reason : ' . $reason; } } $reflector = new ReflectionClass( 'Test' ); echo 'Class name : ' . $reflector->getName() . &quot;n&quot; ; echo &quot;Methods : n&quot; ; var_dump($reflector->getMethods()); Outputs : Class name : Test Methods : array(1) { [0]=> &object(ReflectionMethod)#2 (2) { [&quot;name&quot;]=> string(6) &quot;testMe&quot; [&quot;class&quot;]=> string(4) &quot;Test&quot; } }
  82. 82. Backend interface with reflection
  83. 83. Version 1 -> 2 <ul><li>ZF2 compatible </li><ul><li>Maybe : adding a multiGet() for the roles -> fewer cache queries </li></ul><li>Resources </li><ul><li>isAllowed() gets a new parameter
  84. 84. can be combined with module/controller/action </li></ul><li>Negative privileges and negative roles (!)
  85. 85. Hide controllers and actions from the interface (docblock)
  86. 86. New UI </li><ul><li>Drag-n-drop
  87. 87. Apply roles to usergroup </li></ul></ul>
  88. 88. Setting it up <ul><li>library/Application/Acl.php -> main ACL class
  89. 89. library/Application/Controllers/Plugin/Acl.php -> controller plugin
  90. 90. modules/um/controllers/ </li><ul><li>IndexController.php
  91. 91. RoleController.php
  92. 92. LoginController.php -> Login screen (usually placed somewhere else) </li></ul><li>modules/um/views/scripts/ </li><ul><li>index
  93. 93. role </li></ul><li>Few lines in application.ini </li></ul>
  94. 94. Where to get it <ul><li>Site : http://www.opensource.be/zfacl/ (Oct 25)
  95. 95. Code : Github (see site) </li></ul>
  96. 96. <ul>Questions ? </ul>
  97. 97. <ul>Questions ? </ul>
  98. 98. Contact <ul><li>Web http://techblog.wimgodden.be
  99. 99. Slides http://www.slideshare.net/wimg
  100. 100. Twitter @wimgtr
  101. 101. E-mail [email_address] </li></ul>
  102. 102. Please... <ul>Rate my talk : http://joind.in/4009 </ul>
  103. 103. <ul>Thanks ! </ul>
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×