acl.php

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

Generated on Sun Nov 22 00:30:51 2009 for CakePHP 1.2.x.x (v1.2.4.8284) by doxygen 1.4.7