PhpAclTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. <?php
  2. /**
  3. * PhpAclTest file.
  4. *
  5. * PHP 5
  6. *
  7. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8. * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  9. *
  10. * Licensed under The MIT License
  11. * Redistributions of files must retain the above copyright notice.
  12. *
  13. * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  14. * @link http://cakephp.org CakePHP(tm) Project
  15. * @package Cake.Test.Case.Controller.Component.Acl
  16. * @since CakePHP(tm) v 2.1
  17. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  18. */
  19. App::uses('AclComponent', 'Controller/Component');
  20. App::uses('PhpAcl', 'Controller/Component/Acl');
  21. class_exists('AclComponent');
  22. /**
  23. * Test case for the PhpAcl implementation
  24. *
  25. * @package Cake.Test.Case.Controller.Component.Acl
  26. */
  27. class PhpAclTest extends CakeTestCase {
  28. public function setUp() {
  29. parent::setUp();
  30. Configure::write('Acl.classname', 'PhpAcl');
  31. $Collection = new ComponentCollection();
  32. $this->PhpAcl = new PhpAcl();
  33. $this->Acl = new AclComponent($Collection, array(
  34. 'adapter' => array(
  35. 'config' => CAKE . 'Test' . DS . 'test_app' . DS . 'Config' . DS . 'acl.php',
  36. ),
  37. ));
  38. }
  39. public function testRoleInheritance() {
  40. $roles = $this->Acl->Aro->roles('User/peter');
  41. $this->assertEquals(array('Role/accounting'), $roles[0]);
  42. $this->assertEquals(array('User/peter'), $roles[1]);
  43. $roles = $this->Acl->Aro->roles('hardy');
  44. $this->assertEquals(array('Role/database_manager', 'Role/data_acquirer'), $roles[0]);
  45. $this->assertEquals(array('Role/accounting', 'Role/data_analyst'), $roles[1]);
  46. $this->assertEquals(array('Role/accounting_manager', 'Role/reports'), $roles[2]);
  47. $this->assertEquals(array('User/hardy'), $roles[3]);
  48. }
  49. public function testAddRole() {
  50. $this->assertEquals(array(array(PhpAro::DEFAULT_ROLE)), $this->Acl->Aro->roles('foobar'));
  51. $this->Acl->Aro->addRole(array('User/foobar' => 'Role/accounting'));
  52. $this->assertEquals(array(array('Role/accounting'), array('User/foobar')), $this->Acl->Aro->roles('foobar'));
  53. }
  54. public function testAroResolve() {
  55. $this->Acl->Aro->map = array(
  56. 'User' => 'FooModel/nickname',
  57. 'Role' => 'FooModel/role',
  58. );
  59. $this->assertEquals('Role/default', $this->Acl->Aro->resolve('Foo.bar'));
  60. $this->assertEquals('User/hardy', $this->Acl->Aro->resolve('FooModel/hardy'));
  61. $this->assertEquals('User/hardy', $this->Acl->Aro->resolve('hardy'));
  62. $this->assertEquals('User/hardy', $this->Acl->Aro->resolve(array('FooModel' => array('nickname' => 'hardy'))));
  63. $this->assertEquals('Role/admin', $this->Acl->Aro->resolve(array('FooModel' => array('role' => 'admin'))));
  64. $this->assertEquals('Role/admin', $this->Acl->Aro->resolve('Role/admin'));
  65. $this->assertEquals('Role/admin', $this->Acl->Aro->resolve('admin'));
  66. $this->assertEquals('Role/admin', $this->Acl->Aro->resolve('FooModel/admin'));
  67. $this->assertEquals('Role/accounting', $this->Acl->Aro->resolve('accounting'));
  68. $this->assertEquals(PhpAro::DEFAULT_ROLE, $this->Acl->Aro->resolve('bla'));
  69. $this->assertEquals(PhpAro::DEFAULT_ROLE, $this->Acl->Aro->resolve(array('FooModel' => array('role' => 'hardy'))));
  70. }
  71. /**
  72. * test correct resolution of defined aliases
  73. */
  74. public function testAroAliases() {
  75. $this->Acl->Aro->map = array(
  76. 'User' => 'User/username',
  77. 'Role' => 'User/group_id',
  78. );
  79. $this->Acl->Aro->aliases = array(
  80. 'Role/1' => 'Role/admin',
  81. 'Role/24' => 'Role/accounting',
  82. );
  83. $user = array(
  84. 'User' => array(
  85. 'username' => 'unknown_user',
  86. 'group_id' => '1',
  87. ),
  88. );
  89. // group/1
  90. $this->assertEquals('Role/admin', $this->Acl->Aro->resolve($user));
  91. // group/24
  92. $this->assertEquals('Role/accounting', $this->Acl->Aro->resolve('Role/24'));
  93. $this->assertEquals('Role/accounting', $this->Acl->Aro->resolve('24'));
  94. // check department
  95. $user = array(
  96. 'User' => array(
  97. 'username' => 'foo',
  98. 'group_id' => '25',
  99. ),
  100. );
  101. $this->Acl->Aro->addRole(array('Role/IT' => null));
  102. $this->Acl->Aro->addAlias(array('Role/25' => 'Role/IT'));
  103. $this->Acl->allow('Role/IT', '/rules/debugging/*');
  104. $this->assertEquals(array(array('Role/IT')), $this->Acl->Aro->roles($user));
  105. $this->assertTrue($this->Acl->check($user, '/rules/debugging/stats/pageload'));
  106. $this->assertTrue($this->Acl->check($user, '/rules/debugging/sql/queries'));
  107. // Role/default is allowed users dashboard, but not Role/IT
  108. $this->assertFalse($this->Acl->check($user, '/controllers/users/dashboard'));
  109. $this->assertFalse($this->Acl->check($user, '/controllers/invoices/send'));
  110. // wee add an more specific entry for user foo to also inherit from Role/accounting
  111. $this->Acl->Aro->addRole(array('User/foo' => 'Role/IT, Role/accounting'));
  112. $this->assertTrue($this->Acl->check($user, '/controllers/invoices/send'));
  113. }
  114. /**
  115. * test check method
  116. *
  117. * @return void
  118. */
  119. public function testCheck() {
  120. $this->assertTrue($this->Acl->check('jan', '/controllers/users/Dashboard'));
  121. $this->assertTrue($this->Acl->check('some_unknown_role', '/controllers/users/Dashboard'));
  122. $this->assertTrue($this->Acl->check('Role/admin', 'foo/bar'));
  123. $this->assertTrue($this->Acl->check('role/admin', '/foo/bar'));
  124. $this->assertTrue($this->Acl->check('jan', 'foo/bar'));
  125. $this->assertTrue($this->Acl->check('user/jan', 'foo/bar'));
  126. $this->assertTrue($this->Acl->check('Role/admin', 'controllers/bar'));
  127. $this->assertTrue($this->Acl->check(array('User' => array('username' => 'jan')), '/controllers/bar/bll'));
  128. $this->assertTrue($this->Acl->check('Role/database_manager', 'controllers/db/create'));
  129. $this->assertTrue($this->Acl->check('User/db_manager_2', 'controllers/db/create'));
  130. $this->assertFalse($this->Acl->check('db_manager_2', '/controllers/users/Dashboard'));
  131. // inheritance: hardy -> reports -> data_analyst -> database_manager
  132. $this->assertTrue($this->Acl->check('User/hardy', 'controllers/db/create'));
  133. $this->assertFalse($this->Acl->check('User/jeff', 'controllers/db/create'));
  134. $this->assertTrue($this->Acl->check('Role/database_manager', 'controllers/db/select'));
  135. $this->assertTrue($this->Acl->check('User/db_manager_2', 'controllers/db/select'));
  136. $this->assertFalse($this->Acl->check('User/jeff', 'controllers/db/select'));
  137. $this->assertTrue($this->Acl->check('Role/database_manager', 'controllers/db/drop'));
  138. $this->assertTrue($this->Acl->check('User/db_manager_1', 'controllers/db/drop'));
  139. $this->assertFalse($this->Acl->check('db_manager_2', 'controllers/db/drop'));
  140. $this->assertTrue($this->Acl->check('db_manager_2', 'controllers/invoices/edit'));
  141. $this->assertFalse($this->Acl->check('database_manager', 'controllers/invoices/edit'));
  142. $this->assertFalse($this->Acl->check('db_manager_1', 'controllers/invoices/edit'));
  143. // Role/manager is allowed /controllers/*/*_manager
  144. $this->assertTrue($this->Acl->check('stan', 'controllers/invoices/manager_edit'));
  145. $this->assertTrue($this->Acl->check('Role/manager', 'controllers/baz/manager_foo'));
  146. $this->assertFalse($this->Acl->check('User/stan', 'custom/foo/manager_edit'));
  147. $this->assertFalse($this->Acl->check('stan', 'bar/baz/manager_foo'));
  148. $this->assertFalse($this->Acl->check('Role/accounting', 'bar/baz/manager_foo'));
  149. $this->assertFalse($this->Acl->check('accounting', 'controllers/baz/manager_foo'));
  150. $this->assertTrue($this->Acl->check('User/stan', 'controllers/articles/edit'));
  151. $this->assertTrue($this->Acl->check('stan', 'controllers/articles/add'));
  152. $this->assertTrue($this->Acl->check('stan', 'controllers/articles/publish'));
  153. $this->assertFalse($this->Acl->check('User/stan', 'controllers/articles/delete'));
  154. $this->assertFalse($this->Acl->check('accounting', 'controllers/articles/edit'));
  155. $this->assertFalse($this->Acl->check('accounting', 'controllers/articles/add'));
  156. $this->assertFalse($this->Acl->check('role/accounting', 'controllers/articles/publish'));
  157. }
  158. /**
  159. * lhs of defined rules are case insensitive
  160. */
  161. public function testCheckIsCaseInsensitive() {
  162. $this->assertTrue($this->Acl->check('hardy', 'controllers/forms/new'));
  163. $this->assertTrue($this->Acl->check('Role/data_acquirer', 'controllers/forms/new'));
  164. $this->assertTrue($this->Acl->check('hardy', 'controllers/FORMS/NEW'));
  165. $this->assertTrue($this->Acl->check('Role/data_acquirer', 'controllers/FORMS/NEW'));
  166. }
  167. /**
  168. * allow should work in-memory
  169. */
  170. public function testAllow() {
  171. $this->assertFalse($this->Acl->check('jeff', 'foo/bar'));
  172. $this->Acl->allow('jeff', 'foo/bar');
  173. $this->assertTrue($this->Acl->check('jeff', 'foo/bar'));
  174. $this->assertFalse($this->Acl->check('peter', 'foo/bar'));
  175. $this->assertFalse($this->Acl->check('hardy', 'foo/bar'));
  176. $this->Acl->allow('Role/accounting', 'foo/bar');
  177. $this->assertTrue($this->Acl->check('peter', 'foo/bar'));
  178. $this->assertTrue($this->Acl->check('hardy', 'foo/bar'));
  179. $this->assertFalse($this->Acl->check('Role/reports', 'foo/bar'));
  180. }
  181. /**
  182. * deny should work in-memory
  183. */
  184. public function testDeny() {
  185. $this->assertTrue($this->Acl->check('stan', 'controllers/baz/manager_foo'));
  186. $this->Acl->deny('stan', 'controllers/baz/manager_foo');
  187. $this->assertFalse($this->Acl->check('stan', 'controllers/baz/manager_foo'));
  188. $this->assertTrue($this->Acl->check('Role/manager', 'controllers/baz/manager_foo'));
  189. $this->assertTrue($this->Acl->check('stan', 'controllers/baz/manager_bar'));
  190. $this->assertTrue($this->Acl->check('stan', 'controllers/baz/manager_foooooo'));
  191. }
  192. /**
  193. * test that a deny rule wins over an equally specific allow rule
  194. */
  195. public function testDenyRuleIsStrongerThanAllowRule() {
  196. $this->assertFalse($this->Acl->check('peter', 'baz/bam'));
  197. $this->Acl->allow('peter', 'baz/bam');
  198. $this->assertTrue($this->Acl->check('peter', 'baz/bam'));
  199. $this->Acl->deny('peter', 'baz/bam');
  200. $this->assertFalse($this->Acl->check('peter', 'baz/bam'));
  201. $this->assertTrue($this->Acl->check('stan', 'controllers/reports/foo'));
  202. // stan is denied as he's sales and sales is denied /controllers/*/delete
  203. $this->assertFalse($this->Acl->check('stan', 'controllers/reports/delete'));
  204. $this->Acl->allow('stan', 'controllers/reports/delete');
  205. $this->assertFalse($this->Acl->check('Role/sales', 'controllers/reports/delete'));
  206. $this->assertTrue($this->Acl->check('stan', 'controllers/reports/delete'));
  207. $this->Acl->deny('stan', 'controllers/reports/delete');
  208. $this->assertFalse($this->Acl->check('stan', 'controllers/reports/delete'));
  209. // there is already an equally specific deny rule that will win
  210. $this->Acl->allow('stan', 'controllers/reports/delete');
  211. $this->assertFalse($this->Acl->check('stan', 'controllers/reports/delete'));
  212. }
  213. /**
  214. * test that an invalid configuration throws exception
  215. */
  216. public function testInvalidConfigWithAroMissing() {
  217. $this->setExpectedException(
  218. 'AclException',
  219. '"roles" section not found in configuration'
  220. );
  221. $config = array('aco' => array('allow' => array('foo' => '')));
  222. $this->PhpAcl->build($config);
  223. }
  224. public function testInvalidConfigWithAcosMissing() {
  225. $this->setExpectedException(
  226. 'AclException',
  227. 'Neither "allow" nor "deny" rules were provided in configuration.'
  228. );
  229. $config = array(
  230. 'roles' => array('Role/foo' => null),
  231. );
  232. $this->PhpAcl->build($config);
  233. }
  234. /**
  235. * test resolving of ACOs
  236. */
  237. public function testAcoResolve() {
  238. $this->assertEquals(array('foo', 'bar'), $this->Acl->Aco->resolve('foo/bar'));
  239. $this->assertEquals(array('foo', 'bar'), $this->Acl->Aco->resolve('foo/bar'));
  240. $this->assertEquals(array('foo', 'bar', 'baz'), $this->Acl->Aco->resolve('foo/bar/baz'));
  241. $this->assertEquals(array('foo', '*-bar', '?-baz'), $this->Acl->Aco->resolve('foo/*-bar/?-baz'));
  242. $this->assertEquals(array('foo', 'bar', '[a-f0-9]{24}', '*_bla', 'bla'), $this->Acl->Aco->resolve('foo/bar/[a-f0-9]{24}/*_bla/bla'));
  243. // multiple slashes will be squashed to a single, trimmed and then exploded
  244. $this->assertEquals(array('foo', 'bar'), $this->Acl->Aco->resolve('foo//bar'));
  245. $this->assertEquals(array('foo', 'bar'), $this->Acl->Aco->resolve('//foo///bar/'));
  246. $this->assertEquals(array('foo', 'bar'), $this->Acl->Aco->resolve('/foo//bar//'));
  247. $this->assertEquals(array('foo', 'bar'), $this->Acl->Aco->resolve('/foo // bar'));
  248. $this->assertEquals(array(), $this->Acl->Aco->resolve('/////'));
  249. }
  250. /**
  251. * test that declaring cyclic dependencies should give an error when building the tree
  252. */
  253. public function testAroDeclarationContainsCycles() {
  254. $config = array(
  255. 'roles' => array(
  256. 'Role/a' => null,
  257. 'Role/b' => 'User/b',
  258. 'User/a' => 'Role/a, Role/b',
  259. 'User/b' => 'User/a',
  260. ),
  261. 'rules' => array(
  262. 'allow' => array(
  263. '*' => 'Role/a',
  264. ),
  265. ),
  266. );
  267. $this->expectError('PHPUnit_Framework_Error', 'cycle detected' /* ... */);
  268. $this->PhpAcl->build($config);
  269. }
  270. /**
  271. * test that with policy allow, only denies count
  272. */
  273. public function testPolicy() {
  274. // allow by default
  275. $this->Acl->settings['adapter']['policy'] = PhpAcl::ALLOW;
  276. $this->Acl->adapter($this->PhpAcl);
  277. $this->assertTrue($this->Acl->check('Role/sales', 'foo'));
  278. $this->assertTrue($this->Acl->check('Role/sales', 'controllers/bla/create'));
  279. $this->assertTrue($this->Acl->check('Role/default', 'foo'));
  280. // undefined user, undefined aco
  281. $this->assertTrue($this->Acl->check('foobar', 'foo/bar'));
  282. // deny rule: Role.sales -> controllers.*.delete
  283. $this->assertFalse($this->Acl->check('Role/sales', 'controllers/bar/delete'));
  284. $this->assertFalse($this->Acl->check('Role/sales', 'controllers/bar', 'delete'));
  285. }
  286. }