Skip to content

Commit

Permalink
Merge branch 'wip-82914-m311' into MOODLE_311_STABLE
Browse files Browse the repository at this point in the history
  • Loading branch information
weilai-irl committed Jun 14, 2022
2 parents e297ef9 + 1e676a4 commit b7201fb
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 35 deletions.
6 changes: 5 additions & 1 deletion classes/adminsetting/auth_oidc_admin_setting_redirecturi.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,18 @@
* Displays the redirect URI for easier config.
*/
class auth_oidc_admin_setting_redirecturi extends \admin_setting {
private $url;

/**
* Constructor.
*
* @param $name
* @param $heading
* @param $description
*/
public function __construct($name, $heading, $description) {
public function __construct($name, $heading, $description, $url) {
$this->nosave = true;
$this->url = $url;
parent::__construct($name, $heading, $description, '');
}

Expand Down Expand Up @@ -82,6 +85,7 @@ public function write_setting($data) {
*/
public function output_html($data, $query = '') {
$redirecturl = utils::get_redirecturl();
$redirecturl = $this->url;
$html = \html_writer::tag('h5', $redirecturl);
return format_admin_setting($this, $this->visiblename, $html, $this->description, true, '', null, $query);
}
Expand Down
49 changes: 27 additions & 22 deletions classes/loginflow/authcode.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@

namespace auth_oidc\loginflow;

use auth_oidc\jwt;
use auth_oidc\utils;
use moodle_exception;

defined('MOODLE_INTERNAL') || die();

Expand Down Expand Up @@ -88,7 +90,7 @@ protected function getoidcparam($name, $fallback = '') {
$valclean = preg_replace('/[^A-Za-z0-9\_\-\.\+\/\=]/i', '', $val);
if ($valclean !== $val) {
utils::debug('Authorization error.', 'authcode::cleanoidcparam', $name);
throw new \moodle_exception('errorauthgeneral', 'auth_oidc');
throw new moodle_exception('errorauthgeneral', 'auth_oidc');
}
return $valclean;
}
Expand Down Expand Up @@ -179,25 +181,27 @@ public function initiateauthrequest($promptlogin = false, array $stateparams = a
protected function handleauthresponse(array $authparams) {
global $DB, $SESSION, $USER, $CFG;

$sid = optional_param('session_state', '', PARAM_TEXT);

if (!empty($authparams['error_description'])) {
utils::debug('Authorization error.', 'authcode::handleauthresponse', $authparams);
redirect($CFG->wwwroot, get_string('errorauthgeneral', 'auth_oidc'), null, \core\output\notification::NOTIFY_ERROR);
}

if (!isset($authparams['code'])) {
utils::debug('No auth code received.', 'authcode::handleauthresponse', $authparams);
throw new \moodle_exception('errorauthnoauthcode', 'auth_oidc');
throw new moodle_exception('errorauthnoauthcode', 'auth_oidc');
}

if (!isset($authparams['state'])) {
utils::debug('No state received.', 'authcode::handleauthresponse', $authparams);
throw new \moodle_exception('errorauthunknownstate', 'auth_oidc');
throw new moodle_exception('errorauthunknownstate', 'auth_oidc');
}

// Validate and expire state.
$staterec = $DB->get_record('auth_oidc_state', ['state' => $authparams['state']]);
if (empty($staterec)) {
throw new \moodle_exception('errorauthunknownstate', 'auth_oidc');
throw new moodle_exception('errorauthunknownstate', 'auth_oidc');
}
$orignonce = $staterec->nonce;
$additionaldata = [];
Expand All @@ -214,18 +218,18 @@ protected function handleauthresponse(array $authparams) {
$client = $this->get_oidcclient();
$tokenparams = $client->tokenrequest($authparams['code']);
if (!isset($tokenparams['id_token'])) {
throw new \moodle_exception('errorauthnoidtoken', 'auth_oidc');
throw new moodle_exception('errorauthnoidtoken', 'auth_oidc');
}

// Decode and verify idtoken.
// Decode and verify ID token.
[$oidcuniqid, $idtoken] = $this->process_idtoken($tokenparams['id_token'], $orignonce);

// Check restrictions.
$passed = $this->checkrestrictions($idtoken);
if ($passed !== true && empty($additionaldata['ignorerestrictions'])) {
$errstr = 'User prevented from logging in due to restrictions.';
utils::debug($errstr, 'handleauthresponse', $idtoken);
throw new \moodle_exception('errorrestricted', 'auth_oidc');
throw new moodle_exception('errorrestricted', 'auth_oidc');
}

// This is for setting the system API user.
Expand All @@ -245,7 +249,6 @@ protected function handleauthresponse(array $authparams) {
// Check if OIDC user is already migrated.
$tokenrec = $DB->get_record('auth_oidc_token', ['oidcuniqid' => $oidcuniqid]);
if (isloggedin() && !isguestuser() && (empty($tokenrec) || (isset($USER->auth) && $USER->auth !== 'oidc'))) {

// If user is already logged in and trying to link Microsoft 365 account or use it for OIDC.
// Check if that Microsoft 365 account already exists in moodle.
$userrec = $DB->count_records_sql('SELECT COUNT(*)
Expand All @@ -260,7 +263,7 @@ protected function handleauthresponse(array $authparams) {
} else if ($additionaldata['redirect'] == '/local/o365/ucp.php') {
$redirect = $additionaldata['redirect'].'?action=connection&o365accountconnected=true';
} else {
throw new \moodle_exception('errorinvalidredirect_message', 'auth_oidc');
throw new moodle_exception('errorinvalidredirect_message', 'auth_oidc');
}
redirect(new \moodle_url($redirect));
}
Expand All @@ -276,6 +279,9 @@ protected function handleauthresponse(array $authparams) {
} else {
// Otherwise it's a user logging in normally with OIDC.
$this->handlelogin($oidcuniqid, $authparams, $tokenparams, $idtoken);
if ($USER->id && $DB->record_exists('auth_oidc_token', ['userid' => $USER->id])) {
$DB->set_field('auth_oidc_token', 'sid', $sid, ['userid' => $USER->id]);
}
redirect(core_login_get_return_url());
}
}
Expand All @@ -284,9 +290,9 @@ protected function handleauthresponse(array $authparams) {
* Handle a user migration event.
*
* @param string $oidcuniqid A unique identifier for the user.
* @param array $authparams Paramteres receieved from the auth request.
* @param array $authparams Parameters received from the auth request.
* @param array $tokenparams Parameters received from the token request.
* @param \auth_oidc\jwt $idtoken A JWT object representing the received id_token.
* @param jwt $idtoken A JWT object representing the received id_token.
* @param bool $connectiononly Whether to just connect the user (true), or to connect and change login method (false).
*/
protected function handlemigration($oidcuniqid, $authparams, $tokenparams, $idtoken, $connectiononly = false) {
Expand All @@ -312,7 +318,7 @@ protected function handlemigration($oidcuniqid, $authparams, $tokenparams, $idto
return true;
} else {
// OIDC user connected to user that is not us. Can't continue.
throw new \moodle_exception('errorauthuserconnectedtodifferent', 'auth_oidc');
throw new moodle_exception('errorauthuserconnectedtodifferent', 'auth_oidc');
}
}
}
Expand All @@ -331,7 +337,7 @@ protected function handlemigration($oidcuniqid, $authparams, $tokenparams, $idto
$this->updatetoken($tokenrec->id, $authparams, $tokenparams);
return true;
} else {
throw new \moodle_exception('errorauthuseralreadyconnected', 'auth_oidc');
throw new moodle_exception('errorauthuseralreadyconnected', 'auth_oidc');
}
}

Expand Down Expand Up @@ -418,18 +424,18 @@ protected function check_objects($oidcuniqid, $username) {
* Handle a login event.
*
* @param string $oidcuniqid A unique identifier for the user.
* @param array $authparams Parameters receieved from the auth request.
* @param array $authparams Parameters received from the auth request.
* @param array $tokenparams Parameters received from the token request.
* @param \auth_oidc\jwt $idtoken A JWT object representing the received id_token.
* @param jwt $idtoken A JWT object representing the received id_token.
*/
protected function handlelogin($oidcuniqid, $authparams, $tokenparams, $idtoken) {
protected function handlelogin(string $oidcuniqid, array $authparams, array $tokenparams, jwt $idtoken) {
global $DB, $CFG;

$tokenrec = $DB->get_record('auth_oidc_token', ['oidcuniqid' => $oidcuniqid]);

// Do not continue if auth plugin is not enabled.
if (!is_enabled_auth('oidc')) {
throw new \moodle_exception('erroroidcnotenabled', 'auth_oidc', null, null, '1');
throw new moodle_exception('erroroidcnotenabled', 'auth_oidc', null, null, '1');
}

if (!empty($tokenrec)) {
Expand Down Expand Up @@ -466,10 +472,9 @@ protected function handlelogin($oidcuniqid, $authparams, $tokenparams, $idtoken)
complete_user_login($user);
} else {
// There was a problem in authenticate_user_login.
throw new \moodle_exception('errorauthgeneral', 'auth_oidc', null, null, '2');
throw new moodle_exception('errorauthgeneral', 'auth_oidc', null, null, '2');
}

return true;
} else {
/* No existing token, user not connected. Possibilities:
- Matched user.
Expand Down Expand Up @@ -501,7 +506,7 @@ protected function handlelogin($oidcuniqid, $authparams, $tokenparams, $idtoken)
if (!empty($matchedwith)) {
if ($matchedwith->auth != 'oidc') {
$matchedwith->aadupn = $username;
throw new \moodle_exception('errorusermatched', 'auth_oidc', null, $matchedwith);
throw new moodle_exception('errorusermatched', 'auth_oidc', null, $matchedwith);
}
}
$username = trim(\core_text::strtolower($username));
Expand All @@ -518,7 +523,7 @@ protected function handlelogin($oidcuniqid, $authparams, $tokenparams, $idtoken)
$eventdata = ['other' => ['username' => $username, 'reason' => $failurereason]];
$event = \core\event\user_login_failed::create($eventdata);
$event->trigger();
throw new \moodle_exception('errorauthloginfailednouser', 'auth_oidc', null, null, '1');
throw new moodle_exception('errorauthloginfailednouser', 'auth_oidc', null, null, '1');
}
}

Expand All @@ -543,7 +548,7 @@ protected function handlelogin($oidcuniqid, $authparams, $tokenparams, $idtoken)
redirect($CFG->wwwroot, get_string('errorauthgeneral', 'auth_oidc'), null, \core\output\notification::NOTIFY_ERROR);
}

return true;
}
return true;
}
}
1 change: 0 additions & 1 deletion classes/oidcclient.php
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,6 @@ protected function getnewstate($nonce, array $stateparams = array()) {
* @param array $extraparams Additional parameters to send with the OIDC request.
*/
public function authrequest($promptlogin = false, array $stateparams = array(), array $extraparams = array()) {
global $DB;
if (empty($this->clientid)) {
throw new \moodle_exception('erroroidcclientnocreds', 'auth_oidc');
}
Expand Down
15 changes: 12 additions & 3 deletions classes/utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,17 @@ public static function debug($message, $where = '', $debugdata = null) {
* @return string The redirect URL.
*/
public static function get_redirecturl() {
global $CFG;
$wwwroot = (!empty($CFG->loginhttps)) ? str_replace('http://', 'https://', $CFG->wwwroot) : $CFG->wwwroot;
return $wwwroot.'/auth/oidc/';
$redirecturl = new \moodle_url('/auth/oidc/');
return $redirecturl->out(false);
}

/**
* Get the front channel logout URL that should be set in the identity provider.
*
* @return string The redirect URL.
*/
public static function get_frontchannellogouturl() {
$logouturl = new \moodle_url('/auth/oidc/logout.php');
return $logouturl->out(false);
}
}
5 changes: 3 additions & 2 deletions db/install.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="auth/oidc/db" VERSION="20210602" COMMENT="XMLDB file for Moodle auth/oidc plugin"
<XMLDB PATH="auth/oidc/db" VERSION="20220516" COMMENT="XMLDB file for Moodle auth/oidc plugin"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
>
Expand Down Expand Up @@ -49,6 +49,7 @@
<FIELD NAME="expiry" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="token expiry"/>
<FIELD NAME="refreshtoken" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="refresh token"/>
<FIELD NAME="idtoken" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="id token"/>
<FIELD NAME="sid" TYPE="char" LENGTH="36" NOTNULL="false" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
Expand All @@ -60,4 +61,4 @@
</INDEXES>
</TABLE>
</TABLES>
</XMLDB>
</XMLDB>
14 changes: 14 additions & 0 deletions db/upgrade.php
Original file line number Diff line number Diff line change
Expand Up @@ -312,5 +312,19 @@ function xmldb_auth_oidc_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2021051701, 'auth', 'oidc');
}

if ($oldversion < 2021051721) {
// Define field sid to be added to auth_oidc_token.
$table = new xmldb_table('auth_oidc_token');
$field = new xmldb_field('sid', XMLDB_TYPE_CHAR, '36', null, null, null, null, 'idtoken');

// Conditionally launch add field sid.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}

// Oidc savepoint reached.
upgrade_plugin_savepoint(true, 2021051721, 'auth', 'oidc');
}

return true;
}
10 changes: 6 additions & 4 deletions lang/en/auth_oidc.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,14 @@
$string['cfg_userrestrictions_desc'] = 'Only allow users to log in that meet certain restrictions. <br /><b>How to use user restrictions: </b> <ul><li>Enter a <a href="https://en.wikipedia.org/wiki/Regular_expression">regular expression</a> pattern that matches the usernames of users you want to allow.</li><li>Enter one pattern per line</li><li>If you enter multiple patterns a user will be allowed if they match ANY of the patterns.</li><li>The character "/" should be escaped with "\".</li><li>If you don\'t enter any restrictions above, all users that can log in to the OpenID Connect provider will be accepted by Moodle.</li><li>Any user that does not match any entered pattern(s) will be prevented from logging in using OpenID Connect.</li></ul>';
$string['cfg_userrestrictionscasesensitive_key'] = 'User Restrictions Case Sensitive';
$string['cfg_userrestrictioncasesensitive_desc'] = 'This controls if the "/i" option in regular expression is used in the user restriction match.<br/>If enabled, all user restriction checks will be performed as with case sensitive. Note if this is disabled, any patterns on letter cases will be ignored.';
$string['cfg_signoffintegration_key'] = 'Single sign off';
$string['cfg_signoffintegration_desc'] = 'When connecting to Azure AD, if this option is enabled, when a Moodle user using the OpenID Connect authentication method signs off from Moodle, Moodle will attempt to log the user off from Microsoft 365 as well.
Note the URL of Moodle site ({$a}) needs to be added as a redirect URI in the Azure app created for Moodle Microsoft 365 integration.';
$string['cfg_signoffintegration_key'] = 'Single sign out';
$string['cfg_signoffintegration_desc'] = 'If the option is enabled, when a Moodle user connected to the configured IdP logs out of Moodle, the integration will trigger a request at the logout endpiont below, attempting to log the user off from IdP as well.<br/>
Note for integration with Microsoft Azure AD, the URL of Moodle site ({$a}) needs to be added as a redirect URI in the Azure app created for Moodle and Microsoft 365 integration.';
$string['cfg_logoutendpoint_key'] = 'Logout Endpoint';
$string['cfg_logoutendpoint_desc'] = 'The URI of the logout endpoint from your identity provider to use.';
$string['cfg_frontchannellogouturl_key'] = 'Front-channel Logout URL';
$string['cfg_frontchannellogouturl_desc'] = 'This is the URL that your IdP needs to trigger when it tries to log users out of Moodle.<br/>
For Microsoft Azure AD, the setting is called "Front-channel logout URL" and is configurable in the Azure app.';
$string['cfg_tools'] = 'Tools';
$string['cfg_cleanupoidctokens_key'] = 'Cleanup OpenID Connect Tokens';
$string['cfg_cleanupoidctokens_desc'] = 'If your users are experiencing problems logging in using their Microsoft 365 account, trying cleaning up OpenID Connect tokens. This removes stray and incomplete tokens that can cause errors. WARNING: This may interrupt logins in-process, so it\'s best to do this during downtime.';
Expand Down
47 changes: 47 additions & 0 deletions logout.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Single Sign Out end point.
*
* @package auth_oidc
* @author Lai Wei <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @copyright (C) 2014 onwards Microsoft, Inc. (http://microsoft.com/)
*/

require_once(__DIR__ . '/../../config.php');

$PAGE->set_url('/auth/oidc/logout.php');
$PAGE->set_context(context_system::instance());

$sid = optional_param('sid', '', PARAM_TEXT);

if ($sid) {
if ($authoidctokenrecord = $DB->get_record('auth_oidc_token', ['sid' => $sid])) {
if ($authoidctokenrecord->userid == $USER->id) {
$authsequence = get_enabled_auth_plugins(); // auths, in sequence
foreach($authsequence as $authname) {
$authplugin = get_auth_plugin($authname);
$authplugin->logoutpage_hook();
}

require_logout();
}
}
}

die();
8 changes: 7 additions & 1 deletion settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
use auth_oidc\adminsetting\auth_oidc_admin_setting_loginflow;
use auth_oidc\adminsetting\auth_oidc_admin_setting_redirecturi;
use auth_oidc\adminsetting\auth_oidc_admin_setting_label;
use auth_oidc\utils;

require_once($CFG->dirroot . '/auth/oidc/lib.php');

Expand Down Expand Up @@ -68,7 +69,7 @@

$configkey = new lang_string('cfg_redirecturi_key', 'auth_oidc');
$configdesc = new lang_string('cfg_redirecturi_desc', 'auth_oidc');
$settings->add(new auth_oidc_admin_setting_redirecturi('auth_oidc/redirecturi', $configkey, $configdesc));
$settings->add(new auth_oidc_admin_setting_redirecturi('auth_oidc/redirecturi', $configkey, $configdesc, utils::get_redirecturl()));

$configkey = new lang_string('cfg_forceredirect_key', 'auth_oidc');
$configdesc = new lang_string('cfg_forceredirect_desc', 'auth_oidc');
Expand Down Expand Up @@ -108,6 +109,11 @@
$configdefault = 'https://login.microsoftonline.com/common/oauth2/logout';
$settings->add(new admin_setting_configtext('auth_oidc/logouturi', $configkey, $configdesc, $configdefault, PARAM_TEXT));

$configkey = new lang_string('cfg_frontchannellogouturl_key', 'auth_oidc');
$configdesc = new lang_string('cfg_frontchannellogouturl_desc', 'auth_oidc');
$settings->add(new auth_oidc_admin_setting_redirecturi('auth_oidc/logoutendpoint', $configkey, $configdesc,
utils::get_frontchannellogouturl()));

$label = new lang_string('cfg_debugmode_key', 'auth_oidc');
$desc = new lang_string('cfg_debugmode_desc', 'auth_oidc');
$settings->add(new \admin_setting_configcheckbox('auth_oidc/debugmode', $label, $desc, '0'));
Expand Down
2 changes: 1 addition & 1 deletion version.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

defined('MOODLE_INTERNAL') || die();

$plugin->version = 2021051720;
$plugin->version = 2021051721;
$plugin->requires = 2021051700;
$plugin->release = '3.11.3';
$plugin->component = 'auth_oidc';
Expand Down

0 comments on commit b7201fb

Please sign in to comment.