From 269c9a0010c2495db961c185e83fd52b33b04d73 Mon Sep 17 00:00:00 2001 From: xue <> Date: Tue, 11 Sep 2007 02:09:27 +0000 Subject: Fixed #692, #700. --- framework/Exceptions/TErrorHandler.php | 2 +- framework/Security/TAuthorizationRule.php | 59 +++++++++++++- framework/Web/Services/TPageService.php | 127 +++++++++++++++++++----------- 3 files changed, 137 insertions(+), 51 deletions(-) (limited to 'framework') diff --git a/framework/Exceptions/TErrorHandler.php b/framework/Exceptions/TErrorHandler.php index 2d245a2f..b98ade5e 100644 --- a/framework/Exceptions/TErrorHandler.php +++ b/framework/Exceptions/TErrorHandler.php @@ -300,7 +300,7 @@ class TErrorHandler extends TModule // if PHP exception, we want to show the 2nd stack level context // because the 1st stack level is of little use (it's in error handler) if($exception instanceof TPhpErrorException) - $result=$trace[1]; + $result=isset($trace[0]['file'])?$trace[0]:$trace[1]; else if($exception instanceof TInvalidOperationException) { // in case of getter or setter error, find out the exact file and row diff --git a/framework/Security/TAuthorizationRule.php b/framework/Security/TAuthorizationRule.php index fa1eb134..5b3a3c25 100644 --- a/framework/Security/TAuthorizationRule.php +++ b/framework/Security/TAuthorizationRule.php @@ -14,7 +14,7 @@ * * TAuthorizationRule represents a single authorization rule. * A rule is specified by an action (required), a list of users (optional), - * a list of roles (optional), and a verb (optional). + * a list of roles (optional), a verb (optional), and a list of IP rules (optional). * Action can be either 'allow' or 'deny'. * Guest (anonymous, unauthenticated) users are represented by question mark '?'. * All users (including guest users) are represented by asterisk '*'. @@ -22,6 +22,7 @@ * Users/roles are case-insensitive. * Different users/roles are separated by comma ','. * Verb can be either 'get' or 'post'. If it is absent, it means both. + * IP rules are separated by comma ',' and can contain wild card in the rules (e.g. '192.132.23.33, 192.122.*.*') * * @author Qiang Xue * @version $Id$ @@ -46,6 +47,10 @@ class TAuthorizationRule extends TComponent * @var string verb, may be empty, 'get', or 'post'. */ private $_verb; + /** + * @var string IP patterns + */ + private $_ipRules; /** * @var boolean if this rule applies to everyone */ @@ -65,8 +70,9 @@ class TAuthorizationRule extends TComponent * @param string a comma separated user list * @param string a comma separated role list * @param string verb, can be empty, 'get', or 'post' + * @param string IP rules (separated by comma, can contain wild card *) */ - public function __construct($action,$users,$roles,$verb='') + public function __construct($action,$users,$roles,$verb='',$ipRules='') { $action=strtolower(trim($action)); if($action==='allow' || $action==='deny') @@ -75,6 +81,7 @@ class TAuthorizationRule extends TComponent throw new TInvalidDataValueException('authorizationrule_action_invalid',$action); $this->_users=array(); $this->_roles=array(); + $this->_ipRules=array(); $this->_everyone=false; $this->_guest=false; $this->_authenticated=false; @@ -105,6 +112,11 @@ class TAuthorizationRule extends TComponent $this->_verb=$verb; else throw new TInvalidDataValueException('authorizationrule_verb_invalid',$verb); + foreach(explode(',',$ipRules) as $ipRule) + { + if(($ipRule=trim($ipRule))!=='') + $this->_ipRules[]=$ipRule; + } } /** @@ -139,6 +151,15 @@ class TAuthorizationRule extends TComponent return $this->_verb; } + /** + * @return array list of IP rules. + * @since 3.1.1 + */ + public function getIPRules() + { + return $this->_ipRules; + } + /** * @return boolean if this rule applies to everyone */ @@ -171,6 +192,8 @@ class TAuthorizationRule extends TComponent $decision=($this->_action==='allow')?1:-1; if($this->_verb==='' || strcasecmp($verb,$this->_verb)===0) { + if(!$this->isHostAddressMatched()) + return 0; if($this->_everyone || ($this->_guest && $user->getIsGuest()) || ($this->_authenticated && !$user->getIsGuest())) return $decision; if(in_array(strtolower($user->getName()),$this->_users)) @@ -181,6 +204,38 @@ class TAuthorizationRule extends TComponent } return 0; } + + private function isHostAddressMatched() + { + if(empty($this->_ipRules)) + return 1; + $ip=Prado::getApplication()->getRequest()->getUserHostAddress(); + foreach($this->_ipRules as $rule) + { + if($rule===$ip) + return 1; + if(strpos($rule,'*')!==false) + { + $a=explode('.',$rule); + $b=explode('.',$ip); + if(isset($a[3]) && isset($b[3])) + { + $matched=true; + for($i=0;$i<4;++$i) + { + if($a[$i]!==$b[i] && $a[$i]!=='*') + { + $matched=false; + break; + } + } + if($matched) + return 1; + } + } + } + return 0; + } } diff --git a/framework/Web/Services/TPageService.php b/framework/Web/Services/TPageService.php index ab8d3651..8b7fb52d 100644 --- a/framework/Web/Services/TPageService.php +++ b/framework/Web/Services/TPageService.php @@ -137,7 +137,7 @@ class TPageService extends TService { Prado::trace("Initializing TPageService",'System.Web.Services.TPageService'); - $pageConfig=$this->loadPageConfig($this->getRequestedPagePath(),$config); + $pageConfig=$this->loadPageConfig($config); $this->initPageContext($pageConfig); @@ -169,17 +169,19 @@ class TPageService extends TService // initial page properties (to be set when page runs) $this->_properties=$config->getProperties(); $this->getApplication()->getAuthorizationRules()->mergeWith($config->getRules()); + $pagePath=$this->getRequestedPagePath(); // external configurations - foreach($config->getExternalConfigurations() as $filePath=>$condition) + foreach($config->getExternalConfigurations() as $filePath=>$params) { + list($configPagePath,$condition)=$params; if($condition!==true) $condition=$this->evaluateExpression($condition); if($condition) { if(($path=Prado::getPathOfNamespace($filePath,TApplication::CONFIG_FILE_EXT))===null || !is_file($path)) throw new TConfigurationException('pageservice_includefile_invalid',$filePath); - $c=new TPageConfiguration; - $c->loadFromFile($path,null); + $c=new TPageConfiguration($pagePath); + $c->loadFromFile($path,$configPagePath); $this->applyConfiguration($c); } } @@ -200,19 +202,19 @@ class TPageService extends TService /** * Collects configuration for a page. - * @param string page path in the format of Path.To.Page - * @param TXmlElement additional configuration + * @param TXmlElement additional configuration specified in the application configuration * @return TPageConfiguration */ - protected function loadPageConfig($pagePath,$config=null) + protected function loadPageConfig($config) { $application=$this->getApplication(); + $pagePath=$this->getRequestedPagePath(); if(($cache=$application->getCache())===null) { - $pageConfig=new TPageConfiguration; + $pageConfig=new TPageConfiguration($pagePath); if($config!==null) - $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath()); - $pageConfig->loadFromFiles($pagePath,$this->getBasePath()); + $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath(),''); + $pageConfig->loadFromFiles($this->getBasePath()); } else { @@ -258,10 +260,10 @@ class TPageService extends TService } if(!$configCached) { - $pageConfig=new TPageConfiguration; + $pageConfig=new TPageConfiguration($pagePath); if($config!==null) - $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath()); - $pageConfig->loadFromFiles($pagePath,$this->getBasePath()); + $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath(),''); + $pageConfig->loadFromFiles($this->getBasePath()); $cache->set(self::CONFIG_CACHE_PREFIX.$this->getID().$pagePath,array($pageConfig,$currentTimestamp)); } } @@ -517,6 +519,19 @@ class TPageConfiguration extends TComponent * @var array list of included configurations */ private $_includes=array(); + /** + * @var string the currently request page in the format of Path.To.PageName + */ + private $_pagePath=''; + + /** + * Constructor. + * @param string the currently request page in the format of Path.To.PageName + */ + public function __construct($pagePath) + { + $this->_pagePath=$pagePath; + } /** * @return array list of external configuration files. Each element is like $filePath=>$condition @@ -558,36 +573,40 @@ class TPageConfiguration extends TComponent /** * Loads configuration for a page specified in a path format. - * @param string path to the page (dot-connected format) * @param string root path for pages */ - public function loadFromFiles($pagePath,$basePath) + public function loadFromFiles($basePath) { - $paths=explode('.',$pagePath); + $paths=explode('.',$this->_pagePath); $page=array_pop($paths); $path=$basePath; + $configPagePath=''; foreach($paths as $p) { - $this->loadFromFile($path.DIRECTORY_SEPARATOR.TPageService::CONFIG_FILE,null); + $this->loadFromFile($path.DIRECTORY_SEPARATOR.TPageService::CONFIG_FILE,$configPagePath); $path.=DIRECTORY_SEPARATOR.$p; + if($configPagePath==='') + $configPagePath=$p; + else + $configPagePath.='.'.$p; } - $this->loadFromFile($path.DIRECTORY_SEPARATOR.TPageService::CONFIG_FILE,$page); + $this->loadFromFile($path.DIRECTORY_SEPARATOR.TPageService::CONFIG_FILE,$configPagePath); $this->_rules=new TAuthorizationRuleCollection($this->_rules); } /** * Loads a specific config file. * @param string config file name - * @param string page name, null if page is not required + * @param string the page path that the config file is associated with. The page path doesn't include the page name. */ - public function loadFromFile($fname,$page) + public function loadFromFile($fname,$configPagePath) { - Prado::trace("Loading $page with file $fname",'System.Web.Services.TPageService'); + Prado::trace("Loading page configuration file $fname",'System.Web.Services.TPageService'); if(empty($fname) || !is_file($fname)) return; $dom=new TXmlDocument; if($dom->loadFromFile($fname)) - $this->loadFromXml($dom,dirname($fname),$page); + $this->loadFromXml($dom,dirname($fname),$configPagePath); else throw new TConfigurationException('pageserviceconf_file_invalid',$fname); } @@ -597,13 +616,13 @@ class TPageConfiguration extends TComponent * The configuration includes information for both application * and page service. * @param TXmlElement config xml element - * @param string base path corresponding to this xml element - * @param string page name, null if page is not required + * @param string the directory containing this configuration + * @param string the page path that the config XML is associated with. The page path doesn't include the page name. */ - public function loadFromXml($dom,$configPath,$page=null) + public function loadFromXml($dom,$configPath,$configPagePath) { $this->loadApplicationConfigurationFromXml($dom,$configPath); - $this->loadPageConfigurationFromXml($dom,$configPath,$page); + $this->loadPageConfigurationFromXml($dom,$configPath,$configPagePath); } /** @@ -622,9 +641,9 @@ class TPageConfiguration extends TComponent * Loads the configuration specific for page service. * @param TXmlElement config xml element * @param string base path corresponding to this xml element - * @param string page name, null if page is not required + * @param string the page path that the config XML is associated with. The page path doesn't include the page name. */ - public function loadPageConfigurationFromXml($dom,$configPath,$page=null) + public function loadPageConfigurationFromXml($dom,$configPath,$configPagePath) { // authorization if(($authorizationNode=$dom->getElementByTagName('authorization'))!==null) @@ -632,24 +651,38 @@ class TPageConfiguration extends TComponent $rules=array(); foreach($authorizationNode->getElements() as $node) { - $pages=$node->getAttribute('pages'); + $patterns=$node->getAttribute('pages'); $ruleApplies=false; - if(empty($pages)) + if(empty($patterns)) // null or empty string $ruleApplies=true; - else if($page!==null) + else { - $ps=explode(',',$pages); - foreach($ps as $p) + foreach(explode(',',$patterns) as $pattern) { - if(strcasecmp($page,trim($p))===0) + if(($pattern=trim($pattern))!=='') { - $ruleApplies=true; - break; + // we know $configPagePath and $this->_pagePath + if($configPagePath!=='') // prepend the pattern with ConfigPagePath + $pattern=$configPagePath.'.'.$pattern; + if(strcasecmp($pattern,$this->_pagePath)===0) + { + $ruleApplies=true; + break; + } + if($pattern[strlen($pattern)-1]==='*') // try wildcard matching + { + $pattern=strtolower(substr($pattern,0,strlen($pattern)-1)); + if(strpos(strtolower($this->_pagePath),$pattern)===0) + { + $ruleApplies=true; + break; + } + } } } } if($ruleApplies) - $rules[]=new TAuthorizationRule($node->getTagName(),$node->getAttribute('users'),$node->getAttribute('roles'),$node->getAttribute('verb')); + $rules[]=new TAuthorizationRule($node->getTagName(),$node->getAttribute('users'),$node->getAttribute('roles'),$node->getAttribute('verb'),$node->getAttribute('ip')); } $this->_rules=array_merge($rules,$this->_rules); } @@ -658,16 +691,14 @@ class TPageConfiguration extends TComponent if(($pagesNode=$dom->getElementByTagName('pages'))!==null) { $this->_properties=array_merge($this->_properties,$pagesNode->getAttributes()->toArray()); - if($page!==null) // at the page folder + // at the page folder + foreach($pagesNode->getElementsByTagName('page') as $node) { - foreach($pagesNode->getElementsByTagName('page') as $node) - { - $properties=$node->getAttributes(); - if(($id=$properties->itemAt('id'))===null) - throw new TConfigurationException('pageserviceconf_page_invalid',$configPath); - if(strcasecmp($id,$page)===0) - $this->_properties=array_merge($this->_properties,$properties->toArray()); - } + $properties=$node->getAttributes(); + if(($id=$properties->remove('id'))===null) + throw new TConfigurationException('pageserviceconf_page_invalid',$configPath); + if(($configPagePath==='' && strcasecmp($id,$this->_pagePath)===0) || ($configPath!=='' && strcasecmp($configPagePath.'.'.$id,$this->_pagePath)===0)) + $this->_properties=array_merge($this->_properties,$properties->toArray()); } } @@ -679,9 +710,9 @@ class TPageConfiguration extends TComponent if(($filePath=$node->getAttribute('file'))===null) throw new TConfigurationException('pageserviceconf_includefile_required'); if(isset($this->_includes[$filePath])) - $this->_includes[$filePath]='('.$this->_includes[$filePath].') || ('.$when.')'; + $this->_includes[$filePath]=array($configPagePath,'('.$this->_includes[$filePath][1].') || ('.$when.')'); else - $this->_includes[$filePath]=$when; + $this->_includes[$filePath]=array($configPagePath,$when); } } } -- cgit v1.2.3