Changeset 2213
- Timestamp:
- 09/10/2007 09:09:27 PM
- Files:
-
- trunk/HISTORY (modified) (1 diff)
- trunk/demos/quickstart/protected/pages/Advanced/Auth.page (modified) (1 diff)
- trunk/demos/quickstart/protected/pages/Configurations/PageConfig.page (modified) (1 diff)
- trunk/demos/quickstart/protected/pages/GettingStarted/NewFeatures.page (modified) (1 diff)
- trunk/framework/Exceptions/TErrorHandler.php (modified) (1 diff)
- trunk/framework/Security/TAuthorizationRule.php (modified) (9 diffs)
- trunk/framework/Web/Services/TPageService.php (modified) (13 diffs)
- trunk/tests/FunctionalTests/tickets/index700.php (added)
- trunk/tests/FunctionalTests/tickets/protected700 (added)
- trunk/tests/FunctionalTests/tickets/protected700/application.xml (added)
- trunk/tests/FunctionalTests/tickets/protected700/common (added)
- trunk/tests/FunctionalTests/tickets/protected700/common/BasePage.php (added)
- trunk/tests/FunctionalTests/tickets/protected700/layout (added)
- trunk/tests/FunctionalTests/tickets/protected700/layout/MainLayout.php (added)
- trunk/tests/FunctionalTests/tickets/protected700/layout/MainLayout.tpl (added)
- trunk/tests/FunctionalTests/tickets/protected700/pages (added)
- trunk/tests/FunctionalTests/tickets/protected700/pages/Home.page (added)
- trunk/tests/FunctionalTests/tickets/protected700/pages/UserLogin.page (added)
- trunk/tests/FunctionalTests/tickets/protected700/pages/UserLogin.php (added)
- trunk/tests/FunctionalTests/tickets/protected700/pages/admin (added)
- trunk/tests/FunctionalTests/tickets/protected700/pages/admin/Home.page (added)
- trunk/tests/FunctionalTests/tickets/protected700/pages/admin/Home2.page (added)
- trunk/tests/FunctionalTests/tickets/protected700/pages/admin/config.xml (added)
- trunk/tests/FunctionalTests/tickets/protected700/pages/admin/users (added)
- trunk/tests/FunctionalTests/tickets/protected700/pages/admin/users/Home.page (added)
- trunk/tests/FunctionalTests/tickets/protected700/pages/admin/users/Home2.page (added)
- trunk/tests/FunctionalTests/tickets/protected700/pages/admin/users/config.xml (added)
- trunk/tests/FunctionalTests/tickets/protected700/pages/config.xml (added)
- trunk/tests/FunctionalTests/tickets/protected700/pages/content (added)
- trunk/tests/FunctionalTests/tickets/protected700/pages/content/Home.page (added)
- trunk/tests/FunctionalTests/tickets/protected700/runtime (added)
- trunk/tests/FunctionalTests/tickets/tests/Ticket700TestCase.php (added)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/HISTORY
r2211 r2213 19 19 ENH: Ticket#678 - Improved DateTimeFormatInfo performance (Stever) 20 20 ENH: Ticket#681 - TUrlMapping can construct custom URLs now (Qiang) 21 ENH: Ticket#692,700 - Added support to wildcard in pages configuration; added IP filters in auth rules. (Qiang) 21 22 ENH: Added THead requirement check (Qiang) 22 23 ENH: Added TOutputCache.CacheTime (Qiang) trunk/demos/quickstart/protected/pages/Advanced/Auth.page
r2012 r2213 75 75 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). 76 76 </p> 77 <com:SinceVersion Version="3.1.1" /> 78 <p class="block-content"> 79 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. 80 </p> 81 82 <p class="block-content"> 83 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. 84 </p> 77 85 78 86 <h2 id="5504">Using <tt>TUserManager</tt></h2> trunk/demos/quickstart/protected/pages/Configurations/PageConfig.page
r1695 r2213 40 40 </p> 41 41 42 <com:SinceVersion Version="3.1.1" /> 43 <p class="block-content"> 44 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.) 45 </p> 46 42 47 <div class="last-modified">$Id$</div></com:TContent> trunk/demos/quickstart/protected/pages/GettingStarted/NewFeatures.page
r2211 r2213 14 14 <li>Added a new control <a href="?page=Controls.Slider">TSlider</a> that displays a slider which can be used for numeric input.</li> 15 15 <li>Added Oracle DB support to Active Record.</li> 16 <li>Added support of TDataGrid to allow grouping consecutive cells with the same content.</li> 16 <li>Added support to TDataGrid to allow grouping consecutive cells with the same content.</li> 17 <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> 17 18 </ul> 18 19 trunk/framework/Exceptions/TErrorHandler.php
r2112 r2213 301 301 // because the 1st stack level is of little use (it's in error handler) 302 302 if($exception instanceof TPhpErrorException) 303 $result= $trace[1];303 $result=isset($trace[0]['file'])?$trace[0]:$trace[1]; 304 304 else if($exception instanceof TInvalidOperationException) 305 305 { trunk/framework/Security/TAuthorizationRule.php
r2012 r2213 15 15 * TAuthorizationRule represents a single authorization rule. 16 16 * A rule is specified by an action (required), a list of users (optional), 17 * a list of roles (optional), a nd a verb(optional).17 * a list of roles (optional), a verb (optional), and a list of IP rules (optional). 18 18 * Action can be either 'allow' or 'deny'. 19 19 * Guest (anonymous, unauthenticated) users are represented by question mark '?'. … … 23 23 * Different users/roles are separated by comma ','. 24 24 * Verb can be either 'get' or 'post'. If it is absent, it means both. 25 * IP rules are separated by comma ',' and can contain wild card in the rules (e.g. '192.132.23.33, 192.122.*.*') 25 26 * 26 27 * @author Qiang Xue <qiang.xue@gmail.com> … … 48 49 private $_verb; 49 50 /** 51 * @var string IP patterns 52 */ 53 private $_ipRules; 54 /** 50 55 * @var boolean if this rule applies to everyone 51 56 */ … … 66 71 * @param string a comma separated role list 67 72 * @param string verb, can be empty, 'get', or 'post' 68 */ 69 public function __construct($action,$users,$roles,$verb='') 73 * @param string IP rules (separated by comma, can contain wild card *) 74 */ 75 public function __construct($action,$users,$roles,$verb='',$ipRules='') 70 76 { 71 77 $action=strtolower(trim($action)); … … 76 82 $this->_users=array(); 77 83 $this->_roles=array(); 84 $this->_ipRules=array(); 78 85 $this->_everyone=false; 79 86 $this->_guest=false; … … 106 113 else 107 114 throw new TInvalidDataValueException('authorizationrule_verb_invalid',$verb); 115 foreach(explode(',',$ipRules) as $ipRule) 116 { 117 if(($ipRule=trim($ipRule))!=='') 118 $this->_ipRules[]=$ipRule; 119 } 108 120 } 109 121 … … 138 150 { 139 151 return $this->_verb; 152 } 153 154 /** 155 * @return array list of IP rules. 156 * @since 3.1.1 157 */ 158 public function getIPRules() 159 { 160 return $this->_ipRules; 140 161 } 141 162 … … 172 193 if($this->_verb==='' || strcasecmp($verb,$this->_verb)===0) 173 194 { 195 if(!$this->isHostAddressMatched()) 196 return 0; 174 197 if($this->_everyone || ($this->_guest && $user->getIsGuest()) || ($this->_authenticated && !$user->getIsGuest())) 175 198 return $decision; … … 182 205 return 0; 183 206 } 207 208 private function isHostAddressMatched() 209 { 210 if(empty($this->_ipRules)) 211 return 1; 212 $ip=Prado::getApplication()->getRequest()->getUserHostAddress(); 213 foreach($this->_ipRules as $rule) 214 { 215 if($rule===$ip) 216 return 1; 217 if(strpos($rule,'*')!==false) 218 { 219 $a=explode('.',$rule); 220 $b=explode('.',$ip); 221 if(isset($a[3]) && isset($b[3])) 222 { 223 $matched=true; 224 for($i=0;$i<4;++$i) 225 { 226 if($a[$i]!==$b[i] && $a[$i]!=='*') 227 { 228 $matched=false; 229 break; 230 } 231 } 232 if($matched) 233 return 1; 234 } 235 } 236 } 237 return 0; 238 } 184 239 } 185 240 trunk/framework/Web/Services/TPageService.php
r2112 r2213 138 138 Prado::trace("Initializing TPageService",'System.Web.Services.TPageService'); 139 139 140 $pageConfig=$this->loadPageConfig($ this->getRequestedPagePath(),$config);140 $pageConfig=$this->loadPageConfig($config); 141 141 142 142 $this->initPageContext($pageConfig); … … 170 170 $this->_properties=$config->getProperties(); 171 171 $this->getApplication()->getAuthorizationRules()->mergeWith($config->getRules()); 172 $pagePath=$this->getRequestedPagePath(); 172 173 // external configurations 173 foreach($config->getExternalConfigurations() as $filePath=>$condition) 174 { 174 foreach($config->getExternalConfigurations() as $filePath=>$params) 175 { 176 list($configPagePath,$condition)=$params; 175 177 if($condition!==true) 176 178 $condition=$this->evaluateExpression($condition); … … 179 181 if(($path=Prado::getPathOfNamespace($filePath,TApplication::CONFIG_FILE_EXT))===null || !is_file($path)) 180 182 throw new TConfigurationException('pageservice_includefile_invalid',$filePath); 181 $c=new TPageConfiguration ;182 $c->loadFromFile($path, null);183 $c=new TPageConfiguration($pagePath); 184 $c->loadFromFile($path,$configPagePath); 183 185 $this->applyConfiguration($c); 184 186 } … … 201 203 /** 202 204 * Collects configuration for a page. 203 * @param string page path in the format of Path.To.Page 204 * @param TXmlElement additional configuration 205 * @param TXmlElement additional configuration specified in the application configuration 205 206 * @return TPageConfiguration 206 207 */ 207 protected function loadPageConfig($ pagePath,$config=null)208 protected function loadPageConfig($config) 208 209 { 209 210 $application=$this->getApplication(); 211 $pagePath=$this->getRequestedPagePath(); 210 212 if(($cache=$application->getCache())===null) 211 213 { 212 $pageConfig=new TPageConfiguration ;214 $pageConfig=new TPageConfiguration($pagePath); 213 215 if($config!==null) 214 $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath() );215 $pageConfig->loadFromFiles($ pagePath,$this->getBasePath());216 $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath(),''); 217 $pageConfig->loadFromFiles($this->getBasePath()); 216 218 } 217 219 else … … 259 261 if(!$configCached) 260 262 { 261 $pageConfig=new TPageConfiguration ;263 $pageConfig=new TPageConfiguration($pagePath); 262 264 if($config!==null) 263 $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath() );264 $pageConfig->loadFromFiles($ pagePath,$this->getBasePath());265 $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath(),''); 266 $pageConfig->loadFromFiles($this->getBasePath()); 265 267 $cache->set(self::CONFIG_CACHE_PREFIX.$this->getID().$pagePath,array($pageConfig,$currentTimestamp)); 266 268 } … … 518 520 */ 519 521 private $_includes=array(); 522 /** 523 * @var string the currently request page in the format of Path.To.PageName 524 */ 525 private $_pagePath=''; 526 527 /** 528 * Constructor. 529 * @param string the currently request page in the format of Path.To.PageName 530 */ 531 public function __construct($pagePath) 532 { 533 $this->_pagePath=$pagePath; 534 } 520 535 521 536 /** … … 559 574 /** 560 575 * Loads configuration for a page specified in a path format. 561 * @param string path to the page (dot-connected format)562 576 * @param string root path for pages 563 577 */ 564 public function loadFromFiles($ pagePath,$basePath)565 { 566 $paths=explode('.',$ pagePath);578 public function loadFromFiles($basePath) 579 { 580 $paths=explode('.',$this->_pagePath); 567 581 $page=array_pop($paths); 568 582 $path=$basePath; 583 $configPagePath=''; 569 584 foreach($paths as $p) 570 585 { 571 $this->loadFromFile($path.DIRECTORY_SEPARATOR.TPageService::CONFIG_FILE, null);586 $this->loadFromFile($path.DIRECTORY_SEPARATOR.TPageService::CONFIG_FILE,$configPagePath); 572 587 $path.=DIRECTORY_SEPARATOR.$p; 573 } 574 $this->loadFromFile($path.DIRECTORY_SEPARATOR.TPageService::CONFIG_FILE,$page); 588 if($configPagePath==='') 589 $configPagePath=$p; 590 else 591 $configPagePath.='.'.$p; 592 } 593 $this->loadFromFile($path.DIRECTORY_SEPARATOR.TPageService::CONFIG_FILE,$configPagePath); 575 594 $this->_rules=new TAuthorizationRuleCollection($this->_rules); 576 595 } … … 579 598 * Loads a specific config file. 580 599 * @param string config file name 581 * @param string page name, null if page is not required582 */ 583 public function loadFromFile($fname,$ page)584 { 585 Prado::trace("Loading $page withfile $fname",'System.Web.Services.TPageService');600 * @param string the page path that the config file is associated with. The page path doesn't include the page name. 601 */ 602 public function loadFromFile($fname,$configPagePath) 603 { 604 Prado::trace("Loading page configuration file $fname",'System.Web.Services.TPageService'); 586 605 if(empty($fname) || !is_file($fname)) 587 606 return; 588 607 $dom=new TXmlDocument; 589 608 if($dom->loadFromFile($fname)) 590 $this->loadFromXml($dom,dirname($fname),$ page);609 $this->loadFromXml($dom,dirname($fname),$configPagePath); 591 610 else 592 611 throw new TConfigurationException('pageserviceconf_file_invalid',$fname); … … 598 617 * and page service. 599 618 * @param TXmlElement config xml element 600 * @param string base path corresponding to this xml element601 * @param string page name, null if page is not required602 */ 603 public function loadFromXml($dom,$configPath,$ page=null)619 * @param string the directory containing this configuration 620 * @param string the page path that the config XML is associated with. The page path doesn't include the page name. 621 */ 622 public function loadFromXml($dom,$configPath,$configPagePath) 604 623 { 605 624 $this->loadApplicationConfigurationFromXml($dom,$configPath); 606 $this->loadPageConfigurationFromXml($dom,$configPath,$ page);625 $this->loadPageConfigurationFromXml($dom,$configPath,$configPagePath); 607 626 } 608 627 … … 623 642 * @param TXmlElement config xml element 624 643 * @param string base path corresponding to this xml element 625 * @param string page name, null if page is not required626 */ 627 public function loadPageConfigurationFromXml($dom,$configPath,$ page=null)644 * @param string the page path that the config XML is associated with. The page path doesn't include the page name. 645 */ 646 public function loadPageConfigurationFromXml($dom,$configPath,$configPagePath) 628 647 { 629 648 // authorization … … 633 652 foreach($authorizationNode->getElements() as $node) 634 653 { 635 $pa ges=$node->getAttribute('pages');654 $patterns=$node->getAttribute('pages'); 636 655 $ruleApplies=false; 637 if(empty($pa ges))656 if(empty($patterns)) // null or empty string 638 657 $ruleApplies=true; 639 else if($page!==null)658 else 640 659 { 641 $ps=explode(',',$pages); 642 foreach($ps as $p) 660 foreach(explode(',',$patterns) as $pattern) 643 661 { 644 if( strcasecmp($page,trim($p))===0)662 if(($pattern=trim($pattern))!=='') 645 663 { 646 $ruleApplies=true; 647 break; 664 // we know $configPagePath and $this->_pagePath 665 if($configPagePath!=='') // prepend the pattern with ConfigPagePath 666 $pattern=$configPagePath.'.'.$pattern; 667 if(strcasecmp($pattern,$this->_pagePath)===0) 668 { 669 $ruleApplies=true; 670 break; 671 } 672 if($pattern[strlen($pattern)-1]==='*') // try wildcard matching 673 { 674 $pattern=strtolower(substr($pattern,0,strlen($pattern)-1)); 675 if(strpos(strtolower($this->_pagePath),$pattern)===0) 676 { 677 $ruleApplies=true; 678 break; 679 } 680 } 648 681 } 649 682 } 650 683 } 651 684 if($ruleApplies) 652 $rules[]=new TAuthorizationRule($node->getTagName(),$node->getAttribute('users'),$node->getAttribute('roles'),$node->getAttribute('verb') );685 $rules[]=new TAuthorizationRule($node->getTagName(),$node->getAttribute('users'),$node->getAttribute('roles'),$node->getAttribute('verb'),$node->getAttribute('ip')); 653 686 } 654 687 $this->_rules=array_merge($rules,$this->_rules); … … 659 692 { 660 693 $this->_properties=array_merge($this->_properties,$pagesNode->getAttributes()->toArray()); 661 if($page!==null) // at the page folder 694 // at the page folder 695 foreach($pagesNode->getElementsByTagName('page') as $node) 662 696 { 663 foreach($pagesNode->getElementsByTagName('page') as $node) 664 { 665 $properties=$node->getAttributes(); 666 if(($id=$properties->itemAt('id'))===null) 667 throw new TConfigurationException('pageserviceconf_page_invalid',$configPath); 668 if(strcasecmp($id,$page)===0) 669 $this->_properties=array_merge($this->_properties,$properties->toArray()); 670 } 697 $properties=$node->getAttributes(); 698 if(($id=$properties->remove('id'))===null) 699 throw new TConfigurationException('pageserviceconf_page_invalid',$configPath); 700 if(($configPagePath==='' && strcasecmp($id,$this->_pagePath)===0) || ($configPath!=='' && strcasecmp($configPagePath.'.'.$id,$this->_pagePath)===0)) 701 $this->_properties=array_merge($this->_properties,$properties->toArray()); 671 702 } 672 703 } … … 680 711 throw new TConfigurationException('pageserviceconf_includefile_required'); 681 712 if(isset($this->_includes[$filePath])) 682 $this->_includes[$filePath]= '('.$this->_includes[$filePath].') || ('.$when.')';713 $this->_includes[$filePath]=array($configPagePath,'('.$this->_includes[$filePath][1].') || ('.$when.')'); 683 714 else 684 $this->_includes[$filePath]= $when;715 $this->_includes[$filePath]=array($configPagePath,$when); 685 716 } 686 717 }
