diff options
25 files changed, 540 insertions, 52 deletions
| diff --git a/.gitattributes b/.gitattributes index 9f428965..55ca9840 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2810,6 +2810,7 @@ tests/FunctionalTests/quickstart/Controls/Wizard5TestCase.php -text  tests/FunctionalTests/quickstart/Fundamentals/HangmanTestCase.php -text  tests/FunctionalTests/tickets.php -text  tests/FunctionalTests/tickets/index.php -text +tests/FunctionalTests/tickets/index700.php -text  tests/FunctionalTests/tickets/protected/application.xml -text  tests/FunctionalTests/tickets/protected/controls/down.gif -text  tests/FunctionalTests/tickets/protected/controls/up.gif -text @@ -2934,6 +2935,21 @@ tests/FunctionalTests/tickets/protected/pages/Ticket93.php -text  tests/FunctionalTests/tickets/protected/pages/ToggleTest.page -text  tests/FunctionalTests/tickets/protected/pages/config.xml -text  tests/FunctionalTests/tickets/protected/pages/hotspot.jpg -text +tests/FunctionalTests/tickets/protected700/application.xml -text +tests/FunctionalTests/tickets/protected700/common/BasePage.php -text +tests/FunctionalTests/tickets/protected700/layout/MainLayout.php -text +tests/FunctionalTests/tickets/protected700/layout/MainLayout.tpl -text +tests/FunctionalTests/tickets/protected700/pages/Home.page -text +tests/FunctionalTests/tickets/protected700/pages/UserLogin.page -text +tests/FunctionalTests/tickets/protected700/pages/UserLogin.php -text +tests/FunctionalTests/tickets/protected700/pages/admin/Home.page -text +tests/FunctionalTests/tickets/protected700/pages/admin/Home2.page -text +tests/FunctionalTests/tickets/protected700/pages/admin/config.xml -text +tests/FunctionalTests/tickets/protected700/pages/admin/users/Home.page -text +tests/FunctionalTests/tickets/protected700/pages/admin/users/Home2.page -text +tests/FunctionalTests/tickets/protected700/pages/admin/users/config.xml -text +tests/FunctionalTests/tickets/protected700/pages/config.xml -text +tests/FunctionalTests/tickets/protected700/pages/content/Home.page -text  tests/FunctionalTests/tickets/tests/Ticket121TestCase.php -text  tests/FunctionalTests/tickets/tests/Ticket163TestCase.php -text  tests/FunctionalTests/tickets/tests/Ticket169TestCase.php -text @@ -2969,6 +2985,7 @@ tests/FunctionalTests/tickets/tests/Ticket587TestCase.php -text  tests/FunctionalTests/tickets/tests/Ticket592TestCase.php -text  tests/FunctionalTests/tickets/tests/Ticket653TestCase.php -text  tests/FunctionalTests/tickets/tests/Ticket659TestCase.php -text +tests/FunctionalTests/tickets/tests/Ticket700TestCase.php -text  tests/FunctionalTests/tickets/tests/Ticket72TestCase.php -text  tests/FunctionalTests/tickets/tests/Ticket93TestCase.php -text  tests/FunctionalTests/validators.php -text @@ -18,6 +18,7 @@ ENH: Ticket#672 - ForceSecureConnection to THttpRequest (Carl)  ENH: Ticket#676 - Updated ActiveRecord Oracle Drives (Qiang)  ENH: Ticket#678 - Improved DateTimeFormatInfo performance (Stever)  ENH: Ticket#681 - TUrlMapping can construct custom URLs now (Qiang) +ENH: Ticket#692,700 - Added support to wildcard in pages configuration; added IP filters in auth rules. (Qiang)  ENH: Added THead requirement check (Qiang)  ENH: Added TOutputCache.CacheTime (Qiang)  CHG: Ticket#685 - Slashes and backslashes mixup in PradoBase (Qiang) diff --git a/demos/quickstart/protected/pages/Advanced/Auth.page b/demos/quickstart/protected/pages/Advanced/Auth.page index a306c2ae..1c89e07d 100644 --- a/demos/quickstart/protected/pages/Advanced/Auth.page +++ b/demos/quickstart/protected/pages/Advanced/Auth.page @@ -74,6 +74,14 @@ When a page request is being processed, a list of authorization rules may be ava  <p id="720559" class="block-content">
  In the above example, anonymous users will be denied from posting to <tt>PageID1</tt> and <tt>PageID2</tt>, while <tt>User1</tt> and <tt>User2</tt> and all users of role <tt>Role1</tt> can access the two pages (in both <tt>get</tt> and <tt>post</tt> methods).
  </p>
 +<com:SinceVersion Version="3.1.1" />
 +<p class="block-content">
 +Since version 3.1.1, the <tt>pages</tt> attribute in the authorization rules can take relative page paths with wildcard '*'. For example, <tt>pages="admin.Home"</tt> refers to the <tt>Home</tt> page under the <tt>admin</tt> directory, and <tt>pages="admin.*"</tt> would refer to all pages under the <tt>admin</tt> directory and subdirectories.
 +</p>
 +
 +<p class="block-content">
 +Also introduced in version 3.1.1 are IP rules. They are specified by a new attribute <tt>ip</tt> in authorization rules. The IP rules are used to determine if an authorization rule aplies to an end-user according to his IP address. One can list a few IPs together, separated by comma ','. Wildcard '*' can be used in the rules. For example, <tt>ip="192.168.0.2, 192.168.1.*"</tt> means the rule applies to users whose IP address is 192.168.0.2 or 192.168.1.*. The latter matches any host in the subnet 192.168.1.
 +</p>
  <h2 id="5504">Using <tt>TUserManager</tt></h2>
  <p id="720560" class="block-content">
 diff --git a/demos/quickstart/protected/pages/Configurations/PageConfig.page b/demos/quickstart/protected/pages/Configurations/PageConfig.page index e41019af..c52164b6 100644 --- a/demos/quickstart/protected/pages/Configurations/PageConfig.page +++ b/demos/quickstart/protected/pages/Configurations/PageConfig.page @@ -39,4 +39,9 @@ The <tt><paths></tt>, <tt><modules></tt>, <tt><parameters></tt  Complete specification of page configurations can be found in the <a href="<%~../../../../../docs/specs/config.dtd%>">DTD</a> and <a href="<%~../../../../../docs/specs/config.xsd%>">XSD</a> files.
  </p>
 +<com:SinceVersion Version="3.1.1" />
 +<p class="block-content">
 +Since version 3.1.1, the <tt>id</tt> attribute in the <page> element can be a relative page path pointing to a page in the subdirectory of the directory containing the page configuration. For example, <tt>id="admin.Home"</tt> refers to the <tt>Home</tt> page under the <tt>admin</tt> directory. This enhancement allows developers to centralize their page configurations (e.g. put all page initializations in the aplication configuration or the root page configuration.)
 +</p>
 +
  <div class="last-modified">$Id$</div></com:TContent>
