TinyAuthorize.php 6.9 KB

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