acl.php

Go to the documentation of this file.
00001 <?php
00002 /* SVN FILE: $Id: libs_2controller_2components_2acl_8php-source.html 580 2008-07-01 14:45:49Z gwoo $ */
00003 /**
00004  * Access Control List factory class.
00005  *
00006  * Permissions system.
00007  *
00008  * PHP versions 4 and 5
00009  *
00010  * CakePHP(tm) :  Rapid Development Framework <http://www.cakephp.org/>
00011  * Copyright 2005-2008, Cake Software Foundation, Inc.
00012  *                              1785 E. Sahara Avenue, Suite 490-204
00013  *                              Las Vegas, Nevada 89104
00014  *
00015  * Licensed under The MIT License
00016  * Redistributions of files must retain the above copyright notice.
00017  *
00018  * @filesource
00019  * @copyright       Copyright 2005-2008, Cake Software Foundation, Inc.
00020  * @link                http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
00021  * @package         cake
00022  * @subpackage      cake.cake.libs.controller.components
00023  * @since           CakePHP(tm) v 0.10.0.1076
00024  * @version         $Revision: 580 $
00025  * @modifiedby      $LastChangedBy: gwoo $
00026  * @lastmodified    $Date: 2008-07-01 09:45:49 -0500 (Tue, 01 Jul 2008) $
00027  * @license         http://www.opensource.org/licenses/mit-license.php The MIT License
00028  */
00029 /**
00030  * Access Control List factory class.
00031  *
00032  * Looks for ACL implementation class in core config, and returns an instance of that class.
00033  *
00034  * @package     cake
00035  * @subpackage  cake.cake.libs.controller.components
00036  */
00037 class AclComponent extends Object {
00038 /**
00039  * Instance of an ACL class
00040  *
00041  * @var object
00042  * @access protected
00043  */
00044     var $_Instance = null;
00045 /**
00046  * Constructor. Will return an instance of the correct ACL class.
00047  *
00048  */
00049     function __construct() {
00050         $name = Inflector::camelize(strtolower(Configure::read('Acl.classname')));
00051         if (!class_exists($name)) {
00052             if (App::import('Component', $name)) {
00053                 if (strpos($name, '.') !== false) {
00054                     list($plugin, $name) = explode('.', $name);
00055                 }
00056                 $name .= 'Component';
00057             } else {
00058                 trigger_error(sprintf(__('Could not find %s.', true), $name), E_USER_WARNING);
00059             }
00060         }
00061         $this->_Instance =& new $name();
00062         $this->_Instance->initialize($this);
00063     }
00064 /**
00065  * Startup is not used
00066  *
00067  * @param object $controller Controller using this component
00068  * @return boolean Proceed with component usage (true), or fail (false)
00069  * @access public
00070  */
00071     function startup(&$controller) {
00072         return true;
00073     }
00074 /**
00075  * Empty class defintion, to be overridden in subclasses.
00076  *
00077  * @access protected
00078  */
00079     function _initACL() {
00080     }
00081 /**
00082  * Pass-thru function for ACL check instance.
00083  *
00084  * @param string $aro ARO
00085  * @param string $aco ACO
00086  * @param string $action Action (defaults to *)
00087  * @return boolean Success
00088  * @access public
00089  */
00090     function check($aro, $aco, $action = "*") {
00091         return $this->_Instance->check($aro, $aco, $action);
00092     }
00093 /**
00094  * Pass-thru function for ACL allow instance.
00095  *
00096  * @param string $aro ARO
00097  * @param string $aco ACO
00098  * @param string $action Action (defaults to *)
00099  * @return boolean Success
00100  * @access public
00101  */
00102     function allow($aro, $aco, $action = "*") {
00103         return $this->_Instance->allow($aro, $aco, $action);
00104     }
00105 /**
00106  * Pass-thru function for ACL deny instance.
00107  *
00108  * @param string $aro ARO
00109  * @param string $aco ACO
00110  * @param string $action Action (defaults to *)
00111  * @return boolean Success
00112  * @access public
00113  */
00114     function deny($aro, $aco, $action = "*") {
00115         return $this->_Instance->deny($aro, $aco, $action);
00116     }
00117 /**
00118  * Pass-thru function for ACL inherit instance.
00119  *
00120  * @param string $aro ARO
00121  * @param string $aco ACO
00122  * @param string $action Action (defaults to *)
00123  * @return boolean Success
00124  * @access public
00125  */
00126     function inherit($aro, $aco, $action = "*") {
00127         return $this->_Instance->inherit($aro, $aco, $action);
00128     }
00129 /**
00130  * Pass-thru function for ACL grant instance.
00131  *
00132  * @param string $aro ARO
00133  * @param string $aco ACO
00134  * @param string $action Action (defaults to *)
00135  * @return boolean Success
00136  * @access public
00137  */
00138     function grant($aro, $aco, $action = "*") {
00139         return $this->_Instance->grant($aro, $aco, $action);
00140     }
00141 /**
00142  * Pass-thru function for ACL grant instance.
00143  *
00144  * @param string $aro ARO
00145  * @param string $aco ACO
00146  * @param string $action Action (defaults to *)
00147  * @return boolean Success
00148  * @access public
00149  */
00150     function revoke($aro, $aco, $action = "*") {
00151         return $this->_Instance->revoke($aro, $aco, $action);
00152     }
00153 /**
00154  * Sets the current ARO instance to object from getAro
00155  *
00156  * @param string $id ID of ARO
00157  * @return boolean Success
00158  * @access public
00159  */
00160     function setAro($id) {
00161         return $this->Aro = $this->_Instance->getAro($id);
00162     }
00163 /**
00164 * Sets the current ACO instance to object from getAco
00165  *
00166  * @param string $id ID of ACO
00167  * @return boolean Success
00168  * @access public
00169  */
00170     function setAco($id) {
00171         return $this->Aco = $this->_Instance->getAco($id);
00172     }
00173 /**
00174  * Pass-thru function for ACL getAro instance
00175  * that gets an ARO object from the given id or alias
00176  *
00177  * @param string $id ARO id
00178  * @return object ARO
00179  * @access public
00180  */
00181     function getAro($id) {
00182         return $this->_Instance->getAro($id);
00183     }
00184 /**
00185  * Pass-thru function for ACL getAco instance.
00186  * that gets an ACO object from the given id or alias
00187  *
00188  * @param string $id ACO id
00189  * @return object ACO
00190  * @access public
00191  */
00192     function getAco($id) {
00193         return $this->_Instance->getAco($id);
00194     }
00195 }
00196 /**
00197  * Access Control List abstract class. Not to be instantiated.
00198  * Subclasses of this class are used by AclComponent to perform ACL checks in Cake.
00199  *
00200  * @package     cake
00201  * @subpackage  cake.cake.libs.controller.components
00202  * @abstract
00203  */
00204 class AclBase extends Object {
00205 /**
00206  * This class should never be instantiated, just subclassed.
00207  *
00208  */
00209     function __construct() {
00210         if (strcasecmp(get_class($this), "AclBase") == 0 || !is_subclass_of($this, "AclBase")) {
00211             trigger_error(__("[acl_base] The AclBase class constructor has been called, or the class was instantiated. This class must remain abstract. Please refer to the Cake docs for ACL configuration.", true), E_USER_ERROR);
00212             return NULL;
00213         }
00214     }
00215 /**
00216  * Empty method to be overridden in subclasses
00217  *
00218  * @param string $aro ARO
00219  * @param string $aco ACO
00220  * @param string $action Action (defaults to *)
00221  * @access public
00222  */
00223     function check($aro, $aco, $action = "*") {
00224     }
00225 /**
00226  * Empty method to be overridden in subclasses
00227  *
00228  * @param object $component Component
00229  * @access public
00230  */
00231     function initialize(&$component) {
00232     }
00233 }
00234 /**
00235  * In this file you can extend the AclBase.
00236  *
00237  * @package     cake
00238  * @subpackage  cake.cake.libs.model
00239  */
00240 class DbAcl extends AclBase {
00241 /**
00242  * Constructor
00243  *
00244  */
00245     function __construct() {
00246         parent::__construct();
00247         if (!class_exists('AclNode')) {
00248             uses('model' . DS . 'db_acl');
00249         }
00250         $this->Aro =& ClassRegistry::init(array('class' => 'Aro', 'alias' => 'Aro'));
00251         $this->Aco =& ClassRegistry::init(array('class' => 'Aco', 'alias' => 'Aco'));
00252     }
00253 /**
00254  * Enter description here...
00255  *
00256  * @param object $component
00257  * @access public
00258  */
00259     function initialize(&$component) {
00260         $component->Aro = $this->Aro;
00261         $component->Aco = $this->Aco;
00262     }
00263 /**
00264  * Checks if the given $aro has access to action $action in $aco
00265  *
00266  * @param string $aro ARO
00267  * @param string $aco ACO
00268  * @param string $action Action (defaults to *)
00269  * @return boolean Success (true if ARO has access to action in ACO, false otherwise)
00270  * @access public
00271  */
00272     function check($aro, $aco, $action = "*") {
00273         if ($aro == null || $aco == null) {
00274             return false;
00275         }
00276 
00277         $permKeys = $this->_getAcoKeys($this->Aro->Permission->schema());
00278         $aroPath = $this->Aro->node($aro);
00279         $acoPath = new Set($this->Aco->node($aco));
00280 
00281         if (empty($aroPath) ||  empty($acoPath)) {
00282             trigger_error("DbAcl::check() - Failed ARO/ACO node lookup in permissions check.  Node references:\nAro: " . print_r($aro, true) . "\nAco: " . print_r($aco, true), E_USER_WARNING);
00283             return false;
00284         }
00285         if ($acoPath->get() == null || $acoPath->get() == array()) {
00286             trigger_error("DbAcl::check() - Failed ACO node lookup in permissions check.  Node references:\nAro: " . print_r($aro, true) . "\nAco: " . print_r($aco, true), E_USER_WARNING);
00287             return false;
00288         }
00289 
00290         $aroNode = $aroPath[0];
00291         $acoNode = $acoPath->get();
00292         $acoNode = $acoNode[0];
00293 
00294         if ($action != '*' && !in_array('_' . $action, $permKeys)) {
00295             trigger_error(sprintf(__("ACO permissions key %s does not exist in DbAcl::check()", true), $action), E_USER_NOTICE);
00296             return false;
00297         }
00298 
00299         $inherited = array();
00300         $acoIDs = $acoPath->extract('{n}.' . $this->Aco->alias . '.id');
00301 
00302         for ($i = 0 ; $i < count($aroPath); $i++) {
00303             $permAlias = $this->Aro->Permission->alias;
00304 
00305             $perms = $this->Aro->Permission->find('all', array(
00306                 'conditions' => array(
00307                     "{$permAlias}.aro_id" => $aroPath[$i][$this->Aro->alias]['id'],
00308                     "{$permAlias}.aco_id" => $acoIDs
00309                 ),
00310                 'order' => array($this->Aco->alias . '.lft' => 'desc'),
00311                 'recursive' => 0
00312             ));
00313 
00314             if (empty($perms)) {
00315                 continue;
00316             } else {
00317                 $perms = Set::extract($perms, '{n}.' . $this->Aro->Permission->alias);
00318                 foreach ($perms as $perm) {
00319                     if ($action == '*') {
00320 
00321                         foreach ($permKeys as $key) {
00322                             if (!empty($perm)) {
00323                                 if ($perm[$key] == -1) {
00324                                     return false;
00325                                 } elseif ($perm[$key] == 1) {
00326                                     $inherited[$key] = 1;
00327                                 }
00328                             }
00329                         }
00330 
00331                         if (count($inherited) === count($permKeys)) {
00332                             return true;
00333                         }
00334                     } else {
00335                         switch($perm['_' . $action]) {
00336                             case -1:
00337                                 return false;
00338                             case 0:
00339                                 continue;
00340                             break;
00341                             case 1:
00342                                 return true;
00343                             break;
00344                         }
00345                     }
00346                 }
00347             }
00348         }
00349         return false;
00350     }
00351 /**
00352  * Allow $aro to have access to action $actions in $aco
00353  *
00354  * @param string $aro ARO
00355  * @param string $aco ACO
00356  * @param string $actions Action (defaults to *)
00357  * @param integer $value Value to indicate access type (1 to give access, -1 to deny, 0 to inherit)
00358  * @return boolean Success
00359  * @access public
00360  */
00361     function allow($aro, $aco, $actions = "*", $value = 1) {
00362         $perms = $this->getAclLink($aro, $aco);
00363         $permKeys = $this->_getAcoKeys($this->Aro->Permission->schema());
00364         $save = array();
00365 
00366         if ($perms == false) {
00367             trigger_error(__('DbAcl::allow() - Invalid node', true), E_USER_WARNING);
00368             return false;
00369         }
00370         if (isset($perms[0])) {
00371             $save = $perms[0][$this->Aro->Permission->alias];
00372         }
00373 
00374         if ($actions == "*") {
00375             $permKeys = $this->_getAcoKeys($this->Aro->Permission->schema());
00376             $save = array_combine($permKeys, array_pad(array(), count($permKeys), $value));
00377         } else {
00378             if (!is_array($actions)) {
00379                 $actions = array('_' . $actions);
00380             }
00381             if (is_array($actions)) {
00382                 foreach ($actions as $action) {
00383                     if ($action{0} != '_') {
00384                         $action = '_' . $action;
00385                     }
00386                     if (in_array($action, $permKeys)) {
00387                         $save[$action] = $value;
00388                     }
00389                 }
00390             }
00391         }
00392         list($save['aro_id'], $save['aco_id']) = array($perms['aro'], $perms['aco']);
00393 
00394         if ($perms['link'] != null && count($perms['link']) > 0) {
00395             $save['id'] = $perms['link'][0][$this->Aro->Permission->alias]['id'];
00396         } else {
00397             unset($save['id']);
00398             $this->Aro->Permission->id = null;
00399         }
00400         return ($this->Aro->Permission->save($save) !== false);
00401     }
00402 /**
00403  * Deny access for $aro to action $action in $aco
00404  *
00405  * @param string $aro ARO
00406  * @param string $aco ACO
00407  * @param string $actions Action (defaults to *)
00408  * @return boolean Success
00409  * @access public
00410  */
00411     function deny($aro, $aco, $action = "*") {
00412         return $this->allow($aro, $aco, $action, -1);
00413     }
00414 /**
00415  * Let access for $aro to action $action in $aco be inherited
00416  *
00417  * @param string $aro ARO
00418  * @param string $aco ACO
00419  * @param string $actions Action (defaults to *)
00420  * @return boolean Success
00421  * @access public
00422  */
00423     function inherit($aro, $aco, $action = "*") {
00424         return $this->allow($aro, $aco, $action, 0);
00425     }
00426 /**
00427  * Allow $aro to have access to action $actions in $aco
00428  *
00429  * @param string $aro ARO
00430  * @param string $aco ACO
00431  * @param string $actions Action (defaults to *)
00432  * @return boolean Success
00433  * @see allow()
00434  * @access public
00435  */
00436     function grant($aro, $aco, $action = "*") {
00437         return $this->allow($aro, $aco, $action);
00438     }
00439 /**
00440  * Deny access for $aro to action $action in $aco
00441  *
00442  * @param string $aro ARO
00443  * @param string $aco ACO
00444  * @param string $actions Action (defaults to *)
00445  * @return boolean Success
00446  * @see deny()
00447  * @access public
00448  */
00449     function revoke($aro, $aco, $action = "*") {
00450         return $this->deny($aro, $aco, $action);
00451     }
00452 /**
00453  * Get an array of access-control links between the given Aro and Aco
00454  *
00455  * @param string $aro ARO
00456  * @param string $aco ACO
00457  * @return array Indexed array with: 'aro', 'aco' and 'link'
00458  * @access public
00459  */
00460     function getAclLink($aro, $aco) {
00461         $obj = array();
00462         $obj['Aro'] = $this->Aro->node($aro);
00463         $obj['Aco'] = $this->Aco->node($aco);
00464 
00465         if (empty($obj['Aro']) || empty($obj['Aco'])) {
00466             return false;
00467         }
00468 
00469         return array(
00470             'aro' => Set::extract($obj, 'Aro.0.'.$this->Aro->alias.'.id'),
00471             'aco'  => Set::extract($obj, 'Aco.0.'.$this->Aco->alias.'.id'),
00472             'link' => $this->Aro->Permission->find('all', array('conditions' => array(
00473                 $this->Aro->Permission->alias . '.aro_id' => Set::extract($obj, 'Aro.0.'.$this->Aro->alias.'.id'),
00474                 $this->Aro->Permission->alias . '.aco_id' => Set::extract($obj, 'Aco.0.'.$this->Aco->alias.'.id')
00475             )))
00476         );
00477     }
00478 /**
00479  * Get the keys used in an ACO
00480  *
00481  * @param array $keys Permission model info
00482  * @return array ACO keys
00483  * @access protected
00484  */
00485     function _getAcoKeys($keys) {
00486         $newKeys = array();
00487         $keys = array_keys($keys);
00488         foreach ($keys as $key) {
00489             if (!in_array($key, array('id', 'aro_id', 'aco_id'))) {
00490                 $newKeys[] = $key;
00491             }
00492         }
00493         return $newKeys;
00494     }
00495 }
00496 /**
00497  * In this file you can extend the AclBase.
00498  *
00499  * @package     cake
00500  * @subpackage  cake.cake.libs.model.iniacl
00501  */
00502 class IniAcl extends AclBase {
00503 /**
00504  * Array with configuration, parsed from ini file
00505  *
00506  * @var array
00507  * @access public
00508  */
00509     var $config = null;
00510 /**
00511  * The constructor must be overridden, as AclBase is abstract.
00512  *
00513  */
00514     function __construct() {
00515     }
00516 /**
00517  * Main ACL check function. Checks to see if the ARO (access request object) has access to the ACO (access control object).
00518  * Looks at the acl.ini.php file for permissions (see instructions in /config/acl.ini.php).
00519  *
00520  * @param string $aro ARO
00521  * @param string $aco ACO
00522  * @param string $aco_action Action
00523  * @return boolean Success
00524  * @access public
00525  */
00526     function check($aro, $aco, $aco_action = null) {
00527         if ($this->config == null) {
00528             $this->config = $this->readConfigFile(CONFIGS . 'acl.ini.php');
00529         }
00530         $aclConfig = $this->config;
00531 
00532         if (isset($aclConfig[$aro]['deny'])) {
00533             $userDenies = $this->arrayTrim(explode(",", $aclConfig[$aro]['deny']));
00534 
00535             if (array_search($aco, $userDenies)) {
00536                 return false;
00537             }
00538         }
00539 
00540         if (isset($aclConfig[$aro]['allow'])) {
00541             $userAllows = $this->arrayTrim(explode(",", $aclConfig[$aro]['allow']));
00542 
00543             if (array_search($aco, $userAllows)) {
00544                 return true;
00545             }
00546         }
00547 
00548         if (isset($aclConfig[$aro]['groups'])) {
00549             $userGroups = $this->arrayTrim(explode(",", $aclConfig[$aro]['groups']));
00550 
00551             foreach ($userGroups as $group) {
00552                 if (array_key_exists($group, $aclConfig)) {
00553                     if (isset($aclConfig[$group]['deny'])) {
00554                         $groupDenies=$this->arrayTrim(explode(",", $aclConfig[$group]['deny']));
00555 
00556                         if (array_search($aco, $groupDenies)) {
00557                             return false;
00558                         }
00559                     }
00560 
00561                     if (isset($aclConfig[$group]['allow'])) {
00562                         $groupAllows = $this->arrayTrim(explode(",", $aclConfig[$group]['allow']));
00563 
00564                         if (array_search($aco, $groupAllows)) {
00565                             return true;
00566                         }
00567                     }
00568                 }
00569             }
00570         }
00571         return false;
00572     }
00573 /**
00574  * Parses an INI file and returns an array that reflects the INI file's section structure. Double-quote friendly.
00575  *
00576  * @param string $fileName File
00577  * @return array INI section structure
00578  * @access public
00579  */
00580     function readConfigFile($fileName) {
00581         $fileLineArray = file($fileName);
00582 
00583         foreach ($fileLineArray as $fileLine) {
00584             $dataLine = trim($fileLine);
00585             $firstChar = substr($dataLine, 0, 1);
00586 
00587             if ($firstChar != ';' && $dataLine != '') {
00588                 if ($firstChar == '[' && substr($dataLine, -1, 1) == ']') {
00589                     $sectionName = preg_replace('/[\[\]]/', '', $dataLine);
00590                 } else {
00591                     $delimiter = strpos($dataLine, '=');
00592 
00593                     if ($delimiter > 0) {
00594                         $key = strtolower(trim(substr($dataLine, 0, $delimiter)));
00595                         $value = trim(substr($dataLine, $delimiter + 1));
00596 
00597                         if (substr($value, 0, 1) == '"' && substr($value, -1) == '"') {
00598                             $value = substr($value, 1, -1);
00599                         }
00600 
00601                         $iniSetting[$sectionName][$key]=stripcslashes($value);
00602                     } else {
00603                         if (!isset($sectionName)) {
00604                             $sectionName = '';
00605                         }
00606 
00607                         $iniSetting[$sectionName][strtolower(trim($dataLine))]='';
00608                     }
00609                 }
00610             }
00611         }
00612 
00613         return $iniSetting;
00614     }
00615 /**
00616  * Removes trailing spaces on all array elements (to prepare for searching)
00617  *
00618  * @param array $array Array to trim
00619  * @return array Trimmed array
00620  * @access public
00621  */
00622     function arrayTrim($array) {
00623         foreach ($array as $key => $value) {
00624             $array[$key] = trim($value);
00625         }
00626         array_unshift($array, "");
00627         return $array;
00628     }
00629 }
00630 ?>