|
eZ Publish
[4.0]
|
00001 <?php 00002 // 00003 // Definition of eZSSLZone class 00004 // 00005 // Created on: <12-Jul-2005 13:01:07 vs> 00006 // 00007 // ## BEGIN COPYRIGHT, LICENSE AND WARRANTY NOTICE ## 00008 // SOFTWARE NAME: eZ Publish 00009 // SOFTWARE RELEASE: 4.0.x 00010 // COPYRIGHT NOTICE: Copyright (C) 1999-2008 eZ Systems AS 00011 // SOFTWARE LICENSE: GNU General Public License v2.0 00012 // NOTICE: > 00013 // This program is free software; you can redistribute it and/or 00014 // modify it under the terms of version 2.0 of the GNU General 00015 // Public License as published by the Free Software Foundation. 00016 // 00017 // This program is distributed in the hope that it will be useful, 00018 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 // GNU General Public License for more details. 00021 // 00022 // You should have received a copy of version 2.0 of the GNU General 00023 // Public License along with this program; if not, write to the Free 00024 // Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 00025 // MA 02110-1301, USA. 00026 // 00027 // 00028 // ## END COPYRIGHT, LICENSE AND WARRANTY NOTICE ## 00029 // 00030 00031 00032 /*! 00033 \class eZSSLZone ezsslzone.php 00034 \brief SSL zones handling functionality. 00035 00036 Using functionality of this class you can mark certain parts of you site 00037 as "SSL zones". After that users will be able to access those parts only over SSL. 00038 When entering an SSL zone, user will be automatically switched to SSL. 00039 When leaving an SSL zone, user will be automatically switched to plain HTTP. 00040 Such a switch is called "access mode change" in the comments below. 00041 00042 SSL zones may be defined on either module/view basis, or on subtree basis. 00043 00044 For more details pleaase see doc/feautures/3.8/ssl_zones.txt 00045 */ 00046 00047 //include_once( 'lib/ezutils/classes/ezini.php' ); 00048 //include_once( 'lib/ezutils/classes/ezsys.php' ); 00049 00050 class eZSSLZone 00051 { 00052 const DEFAULT_SSL_PORT = 443; 00053 00054 /*! \privatesection */ 00055 00056 /** 00057 * \static 00058 * Returns true if the SSL zones functionality is enabled, false otherwise. 00059 * The result is cached in memory to save time on multiple invocations. 00060 */ 00061 static function enabled() 00062 { 00063 if ( isset( $GLOBALS['eZSSLZoneEnabled'] ) ) 00064 return $GLOBALS['eZSSLZoneEnabled']; 00065 00066 $enabled = false; 00067 $ini = eZINI::instance(); 00068 if ( $ini->hasVariable( 'SSLZoneSettings', 'SSLZones' ) ) 00069 $enabled = ( $ini->variable( 'SSLZoneSettings', 'SSLZones' ) == 'enabled' ); 00070 00071 return $GLOBALS['eZSSLZoneEnabled'] = $enabled; 00072 } 00073 00074 /** 00075 * \static 00076 */ 00077 static function cacheFileName() 00078 { 00079 //include_once( 'lib/ezutils/classes/ezsys.php' ); 00080 //include_once( 'lib/ezfile/classes/ezdir.php' ); 00081 return eZDir::path( array( eZSys::cacheDirectory(), 'ssl_zones_cache.php' ) ); 00082 } 00083 00084 /** 00085 * \static 00086 */ 00087 static function clearCacheIfNeeded() 00088 { 00089 if ( eZSSLZone::enabled() ) 00090 eZSSLZone::clearCache(); 00091 } 00092 00093 /** 00094 * \static 00095 */ 00096 static function clearCache() 00097 { 00098 eZDebugSetting::writeDebug( 'kernel-ssl-zone', 'Clearing caches.' ); 00099 00100 // clear in-memory cache 00101 unset( $GLOBALS['eZSSLZonesCachedPathStrings'] ); 00102 00103 // and remove cache file 00104 $cacheFileName = eZSSLZone::cacheFileName(); 00105 if ( file_exists( $cacheFileName ) ) 00106 unlink( $cacheFileName ); 00107 } 00108 00109 /** 00110 * \static 00111 * Load content SSL zones definitions. 00112 * Substitute URIs with corresponding path strings 00113 * (e.g. "/news" would be subsituted with "/1/2/50"). 00114 * The result is cached in memory to save time on multiple invocations. 00115 * It is also saved in a cache file that is usually updated by eZContentCacheManager along with content cache. 00116 */ 00117 static function getSSLZones() 00118 { 00119 if ( !isset( $GLOBALS['eZSSLZonesCachedPathStrings'] ) ) // if in-memory cache does not exist 00120 { 00121 $cacheFileName = eZSSLZone::cacheFileName(); 00122 $cacheDirName = eZSys::cacheDirectory(); 00123 00124 // if file cache does not exist then create it 00125 if ( !is_readable( $cacheFileName ) ) 00126 { 00127 $ini = eZINI::instance(); 00128 $sslSubtrees = $ini->variable( 'SSLZoneSettings', 'SSLSubtrees' ); 00129 00130 if ( !isset( $sslSubtrees ) || !$sslSubtrees ) 00131 return array(); 00132 00133 // if there are some content SSL zones defined in the ini settings 00134 // then let's calculate path strings for them 00135 $pathStringsArray = array(); 00136 foreach ( $sslSubtrees as $uri ) 00137 { 00138 //include_once( 'kernel/classes/ezurlaliasml.php' ); 00139 $elements = eZURLAliasML::fetchByPath( $uri ); 00140 if ( count( $elements ) == 0 ) 00141 { 00142 eZDebug::writeError( "Cannot fetch URI '$uri'", 'eZSSLZone::getSSLZones' ); 00143 continue; 00144 } 00145 $action = $elements[0]->attribute( 'action' ); 00146 if ( !preg_match( "#^eznode:(.+)#", $action, $matches ) ) 00147 { 00148 eZDebug::writeError( "Cannot decode action '$action' for URI '$uri'", 'eZSSLZone::getSSLZones' ); 00149 continue; 00150 } 00151 $nodeID = (int)$matches[1]; 00152 $node = eZContentObjectTreeNode::fetch( $nodeID ); 00153 if ( !$node instanceof eZContentObjectTreeNode ) 00154 { 00155 eZDebug::writeError( "cannot fetch node by URI '$uri'", 'eZSSLZone::getSSLZones' ); 00156 continue; 00157 } 00158 $pathStringsArray[$uri] = $node->attribute( 'path_string' ); 00159 unset( $node ); 00160 } 00161 00162 // write calculated path strings to the file 00163 if ( !file_exists( $cacheDirName ) ) 00164 { 00165 eZDir::mkdir( $cacheDirName, false, true ); 00166 } 00167 00168 $fh = fopen( $cacheFileName, 'w' ); 00169 if ( $fh ) 00170 { 00171 fwrite( $fh, "<?php\n\$pathStringsArray = " . var_export( $pathStringsArray, true ) . ";\n?>" ); 00172 fclose( $fh ); 00173 00174 $perm = eZINI::instance()->variable( 'FileSettings', 'StorageFilePermissions' ); 00175 chmod( $cacheFileName, octdec( $perm ) ); 00176 } 00177 00178 return $GLOBALS['eZSSLZonesCachedPathStrings'] = $pathStringsArray; 00179 } 00180 else // if the cache file exists 00181 { 00182 // let's read its contents and return them 00183 include_once( $cacheFileName ); // stores array to $pathStringsArray 00184 return $GLOBALS['eZSSLZonesCachedPathStrings'] = $pathStringsArray; 00185 } 00186 } 00187 00188 // else if in-memory cache already exists then return its contents 00189 $pathStringsArray = $GLOBALS['eZSSLZonesCachedPathStrings']; 00190 00191 return $pathStringsArray; 00192 } 00193 00194 /** 00195 * \static 00196 * Checks if a given module/view pair is in the given list of views. 00197 * Wildcard matching on view name is done. 00198 * 00199 * \return 2 if wildcard match occurs on the given view 00200 * 1 if exact match occurs on the given view 00201 * 0 if the view is not found in the list 00202 */ 00203 static function viewIsInArray( $module, $view, $moduleViews ) 00204 { 00205 if ( in_array( "$module/$view", $moduleViews ) ) 00206 return 2; 00207 if ( in_array( "$module/*", $moduleViews ) ) 00208 return 1; 00209 return 0; 00210 } 00211 00212 /** 00213 * \static 00214 * \return true if the view is defined as 'keep' 00215 */ 00216 static function isKeepModeView( $module, $view ) 00217 { 00218 $ini = eZINI::instance(); 00219 $viewsModes = $ini->variable( 'SSLZoneSettings', 'ModuleViewAccessMode' ); 00220 $sslViews = array_keys( $viewsModes, 'ssl' ); 00221 $keepModeViews = array_keys( $viewsModes, 'keep' ); 00222 00223 if ( eZSSLZone::viewIsInArray( $module, $view, $keepModeViews ) < 00224 eZSSLZone::viewIsInArray( $module, $view, $sslViews ) ) 00225 { 00226 eZDebugSetting::writeDebug( 'kernel-ssl-zone', 'The view cannot choose access mode itself.' ); 00227 return false; 00228 } 00229 00230 return true; 00231 } 00232 00233 /** 00234 * \static 00235 * \param $inSSL The desired access mode. 00236 * 00237 * Change access mode (HTTP/HTTPS): 00238 * - If previous mode was HHTP but $inSSL is true, we switch to SSL. 00239 * - If previous mode was SSL but $inSSL is false, we switch to HTTP. 00240 * - Otherwise no mode change is occured. 00241 * 00242 * Mode change is done by redirect to the same URL, but with changed 00243 * protocol (http/https) and TCP port. 00244 * 00245 * In case of mode change this method does not return (exit() is called). 00246 */ 00247 static function switchIfNeeded( $inSSL ) 00248 { 00249 // if it's undefined whether we should redirect we do nothing 00250 if ( !isset( $inSSL ) ) 00251 return; 00252 00253 // $nowSSl is true if current access mode is HTTPS. 00254 $nowSSL = eZSys::isSSLNow(); 00255 00256 $requestURI = eZSys::requestURI(); 00257 $indexDir = eZSys::indexDir( false ); 00258 00259 $sslZoneRedirectionURL = false; 00260 if ( $nowSSL && !$inSSL ) 00261 { 00262 // switch to plain HTTP 00263 $ini = eZINI::instance(); 00264 $host = $ini->variable( 'SiteSettings', 'SiteURL' ); 00265 $sslZoneRedirectionURL = "http://" . $host . $indexDir . $requestURI; 00266 } 00267 elseif ( !$nowSSL && $inSSL ) 00268 { 00269 // switch to HTTPS 00270 $host = eZSys::serverVariable( 'HTTP_HOST' ); 00271 $host = preg_replace( '/:\d+$/', '', $host ); 00272 00273 $ini = eZINI::instance(); 00274 $sslPort = $ini->variable( 'SiteSettings', 'SSLPort' ); 00275 $sslPortString = ( $sslPort == eZSSLZone::DEFAULT_SSL_PORT ) ? '' : ":$sslPort"; 00276 $sslZoneRedirectionURL = "https://" . $host . $sslPortString . $indexDir . $requestURI; 00277 } 00278 00279 if ( $sslZoneRedirectionURL ) // if a redirection URL is found 00280 { 00281 eZDebugSetting::writeDebug( 'kernel-ssl-zone', "redirecting to [$sslZoneRedirectionURL]" ); 00282 eZHTTPTool::redirect( $sslZoneRedirectionURL, array(), false, false ); 00283 eZExecution::cleanExit(); 00284 } 00285 } 00286 00287 /*! \publicsection */ 00288 00289 /** 00290 * \static 00291 * Check whether the given node should cause access mode change. 00292 * It it should, this method does not return. 00293 * 00294 * \see checkNode() 00295 */ 00296 static function checkNodeID( $module, $view, $nodeID ) 00297 { 00298 if ( !eZSSLZone::enabled() ) 00299 return; 00300 00301 /* If the given module/view is not in the list of 'keep mode' views, 00302 * i.e. it cannot choose access mode itself, 00303 * then do nothing. 00304 */ 00305 if ( !eZSSLZone::isKeepModeView( $module, $view ) ) 00306 return; 00307 00308 // Fetch path string for the given node. 00309 $pathStrings = eZPersistentObject::fetchObjectList( 00310 eZContentObjectTreeNode::definition(), // def 00311 array( 'path_string' ), // field_filters 00312 array( 'node_id' => $nodeID ), // conds 00313 null, // sorts 00314 null, // limit 00315 false // asObject 00316 ); 00317 00318 if ( !$pathStrings ) 00319 { 00320 eZDebug::writeError( "Node #$nodeID not found", "eZSSLZone::checkNodeID" ); 00321 return; 00322 } 00323 00324 eZSSLZone::checkNodePath( $module, $view, $pathStrings[0]['path_string'] ); 00325 } 00326 00327 /** 00328 * \static 00329 * Check whether the given node should cause access mode change. 00330 * It it should, this method does not return. 00331 */ 00332 static function checkNode( $module, $view, &$node, $redirect = true ) 00333 { 00334 if ( !eZSSLZone::enabled() ) 00335 return; 00336 00337 /* If the given module/view is not in the list of 'keep mode' views, 00338 * i.e. it cannot choose access mode itself, 00339 * then do nothing. 00340 */ 00341 if ( !$redirect && !eZSSLZone::isKeepModeView( $module, $view ) ) 00342 return; 00343 00344 //include_once( 'kernel/classes/ezcontentobjecttreenode.php' ); 00345 $pathString = $node->attribute( 'path_string' ); 00346 00347 return eZSSLZone::checkNodePath( $module, $view, $pathString, $redirect ); 00348 } 00349 00350 /** 00351 * \static 00352 * Check whether the given node should cause access mode change. 00353 * It it should, this method does not return. 00354 */ 00355 static function checkNodePath( $module, $view, $pathString, $redirect = true ) 00356 { 00357 if ( !eZSSLZone::enabled() ) 00358 return; 00359 00360 /* If the given module/view is not in the list of 'keep mode' views, 00361 * i.e. it cannot choose access mode itself, 00362 * then do nothing. 00363 */ 00364 if ( !$redirect && !eZSSLZone::isKeepModeView( $module, $view ) ) 00365 return; 00366 00367 // Decide whether the node belongs to an SSL zone or not. 00368 $sslZones = eZSSLZone::getSSLZones(); 00369 00370 $inSSLZone = false; 00371 foreach ( $sslZones as $sslZonePathString ) 00372 { 00373 if ( strpos( $pathString, $sslZonePathString ) === 0 ) 00374 { 00375 $inSSLZone = true; 00376 break; 00377 } 00378 } 00379 00380 eZDebugSetting::writeDebug( 'kernel-ssl-zone', 00381 ( $inSSLZone ? 'yes' : 'no' ), 00382 "Does the node having path $pathString belong to an SSL zone?" ); 00383 00384 if ( $redirect ) 00385 eZSSLZone::switchIfNeeded( $inSSLZone ); 00386 00387 return $inSSLZone; 00388 } 00389 00390 /** 00391 * \static 00392 * Check whether the given object should cause access mode change. 00393 * It it should, this method does not return. 00394 */ 00395 static function checkObject( $module, $view, $object ) 00396 { 00397 if ( !eZSSLZone::enabled() ) 00398 return; 00399 00400 /* If the given module/view is not in the list of 'keep mode' views, 00401 * i.e. it cannot choose access mode itself, 00402 * then do nothing. 00403 */ 00404 if ( !eZSSLZone::isKeepModeView( $module, $view ) ) 00405 return; 00406 00407 $pathStringList = eZPersistentObject::fetchObjectList( 00408 eZContentObjectTreeNode::definition(), // def 00409 array( 'path_string' ), // field_filters 00410 array( 'contentobject_id' => $object->attribute( 'id' ) ), // conds 00411 null, // sorts 00412 null, // limit 00413 false // asObject 00414 ); 00415 00416 if ( is_array( $pathStringList ) && count( $pathStringList ) ) 00417 { 00418 /* The object has some assigned nodes. 00419 * If at least one of those nodes belongs to an SSL zone, 00420 * we switch to SSL. 00421 */ 00422 00423 // "flatten" the array 00424 array_walk( $pathStringList, create_function( '&$a', '$a = $a[\'path_string\'];' ) ); 00425 } 00426 else 00427 { 00428 /* The object has no assigned nodes. 00429 * Let's work with its parent nodes. 00430 * If at least one of the parent nodes belongs to an SSL zone, 00431 * we switch to SSL. 00432 */ 00433 $pathStringList = array(); 00434 $nodes = $object->parentNodes( $object->attribute( 'current' ) ); 00435 if ( !is_array( $nodes ) ) 00436 { 00437 eZDebug::writeError( 'Object ' . $object->attribute( 'is' ) . 00438 'does not have neither assigned nor parent nodes.' ); 00439 } 00440 else 00441 { 00442 foreach( $nodes as $node ) 00443 { 00444 $pathStringList[] = $node->attribute( 'path_string' ); 00445 } 00446 } 00447 } 00448 00449 $inSSL = false; // does the object belong to an SSL zone? 00450 foreach ( $pathStringList as $pathString ) 00451 { 00452 if ( eZSSLZone::checkNodePath( $module, $view, $pathString, false ) ) 00453 { 00454 $inSSL = true; 00455 break; 00456 } 00457 } 00458 00459 eZSSLZone::switchIfNeeded( $inSSL ); 00460 } 00461 00462 /** 00463 * \static 00464 * Decide whether we should change access mode for this module view or not. 00465 * Called from index.php. 00466 */ 00467 static function checkModuleView( $module, $view ) 00468 { 00469 if ( !eZSSLZone::enabled() ) 00470 return; 00471 00472 $ini = eZINI::instance(); 00473 $viewsModes = $ini->variable( 'SSLZoneSettings', 'ModuleViewAccessMode' ); 00474 00475 $sslViews = array_keys( $viewsModes, 'ssl' ); 00476 $keepModeViews = array_keys( $viewsModes, 'keep' ); 00477 00478 $sslPriority = eZSSLZone::viewIsInArray( $module, $view, $sslViews ); 00479 $keepModePriority = eZSSLZone::viewIsInArray( $module, $view, $keepModeViews ); 00480 00481 if ( $sslPriority && $keepModePriority && $sslPriority == $keepModePriority ) 00482 { 00483 eZDebug::writeError( "Configuration error: view $module/$view is defined both as 'ssl' and 'keep'", 00484 'eZSSLZone' ); 00485 return; 00486 } 00487 00488 /* If the view belongs to the list of views we should not change access mode for, 00489 * then do nothing. 00490 * (however, the view may do access mode switch itself later) 00491 */ 00492 if ( $keepModePriority > $sslPriority ) 00493 { 00494 eZDebugSetting::writeDebug( 'kernel-ssl-zone', 'Keeping current access mode...' ); 00495 return; 00496 } 00497 00498 /* Otherwise we look if the view is in the list of SSL views, 00499 * and if it is, we switch to SSL. Else, if it's not, we switch to plain HTTP. 00500 */ 00501 $inSSL = ( $sslPriority > 0 ); 00502 00503 eZDebugSetting::writeDebug( 'kernel-ssl-zone', 00504 ( isset( $inSSL ) ? ( $inSSL?'yes':'no') : 'dunno' ), 00505 'Should we use SSL for this view?' ); 00506 00507 // Change access mode if we need to. 00508 eZSSLZone::switchIfNeeded( $inSSL ); 00509 } 00510 } 00511 ?>