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>
Within Zend Framework : Zend_Acl <ul><li>Flexible
Uses standard role, resource principles </li></ul>
Zend_Acl : the good <ul><li>Recognizable -> easy to get started
No link to specific backend
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
Upcoming SlideShare
Loading in...5
×

Creating fast, dynamic ACLs in Zend Framework (Zend Webinar)

12,476

Published on

Slides from the Zend Webinar on 'Creating fast and dynamic ACLs in Zend Framework' (15 June 2011).

Zend Framework's Access Control Layer system is simple and straight-forward; however, as the number of rules increase in size and complexity, maintenance and performance suffer. The solution: a dynamic, reflection-based ACL system, with built-in caching. Sound complicated?
Don't worry, it's easy to setup and a lot easier to manage! Join this webinar to learn how!

Presenter: Wim Godden

Published in: Technology
0 Comments
5 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
12,476
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
177
Comments
0
Likes
5
Embeds 0
No embeds

No notes for slide
  • Enjoyed dinner
  • Creating fast, dynamic ACLs in Zend Framework (Zend Webinar)

    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. Within Zend Framework : Zend_Acl <ul><li>Flexible
    20. 20. Uses standard role, resource principles </li></ul>
    21. 21. Zend_Acl : the good <ul><li>Recognizable -> easy to get started
    22. 22. No link to specific backend
    23. 23. Allow + deny
    24. 24. Proven, tested </li></ul>
    25. 25. Zend_Acl : the bad & ugly <ul><li>Complexity of rules rises quickly
    26. 26. Performance issues
    27. 27. All rules are in-code
    28. 28. -> maintainability becomes an issue </li></ul>
    29. 29. 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' );
    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( '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' );
    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( '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');
    32. 32. 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');
    33. 33. 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');
    34. 34. Hard to ... <ul><li>maintain all rules
    35. 35. keep track of the rules
    36. 36. debug the rules </li></ul>
    37. 37. Possible solution : database <ul><li>Extend Zend_Acl to database driven design
    38. 38. Good : no code changes required
    39. 39. Bad : more load on DB </li></ul>
    40. 40. A different approach <ul><li>Not THE solution, merely A solution
    41. 41. Uses database, but...
    42. 42. Additional caching layer
    43. 43. ZF Conventional Modular Directory Structure
    44. 44. Backend interface for easy management </li></ul>
    45. 45. 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
    46. 46. Action : view / edit </li></ul><li>Why not integrate with the request itself ? </li></ul>
    47. 47. Controller plugins
    48. 48. 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 $request ->setModuleName( 'auth' ) ->setControllerName( 'auth' ) ->setActionName( 'login' ) ->setDispatched(false); return false; } } }
    49. 49. Initializing the ACL Let's have a look
    50. 50. 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' ); } }
    51. 51. 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
    52. 52. Application_Acl class Application_Acl { public function isAllowed($user = null , $request = null , $privilege = 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 ; } } }
    53. 53. To cache or not to cache - Option 1 (no cache) <ul><li>1 query per ACL request
    54. 54. = 1 query per pageview / ajax request
    55. 55. 99.99% will be identical
    56. 56. -> &quot;Just rely on MySQL query cache !&quot;
    57. 57. FAIL !
    58. 58. Even for cached queries, MySQL connections use memory, I/O, CPU, ... </li></ul>
    59. 59. Caching - Option 2 (cache the main query) <ul><li>1 DB query per user for each unique ACL request
    60. 60. User with 20 privileges -> 20 possible requests
    61. 61. All subsequent pageviews : 1 cache request </li></ul>
    62. 62. 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>
    63. 63. Caching - Option 3 - denormalize in cache
    64. 64. 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
    65. 65. Caching - Option 3 - denormalize in cache <ul><li>1 DB query per user at login (retrieve user's roles)
    66. 66. User with 20 privileges -> just 1 DB query
    67. 67. 1 cache query per pageview + 1 per role
    68. 68. Good : </li><ul><li>Less queries on DB
    69. 69. Less data in the cache (only roles, not full privileges of each user)
    70. 70. 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>
    71. 71. Caching - let's have a look
    72. 72. Managing the roles / privileges <ul><li>Zend_Acl : manual typ(o)ing
    73. 73. Goals : </li><ul><li>automation
    74. 74. easy management </li></ul><li>-> Reflection </li></ul>
    75. 75. Reflection ? <ul><li>Used to inspect objects during run-time
    76. 76. Available since PHP 5.0
    77. 77. Can be applied to : </li><ul><li>Classes
    78. 78. Objects
    79. 79. Methods
    80. 80. Functions
    81. 81. Properties
    82. 82. Extensions </li></ul></ul>
    83. 83. 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; } }
    84. 84. Backend interface with reflection
    85. 85. <ul>Questions ? </ul>
    86. 86. Contact <ul><li>Web http://techblog.wimgodden.be
    87. 87. Slides http://www.slideshare.net/wimg
    88. 88. Twitter @wimgtr
    89. 89. E-mail [email_address] </li></ul>
    90. 90. <ul>Thanks ! </ul>Feel free to rate my webinar at http://tinyurl.com/acltalk
    1. A particular slide catching your eye?

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

    ×