From 6ebf501a378eec10f5b268650e7399b906aeb63c Mon Sep 17 00:00:00 2001 From: Brendan Heywood Date: Thu, 5 Mar 2020 15:27:22 +1100 Subject: [PATCH] Added support for IdP initiation flow #105 --- auth.php | 29 +++++++++++++++---- .../modules/saml/lib/Auth/Source/SP.php | 23 +++++++++++++++ login.php | 5 ++++ version.php | 4 +-- 4 files changed, 54 insertions(+), 7 deletions(-) diff --git a/auth.php b/auth.php index 154ac3b3a..6bcd4b5bc 100644 --- a/auth.php +++ b/auth.php @@ -27,6 +27,7 @@ global $CFG; require_once($CFG->libdir.'/authlib.php'); +require_once($CFG->dirroot.'/login/lib.php'); require_once(__DIR__.'/locallib.php'); /** @@ -538,6 +539,22 @@ public function saml_login() { $auth->requireAuth($params); $attributes = $auth->getAttributes(); + + $this->saml_login_complete($attributes); + } + + + /** + * The user has done the SAML handshake now we can log them in + * + * This is split so we can handle SP and IdP first login flows. + */ + public function saml_login_complete($attributes) { + + // @codingStandardsIgnoreStart + global $CFG, $DB, $USER, $SESSION, $saml2auth; + // @codingStandardsIgnoreEnd + if ($this->config->attrsimple) { $attributes = $this->simplify_attr($attributes); } @@ -610,7 +627,8 @@ public function saml_login() { $adminidp = false; foreach ($saml2auth->metadataentities as $idpentities) { foreach ($idpentities as $md5idpentityid => $idpentity) { - if ($SESSION->saml2idp == $md5idpentityid) { + + if (!empty($SESSION->saml2idp) && $SESSION->saml2idp == $md5idpentityid) { $adminidp = $idpentity->adminidp; break 2; } @@ -638,11 +656,12 @@ public function saml_login() { $USER->site = $CFG->wwwroot; set_moodle_cookie($USER->username); - $urltogo = core_login_get_return_url(); + $wantsurl = core_login_get_return_url(); // If we are not on the page we want, then redirect to it. - if ( qualified_me() !== $urltogo ) { - $this->log(__FUNCTION__ . " redirecting to $urltogo"); - redirect($urltogo); + if ( qualified_me() !== $wantsurl ) { + $this->log(__FUNCTION__ . " redirecting to $wantsurl"); + unset($SESSION->wantsurl); + redirect($wantsurl); exit; } else { $this->log(__FUNCTION__ . " continuing onto " . qualified_me() ); diff --git a/extlib/simplesamlphp/modules/saml/lib/Auth/Source/SP.php b/extlib/simplesamlphp/modules/saml/lib/Auth/Source/SP.php index 7b44ba98f..c5828025d 100644 --- a/extlib/simplesamlphp/modules/saml/lib/Auth/Source/SP.php +++ b/extlib/simplesamlphp/modules/saml/lib/Auth/Source/SP.php @@ -271,6 +271,21 @@ public function getIdPMetadata($entityId) { assert(is_string($entityId)); + global $saml2auth; + if ($this->idp !== null && $this->idp !== $entityId) { + foreach ($saml2auth->metadataentities as $metadataurl => $idpentities) { + if ($metadataurl == $entityId) { + foreach ($idpentities as $key => $val) { + if ($key == $this->idp) { + $this->idp = null; + } + break 2; + + } + } + } + } + if ($this->idp !== null && $this->idp !== $entityId) { throw new \SimpleSAML\Error\Exception('Cannot retrieve metadata for IdP '. var_export($entityId, true).' because it isn\'t a valid IdP for this SP.'); @@ -1119,12 +1134,20 @@ public function handleLogout($idpEntityId) */ public static function handleUnsolicitedAuth($authId, array $state, $redirectTo) { + global $SESSION, $saml2auth; + assert(is_string($authId)); assert(is_string($redirectTo)); $session = \SimpleSAML\Session::getSessionFromRequest(); $session->doLogin($authId, State::getPersistentAuthData($state)); + // Moodle hack to handle IdP unsolicited logins. + $wantsurl = (new \moodle_url($redirectTo))->out(false); + $SESSION->wantsurl = $wantsurl; + $saml2auth->saml_login_complete($state['Attributes']); + // Should never get to here. + \SimpleSAML\Utils\HTTP::redirectUntrustedURL($redirectTo); } diff --git a/login.php b/login.php index 6353b6f96..0de4af4c0 100644 --- a/login.php +++ b/login.php @@ -31,6 +31,11 @@ require_once(__DIR__ . '/../../config.php'); require('setup.php'); +$wantsurl = optional_param('wantsurl', '', PARAM_LOCALURL); // Overrides $SESSION->wantsurl if given. +if ($wantsurl !== '') { + $SESSION->wantsurl = (new moodle_url($wantsurl))->out(false); +} + // Crap for hash anchor handling. $saml2auth->saml_login(); diff --git a/version.php b/version.php index 7108517fe..764d22ce4 100644 --- a/version.php +++ b/version.php @@ -24,8 +24,8 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2020022102; // The current plugin version (Date: YYYYMMDDXX). -$plugin->release = 2020022102; // Match release exactly to version. +$plugin->version = 2020030500; // The current plugin version (Date: YYYYMMDDXX). +$plugin->release = 2020030500; // Match release exactly to version. $plugin->requires = 2017051509; // Requires PHP 7, 2017051509 = T12. M3.3 // Strictly we require either Moodle 3.5 OR // we require Totara 3.3, but the version number