\ No newline at end of file diff --git a/demos/quickstart/protected/pages/GettingStarted/NewFeatures.page b/demos/quickstart/protected/pages/GettingStarted/NewFeatures.page index be754e65..e3e5c0e4 100644 --- a/demos/quickstart/protected/pages/GettingStarted/NewFeatures.page +++ b/demos/quickstart/protected/pages/GettingStarted/NewFeatures.page @@ -13,7 +13,8 @@ This page summarizes the main new features that are introduced in each PRADO rel  <li>Added a new control <a href="?page=Controls.Captcha">TCaptcha</a> that displays a CAPTCHA to keep spammers from signing up for certain accounts online. A related validator <tt>TCaptchaValidator</tt> is also implemented.</li>  <li>Added a new control <a href="?page=Controls.Slider">TSlider</a> that displays a slider which can be used for numeric input.</li>
  <li>Added Oracle DB support to Active Record.</li>
 -<li>Added support of TDataGrid to allow grouping consecutive cells with the same content.</li>
 +<li>Added support to TDataGrid to allow grouping consecutive cells with the same content.</li>
 +<li>Added support to allow configuring page properties and authorization rules using <a href="?page=Configurations.PageConfig">relative page paths</a> in application and page configurations. Added support to allow <a href="?page=Advanced.Auth">authorization</a> based on remote host address.</li>
  </ul>
  <h2 id="8006">Version 3.1.0</h2>
 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);
  		}
  	}
  }
 diff --git a/tests/FunctionalTests/tickets/index700.php b/tests/FunctionalTests/tickets/index700.php new file mode 100644 index 00000000..d35f789f --- /dev/null +++ b/tests/FunctionalTests/tickets/index700.php @@ -0,0 +1,8 @@ +<?php + +require_once(dirname(__FILE__).'/../../../framework/prado.php'); + +$app=new TApplication('protected700/application.xml'); +$app->run(); + +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected700/application.xml b/tests/FunctionalTests/tickets/protected700/application.xml new file mode 100644 index 00000000..cb00aae3 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected700/application.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<application id="Ticket700Tests" Mode="Debug"> +<paths> +	<using namespace="Application.common.*" /> +</paths> + +<services> +	<service id="page" class="TPageService" BasePageClass="Application.common.BasePage"> +		<modules> +			<module id="users" class="System.Security.TUserManager" PasswordMode="Clear"> +				<user name="AdminUser" password="demo" /> +				<user name="NormalUser" password="demo" /> +				<role name="admin" users="AdminUser" /> +				<role name="user" users="NormalUser" /> +			</module> +			<module id="auth" class="System.Security.TAuthManager" UserManager="users" LoginPage="UserLogin" /> +		</modules> + +		<authorization> +			<allow pages="Home" users="*" /> +			<deny pages="admin.*" users="?" /> +			<deny pages="content.*" users="*" /> +			<allow users="*" /> +		</authorization> + +		<pages MasterClass="Application.layout.MainLayout" Param1="Set at app config" Param5="Set at app config"> +			<page id="Home" Param2="Set at app config" /> +			<page id="admin.Home" Param3="Set at app config" Param4="Set at app config" /> +		</pages> +	</service> +</services> +</application> diff --git a/tests/FunctionalTests/tickets/protected700/common/BasePage.php b/tests/FunctionalTests/tickets/protected700/common/BasePage.php new file mode 100644 index 00000000..1e40f754 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected700/common/BasePage.php @@ -0,0 +1,68 @@ +<?php
 +
 +class BasePage extends TPage
 +{
 +	private $_param1='default 1';
 +	private $_param2='default 2';
 +	private $_param3='default 3';
 +	private $_param4='default 4';
 +	private $_param5='default 5';
 +
 +	public function onInit($param)
 +	{
 +		parent::onInit($param);
 +		$this->Title=$this->PagePath;
 +	}
 +
 +	public function getParam1()
 +	{
 +		return $this->_param1;
 +	}
 +
 +	public function setParam1($value)
 +	{
 +		$this->_param1=$value;
 +	}
 +
 +	public function getParam2()
 +	{
 +		return $this->_param2;
 +	}
 +
 +	public function setParam2($value)
 +	{
 +		$this->_param2=$value;
 +	}
 +
 +	public function getParam3()
 +	{
 +		return $this->_param3;
 +	}
 +
 +	public function setParam3($value)
 +	{
 +		$this->_param3=$value;
 +	}
 +
 +	public function getParam4()
 +	{
 +		return $this->_param4;
 +	}
 +
 +	public function setParam4($value)
 +	{
 +		$this->_param4=$value;
 +	}
 +
 +	public function getParam5()
 +	{
 +		return $this->_param5;
 +	}
 +
 +	public function setParam5($value)
 +	{
 +		$this->_param5=$value;
 +	}
 +}
 +
 +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected700/layout/MainLayout.php b/tests/FunctionalTests/tickets/protected700/layout/MainLayout.php new file mode 100644 index 00000000..3e0a3d19 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected700/layout/MainLayout.php @@ -0,0 +1,12 @@ +<?php
 +
 +class MainLayout extends TTemplateControl
 +{
 +	public function logout($sender,$param)
 +	{
 +		$this->Application->getModule('auth')->logout();
 +		$this->Response->reload();
 +	}
 +}
 +
 +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected700/layout/MainLayout.tpl b/tests/FunctionalTests/tickets/protected700/layout/MainLayout.tpl new file mode 100644 index 00000000..acbfa0e6 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected700/layout/MainLayout.tpl @@ -0,0 +1,26 @@ +<html>
 +<com:THead />
 +<body>
 +
 +<com:TForm>
 +
 +<h1><%= $this->Page->Title %></h1>
 +
 +<com:TContentPlaceHolder ID="Main" />
 +
 +<hr/>
 +<ul>
 +<li><a id="pageHome" href="<%=$this->Service->constructUrl('Home')%>">Home</a></li>
 +<li><a id="pageAdminHome" href="<%=$this->Service->constructUrl('admin.Home')%>">admin.Home</a></li>
 +<li><a id="pageAdminHome2" href="<%=$this->Service->constructUrl('admin.Home2')%>">admin.Home2</a></li>
 +<li><a id="pageAdminUsersHome" href="<%=$this->Service->constructUrl('admin.users.Home')%>">admin.users.Home</a></li>
 +<li><a id="pageAdminUsersHome2" href="<%=$this->Service->constructUrl('admin.users.Home2')%>">admin.users.Home2</a></li>
 +<li><a id="pageContentHome" href="<%=$this->Service->constructUrl('content.Home')%>">content.Home</a></li>
 +</ul>
 +<hr/>
 +<a href="<%=$this->Service->constructUrl('UserLogin')%>">Login</a> |
 +<com:TLinkButton ID="Logout" Text="Logout" OnClick="logout" /> (<%= $this->User->Name %>)
 +</com:TForm>
 +
 +</body>
 +</html>
\ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected700/pages/Home.page b/tests/FunctionalTests/tickets/protected700/pages/Home.page new file mode 100644 index 00000000..7d1c1187 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected700/pages/Home.page @@ -0,0 +1,14 @@ +<com:TContent ID="Main">
 +
 +|Param1: <%= $this->Param1 %>|
 +<br/>
 +|Param2: <%= $this->Param2 %>|
 +<br/>
 +|Param3: <%= $this->Param3 %>|
 +<br/>
 +|Param4: <%= $this->Param4 %>|
 +<br/>
 +|Param5: <%= $this->Param5 %>|
 +<br/>
 +
 +</com:TContent>
\ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected700/pages/UserLogin.page b/tests/FunctionalTests/tickets/protected700/pages/UserLogin.page new file mode 100644 index 00000000..07d4ece9 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected700/pages/UserLogin.page @@ -0,0 +1,28 @@ +<com:TContent ID="Main" >
 +
 +	<com:TLabel
 +		ForControl="Username"
 +		Text="User Name"
 +		CssClass="label"/>
 +	<com:TTextBox ID="Username"
 +		AccessKey="u"
 +		ValidationGroup="login"
 +		CssClass="textbox"/>
 +<br/>
 +	<com:TLabel
 +		ForControl="Password"
 +		Text="Password"
 +		CssClass="label"/>
 +	<com:TTextBox ID="Password"
 +		AccessKey="p"
 +		CssClass="textbox"
 +		ValidationGroup="login"
 +		TextMode="Password"/>
 +<br/>
 +	<com:TButton ID="LoginButton"
 +		OnClick="loginButtonClicked"
 +		Text="Login"
 +		ValidationGroup="login"
 +		CssClass="button"/>
 +
 +</com:TContent>
\ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected700/pages/UserLogin.php b/tests/FunctionalTests/tickets/protected700/pages/UserLogin.php new file mode 100644 index 00000000..37258879 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected700/pages/UserLogin.php @@ -0,0 +1,13 @@ +<?php
 +
 +class UserLogin extends BasePage
 +{
 +	public function loginButtonClicked($sender,$param)
 +	{
 +		$authManager=$this->Application->getModule('auth');
 +		$authManager->login($this->Username->Text,$this->Password->Text);
 +		$this->Response->redirect($this->Service->constructUrl('Home'));
 +	}
 +}
 +
 +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected700/pages/admin/Home.page b/tests/FunctionalTests/tickets/protected700/pages/admin/Home.page new file mode 100644 index 00000000..7d1c1187 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected700/pages/admin/Home.page @@ -0,0 +1,14 @@ +<com:TContent ID="Main">
 +
 +|Param1: <%= $this->Param1 %>|
 +<br/>
 +|Param2: <%= $this->Param2 %>|
 +<br/>
 +|Param3: <%= $this->Param3 %>|
 +<br/>
 +|Param4: <%= $this->Param4 %>|
 +<br/>
 +|Param5: <%= $this->Param5 %>|
 +<br/>
 +
 +</com:TContent>
\ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected700/pages/admin/Home2.page b/tests/FunctionalTests/tickets/protected700/pages/admin/Home2.page new file mode 100644 index 00000000..7d1c1187 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected700/pages/admin/Home2.page @@ -0,0 +1,14 @@ +<com:TContent ID="Main">
 +
 +|Param1: <%= $this->Param1 %>|
 +<br/>
 +|Param2: <%= $this->Param2 %>|
 +<br/>
 +|Param3: <%= $this->Param3 %>|
 +<br/>
 +|Param4: <%= $this->Param4 %>|
 +<br/>
 +|Param5: <%= $this->Param5 %>|
 +<br/>
 +
 +</com:TContent>
\ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected700/pages/admin/config.xml b/tests/FunctionalTests/tickets/protected700/pages/admin/config.xml new file mode 100644 index 00000000..04ac6bdd --- /dev/null +++ b/tests/FunctionalTests/tickets/protected700/pages/admin/config.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?>
 +<configuration>
 +	<authorization>
 +		<allow pages="Home2" users="*" />
 +		<deny pages="users.Home" roles="user" />
 +	</authorization>
 +	<pages Param2="Set at admin">
 +		<page id="users.Home" Param1="Set at admin" Param4="Set at admin" />
 +		<page id="Home" Param3="Set at admin" />
 +	</pages>
 +</configuration>
\ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected700/pages/admin/users/Home.page b/tests/FunctionalTests/tickets/protected700/pages/admin/users/Home.page new file mode 100644 index 00000000..7d1c1187 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected700/pages/admin/users/Home.page @@ -0,0 +1,14 @@ +<com:TContent ID="Main">
 +
 +|Param1: <%= $this->Param1 %>|
 +<br/>
 +|Param2: <%= $this->Param2 %>|
 +<br/>
 +|Param3: <%= $this->Param3 %>|
 +<br/>
 +|Param4: <%= $this->Param4 %>|
 +<br/>
 +|Param5: <%= $this->Param5 %>|
 +<br/>
 +
 +</com:TContent>
\ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected700/pages/admin/users/Home2.page b/tests/FunctionalTests/tickets/protected700/pages/admin/users/Home2.page new file mode 100644 index 00000000..7d1c1187 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected700/pages/admin/users/Home2.page @@ -0,0 +1,14 @@ +<com:TContent ID="Main">
 +
 +|Param1: <%= $this->Param1 %>|
 +<br/>
 +|Param2: <%= $this->Param2 %>|
 +<br/>
 +|Param3: <%= $this->Param3 %>|
 +<br/>
 +|Param4: <%= $this->Param4 %>|
 +<br/>
 +|Param5: <%= $this->Param5 %>|
 +<br/>
 +
 +</com:TContent>
\ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected700/pages/admin/users/config.xml b/tests/FunctionalTests/tickets/protected700/pages/admin/users/config.xml new file mode 100644 index 00000000..175ea5c7 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected700/pages/admin/users/config.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?>
 +<configuration>
 +	<pages>
 +		<page id="Home" Param2="Set at admin.users" />
 +	</pages>
 +</configuration>
\ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected700/pages/config.xml b/tests/FunctionalTests/tickets/protected700/pages/config.xml new file mode 100644 index 00000000..416de43f --- /dev/null +++ b/tests/FunctionalTests/tickets/protected700/pages/config.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?>
 +<configuration>
 +	<authorization>
 +		<allow pages="content.*" users="*" />
 +	</authorization>
 +	<pages Param2="Set at root">
 +		<page id="users.Home" Param1="Set at admin" Param4="Set at admin" />
 +		<page id="Home" Param5="Set at root" />
 +	</pages>
 +</configuration>
\ No newline at end of file diff --git a/tests/FunctionalTests/tickets/protected700/pages/content/Home.page b/tests/FunctionalTests/tickets/protected700/pages/content/Home.page new file mode 100644 index 00000000..7d1c1187 --- /dev/null +++ b/tests/FunctionalTests/tickets/protected700/pages/content/Home.page @@ -0,0 +1,14 @@ +<com:TContent ID="Main">
 +
 +|Param1: <%= $this->Param1 %>|
 +<br/>
 +|Param2: <%= $this->Param2 %>|
 +<br/>
 +|Param3: <%= $this->Param3 %>|
 +<br/>
 +|Param4: <%= $this->Param4 %>|
 +<br/>
 +|Param5: <%= $this->Param5 %>|
 +<br/>
 +
 +</com:TContent>
