| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- <?php
- App::uses('Inflector', 'Utility');
- App::uses('Hash', 'Utility');
- App::uses('BaseAuthorize', 'Controller/Component/Auth');
- if (!defined('CLASS_USER')) {
- define('CLASS_USER', 'User'); // override if you have it in a plugin: PluginName.User etc
- }
- if (!defined('AUTH_CACHE')) {
- define('AUTH_CACHE', '_cake_core_'); // use the most persistent cache by default
- }
- if (!defined('ACL_FILE')) {
- define('ACL_FILE', 'acl.ini'); // stored in /app/Config/
- }
- /**
- * Probably the most simple and fastest Acl out there.
- * Only one config file `acl.ini` necessary
- * Doesn't even need a Role Model / roles table
- * Uses most persistent _cake_core_ cache by default
- * @link http://www.dereuromark.de/2011/12/18/tinyauth-the-fastest-and-easiest-authorization-for-cake2
- *
- * Usage:
- * Include it in your beforeFilter() method of the AppController
- * $this->Auth->authorize = array('Tools.Tiny');
- *
- * Or with admin prefix protection only
- * $this->Auth->authorize = array('Tools.Tiny'=>array('allowUser'=>true));
- *
- * @version 1.2 - now allows other parent model relations besides Role/role_id
- * @author Mark Scherer
- * @cakephp 2.x
- * @license MIT
- */
- class TinyAuthorize extends BaseAuthorize {
- protected $_acl = null;
- protected $_defaults = array(
- 'superadminRole' => null, // quick way to allow access to every action
- 'allowUser' => false, // quick way to allow user access to non prefixed urls
- 'allowAdmin' => false, // quick way to allow admin access to admin prefixed urls
- 'adminPrefix' => 'admin_',
- 'adminRole' => null, // needed together with adminPrefix if allowAdmin is enabled
- 'cache' => AUTH_CACHE,
- 'cacheKey' => 'tiny_auth_acl',
- 'autoClearCache' => false, // usually done by Cache automatically in debug mode,
- 'aclModel' => 'Role', // only for multiple roles per user (HABTM)
- 'aclKey' => 'role_id', // only for single roles per user (BT)
- );
- public function __construct(ComponentCollection $Collection, $settings = array()) {
- $settings = array_merge($this->_defaults, $settings);
- parent::__construct($Collection, $settings);
- if (Cache::config($settings['cache']) === false) {
- throw new CakeException(__d('dev', 'TinyAuth could not find `%s` cache - expects at least a `default` cache', $settings['cache']));
- }
- }
- /**
- * Authorize a user using the AclComponent.
- * allows single or multi role based authorization
- *
- * Examples:
- * - User HABTM Roles (Role array in User array)
- * - User belongsTo Roles (role_id in User array)
- *
- * @param array $user The user to authorize
- * @param CakeRequest $request The request needing authorization.
- * @return bool Success
- */
- public function authorize($user, CakeRequest $request) {
- if (isset($user[$this->settings['aclModel']])) {
- if (isset($user[$this->settings['aclModel']][0]['id'])) {
- $roles = Hash::extract($user[$this->settings['aclModel']], '{n}.id');
- } elseif (isset($user[$this->settings['aclModel']]['id'])) {
- $roles = array($user[$this->settings['aclModel']]['id']);
- } else {
- $roles = (array)$user[$this->settings['aclModel']];
- }
- } elseif (isset($user[$this->settings['aclKey']])) {
- $roles = array($user[$this->settings['aclKey']]);
- } else {
- $acl = $this->settings['aclModel'] . '/' . $this->settings['aclKey'];
- trigger_error(__d('dev', 'Missing acl information (%s) in user session', $acl));
- $roles = array();
- }
- return $this->validate($roles, $request->params['plugin'], $request->params['controller'], $request->params['action']);
- }
- /**
- * Validate the url to the role(s)
- * allows single or multi role based authorization
- *
- * @return bool Success
- */
- public function validate($roles, $plugin, $controller, $action) {
- $action = Inflector::underscore($action);
- $controller = Inflector::underscore($controller);
- $plugin = Inflector::underscore($plugin);
- if (!empty($this->settings['allowUser'])) {
- // all user actions are accessable for logged in users
- if (mb_strpos($action, $this->settings['adminPrefix']) !== 0) {
- return true;
- }
- }
- if (!empty($this->settings['allowAdmin']) && !empty($this->settings['adminRole'])) {
- // all admin actions are accessable for logged in admins
- if (mb_strpos($action, $this->settings['adminPrefix']) === 0) {
- if (in_array((string)$this->settings['adminRole'], $roles)) {
- return true;
- }
- }
- }
- if ($this->_acl === null) {
- $this->_acl = $this->_getAcl();
- }
- // allow_all check
- if (!empty($this->settings['superadminRole'])) {
- foreach ($roles as $role) {
- if ($role == $this->settings['superadminRole']) {
- return true;
- }
- }
- }
- // controller wildcard
- if (isset($this->_acl[$controller]['*'])) {
- $matchArray = $this->_acl[$controller]['*'];
- if (in_array('-1', $matchArray)) {
- return true;
- }
- foreach ($roles as $role) {
- if (in_array((string)$role, $matchArray)) {
- return true;
- }
- }
- }
- // specific controller/action
- if (!empty($controller) && !empty($action)) {
- if (array_key_exists($controller, $this->_acl) && !empty($this->_acl[$controller][$action])) {
- $matchArray = $this->_acl[$controller][$action];
- // direct access? (even if he has no roles = GUEST)
- if (in_array('-1', $matchArray)) {
- return true;
- }
- // normal access (rolebased)
- foreach ($roles as $role) {
- if (in_array((string)$role, $matchArray)) {
- return true;
- }
- }
- }
- }
- return false;
- }
- /**
- * @return object The User model
- */
- public function getModel() {
- return ClassRegistry::init(CLASS_USER);
- }
- /**
- * Parse ini file and returns the allowed roles per action
- * - uses cache for maximum performance
- * improved speed by several actions before caching:
- * - resolves role slugs to their primary key / identifier
- * - resolves wildcards to their verbose translation
- *
- * @return array Roles
- */
- protected function _getAcl($path = null) {
- if ($path === null) {
- $path = APP . 'Config' . DS;
- }
- $res = array();
- if ($this->settings['autoClearCache'] && Configure::read('debug') > 0) {
- Cache::delete($this->settings['cacheKey'], $this->settings['cache']);
- }
- if (($roles = Cache::read($this->settings['cacheKey'], $this->settings['cache'])) !== false) {
- return $roles;
- }
- if (!file_exists($path . ACL_FILE)) {
- touch($path . ACL_FILE);
- }
- if (function_exists('parse_ini_file')) {
- $iniArray = parse_ini_file($path . ACL_FILE, true);
- } else {
- $iniArray = parse_ini_string(file_get_contents($path . ACL_FILE), true);
- }
- $availableRoles = Configure::read($this->settings['aclModel']);
- if (!is_array($availableRoles)) {
- $Model = $this->getModel();
- if (!isset($Model->{$this->settings['aclModel']})) {
- throw new CakeException('Missing relationship between User and Role.');
- }
- $availableRoles = $Model->{$this->settings['aclModel']}->find('list', array('fields' => array('alias', 'id')));
- Configure::write($this->settings['aclModel'], $availableRoles);
- }
- if (!is_array($availableRoles) || !is_array($iniArray)) {
- trigger_error(__d('dev', 'Invalid Role Setup for TinyAuthorize (no roles found)'));
- return array();
- }
- foreach ($iniArray as $key => $array) {
- list($plugin, $controllerName) = pluginSplit($key);
- $controllerName = Inflector::underscore($controllerName);
- foreach ($array as $actions => $roles) {
- $actions = explode(',', $actions);
- $roles = explode(',', $roles);
- foreach ($roles as $key => $role) {
- if (!($role = trim($role))) {
- continue;
- }
- if ($role === '*') {
- unset($roles[$key]);
- $roles = array_merge($roles, array_keys(Configure::read($this->settings['aclModel'])));
- }
- }
- foreach ($actions as $action) {
- if (!($action = trim($action))) {
- continue;
- }
- $actionName = Inflector::underscore($action);
- foreach ($roles as $role) {
- if (!($role = trim($role)) || $role === '*') {
- continue;
- }
- $newRole = Configure::read($this->settings['aclModel'] . '.' . strtolower($role));
- if (!empty($res[$controllerName][$actionName]) && in_array((string)$newRole, $res[$controllerName][$actionName])) {
- continue;
- }
- $res[$controllerName][$actionName][] = $newRole;
- }
- }
- }
- }
- Cache::write($this->settings['cacheKey'], $res, $this->settings['cache']);
- return $res;
- }
- }
|