diff --git a/lib/SimpleSAML/Configuration.php b/lib/SimpleSAML/Configuration.php index dbc37ebc239ea7d1a044389b3ff30ddc954f8108..25627f565133a43b6541fa5c9062f8c50c35af71 100644 --- a/lib/SimpleSAML/Configuration.php +++ b/lib/SimpleSAML/Configuration.php @@ -608,11 +608,12 @@ class Configuration implements Utils\ClearableState $path = $this->configuration[$name]; } + $path = $this->resolvePath($path); if ($path === null) { return null; } - return $this->resolvePath($path).'/'; + return $path.'/'; } diff --git a/lib/SimpleSAML/Database.php b/lib/SimpleSAML/Database.php index 685fe568f7472f898688aa67e3274e4b5635f818..8a1b67fe2b8e389266cd83e54628156b6b43313c 100644 --- a/lib/SimpleSAML/Database.php +++ b/lib/SimpleSAML/Database.php @@ -195,7 +195,7 @@ class Database * @param array $params Parameters * * @throws \Exception If an error happens while trying to execute the query. - * @return bool|\PDOStatement object + * @return \PDOStatement object */ private function query($db, $stmt, $params) { diff --git a/lib/SimpleSAML/IdP.php b/lib/SimpleSAML/IdP.php index 161ec110f9c4001d3c5dfe5231f000faa068b8e1..d9c895ad8acf9d0cc793e28086225e8d6ba13377 100644 --- a/lib/SimpleSAML/IdP.php +++ b/lib/SimpleSAML/IdP.php @@ -40,9 +40,9 @@ class IdP * We use this to support cross-protocol logout until * we implement a cross-protocol IdP. * - * @var string|null + * @var string */ - private $associationGroup = null; + private $associationGroup; /** * The configuration for this IdP. @@ -70,6 +70,7 @@ class IdP assert(is_string($id)); $this->id = $id; + $this->associationGroup = $id; $metadata = MetaDataStorageHandler::getMetadataHandler(); $globalConfig = Configuration::getInstance(); @@ -98,11 +99,7 @@ class IdP // probably no SAML 2 IdP configured for this host. Ignore the error } } else { - assert(false); - } - - if ($this->associationGroup === null) { - $this->associationGroup = $this->id; + throw new \Exception("Protocol not implemented."); } $auth = $this->config->getString('auth'); @@ -435,7 +432,6 @@ class IdP * Find the logout handler of this IdP. * * @return IdP\LogoutHandlerInterface The logout handler class. - * * @throws Exception If we cannot find a logout handler. */ public function getLogoutHandler() @@ -453,6 +449,7 @@ class IdP throw new Error\Exception('Unknown logout handler: '.var_export($logouttype, true)); } + /** @var IdP\LogoutHandlerInterface */ return new $handler($this); } @@ -505,8 +502,10 @@ class IdP $this->authSource->logout($returnTo); - $handler = $this->getLogoutHandler(); - $handler->startLogout($state, $assocId); + if ($assocId !== null) { + $handler = $this->getLogoutHandler(); + $handler->startLogout($state, $assocId); + } assert(false); } @@ -526,8 +525,11 @@ class IdP assert(is_string($assocId)); assert(is_string($relayState) || $relayState === null); + $index = strpos($assocId, ':'); + assert(is_int($index)); + $session = Session::getSessionFromRequest(); - $session->deleteData('core:idp-ssotime', $this->id.';'.substr($assocId, strpos($assocId, ':') + 1)); + $session->deleteData('core:idp-ssotime', $this->id.';'.substr($assocId, $index + 1)); $handler = $this->getLogoutHandler(); $handler->onResponse($assocId, $relayState, $error); diff --git a/lib/SimpleSAML/Logger.php b/lib/SimpleSAML/Logger.php index ab19465d6432728b1a556037c9b4ead9fb1b9fe5..1bdc018740896e44faa3a0d77d9b14cfa9d087a1 100644 --- a/lib/SimpleSAML/Logger.php +++ b/lib/SimpleSAML/Logger.php @@ -13,9 +13,14 @@ namespace SimpleSAML; class Logger { /** - * @var \SimpleSAML\Logger\LoggingHandlerInterface|false|null + * @var \SimpleSAML\Logger\LoggingHandlerInterface */ - private static $loggingHandler = null; + private static $loggingHandler; + + /** + * @var bool + */ + private static $initializing = false; /** * @var integer|null @@ -402,8 +407,7 @@ class Logger */ private static function createLoggingHandler($handler = null) { - // set to false to indicate that it is being initialized - self::$loggingHandler = false; + self::$initializing = true; // a set of known logging handlers $known_handlers = [ @@ -438,12 +442,17 @@ class Logger } $handler = $known_handlers[$handler]; } + + /** @var \SimpleSAML\Logger\LoggingHandlerInterface */ self::$loggingHandler = new $handler($config); self::$format = $config->getString('logging.format', self::$format); self::$loggingHandler->setLogFormat(self::$format); + + self::$initializing = false; } + /** * @param int $level * @param string $string @@ -452,20 +461,20 @@ class Logger */ private static function log($level, $string, $statsLog = false) { - if (self::$loggingHandler === false) { + if (self::$initializing) { // some error occurred while initializing logging self::defer($level, $string, $statsLog); return; } elseif (php_sapi_name() === 'cli' || defined('STDIN')) { // we are being executed from the CLI, nowhere to log - if (is_null(self::$loggingHandler)) { - self::createLoggingHandler('SimpleSAML\Logger\StandardErrorLoggingHandler'); + if (!isset(self::$loggingHandler)) { + self::createLoggingHandler(\SimpleSAML\Logger\StandardErrorLoggingHandler::class); } $_SERVER['REMOTE_ADDR'] = "CLI"; if (self::$trackid === self::NO_TRACKID) { self::$trackid = 'CL'.bin2hex(openssl_random_pseudo_bytes(4)); } - } elseif (self::$loggingHandler === null) { + } elseif (!isset(self::$loggingHandler)) { // Initialize logging self::createLoggingHandler(); self::flush(); diff --git a/lib/SimpleSAML/Memcache.php b/lib/SimpleSAML/Memcache.php index 904649775459233862a753dd2c84bc10d630e893..5f6577ef578b1ef22f218898be023e8eb5e85f2a 100644 --- a/lib/SimpleSAML/Memcache.php +++ b/lib/SimpleSAML/Memcache.php @@ -26,7 +26,7 @@ class Memcache /** * Cache of the memcache servers we are using. * - * @var \Memcache[]|null + * @var \Memcache[]|\Memcached[]|null */ private static $serverGroups = null; @@ -167,7 +167,7 @@ class Memcache // store this object to all groups of memcache servers foreach (self::getMemcacheServers() as $server) { - if (self::$extension === '\memcached') { + if (self::$extension === \Memcached::class) { $server->set($key, $savedInfoSerialized, $expire); } else { $server->set($key, $savedInfoSerialized, 0, $expire); @@ -212,7 +212,7 @@ class Memcache * The timeout for contacting this server, in seconds. * The default value is 3 seconds. * - * @param \Memcache $memcache The Memcache object we should add this server to. + * @param \Memcache|\Memcached $memcache The Memcache object we should add this server to. * @param array $server An associative array with the configuration options for the server to add. * @return void * @@ -290,7 +290,7 @@ class Memcache } // add this server to the Memcache object - if (self::$extension === '\memcached') { + if ($memcache instanceof \Memcached) { $memcache->addServer($hostname, $port); } else { $memcache->addServer($hostname, $port, true, $weight, $timeout, $timeout, true); @@ -304,24 +304,27 @@ class Memcache * * @param array $group Array of servers which should be created as a group. * - * @return \Memcache A Memcache object of the servers in the group + * @return \Memcache|\Memcached A Memcache object of the servers in the group * * @throws \Exception If the servers configuration is invalid. */ private static function loadMemcacheServerGroup(array $group) { - $class = class_exists('\Memcache') ? '\Memcache' : (class_exists('\Memcached') ? '\Memcached' : false); - if (!$class) { + if (class_exists(\Memcached::class)) { + $memcache = new \Memcached(); + self::$extension = \Memcached::class; + } elseif (class_exists(\Memcache::class)) { + $memcache = new \Memcache(); + self::$extension = \Memcache::class; + } else { throw new \Exception( 'Missing Memcached implementation. You must install either the Memcache or Memcached extension.' ); - } elseif (strtolower($class) === '\memcache') { - Logger::warning("The use of PHP-extension memcache is deprecated. Please migrate to the memcached extension."); } - self::$extension = strtolower($class); - // create the \Memcache object - $memcache = new $class(); + if (self::$extension === '\memcache') { + Logger::warning("The use of PHP-extension memcache is deprecated. Please migrate to the memcached extension."); + } // iterate over all the servers in the group and add them to the Memcache object foreach ($group as $index => $server) { @@ -346,6 +349,7 @@ class Memcache self::addMemcacheServer($memcache, $server); } + /** @var \Memcache|\Memcached */ return $memcache; } @@ -354,7 +358,7 @@ class Memcache * This function gets a list of all configured memcache servers. This list is initialized based * on the content of 'memcache_store.servers' in the configuration. * - * @return \Memcache[] Array with Memcache objects. + * @return \Memcache[]|\Memcached[] Array with Memcache objects. * * @throws \Exception If the servers configuration is invalid. */ diff --git a/lib/SimpleSAML/Session.php b/lib/SimpleSAML/Session.php index 164f2c5eab1c1c348a51d363e6ea4b4796c9a988..c4a32d6aa1a158b228d46a45774422afe0aeae12 100644 --- a/lib/SimpleSAML/Session.php +++ b/lib/SimpleSAML/Session.php @@ -32,7 +32,6 @@ class Session implements \Serializable, Utils\ClearableState */ const DATA_TIMEOUT_SESSION_END = 'sessionEndTimeout'; - /** * The list of loaded session objects. * @@ -42,13 +41,12 @@ class Session implements \Serializable, Utils\ClearableState */ private static $sessions = []; - /** * This variable holds the instance of the session - Singleton approach. * * Warning: do not set the instance manually, call Session::load() instead. */ - private static $instance = null; + private static $instance; /** * The global configuration. @@ -60,7 +58,7 @@ class Session implements \Serializable, Utils\ClearableState /** * The session ID of this session. * - * @var string + * @var string|null */ private $sessionId; @@ -163,7 +161,6 @@ class Session implements \Serializable, Utils\ClearableState $this->trackid = 'TR'.bin2hex(openssl_random_pseudo_bytes(4)); Logger::setTrackId($this->trackid); $this->transient = true; - } else { // regular session $sh = SessionHandler::getSessionHandler(); @@ -312,6 +309,7 @@ class Session implements \Serializable, Utils\ClearableState } // we must have a session now, either regular or transient + /** @var \SimpleSAML\Session */ return self::$instance; } @@ -523,7 +521,7 @@ class Session implements \Serializable, Utils\ClearableState /** * Retrieve the session ID of this session. * - * @return string The session ID. + * @return string|null The session ID, or null if this is a transient session. */ public function getSessionId() { diff --git a/lib/SimpleSAML/SessionHandler.php b/lib/SimpleSAML/SessionHandler.php index 90cc5536961b4f49a38dc20b3c283a0c4adc52d7..91fc08b26df9f640200665288d395aa25a870a8a 100644 --- a/lib/SimpleSAML/SessionHandler.php +++ b/lib/SimpleSAML/SessionHandler.php @@ -21,9 +21,9 @@ abstract class SessionHandler * instance of the session handler. This variable will be NULL if * we haven't instantiated a session handler yet. * - * @var \SimpleSAML\SessionHandler|null + * @var \SimpleSAML\SessionHandler */ - protected static $sessionHandler = null; + protected static $sessionHandler; /** diff --git a/lib/SimpleSAML/SessionHandlerPHP.php b/lib/SimpleSAML/SessionHandlerPHP.php index f76433f077fc89563fd7018a8eb1eb501b1d83e0..280584f70210f32fb873c3aa58b57c3ccff145de 100644 --- a/lib/SimpleSAML/SessionHandlerPHP.php +++ b/lib/SimpleSAML/SessionHandlerPHP.php @@ -77,6 +77,7 @@ class SessionHandlerPHP extends SessionHandler if (!headers_sent()) { if (version_compare(PHP_VERSION, '7.3.0', '>=')) { + /** @psalm-suppress InvalidArgument This annotation may be removed in Psalm >=3.0.15 */ session_set_cookie_params([ 'lifetime' => $params['lifetime'], 'path' => $params['path'], @@ -168,6 +169,10 @@ class SessionHandlerPHP extends SessionHandler if (($sid_length * $sid_bits_per_char) < 128) { Logger::warning("Unsafe defaults used for sessionId generation!"); } + /** + * This annotation may be removed as soon as we start using vimeo/psalm 3.x + * @psalm-suppress TooFewArguments + */ $sessionId = session_create_id(); } else { $sessionId = bin2hex(openssl_random_pseudo_bytes(16)); @@ -358,6 +363,7 @@ class SessionHandlerPHP extends SessionHandler } if (version_compare(PHP_VERSION, '7.3.0', '>=')) { + /** @psalm-suppress InvalidArgument This annotation may be removed in Psalm >=3.0.15 */ session_set_cookie_params($cookieParams); } else { session_set_cookie_params( @@ -369,7 +375,7 @@ class SessionHandlerPHP extends SessionHandler ); } - session_id($sessionID); + session_id(strval($sessionID)); @session_start(); } } diff --git a/lib/SimpleSAML/SessionHandlerStore.php b/lib/SimpleSAML/SessionHandlerStore.php index 669a4c58f0b3db122613aa8720cce98481912021..89c7c16c45184214ffaf2051b72a969588b162fe 100644 --- a/lib/SimpleSAML/SessionHandlerStore.php +++ b/lib/SimpleSAML/SessionHandlerStore.php @@ -69,6 +69,12 @@ class SessionHandlerStore extends SessionHandlerCookie */ public function saveSession(Session $session) { + if ($session->isTransient()) { + // transient session, nothing to save + return; + } + + /** @var string $sessionId */ $sessionId = $session->getSessionId(); $config = Configuration::getInstance(); diff --git a/lib/SimpleSAML/Store.php b/lib/SimpleSAML/Store.php index c2c4fceddad748f53535e609cbf50cc59d0c367b..50d5b3162200812474a28dab988d95389658fb8c 100644 --- a/lib/SimpleSAML/Store.php +++ b/lib/SimpleSAML/Store.php @@ -16,7 +16,7 @@ abstract class Store implements Utils\ClearableState * * This is false if the data store isn't enabled, and null if we haven't attempted to initialize it. * - * @var \SimpleSAML\Store|false + * @var \SimpleSAML\Store|false|null */ private static $instance; @@ -64,6 +64,7 @@ abstract class Store implements Utils\ClearableState $c ); } + /** @var \SimpleSAML\Store|false */ self::$instance = new $className(); } diff --git a/lib/SimpleSAML/Utilities.php b/lib/SimpleSAML/Utilities.php index 33392a458a08e53577860092c8c1d3e53ebb23aa..3ea08caa743099613951ffc15b783f7b99893527 100644 --- a/lib/SimpleSAML/Utilities.php +++ b/lib/SimpleSAML/Utilities.php @@ -2,6 +2,8 @@ namespace SimpleSAML; +use SimpleSAML\Error\Error; + /** * Misc static functions that is used several places.in example parsing and id generation. * @@ -674,6 +676,7 @@ class Utilities * @param string $destination * @param array $post * @return string + * @throws Error If the current session is a transient session. */ public static function createHttpPostRedirectLink($destination, $post) { @@ -687,6 +690,10 @@ class Utilities ]; $session = \SimpleSAML\Session::getSessionFromRequest(); + if ($session->isTransient()) { + throw new Error('Cannot save data to a transient session'); + } + $session->setData('core_postdatalink', $postId, $postData); $redirInfo = base64_encode(\SimpleSAML\Utils\Crypto::aesEncrypt($session->getSessionId().':'.$postId)); diff --git a/lib/SimpleSAML/Utils/HTTP.php b/lib/SimpleSAML/Utils/HTTP.php index 2ddc64ac584ea03002ea2a355880a06fee6fd9db..2fc9f2226f8793ae7c1cc64f5265e4fa67689570 100644 --- a/lib/SimpleSAML/Utils/HTTP.php +++ b/lib/SimpleSAML/Utils/HTTP.php @@ -32,13 +32,14 @@ class HTTP $session = Session::getSessionFromRequest(); $id = self::savePOSTData($session, $destination, $data); - // get the session ID - $session_id = $session->getSessionId(); - if (is_null($session_id)) { + if ($session->isTransient()) { // this is a transient session, it is pointless to continue throw new Error\Exception('Cannot save POST data to a transient session.'); } + /** @var string $session_id */ + $session_id = $session->getSessionId(); + // encrypt the session ID and the random ID $info = base64_encode(Crypto::aesEncrypt($session_id.':'.$id)); diff --git a/modules/saml/lib/SP/LogoutStore.php b/modules/saml/lib/SP/LogoutStore.php index 6142654ac02c3c8f91eb0913f7d1c68eff1b4ad5..b4f04a7bf1237dabad7a2120e3b4aa4c1d4a2245 100644 --- a/modules/saml/lib/SP/LogoutStore.php +++ b/modules/saml/lib/SP/LogoutStore.php @@ -318,6 +318,12 @@ class LogoutStore assert(is_string($sessionIndex) || $sessionIndex === null); assert(is_int($expire)); + $session = Session::getSessionFromRequest(); + if ($session->isTransient()) { + // transient sessions are useless for this purpose, nothing to do + return; + } + if ($sessionIndex === null) { /* This IdP apparently did not include a SessionIndex, and thus probably does not * support SLO. We still want to add the session to the data store just in case @@ -346,7 +352,7 @@ class LogoutStore $sessionIndex = sha1($sessionIndex); } - $session = Session::getSessionFromRequest(); + /** @var string $sessionId */ $sessionId = $session->getSessionId(); if ($store instanceof Store\SQL) { diff --git a/psalm.xml b/psalm.xml index da1d4dbaef71e6b2ce742152994351607b4d9ba2..0c1de8a54e3cf09ac4974c7fabf7873cef904922 100644 --- a/psalm.xml +++ b/psalm.xml @@ -8,20 +8,7 @@ allowStringToStandInForClass="true" > <projectFiles> - <directory name="lib/SimpleSAML/Auth" /> - <directory name="lib/SimpleSAML/Bindings" /> - <directory name="lib/SimpleSAML/Error" /> - <directory name="lib/SimpleSAML/HTTP" /> - <directory name="lib/SimpleSAML/IdP" /> - <directory name="lib/SimpleSAML/Locale" /> - <directory name="lib/SimpleSAML/Logger" /> - <directory name="lib/SimpleSAML/Metadata" /> - <directory name="lib/SimpleSAML/Module" /> - <directory name="lib/SimpleSAML/Stats" /> - <directory name="lib/SimpleSAML/Store" /> - <directory name="lib/SimpleSAML/Utils" /> - <directory name="lib/SimpleSAML/XHTML" /> - <directory name="lib/SimpleSAML/XML" /> + <directory name="lib/SimpleSAML" /> <!-- Replaces all modules/... with this one-liner for 2.0 <directory name="modules" /> @@ -87,6 +74,7 @@ <UnresolvableInclude> <errorLevel type="suppress"> <file name="bin/*.php" /> + <file name="lib/SimpleSAML/Module.php" /> <file name="lib/SimpleSAML/XHTML/Template.php" /> <file name="modules/*/bin/*.php" /> <file name="tests/bootstrap.php" />