\ No newline at end of file diff --git a/tests/FunctionalTests/tickets/tests/Ticket700TestCase.php b/tests/FunctionalTests/tickets/tests/Ticket700TestCase.php new file mode 100644 index 00000000..e7ff30b3 --- /dev/null +++ b/tests/FunctionalTests/tickets/tests/Ticket700TestCase.php @@ -0,0 +1,72 @@ +<?php
 +class Ticket700TestCase extends SeleniumTestCase
 +{
 +	function test()
 +	{
 +		// page: Home
 +		$this->open('tickets/index700.php');
 +		$this->clickAndWait('ctl0_Logout');
 +		$this->clickAndWait('pageHome');
 +		$this->assertTitle("Home");
 +		$this->assertTextPresent('|Param1: Set at app config|');
 +		$this->assertTextPresent('|Param2: Set at root|');
 +		$this->assertTextPresent('|Param3: default 3|');
 +		$this->assertTextPresent('|Param4: default 4|');
 +		$this->assertTextPresent('|Param5: Set at root|');
 +
 +		// page: admin.Home
 +		$this->clickAndWait('pageAdminHome');
 +		$this->assertTitle('UserLogin');
 +		$this->type('ctl0_Main_Username','AdminUser');
 +		$this->type('ctl0_Main_Password','demo');
 +		$this->clickAndWait('ctl0_Main_LoginButton');
 +		$this->clickAndWait('pageAdminHome');
 +		$this->assertTitle('admin.Home');
 +		$this->assertTextPresent('|Param1: Set at app config|');
 +		$this->assertTextPresent('|Param2: Set at admin|');
 +		$this->assertTextPresent('|Param3: Set at admin|');
 +		$this->assertTextPresent('|Param4: Set at app config|');
 +		$this->assertTextPresent('|Param5: Set at app config|');
 +
 +		// page: admin.Home2
 +		$this->clickAndWait('pageAdminHome2');
 +		$this->assertTitle('admin.Home2');
 +		$this->clickAndWait('ctl0_Logout');
 +		$this->clickAndWait('pageAdminHome2');
 +		$this->assertTitle('admin.Home2');
 +
 +		// page: admin.users.Home
 +		$this->clickAndWait('pageAdminUsersHome');
 +		$this->assertTitle('UserLogin');
 +		$this->type('ctl0_Main_Username','NormalUser');
 +		$this->type('ctl0_Main_Password','demo');
 +		$this->clickAndWait('ctl0_Main_LoginButton');
 +		$this->clickAndWait('pageAdminUsersHome');
 +		$this->assertTitle('UserLogin');
 +		$this->type('ctl0_Main_Username','AdminUser');
 +		$this->type('ctl0_Main_Password','demo');
 +		$this->clickAndWait('ctl0_Main_LoginButton');
 +		$this->clickAndWait('pageAdminUsersHome');
 +		$this->assertTitle('admin.users.Home');
 +		$this->assertTextPresent('|Param1: Set at admin|');
 +		$this->assertTextPresent('|Param2: Set at admin.users|');
 +		$this->assertTextPresent('|Param3: default 3|');
 +		$this->assertTextPresent('|Param4: Set at admin|');
 +		$this->assertTextPresent('|Param5: Set at app config|');
 +
 +		// page: admin.users.Home2
 +		$this->clickAndWait('pageAdminUsersHome2');
 +		$this->assertTitle('admin.users.Home2');
 +
 +		// page: content.Home
 +		$this->clickAndWait('pageContentHome');
 +		$this->assertTitle('content.Home');
 +		$this->assertTextPresent('|Param1: Set at app config|');
 +		$this->assertTextPresent('|Param2: Set at root|');
 +		$this->assertTextPresent('|Param3: default 3|');
 +		$this->assertTextPresent('|Param4: default 4|');
 +		$this->assertTextPresent('|Param5: Set at app config|');
 +		$this->clickAndWait('ctl0_Logout');
 +	}
 +}
 +?>
\ No newline at end of file | 
