TinyAuthorize.php 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. <?php
  2. App::uses('Inflector', 'Utility');
  3. if (!defined('CLASS_USER')) {
  4. define('CLASS_USER', 'User'); # override if you have it in a plugin: PluginName.User etc
  5. }
  6. if (!defined('AUTH_CACHE')) {
  7. define('AUTH_CACHE', '_cake_core_'); # use the most persistent cache by default
  8. }
  9. if (!defined('ACL_FILE')) {
  10. define('ACL_FILE', 'acl.ini'); # stored in /app/Config/
  11. }
  12. /**
  13. * Probably the most simple and fastest Acl out there.
  14. * Only one config file `acl.ini` necessary
  15. * Doesn't even need a Role Model / roles table
  16. * @link http://www.dereuromark.de/2011/12/18/tinyauth-the-fastest-and-easiest-authorization-for-cake2
  17. *
  18. * Usage:
  19. * Include it in your beforeFilter() method of the AppController
  20. * $this->Auth->authorize = array('Tools.Tiny');
  21. *
  22. * Or with admin prefix protection only
  23. * $this->Auth->authorize = array('Tools.Tiny'=>array('allowUser'=>true));
  24. *
  25. * @version 1.1 - now uses most persistent _cake_core_ cache by default
  26. * @author Mark Scherer
  27. * @cakephp 2.0
  28. * @license MIT
  29. * 2011-12-31 ms
  30. */
  31. class TinyAuthorize extends BaseAuthorize {
  32. protected $_matchArray = array();
  33. protected $_defaults = array(
  34. 'allowUser' => false, # quick way to allow user access to non prefixed urls
  35. 'adminPrefix' => 'admin_',
  36. 'cache' => AUTH_CACHE,
  37. 'cacheKey' => 'tiny_auth_acl',
  38. 'autoClearCache' => false # usually done by Cache automatically in debug mode
  39. );
  40. public function __construct(ComponentCollection $Collection, $settings = array()) {
  41. $settings = am($this->_defaults, $settings);
  42. parent::__construct($Collection, $settings);
  43. if (Cache::config($settings['cache']) === false) {
  44. throw new CakeException(__('TinyAuth could not find `%s` cache - expects at least a `default` cache', $settings['cache']));
  45. }
  46. $this->_matchArray = $this->_getRoles();
  47. }
  48. /**
  49. * Authorize a user using the AclComponent.
  50. * allows single or multi role based authorization
  51. * - User HABTM Roles (Role array in User array)
  52. * - User belongsTo Roles (role_id in User array)
  53. *
  54. * @param array $user The user to authorize
  55. * @param CakeRequest $request The request needing authorization.
  56. * @return boolean
  57. */
  58. public function authorize($user, CakeRequest $request) {
  59. if (isset($user['Role'])) {
  60. $roles = (array)$user['Role'];
  61. } elseif (isset($user['role_id'])) {
  62. $roles = array($user['role_id']);
  63. } else {
  64. trigger_error(__('missing roles information in user session'));
  65. $roles = array();
  66. }
  67. return $this->validate($roles, $request->params['plugin'], $request->params['controller'], $request->params['action']);
  68. }
  69. /**
  70. * validate the url to the role(s)
  71. * allows single or multi role based authorization
  72. * @return bool $success
  73. */
  74. public function validate($roles, $plugin, $controller, $action) {
  75. $action = Inflector::underscore($action);
  76. $controller = Inflector::underscore($controller);
  77. $plugin = Inflector::underscore($plugin);
  78. if (!empty($this->settings['allowUser'])) {
  79. # all user actions are accessable for logged in users
  80. if (mb_strpos($action, $this->settings['adminPrefix']) !== 0) {
  81. return true;
  82. }
  83. }
  84. if (isset($this->_matchArray[$controller]['*'])) {
  85. $matchArray = $this->_matchArray[$controller]['*'];
  86. if (in_array(-1, $matchArray)) {
  87. return true;
  88. }
  89. foreach ($roles as $role) {
  90. if (in_array($role, $matchArray)) {
  91. return true;
  92. }
  93. }
  94. }
  95. if (!empty($controller) && !empty($action)) {
  96. if (array_key_exists($controller, $this->_matchArray) && !empty($this->_matchArray[$controller][$action])) {
  97. $matchArray = $this->_matchArray[$controller][$action];
  98. # direct access? (even if he has no roles = GUEST)
  99. if (in_array(-1, $matchArray)) {
  100. return true;
  101. }
  102. # normal access (rolebased)
  103. foreach ($roles as $role) {
  104. if (in_array($role, $matchArray)) {
  105. return true;
  106. }
  107. }
  108. }
  109. }
  110. return false;
  111. }
  112. /**
  113. * @return object $User: the User model
  114. */
  115. public function getModel() {
  116. return ClassRegistry::init(CLASS_USER);
  117. }
  118. /**
  119. * parse ini file and returns the allowed roles per action
  120. * - uses cache for maximum performance
  121. * improved speed by several actions before caching:
  122. * - resolves role slugs to their primary key / identifier
  123. * - resolves wildcards to their verbose translation
  124. * @return array $roles
  125. */
  126. protected function _getRoles() {
  127. $res = array();
  128. if ($this->settings['autoClearCache'] && Configure::read('debug') > 0) {
  129. Cache::delete($this->settings['cacheKey'], $this->settings['cache']);
  130. }
  131. if (($roles = Cache::read($this->settings['cacheKey'], $this->settings['cache'])) !== false) {
  132. return $roles;
  133. }
  134. if (!file_exists(APP . 'Config' . DS . ACL_FILE)) {
  135. touch(APP . 'Config' . DS . ACL_FILE);
  136. }
  137. $iniArray = parse_ini_file(APP . 'Config' . DS . ACL_FILE, true);
  138. $availableRoles = Configure::read('Role');
  139. if (!is_array($availableRoles)) {
  140. $Model = $this->getModel();
  141. $availableRoles = $Model->Role->find('list', array('fields'=>array('alias', 'id')));
  142. Configure::write('Role', $availableRoles);
  143. }
  144. if (!is_array($availableRoles) || !is_array($iniArray)) {
  145. trigger_error('Invalid Role Setup for TinyAuthorize (no roles found)');
  146. return false;
  147. }
  148. foreach ($iniArray as $key => $array) {
  149. list($plugin, $controllerName) = pluginSplit($key);
  150. $controllerName = Inflector::underscore($controllerName);
  151. foreach ($array as $actions => $roles) {
  152. $actions = explode(',', $actions);
  153. $roles = explode(',', $roles);
  154. foreach ($roles as $key => $role) {
  155. if (!($role = trim($role))) {
  156. continue;
  157. }
  158. if ($role == '*') {
  159. unset($roles[$key]);
  160. $roles = array_merge($roles, array_keys(Configure::read('Role')));
  161. }
  162. }
  163. foreach ($actions as $action) {
  164. if (!($action = trim($action))) {
  165. continue;
  166. }
  167. $actionName = Inflector::underscore($action);
  168. foreach ($roles as $role) {
  169. if (!($role = trim($role)) || $role == '*') {
  170. continue;
  171. }
  172. $newRole = Configure::read('Role.'.strtolower($role));
  173. if (!empty($res[$controllerName][$actionName]) && in_array($newRole, $res[$controllerName][$actionName])) {
  174. continue;
  175. }
  176. $res[$controllerName][$actionName][] = $newRole;
  177. }
  178. }
  179. }
  180. }
  181. Cache::write($this->settings['cacheKey'], $res, $this->settings['cache']);
  182. return $res;
  183. }
  184. }