MDL-58544 oauth2: Allow trusted issuers

Add a setting to each issuer that skips the email confirmation when creating and linking accounts.
This commit is contained in:
Damyon Wiese 2017-05-02 13:46:19 +08:00
parent f4a2d69631
commit 859e2033cb
9 changed files with 145 additions and 19 deletions

View File

@ -119,6 +119,10 @@ class issuer extends persistent {
$mform->addElement('checkbox', 'showonloginpage', get_string('issuershowonloginpage', 'tool_oauth2'));
$mform->addHelpButton('showonloginpage', 'issuershowonloginpage', 'tool_oauth2');
// Require confirmation email for new accounts.
$mform->addElement('advcheckbox', 'requireconfirmation', get_string('issuerrequireconfirmation', 'tool_oauth2'));
$mform->addHelpButton('requireconfirmation', 'issuerrequireconfirmation', 'tool_oauth2');
$mform->addElement('hidden', 'sortorder');
$mform->setType('sortorder', PARAM_INT);

View File

@ -81,6 +81,8 @@ $string['issuername_help'] = 'Name of the identity issuer. May be displayed on l
$string['issuername'] = 'Name';
$string['issuershowonloginpage_help'] = 'If the OpenID Connect Authentication plugin is enabled, this login issuer will be listed on the login page to allow users to log in with accounts from this issuer.';
$string['issuershowonloginpage'] = 'Show on login page.';
$string['issuerrequireconfirmation_help'] = 'Require that all users verify their email address before they can log in with OAuth. This applies to newly created accounts as part of the login process, or when an existing Moodle account is connected to an OAuth login via matching email addresses.';
$string['issuerrequireconfirmation'] = 'Require email verification';
$string['issuers'] = 'Issuers';
$string['loginissuer'] = 'Allow login';
$string['notconfigured'] = 'Not configured';

View File

@ -239,6 +239,50 @@ class api {
return true;
}
/**
* Create an account with a linked login that is already confirmed.
*
* @param array $userinfo as returned from an oauth client.
* @param \core\oauth2\issuer $issuer
* @return bool
*/
public static function create_new_confirmed_account($userinfo, $issuer) {
global $CFG, $DB;
require_once($CFG->dirroot.'/user/profile/lib.php');
require_once($CFG->dirroot.'/user/lib.php');
$user = new stdClass();
$user->username = $userinfo['username'];
$user->email = $userinfo['email'];
$user->auth = 'oauth2';
$user->mnethostid = $CFG->mnet_localhost_id;
$user->lastname = isset($userinfo['lastname']) ? $userinfo['lastname'] : '';
$user->firstname = isset($userinfo['firstname']) ? $userinfo['firstname'] : '';
$user->url = isset($userinfo['url']) ? $userinfo['url'] : '';
$user->alternatename = isset($userinfo['alternatename']) ? $userinfo['alternatename'] : '';
$user->secret = random_string(15);
$user->password = '';
// This user is confirmed.
$user->confirmed = 1;
$user->id = user_create_user($user, false, true);
// The linked account is pre-confirmed.
$record = new stdClass();
$record->issuerid = $issuer->get('id');
$record->username = $userinfo['username'];
$record->userid = $user->id;
$record->email = $userinfo['email'];
$record->confirmtoken = '';
$record->confirmtokenexpires = 0;
$linkedlogin = new linked_login(0, $record);
$linkedlogin->create();
return $user;
}
/**
* Send an email with a link to confirm creating this account.
*

View File

@ -450,15 +450,21 @@ class auth extends \auth_plugin_base {
$moodleuser = \core_user::get_user_by_email($userinfo['email']);
if (!empty($moodleuser)) {
$PAGE->set_url('/auth/oauth2/confirm-link-login.php');
$PAGE->set_context(context_system::instance());
if ($issuer->get('requireconfirmation')) {
$PAGE->set_url('/auth/oauth2/confirm-link-login.php');
$PAGE->set_context(context_system::instance());
\auth_oauth2\api::send_confirm_link_login_email($userinfo, $issuer, $moodleuser->id);
// Request to link to existing account.
$emailconfirm = get_string('emailconfirmlink', 'auth_oauth2');
$message = get_string('emailconfirmlinksent', 'auth_oauth2', $moodleuser->email);
$this->print_confirm_required($emailconfirm, $message);
exit();
\auth_oauth2\api::send_confirm_link_login_email($userinfo, $issuer, $moodleuser->id);
// Request to link to existing account.
$emailconfirm = get_string('emailconfirmlink', 'auth_oauth2');
$message = get_string('emailconfirmlinksent', 'auth_oauth2', $moodleuser->email);
$this->print_confirm_required($emailconfirm, $message);
exit();
} else {
\auth_oauth2\api::link_login($userinfo, $issuer, $moodleuser->id, true);
$userinfo = get_complete_user_data('id', $moodleuser->id);
// No redirect, we will complete this login.
}
} else {
// This is a new account.
@ -506,17 +512,25 @@ class auth extends \auth_plugin_base {
redirect(new moodle_url($CFG->httpswwwroot . '/login/index.php'));
}
$PAGE->set_url('/auth/oauth2/confirm-account.php');
$PAGE->set_context(context_system::instance());
if ($issuer->get('requireconfirmation')) {
$PAGE->set_url('/auth/oauth2/confirm-account.php');
$PAGE->set_context(context_system::instance());
// Create a new (unconfirmed account) and send an email to confirm it.
$user = \auth_oauth2\api::send_confirm_account_email($userinfo, $issuer);
// Create a new (unconfirmed account) and send an email to confirm it.
$user = \auth_oauth2\api::send_confirm_account_email($userinfo, $issuer);
$this->update_picture($user);
$emailconfirm = get_string('emailconfirm');
$message = get_string('emailconfirmsent', '', $userinfo['email']);
$this->print_confirm_required($emailconfirm, $message);
exit();
$this->update_picture($user);
$emailconfirm = get_string('emailconfirm');
$message = get_string('emailconfirmsent', '', $userinfo['email']);
$this->print_confirm_required($emailconfirm, $message);
exit();
} else {
// Create a new confirmed account.
$newuser = \auth_oauth2\api::create_new_confirmed_account($userinfo, $issuer);
$userinfo = get_complete_user_data('id', $newuser->id);
// No redirect, we will complete this login.
}
}
}

View File

@ -98,4 +98,46 @@ class auth_oauth2_external_testcase extends advanced_testcase {
$this->assertCount(1, $linkedlogins);
}
/**
* Test auto-confirming linked logins.
*/
public function test_linked_logins() {
$this->resetAfterTest();
$this->setAdminUser();
$issuer = \core\oauth2\api::create_standard_issuer('google');
$user = $this->getDataGenerator()->create_user();
$info = [];
$info['username'] = 'banana';
$info['email'] = 'banana@example.com';
\auth_oauth2\api::link_login($info, $issuer, $user->id, false);
// Try and match a user with a linked login.
$match = \auth_oauth2\api::match_username_to_user('banana', $issuer);
$this->assertEquals($user->id, $match->get('userid'));
$linkedlogins = \auth_oauth2\api::get_linked_logins($user->id, $issuer);
\auth_oauth2\api::delete_linked_login($linkedlogins[0]->get('id'));
$match = \auth_oauth2\api::match_username_to_user('banana', $issuer);
$this->assertFalse($match);
$info = [];
$info['username'] = 'apple';
$info['email'] = 'apple@example.com';
$info['firstname'] = 'Apple';
$info['lastname'] = 'Fruit';
$info['url'] = 'http://apple.com/';
$info['alternamename'] = 'Beatles';
$newuser = \auth_oauth2\api::create_new_confirmed_account($info, $issuer);
$match = \auth_oauth2\api::match_username_to_user('apple', $issuer);
$this->assertEquals($newuser->id, $match->get('userid'));
}
}

View File

@ -100,6 +100,10 @@ class issuer extends persistent {
'sortorder' => array(
'type' => PARAM_INT,
'default' => 0,
),
'requireconfirmation' => array(
'type' => PARAM_BOOL,
'default' => true
)
);
}

3
lib/db/install.xml Normal file → Executable file
View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="lib/db" VERSION="20170316" COMMENT="XMLDB file for core Moodle tables"
<XMLDB PATH="lib/db" VERSION="20170502" COMMENT="XMLDB file for core Moodle tables"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
>
@ -3502,6 +3502,7 @@
<FIELD NAME="enabled" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
<FIELD NAME="showonloginpage" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
<FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The defined sort order."/>
<FIELD NAME="requireconfirmation" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>

View File

@ -2887,5 +2887,20 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2017061301.00);
}
if ($oldversion < 2017062700.00) {
// Define field requireconfirmation to be added to oauth2_issuer.
$table = new xmldb_table('oauth2_issuer');
$field = new xmldb_field('requireconfirmation', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '1', 'sortorder');
// Conditionally launch add field requireconfirmation.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Main savepoint reached.
upgrade_main_savepoint(true, 2017062700.00);
}
return true;
}

View File

@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2017062200.00; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2017062700.00; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.