PageRenderTime 50ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/Acl/Controller/Component/Auth/AclCachedAuthorize.php

https://github.com/kareypowell/croogo
PHP | 267 lines | 197 code | 28 blank | 42 comment | 36 complexity | 00455ed6267c1aaf53d6de3e223d4e74 MD5 | raw file
  1. <?php
  2. App::uses('BaseAuthorize', 'Controller/Component/Auth');
  3. /**
  4. * An authentication adapter for AuthComponent. Provides similar functionality
  5. * to ActionsAuthorize class from CakePHP core _with_ caching capability.
  6. *
  7. * @package Croogo.Acl.Controller.Component.Auth
  8. * @since 1.5
  9. * @author Rachman Chavik <rchavik@xintesa.com>
  10. * @see RowLevelAclComponent
  11. * @license http://www.opensource.org/licenses/mit-license.php The MIT License
  12. * @link http://www.croogo.org
  13. */
  14. class AclCachedAuthorize extends BaseAuthorize {
  15. /**
  16. * Constructor
  17. */
  18. public function __construct(ComponentCollection $collection, $settings = array()) {
  19. parent::__construct($collection, $settings);
  20. $this->_setPrefixMappings();
  21. }
  22. /**
  23. * sets the crud mappings for prefix routes.
  24. *
  25. * @return void
  26. */
  27. protected function _setPrefixMappings() {
  28. if (!Configure::read('Access Control.rowLevel')) {
  29. return;
  30. }
  31. $crud = array('create', 'read', 'update', 'delete');
  32. $map = array_combine($crud, $crud);
  33. $Controller = $this->Controller();
  34. if ($Controller->Components->attached('RowLevelAcl')) {
  35. $settings = $Controller->Components->RowLevelAcl->settings;
  36. if (isset($settings['actionMap'])) {
  37. $map = array_merge($map, $settings['actionMap']);
  38. }
  39. }
  40. $prefixes = Router::prefixes();
  41. if (!empty($prefixes)) {
  42. foreach ($prefixes as $prefix) {
  43. $map = array_merge($map, array(
  44. $prefix . '_moveup' => 'update',
  45. $prefix . '_movedown' => 'update',
  46. $prefix . '_process' => 'delete',
  47. $prefix . '_index' => 'read',
  48. $prefix . '_add' => 'create',
  49. $prefix . '_edit' => 'update',
  50. $prefix . '_view' => 'read',
  51. $prefix . '_remove' => 'delete',
  52. $prefix . '_create' => 'create',
  53. $prefix . '_read' => 'read',
  54. $prefix . '_update' => 'update',
  55. $prefix . '_delete' => 'delete'
  56. ));
  57. }
  58. }
  59. $this->mapActions($map);
  60. }
  61. /**
  62. * Checks whether $user is an administrator
  63. *
  64. * @param bool True if user has administrative role
  65. */
  66. protected function _isAdmin($user) {
  67. static $Role = null;
  68. if (empty($user['role_id'])) {
  69. return false;
  70. }
  71. if (empty($this->_adminRole)) {
  72. if (empty($Role)) {
  73. $Role = ClassRegistry::init('Users.Role');
  74. $Role->Behaviors->attach('Croogo.Aliasable');
  75. }
  76. $this->_adminRole = $Role->byAlias('admin');
  77. }
  78. return $user['role_id'] == $this->_adminRole;
  79. }
  80. /**
  81. * Get the action path for a given request.
  82. *
  83. * @see BaseAuthorize::action()
  84. */
  85. public function action(CakeRequest $request, $path = '/:plugin/:controller/:action') {
  86. $apiPath = Configure::read('Croogo.Api.path');
  87. if (!$request->is('api')) {
  88. $path = str_replace(
  89. array($apiPath, ':prefix/'),
  90. array(null, null),
  91. $path);
  92. return parent::action($request, $path);
  93. }
  94. $api = isset($request['api']) ? $apiPath : null;
  95. if (isset($request['prefix'])) {
  96. $prefix = $request['prefix'];
  97. $action = str_replace($request['prefix'] . '_', '', $request['action']);
  98. } else {
  99. $prefix = null;
  100. $action = $request['action'];
  101. }
  102. $plugin = empty($request['plugin']) ? null : Inflector::camelize($request['plugin']);
  103. $controller = Inflector::camelize($request['controller']);
  104. $path = str_replace(
  105. array($apiPath, ':prefix', ':plugin', ':controller', ':action'),
  106. array($api, $prefix, $plugin, $controller, $action),
  107. $this->settings['actionPath'] . $path
  108. );
  109. $path = str_replace('//', '/', $path);
  110. return trim($path, '/');
  111. }
  112. /**
  113. * check request request authorization
  114. *
  115. */
  116. public function authorize($user, CakeRequest $request) {
  117. // Admin role is allowed to perform all actions, bypassing ACL
  118. if ($this->_isAdmin($user)) {
  119. return true;
  120. }
  121. $allowed = false;
  122. $Acl = $this->_Collection->load('Acl');
  123. list($plugin, $userModel) = pluginSplit($this->settings['userModel']);
  124. $path = '/:plugin/:controller/:action';
  125. if ($request->is('api')) {
  126. $path = '/:prefix' . $path;
  127. }
  128. $action = $this->action($request, $path);
  129. $cacheName = 'permissions_' . strval($user['id']);
  130. if (($permissions = Cache::read($cacheName, 'permissions')) === false) {
  131. $permissions = array();
  132. Cache::write($cacheName, $permissions, 'permissions');
  133. }
  134. if (!isset($permissions[$action])) {
  135. $User = ClassRegistry::init($this->settings['userModel']);
  136. $User->id = $user['id'];
  137. $allowed = $Acl->check($User, $action);
  138. $permissions[$action] = $allowed;
  139. Cache::write($cacheName, $permissions, 'permissions');
  140. $hit = false;
  141. } else {
  142. $allowed = $permissions[$action];
  143. $hit = true;
  144. }
  145. if (Configure::read('debug')) {
  146. $status = $allowed ? ' allowed.' : ' denied.';
  147. $cached = $hit ? ' (cache hit)' : ' (cache miss)';
  148. CakeLog::write(LOG_ERR, $user['username'] . ' - ' . $action . $status . $cached);
  149. }
  150. if (!$allowed) {
  151. return false;
  152. }
  153. if (!Configure::read('Access Control.rowLevel')) {
  154. return $allowed;
  155. }
  156. // bail out when controller's primary model does not want row level acl
  157. $controller = $this->controller();
  158. $model = $controller->modelClass;
  159. $Model = $controller->{$model};
  160. if ($Model && !$Model->Behaviors->attached('RowLevelAcl')) {
  161. return $allowed;
  162. }
  163. $primaryKey = $Model->primaryKey;
  164. $ids = array();
  165. if ($request->is('get') && !empty($request->params['pass'][0])) {
  166. // collect id from actions such as: Nodes/admin_edit/1
  167. $ids[] = $request->params['pass'][0];
  168. } elseif (($request->is('post') || $request->is('put')) && isset($request->data[$model]['action'])) {
  169. // collect ids from 'bulk' processing action such as: Nodes/admin_process
  170. foreach ($request->data[$model] as $id => $flag) {
  171. if (isset($flag[$primaryKey]) && $flag[$primaryKey] == 1) {
  172. $ids[] = $id;
  173. }
  174. }
  175. }
  176. foreach ($ids as $id) {
  177. if (is_numeric($id)) {
  178. try {
  179. $allowed = $this->_authorizeByContent($user, $request, $id);
  180. } catch (CakeException $e) {
  181. $allowed = false;
  182. }
  183. } else {
  184. continue;
  185. }
  186. if (!$allowed) {
  187. break;
  188. }
  189. }
  190. return $allowed;
  191. }
  192. /**
  193. * Checks authorization by content
  194. *
  195. * @throws CakeException
  196. */
  197. protected function _authorizeByContent($user, CakeRequest $request, $id) {
  198. if (!isset($this->settings['actionMap'][$request->params['action']])) {
  199. throw new CakeException(
  200. __d('croogo', '_authorizeByContent() - Access of un-mapped action "%1$s" in controller "%2$s"',
  201. $request->action,
  202. $request->controller
  203. ));
  204. }
  205. list($plugin, $userModel) = pluginSplit($this->settings['userModel']);
  206. $acoNode = array(
  207. 'model' => $this->_Controller->modelClass,
  208. 'foreign_key' => $id,
  209. );
  210. $alias = sprintf('%s.%s', $acoNode['model'], $acoNode['foreign_key']);
  211. $action = $this->settings['actionMap'][$request->params['action']];
  212. $cacheName = 'permissions_content_' . strval($user['id']);
  213. if (($permissions = Cache::read($cacheName, 'permissions')) === false) {
  214. $permissions = array();
  215. Cache::write($cacheName, $permissions, 'permissions');
  216. }
  217. if (!isset($permissions[$alias][$action])) {
  218. $Acl = $this->_Collection->load('Acl');
  219. try {
  220. $allowed = $Acl->check(array($userModel => $user), $acoNode, $action);
  221. } catch (Exception $e) {
  222. CakeLog::warning('authorizeByContent: ' . $e->getMessage());
  223. $allowed = false;
  224. }
  225. $permissions[$alias][$action] = $allowed;
  226. Cache::write($cacheName, $permissions, 'permissions');
  227. $hit = false;
  228. } else {
  229. $allowed = $permissions[$alias][$action];
  230. $hit = true;
  231. }
  232. if (Configure::read('debug')) {
  233. $status = $allowed ? ' allowed.' : ' denied.';
  234. $cached = $hit ? ' (cache hit)' : ' (cache miss)';
  235. CakeLog::write(LOG_ERR, $user['username'] . ' - ' . $action . '/' . $id . $status . $cached);
  236. }
  237. return $allowed;
  238. }
  239. }