Your SlideShare is downloading. ×
0
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,037

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,037
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

Transcript of "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.

×