summaryrefslogtreecommitdiff
path: root/framework
diff options
context:
space:
mode:
Diffstat (limited to 'framework')
-rw-r--r--framework/Exceptions/TErrorHandler.php2
-rw-r--r--framework/Security/TAuthorizationRule.php59
-rw-r--r--framework/Web/Services/TPageService.php127
3 files changed, 137 insertions, 51 deletions
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 <qiang.xue@gmail.com>
* @version $Id$
@@ -47,6 +48,10 @@ class TAuthorizationRule extends TComponent
*/
private $_verb;
/**
+ * @var string IP patterns
+ */
+ private $_ipRules;
+ /**
* @var boolean if this rule applies to everyone
*/
private $_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;
+ }
}
/**
@@ -140,6 +152,15 @@ class TAuthorizationRule extends TComponent
}
/**
+ * @return array list of IP rules.
+ * @since 3.1.1
+ */
+ public function getIPRules()
+ {
+ return $this->_ipRules;
+ }
+
+ /**
* @return boolean if this rule applies to everyone
*/
public function getGuestApplied()
@@ -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);
}
}
}