first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+64
View File
@@ -0,0 +1,64 @@
<?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/>.
/**
* Open ID authentication.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/authlib.php');
/**
* Plugin for oauth2 authentication.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
*/
class auth_plugin_oauth2 extends \auth_oauth2\auth {
/**
* Test the various configured Oauth2 providers.
*/
public function test_settings() {
global $OUTPUT;
$authplugin = get_auth_plugin('oauth2');
$idps = $authplugin->loginpage_idp_list('');
$templateidps = [];
if (empty($idps)) {
echo $OUTPUT->notification(get_string('noconfiguredidps', 'auth_oauth2'), 'notifyproblem');
return;
} else {
foreach ($idps as $idp) {
$idpid = $idp['url']->get_param('id');
$sesskey = $idp['url']->get_param('sesskey');
$testurl = new moodle_url('/auth/oauth2/test.php', ['id' => $idpid, 'sesskey' => $sesskey]);
$templateidps[] = ['name' => $idp['name'], 'url' => $testurl->out(), 'iconurl' => $idp['iconurl']];
}
echo $OUTPUT->render_from_template('auth_oauth2/idps', ['idps' => $templateidps]);
}
}
}
+428
View File
@@ -0,0 +1,428 @@
<?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/>.
/**
* Class for loading/storing oauth2 linked logins from the DB.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_oauth2;
use context_user;
use stdClass;
use moodle_exception;
use moodle_url;
defined('MOODLE_INTERNAL') || die();
/**
* Static list of api methods for auth oauth2 configuration.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class api {
/**
* Remove all linked logins that are using issuers that have been deleted.
*
* @param int $issuerid The issuer id of the issuer to check, or false to check all (defaults to all)
* @return boolean
*/
public static function clean_orphaned_linked_logins($issuerid = false) {
return linked_login::delete_orphaned($issuerid);
}
/**
* List linked logins
*
* Requires auth/oauth2:managelinkedlogins capability at the user context.
*
* @param int $userid (defaults to $USER->id)
* @return boolean
*/
public static function get_linked_logins($userid = false) {
global $USER;
if ($userid === false) {
$userid = $USER->id;
}
if (\core\session\manager::is_loggedinas()) {
throw new moodle_exception('notwhileloggedinas', 'auth_oauth2');
}
$context = context_user::instance($userid);
require_capability('auth/oauth2:managelinkedlogins', $context);
return linked_login::get_records(['userid' => $userid, 'confirmtoken' => '']);
}
/**
* See if there is a match for this username and issuer in the linked_login table.
*
* @param string $username as returned from an oauth client.
* @param \core\oauth2\issuer $issuer
* @return stdClass User record if found.
*/
public static function match_username_to_user($username, $issuer) {
$params = [
'issuerid' => $issuer->get('id'),
'username' => $username
];
$result = linked_login::get_record($params);
if ($result) {
$user = \core_user::get_user($result->get('userid'));
if (!empty($user) && !$user->deleted) {
return $result;
}
}
return false;
}
/**
* Link a login to this account.
*
* Requires auth/oauth2:managelinkedlogins capability at the user context.
*
* @param array $userinfo as returned from an oauth client.
* @param \core\oauth2\issuer $issuer
* @param int $userid (defaults to $USER->id)
* @param bool $skippermissions During signup we need to set this before the user is setup for capability checks.
* @return bool
*/
public static function link_login($userinfo, $issuer, $userid = false, $skippermissions = false) {
global $USER;
if ($userid === false) {
$userid = $USER->id;
}
if (linked_login::has_existing_issuer_match($issuer, $userinfo['username'])) {
throw new moodle_exception('alreadylinked', 'auth_oauth2');
}
if (\core\session\manager::is_loggedinas()) {
throw new moodle_exception('notwhileloggedinas', 'auth_oauth2');
}
$context = context_user::instance($userid);
if (!$skippermissions) {
require_capability('auth/oauth2:managelinkedlogins', $context);
}
$record = new stdClass();
$record->issuerid = $issuer->get('id');
$record->username = $userinfo['username'];
$record->userid = $userid;
$existing = linked_login::get_record((array)$record);
if ($existing) {
$existing->set('confirmtoken', '');
$existing->update();
return $existing;
}
$record->email = $userinfo['email'];
$record->confirmtoken = '';
$record->confirmtokenexpires = 0;
$linkedlogin = new linked_login(0, $record);
return $linkedlogin->create();
}
/**
* Send an email with a link to confirm linking this account.
*
* @param array $userinfo as returned from an oauth client.
* @param \core\oauth2\issuer $issuer
* @param int $userid (defaults to $USER->id)
* @return bool
*/
public static function send_confirm_link_login_email($userinfo, $issuer, $userid) {
$record = new stdClass();
$record->issuerid = $issuer->get('id');
$record->username = $userinfo['username'];
$record->userid = $userid;
if (linked_login::has_existing_issuer_match($issuer, $userinfo['username'])) {
throw new moodle_exception('alreadylinked', 'auth_oauth2');
}
$record->email = $userinfo['email'];
$record->confirmtoken = random_string(32);
$expires = new \DateTime('NOW');
$expires->add(new \DateInterval('PT30M'));
$record->confirmtokenexpires = $expires->getTimestamp();
$linkedlogin = new linked_login(0, $record);
$linkedlogin->create();
// Construct the email.
$site = get_site();
$supportuser = \core_user::get_support_user();
$user = get_complete_user_data('id', $userid);
$data = new stdClass();
$data->fullname = fullname($user);
$data->sitename = format_string($site->fullname);
$data->admin = generate_email_signoff();
$data->issuername = format_string($issuer->get('name'));
$data->linkedemail = format_string($linkedlogin->get('email'));
$subject = get_string('confirmlinkedloginemailsubject', 'auth_oauth2', format_string($site->fullname));
$params = [
'token' => $linkedlogin->get('confirmtoken'),
'userid' => $userid,
'username' => $userinfo['username'],
'issuerid' => $issuer->get('id'),
];
$confirmationurl = new moodle_url('/auth/oauth2/confirm-linkedlogin.php', $params);
$data->link = $confirmationurl->out(false);
$message = get_string('confirmlinkedloginemail', 'auth_oauth2', $data);
$data->link = $confirmationurl->out();
$messagehtml = text_to_html(get_string('confirmlinkedloginemail', 'auth_oauth2', $data), false, false, true);
$user->mailformat = 1; // Always send HTML version as well.
// Directly email rather than using the messaging system to ensure its not routed to a popup or jabber.
return email_to_user($user, $supportuser, $subject, $message, $messagehtml);
}
/**
* Look for a waiting confirmation token, and if we find a match - confirm it.
*
* @param int $userid
* @param string $username
* @param int $issuerid
* @param string $token
* @return boolean True if we linked.
*/
public static function confirm_link_login($userid, $username, $issuerid, $token) {
if (empty($token) || empty($userid) || empty($issuerid) || empty($username)) {
return false;
}
$params = [
'userid' => $userid,
'username' => $username,
'issuerid' => $issuerid,
'confirmtoken' => $token,
];
$login = linked_login::get_record($params);
if (empty($login)) {
return false;
}
$expires = $login->get('confirmtokenexpires');
if (time() > $expires) {
$login->delete();
return;
}
$login->set('confirmtokenexpires', 0);
$login->set('confirmtoken', '');
$login->update();
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->auth = 'oauth2';
$user->mnethostid = $CFG->mnet_localhost_id;
$user->secret = random_string(15);
$user->password = '';
$user->confirmed = 1; // Set the user to confirmed.
$user = self::save_user($userinfo, $user);
// 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.
*
* @param array $userinfo as returned from an oauth client.
* @param \core\oauth2\issuer $issuer
* @param int $userid (defaults to $USER->id)
* @return bool
*/
public static function send_confirm_account_email($userinfo, $issuer) {
global $CFG, $DB;
require_once($CFG->dirroot.'/user/profile/lib.php');
require_once($CFG->dirroot.'/user/lib.php');
if (linked_login::has_existing_issuer_match($issuer, $userinfo['username'])) {
throw new moodle_exception('alreadylinked', 'auth_oauth2');
}
$user = new stdClass();
$user->auth = 'oauth2';
$user->mnethostid = $CFG->mnet_localhost_id;
$user->secret = random_string(15);
$user->password = '';
$user->confirmed = 0; // The user is not yet confirmed.
$user = self::save_user($userinfo, $user);
// 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();
// Construct the email.
$site = get_site();
$supportuser = \core_user::get_support_user();
$user = get_complete_user_data('id', $user->id);
$data = new stdClass();
$data->fullname = fullname($user);
$data->sitename = format_string($site->fullname);
$data->admin = generate_email_signoff();
$subject = get_string('confirmaccountemailsubject', 'auth_oauth2', format_string($site->fullname));
$params = [
'token' => $user->secret,
'username' => $userinfo['username']
];
$confirmationurl = new moodle_url('/auth/oauth2/confirm-account.php', $params);
$data->link = $confirmationurl->out(false);
$message = get_string('confirmaccountemail', 'auth_oauth2', $data);
$data->link = $confirmationurl->out();
$messagehtml = text_to_html(get_string('confirmaccountemail', 'auth_oauth2', $data), false, false, true);
$user->mailformat = 1; // Always send HTML version as well.
// Directly email rather than using the messaging system to ensure its not routed to a popup or jabber.
email_to_user($user, $supportuser, $subject, $message, $messagehtml);
return $user;
}
/**
* Delete a users own linked login
*
* Requires auth/oauth2:managelinkedlogins capability at the user context.
*
* @param int $linkedloginid
* @return boolean
*/
public static function delete_linked_login($linkedloginid) {
global $USER;
if (\core\session\manager::is_loggedinas()) {
throw new moodle_exception('notwhileloggedinas', 'auth_oauth2');
}
$login = linked_login::get_record([
'id' => $linkedloginid,
'userid' => $USER->id,
'confirmtoken' => '',
], MUST_EXIST);
$context = context_user::instance($login->get('userid'));
require_capability('auth/oauth2:managelinkedlogins', $context);
$login->delete();
}
/**
* Delete linked logins for a user.
*
* @param \core\event\user_deleted $event
* @return boolean
*/
public static function user_deleted(\core\event\user_deleted $event) {
global $DB;
$userid = $event->objectid;
return $DB->delete_records(linked_login::TABLE, ['userid' => $userid]);
}
/**
* Is the plugin enabled.
*
* @return bool
*/
public static function is_enabled() {
return is_enabled_auth('oauth2');
}
/**
* Create a new user & update the profile fields
*
* @param array $userinfo
* @param object $user
* @return object
*/
private static function save_user(array $userinfo, object $user): object {
// Map supplied issuer user info to Moodle user fields.
$userfieldmapping = new \core\oauth2\user_field_mapping();
$userfieldlist = $userfieldmapping->get_internalfields();
$hasprofilefield = false;
foreach ($userfieldlist as $field) {
if (isset($userinfo[$field]) && $userinfo[$field]) {
$user->$field = $userinfo[$field];
// Check whether the profile fields exist or not.
$hasprofilefield = $hasprofilefield || strpos($field, \core_user\fields::PROFILE_FIELD_PREFIX) === 0;
}
}
// Create a new user.
$user->id = user_create_user($user, false, true);
// If profile fields exist then save custom profile fields data.
if ($hasprofilefield) {
profile_save_data($user);
}
return $user;
}
}
+651
View File
@@ -0,0 +1,651 @@
<?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/>.
/**
* Anobody can login with any password.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
*/
namespace auth_oauth2;
defined('MOODLE_INTERNAL') || die();
use pix_icon;
use moodle_url;
use core_text;
use context_system;
use stdClass;
use core\oauth2\issuer;
use core\oauth2\client;
require_once($CFG->libdir.'/authlib.php');
require_once($CFG->dirroot.'/user/lib.php');
require_once($CFG->dirroot.'/user/profile/lib.php');
/**
* Plugin for oauth2 authentication.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
*/
class auth extends \auth_plugin_base {
/**
* @var stdClass $userinfo The set of user info returned from the oauth handshake
*/
private static $userinfo;
/**
* @var stdClass $userpicture The url to a picture.
*/
private static $userpicture;
/**
* Constructor.
*/
public function __construct() {
$this->authtype = 'oauth2';
$this->config = get_config('auth_oauth2');
}
/**
* Returns true if the username and password work or don't exist and false
* if the user exists and the password is wrong.
*
* @param string $username The username
* @param string $password The password
* @return bool Authentication success or failure.
*/
public function user_login($username, $password) {
$cached = $this->get_static_user_info();
if (empty($cached)) {
// This means we were called as part of a normal login flow - without using oauth.
return false;
}
$verifyusername = $cached['username'];
if ($verifyusername == $username) {
return true;
}
return false;
}
/**
* We don't want to allow users setting an internal password.
*
* @return bool
*/
public function prevent_local_passwords() {
return true;
}
/**
* Returns true if this authentication plugin is 'internal'.
*
* @return bool
*/
public function is_internal() {
return false;
}
/**
* Indicates if moodle should automatically update internal user
* records with data from external sources using the information
* from auth_plugin_base::get_userinfo().
*
* @return bool true means automatically copy data from ext to user table
*/
public function is_synchronised_with_external() {
return true;
}
/**
* Returns true if this authentication plugin can change the user's
* password.
*
* @return bool
*/
public function can_change_password() {
return false;
}
/**
* Returns the URL for changing the user's pw, or empty if the default can
* be used.
*
* @return moodle_url
*/
public function change_password_url() {
return null;
}
/**
* Returns true if plugin allows resetting of internal password.
*
* @return bool
*/
public function can_reset_password() {
return false;
}
/**
* Returns true if plugin can be manually set.
*
* @return bool
*/
public function can_be_manually_set() {
return true;
}
/**
* Return the userinfo from the oauth handshake. Will only be valid
* for the logged in user.
* @param string $username
*/
public function get_userinfo($username) {
$cached = $this->get_static_user_info();
if (!empty($cached) && $cached['username'] == $username) {
return $cached;
}
return false;
}
/**
* Return a list of identity providers to display on the login page.
*
* @param string|moodle_url $wantsurl The requested URL.
* @return array List of arrays with keys url, iconurl and name.
*/
public function loginpage_idp_list($wantsurl) {
$providers = \core\oauth2\api::get_all_issuers(true);
$result = [];
if (empty($wantsurl)) {
$wantsurl = '/';
}
foreach ($providers as $idp) {
if ($idp->is_available_for_login()) {
$params = ['id' => $idp->get('id'), 'wantsurl' => $wantsurl, 'sesskey' => sesskey()];
$url = new moodle_url('/auth/oauth2/login.php', $params);
$icon = $idp->get('image');
$result[] = ['url' => $url, 'iconurl' => $icon, 'name' => $idp->get_display_name()];
}
}
return $result;
}
/**
* Statically cache the user info from the oauth handshake
* @param stdClass $userinfo
*/
private function set_static_user_info($userinfo) {
self::$userinfo = $userinfo;
}
/**
* Get the static cached user info
* @return stdClass
*/
private function get_static_user_info() {
return self::$userinfo;
}
/**
* Statically cache the user picture from the oauth handshake
* @param string $userpicture
*/
private function set_static_user_picture($userpicture) {
self::$userpicture = $userpicture;
}
/**
* Get the static cached user picture
* @return string
*/
private function get_static_user_picture() {
return self::$userpicture;
}
/**
* If this user has no picture - but we got one from oauth - set it.
* @param stdClass $user
* @return boolean True if the image was updated.
*/
private function update_picture($user) {
global $CFG, $DB, $USER;
require_once($CFG->libdir . '/filelib.php');
require_once($CFG->libdir . '/gdlib.php');
require_once($CFG->dirroot . '/user/lib.php');
$fs = get_file_storage();
$userid = $user->id;
if (!empty($user->picture)) {
return false;
}
if (!empty($CFG->enablegravatar)) {
return false;
}
$picture = $this->get_static_user_picture();
if (empty($picture)) {
return false;
}
$context = \context_user::instance($userid, MUST_EXIST);
$fs->delete_area_files($context->id, 'user', 'newicon');
$filerecord = array(
'contextid' => $context->id,
'component' => 'user',
'filearea' => 'newicon',
'itemid' => 0,
'filepath' => '/',
'filename' => 'image'
);
try {
$fs->create_file_from_string($filerecord, $picture);
} catch (\file_exception $e) {
return get_string($e->errorcode, $e->module, $e->a);
}
$iconfile = $fs->get_area_files($context->id, 'user', 'newicon', false, 'itemid', false);
// There should only be one.
$iconfile = reset($iconfile);
// Something went wrong while creating temp file - remove the uploaded file.
if (!$iconfile = $iconfile->copy_content_to_temp()) {
$fs->delete_area_files($context->id, 'user', 'newicon');
return false;
}
// Copy file to temporary location and the send it for processing icon.
$newpicture = (int) process_new_icon($context, 'user', 'icon', 0, $iconfile);
// Delete temporary file.
@unlink($iconfile);
// Remove uploaded file.
$fs->delete_area_files($context->id, 'user', 'newicon');
// Set the user's picture.
$updateuser = new stdClass();
$updateuser->id = $userid;
$updateuser->picture = $newpicture;
$USER->picture = $newpicture;
user_update_user($updateuser);
return true;
}
/**
* Update user data according to data sent by authorization server.
*
* @param array $externaldata data from authorization server
* @param stdClass $userdata Current data of the user to be updated
* @return stdClass The updated user record, or the existing one if there's nothing to be updated.
*/
private function update_user(array $externaldata, $userdata) {
$user = (object) [
'id' => $userdata->id,
];
// We can only update if the default authentication type of the user is set to OAuth2 as well. Otherwise, we might mess
// up the user data of other users that use different authentication mechanisms (e.g. linked logins).
if ($userdata->auth !== $this->authtype) {
return $userdata;
}
$allfields = array_merge($this->userfields, $this->get_custom_user_profile_fields());
// Go through each field from the external data.
foreach ($externaldata as $fieldname => $value) {
if (!in_array($fieldname, $allfields)) {
// Skip if this field doesn't belong to the list of fields that can be synced with the OAuth2 issuer.
continue;
}
$userhasfield = property_exists($userdata, $fieldname);
// Find out if it is a profile field.
$isprofilefield = strpos($fieldname, 'profile_field_') === 0;
$profilefieldname = str_replace('profile_field_', '', $fieldname);
$userhasprofilefield = $isprofilefield && array_key_exists($profilefieldname, $userdata->profile);
// Just in case this field is on the list, but not part of the user data. This shouldn't happen though.
if (!($userhasfield || $userhasprofilefield)) {
continue;
}
// Get the old value.
$oldvalue = $isprofilefield ? (string) $userdata->profile[$profilefieldname] : (string) $userdata->$fieldname;
// Get the lock configuration of the field.
if (!empty($this->config->{'field_lock_' . $fieldname})) {
$lockvalue = $this->config->{'field_lock_' . $fieldname};
} else {
$lockvalue = 'unlocked';
}
// We should update fields that meet the following criteria:
// - Lock value set to 'unlocked'; or 'unlockedifempty', given the current value is empty.
// - The value has changed.
if ($lockvalue === 'unlocked' || ($lockvalue === 'unlockedifempty' && empty($oldvalue))) {
$value = (string)$value;
if ($oldvalue !== $value) {
$user->$fieldname = $value;
}
}
}
// Update the user data.
user_update_user($user, false);
// Save user profile data.
profile_save_data($user);
// Refresh user for $USER variable.
return get_complete_user_data('id', $user->id);
}
/**
* Confirm the new user as registered.
*
* @param string $username
* @param string $confirmsecret
*/
public function user_confirm($username, $confirmsecret) {
global $DB;
$user = get_complete_user_data('username', $username);
if (!empty($user)) {
if ($user->auth != $this->authtype) {
return AUTH_CONFIRM_ERROR;
} else if ($user->secret === $confirmsecret && $user->confirmed) {
return AUTH_CONFIRM_ALREADY;
} else if ($user->secret === $confirmsecret) { // They have provided the secret key to get in.
$DB->set_field("user", "confirmed", 1, array("id" => $user->id));
return AUTH_CONFIRM_OK;
}
} else {
return AUTH_CONFIRM_ERROR;
}
}
/**
* Print a page showing that a confirm email was sent with instructions.
*
* @param string $title
* @param string $message
*/
public function print_confirm_required($title, $message) {
global $PAGE, $OUTPUT, $CFG;
$PAGE->navbar->add($title);
$PAGE->set_title($title);
$PAGE->set_heading($PAGE->course->fullname);
echo $OUTPUT->header();
notice($message, "$CFG->wwwroot/index.php");
}
/**
* Complete the login process after oauth handshake is complete.
* @param \core\oauth2\client $client
* @param string $redirecturl
* @return void Either redirects or throws an exception
*/
public function complete_login(client $client, $redirecturl) {
global $CFG, $SESSION, $PAGE;
$rawuserinfo = $client->get_raw_userinfo();
$userinfo = $client->get_userinfo();
if (!$userinfo) {
// Trigger login failed event.
$failurereason = AUTH_LOGIN_NOUSER;
$event = \core\event\user_login_failed::create(['other' => ['username' => 'unknown',
'reason' => $failurereason]]);
$event->trigger();
$errormsg = get_string('loginerror_nouserinfo', 'auth_oauth2');
$SESSION->loginerrormsg = $errormsg;
$client->log_out();
redirect(new moodle_url('/login/index.php'));
}
if (empty($userinfo['username']) || empty($userinfo['email'])) {
// Trigger login failed event.
$failurereason = AUTH_LOGIN_NOUSER;
$event = \core\event\user_login_failed::create(['other' => ['username' => 'unknown',
'reason' => $failurereason]]);
$event->trigger();
$errormsg = get_string('loginerror_userincomplete', 'auth_oauth2');
$SESSION->loginerrormsg = $errormsg;
$client->log_out();
redirect(new moodle_url('/login/index.php'));
}
$userinfo['username'] = trim(core_text::strtolower($userinfo['username']));
$oauthemail = $userinfo['email'];
// Once we get here we have the user info from oauth.
$userwasmapped = false;
// Clean and remember the picture / lang.
if (!empty($userinfo['picture'])) {
$this->set_static_user_picture($userinfo['picture']);
unset($userinfo['picture']);
}
if (!empty($userinfo['lang'])) {
$userinfo['lang'] = str_replace('-', '_', trim(core_text::strtolower($userinfo['lang'])));
if (!get_string_manager()->translation_exists($userinfo['lang'], false)) {
unset($userinfo['lang']);
}
}
$issuer = $client->get_issuer();
// First we try and find a defined mapping.
$linkedlogin = api::match_username_to_user($userinfo['username'], $issuer);
if (!empty($linkedlogin) && empty($linkedlogin->get('confirmtoken'))) {
$mappeduser = get_complete_user_data('id', $linkedlogin->get('userid'));
if ($mappeduser && $mappeduser->suspended) {
$failurereason = AUTH_LOGIN_SUSPENDED;
$event = \core\event\user_login_failed::create([
'userid' => $mappeduser->id,
'other' => [
'username' => $userinfo['username'],
'reason' => $failurereason
]
]);
$event->trigger();
$SESSION->loginerrormsg = get_string('invalidlogin');
$client->log_out();
redirect(new moodle_url('/login/index.php'));
} else if ($mappeduser && ($mappeduser->confirmed || !$issuer->get('requireconfirmation'))) {
// Update user fields.
$userinfo = $this->update_user($userinfo, $mappeduser);
$userwasmapped = true;
} else {
// Trigger login failed event.
$failurereason = AUTH_LOGIN_UNAUTHORISED;
$event = \core\event\user_login_failed::create(['other' => ['username' => $userinfo['username'],
'reason' => $failurereason]]);
$event->trigger();
$errormsg = get_string('confirmationpending', 'auth_oauth2');
$SESSION->loginerrormsg = $errormsg;
$client->log_out();
redirect(new moodle_url('/login/index.php'));
}
} else if (!empty($linkedlogin)) {
// Trigger login failed event.
$failurereason = AUTH_LOGIN_UNAUTHORISED;
$event = \core\event\user_login_failed::create(['other' => ['username' => $userinfo['username'],
'reason' => $failurereason]]);
$event->trigger();
$errormsg = get_string('confirmationpending', 'auth_oauth2');
$SESSION->loginerrormsg = $errormsg;
$client->log_out();
redirect(new moodle_url('/login/index.php'));
}
if (!$issuer->is_valid_login_domain($oauthemail)) {
// Trigger login failed event.
$failurereason = AUTH_LOGIN_UNAUTHORISED;
$event = \core\event\user_login_failed::create(['other' => ['username' => $userinfo['username'],
'reason' => $failurereason]]);
$event->trigger();
$errormsg = get_string('notloggedindebug', 'auth_oauth2', get_string('loginerror_invaliddomain', 'auth_oauth2'));
$SESSION->loginerrormsg = $errormsg;
$client->log_out();
redirect(new moodle_url('/login/index.php'));
}
if (!$userwasmapped) {
// No defined mapping - we need to see if there is an existing account with the same email.
$moodleuser = \core_user::get_user_by_email($userinfo['email']);
if (!empty($moodleuser)) {
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();
} else {
\auth_oauth2\api::link_login($userinfo, $issuer, $moodleuser->id, true);
// We dont have profile loaded on $moodleuser, so load it.
require_once($CFG->dirroot.'/user/profile/lib.php');
profile_load_custom_fields($moodleuser);
$userinfo = $this->update_user($userinfo, $moodleuser);
// No redirect, we will complete this login.
}
} else {
// This is a new account.
$exists = \core_user::get_user_by_username($userinfo['username']);
// Creating a new user?
if ($exists) {
// Trigger login failed event.
$failurereason = AUTH_LOGIN_FAILED;
$event = \core\event\user_login_failed::create(['other' => ['username' => $userinfo['username'],
'reason' => $failurereason]]);
$event->trigger();
// The username exists but the emails don't match. Refuse to continue.
$errormsg = get_string('accountexists', 'auth_oauth2');
$SESSION->loginerrormsg = $errormsg;
$client->log_out();
redirect(new moodle_url('/login/index.php'));
}
if (email_is_not_allowed($userinfo['email'])) {
// Trigger login failed event.
$failurereason = AUTH_LOGIN_FAILED;
$event = \core\event\user_login_failed::create(['other' => ['username' => $userinfo['username'],
'reason' => $failurereason]]);
$event->trigger();
// The username exists but the emails don't match. Refuse to continue.
$reason = get_string('loginerror_invaliddomain', 'auth_oauth2');
$errormsg = get_string('notloggedindebug', 'auth_oauth2', $reason);
$SESSION->loginerrormsg = $errormsg;
$client->log_out();
redirect(new moodle_url('/login/index.php'));
}
if (!empty($CFG->authpreventaccountcreation)) {
// Trigger login failed event.
$failurereason = AUTH_LOGIN_UNAUTHORISED;
$event = \core\event\user_login_failed::create(['other' => ['username' => $userinfo['username'],
'reason' => $failurereason]]);
$event->trigger();
// The username does not exist and settings prevent creating new accounts.
$reason = get_string('loginerror_cannotcreateaccounts', 'auth_oauth2');
$errormsg = get_string('notloggedindebug', 'auth_oauth2', $reason);
$SESSION->loginerrormsg = $errormsg;
$client->log_out();
redirect(new moodle_url('/login/index.php'));
}
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);
$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.
}
}
}
// We used to call authenticate_user - but that won't work if the current user has a different default authentication
// method. Since we now ALWAYS link a login - if we get to here we can directly allow the user in.
$user = (object) $userinfo;
// Add extra loggedin info.
$this->set_extrauserinfo((array)$rawuserinfo);
complete_user_login($user, $this->get_extrauserinfo());
$this->update_picture($user);
redirect($redirecturl);
}
/**
* Returns information on how the specified user can change their password.
* The password of the oauth2 accounts is not stored in Moodle.
*
* @param stdClass $user A user object
* @return string[] An array of strings with keys subject and message
*/
public function get_password_change_info(stdClass $user): array {
$site = get_site();
$data = new stdClass();
$data->firstname = $user->firstname;
$data->lastname = $user->lastname;
$data->username = $user->username;
$data->sitename = format_string($site->fullname);
$data->admin = generate_email_signoff();
$message = get_string('emailpasswordchangeinfo', 'auth_oauth2', $data);
$subject = get_string('emailpasswordchangeinfosubject', 'auth_oauth2', format_string($site->fullname));
return [
'subject' => $subject,
'message' => $message
];
}
}
+113
View File
@@ -0,0 +1,113 @@
<?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/>.
/**
* Class for loading/storing issuers from the DB.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_oauth2;
defined('MOODLE_INTERNAL') || die();
use core\persistent;
/**
* Class for loading/storing issuer from the DB
*
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class linked_login extends persistent {
const TABLE = 'auth_oauth2_linked_login';
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'issuerid' => array(
'type' => PARAM_INT
),
'userid' => array(
'type' => PARAM_INT
),
'username' => array(
'type' => PARAM_RAW
),
'email' => array(
'type' => PARAM_RAW
),
'confirmtoken' => array(
'type' => PARAM_RAW
),
'confirmtokenexpires' => array(
'type' => PARAM_INT
)
);
}
/**
* Check whether there are any valid linked accounts for this issuer
* and username combination.
*
* @param \core\oauth2\issuer $issuer The issuer
* @param string $username The username to check
*/
public static function has_existing_issuer_match(\core\oauth2\issuer $issuer, $username) {
global $DB;
$where = "issuerid = :issuerid
AND username = :username
AND (confirmtokenexpires = 0 OR confirmtokenexpires > :maxexpiry)";
$count = $DB->count_records_select(static::TABLE, $where, [
'issuerid' => $issuer->get('id'),
'username' => $username,
'maxexpiry' => (new \DateTime('NOW'))->getTimestamp(),
]);
return $count > 0;
}
/**
* Remove all linked logins that are using issuers that have been deleted.
*
* @param int $issuerid The issuer id of the issuer to check, or false to check all (defaults to all)
* @return boolean
*/
public static function delete_orphaned($issuerid = false) {
global $DB;
// Delete any linked_login entries with a issuerid
// which does not exist in the issuer table.
// In the left join, the issuer id will be null
// where a match linked_login.issuerid is not found.
$sql = "DELETE FROM {" . self::TABLE . "}
WHERE issuerid NOT IN (SELECT id FROM {" . \core\oauth2\issuer::TABLE . "})";
$params = [];
if (!empty($issuerid)) {
$sql .= ' AND issuerid = ?';
$params['issuerid'] = $issuerid;
}
return $DB->execute($sql, $params);
}
}
+96
View File
@@ -0,0 +1,96 @@
<?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/>.
/**
* Output rendering for the plugin.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_oauth2\output;
use plugin_renderer_base;
use html_table;
use html_table_cell;
use html_table_row;
use html_writer;
use auth_oauth2\linked_login;
use moodle_url;
defined('MOODLE_INTERNAL') || die();
/**
* Implements the plugin renderer
*
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class renderer extends plugin_renderer_base {
/**
* This function will render one beautiful table with all the linked_logins.
*
* @param linked_login[] $linkedlogins - list of all linked logins.
* @return string HTML to output.
*/
public function linked_logins_table($linkedlogins) {
global $CFG;
$table = new html_table();
$table->head = [
get_string('issuer', 'auth_oauth2'),
get_string('info', 'auth_oauth2'),
get_string('edit'),
];
$table->attributes['class'] = 'admintable generaltable';
$data = [];
$index = 0;
foreach ($linkedlogins as $linkedlogin) {
// Issuer.
$issuerid = $linkedlogin->get('issuerid');
$issuer = \core\oauth2\api::get_issuer($issuerid);
$issuercell = new html_table_cell(s($issuer->get('name')));
// Issuer.
$username = $linkedlogin->get('username');
$email = $linkedlogin->get('email');
$usernamecell = new html_table_cell(s($email) . ', (' . s($username) . ')');
$links = '';
// Delete.
$deleteparams = ['linkedloginid' => $linkedlogin->get('id'), 'action' => 'delete', 'sesskey' => sesskey()];
$deleteurl = new moodle_url('/auth/oauth2/linkedlogins.php', $deleteparams);
$deletelink = html_writer::link($deleteurl, $this->pix_icon('t/delete', get_string('delete')));
$links .= ' ' . $deletelink;
$editcell = new html_table_cell($links);
$row = new html_table_row([
$issuercell,
$usernamecell,
$editcell,
]);
$data[] = $row;
$index++;
}
$table->data = $data;
return html_writer::table($table);
}
}
+210
View File
@@ -0,0 +1,210 @@
<?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/>.
/**
* Privacy class for requesting user data for auth_oauth2.
*
* @package auth_oauth2
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_oauth2\privacy;
defined('MOODLE_INTERNAL') || die();
use core_privacy\local\metadata\collection;
use core_privacy\local\request\contextlist;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\transform;
use core_privacy\local\request\writer;
use core_privacy\local\request\userlist;
use core_privacy\local\request\approved_userlist;
/**
* Privacy provider for auth_oauth2
*
* @package auth_oauth2
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
\core_privacy\local\metadata\provider,
\core_privacy\local\request\core_userlist_provider,
\core_privacy\local\request\plugin\provider {
/**
* Get information about the user data stored by this plugin.
*
* @param collection $collection An object for storing metadata.
* @return collection The metadata.
*/
public static function get_metadata(collection $collection): collection {
$authfields = [
'timecreated' => 'privacy:metadata:auth_oauth2:timecreated',
'timemodified' => 'privacy:metadata:auth_oauth2:timemodified',
'usermodified' => 'privacy:metadata:auth_oauth2:usermodified',
'userid' => 'privacy:metadata:auth_oauth2:userid',
'issuerid' => 'privacy:metadata:auth_oauth2:issuerid',
'username' => 'privacy:metadata:auth_oauth2:username',
'email' => 'privacy:metadata:auth_oauth2:email',
'confirmtoken' => 'privacy:metadata:auth_oauth2:confirmtoken',
'confirmtokenexpires' => 'privacy:metadata:auth_oauth2:confirmtokenexpires'
];
$collection->add_database_table('auth_oauth2_linked_login', $authfields, 'privacy:metadata:auth_oauth2:tableexplanation');
// Regarding this block, we are unable to export or purge this data, as
// it would damage the oauth2 data across the whole site.
foreach ([
'oauth2_endpoint',
'oauth2_user_field_mapping',
'oauth2_access_token',
'oauth2_system_account',
] as $tablename) {
$collection->add_database_table($tablename, [
'usermodified' => 'privacy:metadata:auth_oauth2:usermodified',
], 'privacy:metadata:auth_oauth2:tableexplanation');
}
$collection->link_subsystem('core_auth', 'privacy:metadata:auth_oauth2:authsubsystem');
return $collection;
}
/**
* Return all contexts for this userid. In this situation the user context.
*
* @param int $userid The user ID.
* @return contextlist The list of context IDs.
*/
public static function get_contexts_for_userid(int $userid): contextlist {
$sql = "SELECT ctx.id
FROM {auth_oauth2_linked_login} ao
JOIN {context} ctx ON ctx.instanceid = ao.userid AND ctx.contextlevel = :contextlevel
WHERE ao.userid = :userid";
$params = ['userid' => $userid, 'contextlevel' => CONTEXT_USER];
$contextlist = new contextlist();
$contextlist->add_from_sql($sql, $params);
return $contextlist;
}
/**
* Get the list of users within a specific context.
*
* @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
*/
public static function get_users_in_context(userlist $userlist) {
$context = $userlist->get_context();
if (!$context instanceof \context_user) {
return;
}
$sql = "SELECT userid
FROM {auth_oauth2_linked_login}
WHERE userid = ?";
$params = [$context->instanceid];
$userlist->add_from_sql('userid', $sql, $params);
}
/**
* Export all oauth2 information for the list of contexts and this user.
*
* @param approved_contextlist $contextlist The list of approved contexts for a user.
*/
public static function export_user_data(approved_contextlist $contextlist) {
global $DB;
// Export oauth2 linked accounts.
$context = \context_user::instance($contextlist->get_user()->id);
$sql = "SELECT ll.id, ll.username, ll.email, ll.timecreated, ll.timemodified, oi.name as issuername
FROM {auth_oauth2_linked_login} ll JOIN {oauth2_issuer} oi ON oi.id = ll.issuerid
WHERE ll.userid = :userid";
if ($oauth2accounts = $DB->get_records_sql($sql, ['userid' => $contextlist->get_user()->id])) {
foreach ($oauth2accounts as $oauth2account) {
$data = (object)[
'timecreated' => transform::datetime($oauth2account->timecreated),
'timemodified' => transform::datetime($oauth2account->timemodified),
'issuerid' => $oauth2account->issuername,
'username' => $oauth2account->username,
'email' => $oauth2account->email
];
writer::with_context($context)->export_data([
get_string('privacy:metadata:auth_oauth2', 'auth_oauth2'),
$oauth2account->issuername
], $data);
}
}
}
/**
* Delete all user data for this context.
*
* @param \context $context The context to delete data for.
*/
public static function delete_data_for_all_users_in_context(\context $context) {
if ($context->contextlevel != CONTEXT_USER) {
return;
}
static::delete_user_data($context->instanceid);
}
/**
* Delete multiple users within a single context.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
*/
public static function delete_data_for_users(approved_userlist $userlist) {
$context = $userlist->get_context();
if ($context instanceof \context_user) {
static::delete_user_data($context->instanceid);
}
}
/**
* Delete all user data for this user only.
*
* @param approved_contextlist $contextlist The list of approved contexts for a user.
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
if (empty($contextlist->count())) {
return;
}
$userid = $contextlist->get_user()->id;
foreach ($contextlist->get_contexts() as $context) {
if ($context->contextlevel != CONTEXT_USER) {
continue;
}
if ($context->instanceid == $userid) {
// Because we only use user contexts the instance ID is the user ID.
static::delete_user_data($context->instanceid);
}
}
}
/**
* This does the deletion of user data for the auth_oauth2.
*
* @param int $userid The user ID
*/
protected static function delete_user_data(int $userid) {
global $DB;
// Because we only use user contexts the instance ID is the user ID.
$DB->delete_records('auth_oauth2_linked_login', ['userid' => $userid]);
}
}
+97
View File
@@ -0,0 +1,97 @@
<?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/>.
/**
* Confirm self oauth2 user.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require('../../config.php');
require_once($CFG->libdir . '/authlib.php');
$usersecret = required_param('token', PARAM_RAW);
$username = required_param('username', PARAM_USERNAME);
$redirect = optional_param('redirect', '', PARAM_LOCALURL); // Where to redirect the browser once the user has been confirmed.
$PAGE->set_url('/auth/oauth2/confirm-account.php');
$PAGE->set_context(context_system::instance());
$auth = get_auth_plugin('oauth2');
if (!\auth_oauth2\api::is_enabled()) {
throw new \moodle_exception('notenabled', 'auth_oauth2');
}
$confirmed = $auth->user_confirm($username, $usersecret);
if ($confirmed == AUTH_CONFIRM_ALREADY && !isloggedin()) {
$user = get_complete_user_data('username', $username);
$PAGE->navbar->add(get_string("alreadyconfirmed"));
$PAGE->set_title(get_string("alreadyconfirmed"));
$PAGE->set_heading($COURSE->fullname);
echo $OUTPUT->header();
echo $OUTPUT->box_start('generalbox centerpara boxwidthnormal boxaligncenter');
echo "<p>".get_string("alreadyconfirmed")."</p>\n";
echo $OUTPUT->single_button("$CFG->wwwroot/course/", get_string('courses'));
echo $OUTPUT->box_end();
echo $OUTPUT->footer();
exit;
} else if ($confirmed == AUTH_CONFIRM_OK) {
// The user has confirmed successfully, let's log them in.
if (!$user = get_complete_user_data('username', $username)) {
throw new \moodle_exception('cannotfinduser', '', '', s($username));
}
if ($user->id == $USER->id) {
// Check where to go, $redirect has a higher preference.
if (empty($redirect) and !empty($SESSION->wantsurl) ) {
$redirect = $SESSION->wantsurl;
unset($SESSION->wantsurl);
}
if (!empty($redirect)) {
redirect($redirect);
}
}
$PAGE->navbar->add(get_string("confirmed"));
$PAGE->set_title(get_string("confirmed"));
$PAGE->set_heading($COURSE->fullname);
echo $OUTPUT->header();
echo $OUTPUT->box_start('generalbox centerpara boxwidthnormal boxaligncenter');
echo "<h3>".get_string("thanks").", ". fullname($user) . "</h3>\n";
echo "<p>".get_string("confirmed")."</p>\n";
if (!isloggedin() || isguestuser()) {
echo $OUTPUT->single_button(get_login_url(), get_string('login'));
} else {
echo $OUTPUT->single_button("$CFG->wwwroot/login/logout.php", get_string('logout'));
}
echo $OUTPUT->box_end();
echo $OUTPUT->footer();
exit;
} else {
if (!isloggedin()) {
\core\notification::error(get_string('confirmationinvalid', 'auth_oauth2'));
}
}
redirect("$CFG->wwwroot/");
+88
View File
@@ -0,0 +1,88 @@
<?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/>.
/**
* Confirm self oauth2 user.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require('../../config.php');
require_once($CFG->libdir . '/authlib.php');
$token = required_param('token', PARAM_RAW);
$username = required_param('username', PARAM_USERNAME);
$userid = required_param('userid', PARAM_INT);
$issuerid = required_param('issuerid', PARAM_INT);
$redirect = optional_param('redirect', '', PARAM_LOCALURL); // Where to redirect the browser once the user has been confirmed.
$PAGE->set_url('/auth/oauth2/confirm-linkedlogin.php');
$PAGE->set_context(context_system::instance());
if (!\auth_oauth2\api::is_enabled()) {
throw new \moodle_exception('notenabled', 'auth_oauth2');
}
$confirmed = \auth_oauth2\api::confirm_link_login($userid, $username, $issuerid, $token);
if ($confirmed) {
// The user has confirmed successfully, let's log them in.
if (!$user = get_complete_user_data('id', $userid)) {
throw new \moodle_exception('cannotfinduser', '', '', $userid);
}
if ($user->id == $USER->id) {
// Check where to go, $redirect has a higher preference.
if (empty($redirect) and !empty($SESSION->wantsurl) ) {
$redirect = $SESSION->wantsurl;
unset($SESSION->wantsurl);
}
if (!empty($redirect)) {
redirect($redirect);
}
}
$PAGE->navbar->add(get_string("confirmed"));
$PAGE->set_title(get_string("confirmed"));
$PAGE->set_heading($COURSE->fullname);
echo $OUTPUT->header();
echo $OUTPUT->box_start('generalbox centerpara boxwidthnormal boxaligncenter');
echo "<h3>".get_string("thanks").", ". fullname($user) . "</h3>\n";
echo "<p>".get_string("confirmed")."</p>\n";
// If $wantsurl and $redirect are empty, then the button will navigate the identical user to the dashboard.
if ($user->id == $USER->id) {
echo $OUTPUT->single_button("$CFG->wwwroot/course/", get_string('courses'));
} else if (!isloggedin() || isguestuser()) {
echo $OUTPUT->single_button(get_login_url(), get_string('login'));
} else {
echo $OUTPUT->single_button("$CFG->wwwroot/login/logout.php", get_string('logout'));
}
echo $OUTPUT->box_end();
echo $OUTPUT->footer();
exit;
} else {
// Avoid error if logged-in user visiting the page.
if (!isloggedin()) {
\core\notification::error(get_string('confirmationinvalid', 'auth_oauth2'));
}
}
redirect("$CFG->wwwroot/");
+37
View File
@@ -0,0 +1,37 @@
<?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/>.
/**
* Capability definitions for this plugin.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$capabilities = [
'auth/oauth2:managelinkedlogins' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_USER,
'archetypes' => array(
'user' => CAP_ALLOW
)
),
];
+31
View File
@@ -0,0 +1,31 @@
<?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/>.
/**
* This file definies observers needed by the plugin.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// List of observers.
$observers = [
[
'eventname' => '\core\event\user_deleted',
'callback' => '\auth_oauth2\api::user_deleted',
],
];
+32
View File
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="auth/oauth2/db" VERSION="20170323" COMMENT="XMLDB file for Moodle auth/oauth2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
>
<TABLES>
<TABLE NAME="auth_oauth2_linked_login" COMMENT="Accounts linked to a users Moodle account.">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="usermodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The user account this oauth login is linked to."/>
<FIELD NAME="issuerid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="username" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="The external username to map to this moodle account"/>
<FIELD NAME="email" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="The external email to map to this moodle account"/>
<FIELD NAME="confirmtoken" TYPE="char" LENGTH="64" NOTNULL="true" SEQUENCE="false" COMMENT="If this is not empty - the user has not confirmed their email to create the link."/>
<FIELD NAME="confirmtokenexpires" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="usermodified_key" TYPE="foreign" FIELDS="usermodified" REFTABLE="user" REFFIELDS="id"/>
<KEY NAME="userid_key" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
<KEY NAME="issuerid_key" TYPE="foreign" FIELDS="issuerid" REFTABLE="oauth2_issuer" REFFIELDS="id"/>
<KEY NAME="uniq_key" TYPE="unique" FIELDS="userid, issuerid, username"/>
</KEYS>
<INDEXES>
<INDEX NAME="search_index" UNIQUE="false" FIELDS="issuerid, username"/>
</INDEXES>
</TABLE>
</TABLES>
</XMLDB>
+45
View File
@@ -0,0 +1,45 @@
<?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/>.
/**
* OAuth2 authentication plugin upgrade code
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Upgrade function
*
* @param int $oldversion the version we are upgrading from
* @return bool result
*/
function xmldb_auth_oauth2_upgrade($oldversion) {
// Automatically generated Moodle v4.1.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.2.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.3.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.4.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+123
View File
@@ -0,0 +1,123 @@
<?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/>.
/**
* Strings for component 'auth_oauth2', language 'en'.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['accountexists'] = 'A user already exists on this site with this username. If this is your account, log in by entering your username and password and add it as a linked login via your preferences page.';
$string['auth_oauth2description'] = 'OAuth 2 standards based authentication';
$string['auth_oauth2settings'] = 'OAuth 2 authentication settings.';
$string['confirmaccountemail'] = 'Hi {$a->fullname},
A new account has been requested at \'{$a->sitename}\'
using your email address.
To confirm your new account, please go to this web address:
{$a->link}
In most mail programs, this should appear as a blue link
which you can just click on. If that doesn\'t work,
then cut and paste the address into the address
line at the top of your web browser window.
If you need help, please contact the site administrator,
{$a->admin}
If you did not do this, someone else could be trying to compromise your account.
Please contact the site administrator immediately.';
$string['confirmaccountemailsubject'] = '{$a}: account confirmation';
$string['confirmationinvalid'] = 'The confirmation link is either invalid, or has expired. Please start the login process again to generate a new confirmation email.';
$string['confirmationpending'] = 'This account is pending email confirmation.';
$string['confirmlinkedloginemail'] = 'Hi {$a->fullname},
A request has been made to link the {$a->issuername} login
{$a->linkedemail} to your account at \'{$a->sitename}\'
using your email address.
To confirm this request and link these logins, please go to this web address:
{$a->link}
In most mail programs, this should appear as a blue link
which you can just click on. If that doesn\'t work,
then cut and paste the address into the address
line at the top of your web browser window.
If you need help, please contact the site administrator,
{$a->admin}
If you did not do this, someone else could be trying to compromise your account.
Please contact the site administrator immediately.';
$string['confirmlinkedloginemailsubject'] = '{$a}: linked login confirmation';
$string['createaccountswarning'] = 'This authentication plugin allows users to create accounts on your site. You may want to enable the setting "authpreventaccountcreation" if you use this plugin.';
$string['createnewlinkedlogin'] = 'Link a new account ({$a})';
$string['emailconfirmlink'] = 'Link your accounts';
$string['emailconfirmlinksent'] = '<p>An existing account was found with this email address but it is not linked yet.</p>
<p>The accounts must be linked before you can log in.</p>
<p>An email should have been sent to your address at <b>{$a}</b>.</p>
<p>It contains easy instructions to link your accounts.</p>
<p>If you have any difficulty, contact the site administrator.</p>';
$string['emailpasswordchangeinfo'] = 'Hi {$a->firstname},
Someone (probably you) has requested a new password for your account on \'{$a->sitename}\'.
However your password cannot be reset because you are using your account on another site to log in.
Please log in as before, using the link on the login page.
{$a->admin}';
$string['emailpasswordchangeinfosubject'] = '{$a}: Change password information';
$string['info'] = 'External account';
$string['issuer'] = 'OAuth 2 service';
$string['issuernologin'] = 'This issuer can not be used to login';
$string['key'] = 'Key';
$string['linkedlogins'] = 'Linked logins';
$string['linkedloginshelp'] = 'Help with linked logins';
$string['loggedin'] = 'User successfully authenticated with provider.';
$string['loginerror_userincomplete'] = 'The user information returned did not contain a username and email address. The OAuth 2 service may be configured incorrectly.';
$string['loginerror_nouserinfo'] = 'No user information was returned. The OAuth 2 service may be configured incorrectly.';
$string['loginerror_invaliddomain'] = 'The email address is not allowed at this site.';
$string['loginerror_authenticationfailed'] = 'The authentication process failed.';
$string['loginerror_cannotcreateaccounts'] = 'An account with your email address could not be found.';
$string['noconfiguredidps'] = 'There are no configured OAuth2 providers.';
$string['noissuersavailable'] = 'None of the configured OAuth 2 services allow you to link login accounts.';
$string['notloggedindebug'] = 'The login attempt failed. Reason: {$a}';
$string['notwhileloggedinas'] = 'Linked logins cannot be managed while logged in as another user.';
$string['oauth2:managelinkedlogins'] = 'Manage own linked login accounts';
$string['notenabled'] = 'Sorry, OAuth 2 authentication plugin is not enabled';
$string['plugindescription'] = 'This authentication plugin displays a list of the configured identity providers on the login page. Selecting an identity provider allows users to login with their credentials from an OAuth 2 provider.';
$string['pluginname'] = 'OAuth 2';
$string['alreadylinked'] = 'This external account is already linked to an account on this site';
$string['privacy:metadata:auth_oauth2'] = 'OAuth 2 authentication';
$string['privacy:metadata:auth_oauth2:authsubsystem'] = 'This plugin is connected to the authentication subsystem.';
$string['privacy:metadata:auth_oauth2:confirmtoken'] = 'The confirmation token.';
$string['privacy:metadata:auth_oauth2:confirmtokenexpires'] = 'The timestamp when the confirmation token expires.';
$string['privacy:metadata:auth_oauth2:email'] = 'The external email that maps to this account.';
$string['privacy:metadata:auth_oauth2:issuerid'] = 'The ID of the OAuth 2 issuer for this OAuth 2 login';
$string['privacy:metadata:auth_oauth2:tableexplanation'] = 'OAuth 2 accounts linked to a user\'s Moodle account.';
$string['privacy:metadata:auth_oauth2:timecreated'] = 'The timestamp when the user account was linked to the OAuth 2 login.';
$string['privacy:metadata:auth_oauth2:timemodified'] = 'The timestamp when this record was modified.';
$string['privacy:metadata:auth_oauth2:userid'] = 'The ID of the user account which the OAuth 2 login is linked to.';
$string['privacy:metadata:auth_oauth2:usermodified'] = 'The ID of the user who modified this account.';
$string['privacy:metadata:auth_oauth2:username'] = 'The external username that maps to this account.';
$string['testidplogin'] = 'Test login with:';
$string['userinfo'] = 'User data from provider:';
$string['value'] = 'Value';
+60
View File
@@ -0,0 +1,60 @@
<?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/>.
/**
* Callbacks for auth_oauth2
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Navigation hook to add to preferences page.
*
* @param navigation_node $useraccount
* @param stdClass $user
* @param context_user $context
* @param stdClass $course
* @param context_course $coursecontext
*/
function auth_oauth2_extend_navigation_user_settings(navigation_node $useraccount,
stdClass $user,
context_user $context,
stdClass $course,
context_course $coursecontext) {
global $USER;
if (\auth_oauth2\api::is_enabled() && !\core\session\manager::is_loggedinas()) {
if (has_capability('auth/oauth2:managelinkedlogins', $context) && $user->id == $USER->id) {
$parent = $useraccount->parent->find('useraccount', navigation_node::TYPE_CONTAINER);
$parent->add(get_string('linkedlogins', 'auth_oauth2'), new moodle_url('/auth/oauth2/linkedlogins.php'));
}
}
}
/**
* Callback to remove linked logins for deleted users.
*
* @param stdClass $user
*/
function auth_oauth2_pre_user_delete($user) {
global $DB;
$DB->delete_records(auth_oauth2\linked_login::TABLE, ['userid' => $user->id]);
}
+127
View File
@@ -0,0 +1,127 @@
<?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/>.
/**
* OAuth 2 Linked login configuration page.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../../config.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->libdir.'/tablelib.php');
$PAGE->set_url('/auth/oauth2/linkedlogins.php');
$PAGE->set_context(context_user::instance($USER->id));
$PAGE->set_pagelayout('admin');
$strheading = get_string('linkedlogins', 'auth_oauth2');
$PAGE->set_title($strheading);
$PAGE->set_heading($strheading);
require_login();
if (!\auth_oauth2\api::is_enabled()) {
throw new \moodle_exception('notenabled', 'auth_oauth2');
}
$action = optional_param('action', '', PARAM_ALPHAEXT);
if ($action == 'new') {
require_sesskey();
$issuerid = required_param('issuerid', PARAM_INT);
$issuer = \core\oauth2\api::get_issuer($issuerid);
if (!$issuer->is_available_for_login()) {
throw new \moodle_exception('issuernologin', 'auth_oauth2');
}
// We do a login dance with this issuer.
$addparams = ['action' => 'new', 'issuerid' => $issuerid, 'sesskey' => sesskey()];
$addurl = new moodle_url('/auth/oauth2/linkedlogins.php', $addparams);
$client = \core\oauth2\api::get_user_oauth_client($issuer, $addurl);
if (optional_param('logout', false, PARAM_BOOL)) {
$client->log_out();
}
if (!$client->is_logged_in()) {
redirect($client->get_login_url());
}
$userinfo = $client->get_userinfo();
if (!empty($userinfo)) {
try {
\auth_oauth2\api::link_login($userinfo, $issuer);
redirect($PAGE->url, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
} catch (Exception $e) {
redirect($PAGE->url, $e->getMessage(), null, \core\output\notification::NOTIFY_ERROR);
}
} else {
redirect($PAGE->url, get_string('notloggedin', 'auth_oauth2'), null, \core\output\notification::NOTIFY_ERROR);
}
} else if ($action == 'delete') {
require_sesskey();
$linkedloginid = required_param('linkedloginid', PARAM_INT);
auth_oauth2\api::delete_linked_login($linkedloginid);
redirect($PAGE->url, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
}
$renderer = $PAGE->get_renderer('auth_oauth2');
$linkedloginid = optional_param('id', '', PARAM_RAW);
$linkedlogin = null;
auth_oauth2\api::clean_orphaned_linked_logins();
$issuers = \core\oauth2\api::get_all_issuers(true);
$anyshowinloginpage = false;
$issuerbuttons = array();
foreach ($issuers as $issuer) {
if (!$issuer->is_available_for_login()) {
continue;
}
$anyshowinloginpage = true;
$addparams = ['action' => 'new', 'issuerid' => $issuer->get('id'), 'sesskey' => sesskey(), 'logout' => true];
$addurl = new moodle_url('/auth/oauth2/linkedlogins.php', $addparams);
$issuerbuttons[$issuer->get('id')] = $renderer->single_button($addurl, get_string('createnewlinkedlogin', 'auth_oauth2',
s($issuer->get_display_name())));
}
if (!$anyshowinloginpage) {
// Just a notification that we can't make it.
$preferencesurl = new moodle_url('/user/preferences.php');
redirect($preferencesurl, get_string('noissuersavailable', 'auth_oauth2'), null, \core\output\notification::NOTIFY_WARNING);
}
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('linkedlogins', 'auth_oauth2'));
echo $OUTPUT->doc_link('Linked_Logins', get_string('linkedloginshelp', 'auth_oauth2'));
$linkedlogins = auth_oauth2\api::get_linked_logins();
echo $renderer->linked_logins_table($linkedlogins);
foreach ($issuerbuttons as $issuerbutton) {
echo $issuerbutton;
}
echo $OUTPUT->footer();
+59
View File
@@ -0,0 +1,59 @@
<?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/>.
/**
* Open ID authentication. This file is a simple login entry point for OAuth identity providers.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
*/
require_once('../../config.php');
$issuerid = required_param('id', PARAM_INT);
$wantsurl = new moodle_url(optional_param('wantsurl', '', PARAM_URL));
$PAGE->set_context(context_system::instance());
$PAGE->set_url(new moodle_url('/auth/oauth2/login.php', ['id' => $issuerid]));
require_sesskey();
if (!\auth_oauth2\api::is_enabled()) {
throw new \moodle_exception('notenabled', 'auth_oauth2');
}
$issuer = new \core\oauth2\issuer($issuerid);
if (!$issuer->is_available_for_login()) {
throw new \moodle_exception('issuernologin', 'auth_oauth2');
}
$returnparams = ['wantsurl' => $wantsurl, 'sesskey' => sesskey(), 'id' => $issuerid];
$returnurl = new moodle_url('/auth/oauth2/login.php', $returnparams);
$client = \core\oauth2\api::get_user_oauth_client($issuer, $returnurl);
if ($client) {
if (!$client->is_logged_in()) {
redirect($client->get_login_url());
}
$auth = new \auth_oauth2\auth();
$auth->complete_login($client, $wantsurl);
} else {
throw new moodle_exception('Could not get an OAuth client.');
}
+36
View File
@@ -0,0 +1,36 @@
<?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/>.
/**
* Admin settings and defaults.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
if ($ADMIN->fulltree) {
$warning = $OUTPUT->notification(get_string('createaccountswarning', 'auth_oauth2'), 'warning');
$settings->add(new admin_setting_heading('auth_oauth2/pluginname', '', $warning));
$authplugin = get_auth_plugin('oauth2');
display_auth_lock_options($settings, $authplugin->authtype, $authplugin->userfields,
get_string('auth_fieldlocks_help', 'auth'), false, false,
$authplugin->customfields);
}
@@ -0,0 +1,65 @@
{{!
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/>.
}}
{{!
@template auth_oauth2/idpresponse
Render response returned from the oAuth IdP for
supplied test user.
Classes required for JS:
* none
Data attributes required for JS:
* none
Context variables required for this template:
* pairs - array of key value pairs return from the oAuth IdP
* name - the name field from the IdP for the field
* value - the value of the field for the supplied test user
Example context (json):
{
"pairs": [{
"name": "firstname",
"value": "Jane"
},
{
"name": "lastname",
"value": "Awesome"
}]
}
}}
<div class="container mb-3">
<h3>{{#str}}userinfo, auth_oauth2{{/str}}</h3>
<table class="table">
<thead>
<tr>
<th scope="col">{{#str}}key, auth_oauth2{{/str}}</th>
<th scope="col">{{#str}}value, auth_oauth2{{/str}}</th>
</tr>
</thead>
<tbody>
{{#pairs}}
<tr>
<td>{{name}}</td>
<td>{{{value}}}</td>
</tr>
{{/pairs}}
</tbody>
</table>
</div>
+60
View File
@@ -0,0 +1,60 @@
{{!
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/>.
}}
{{!
@template auth_oauth2/idps
Render available oauth2 idps for testing.
Classes required for JS:
* none
Data attributes required for JS:
* none
Context variables required for this template:
* idps - array of oAuth IdP objects
* name - the display name of the IdP
* url - the url of the oAuth test endpoint
* iconurl - the icon of the oAuth IdP
Example context (json):
{
"idps": [{
"name": "Microsoft",
"url": "http:\/\/localhost\/auth\/oauth2\/test.php?id=2&amp;sesskey=4js6afElZh",
"iconurl": "https:\/\/www.microsoft.com\/favicon.ico"
}]
}
}}
<div class="container mb-3">
<div class="potentialidps-header row">
<h3>{{#str}}testidplogin, auth_oauth2{{/str}}</h3>
</div>
<div class="potentialidps row">
<div class="potentialidplist">
{{#idps}}
<div class="potentialidp mb-2">
<a class="btn btn-secondary btn-block" href="{{{url}}}" title="{{name}}">
{{#iconurl}}<img src="{{{iconurl}}}" alt="{{name}}" width="24" height="24" class="mr-1"/>{{/iconurl}}
{{name}}
</a>
</div>
{{/idps}}
</div>
</div>
</div>
+85
View File
@@ -0,0 +1,85 @@
<?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/>.
/**
* This file allows for testing of login via configured oauth2 IDP poviders.
*
* @package auth_oauth2
* @copyright 2021 Matt Porritt <mattp@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
*/
// Require_login is not needed here.
// phpcs:disable moodle.Files.RequireLogin.Missing
require_once('../../config.php');
require_sesskey();
$issuerid = required_param('id', PARAM_INT);
$url = new moodle_url('/auth/oauth2/test.php', ['id' => $issuerid, 'sesskey' => sesskey()]);
$PAGE->set_context(context_system::instance());
$PAGE->set_url($url);
$PAGE->set_pagelayout('admin');
if (!\auth_oauth2\api::is_enabled()) {
throw new \moodle_exception('notenabled', 'auth_oauth2');
}
$issuer = new \core\oauth2\issuer($issuerid);
if (!$issuer->is_available_for_login()) {
throw new \moodle_exception('issuernologin', 'auth_oauth2');
}
$client = \core\oauth2\api::get_user_oauth_client($issuer, $url);
if ($client) {
// We have a valid client, now lets see if we can log into the IDP.
if (!$client->is_logged_in()) {
redirect($client->get_login_url());
}
echo $OUTPUT->header();
// We were successful logging into the IDP.
echo $OUTPUT->notification(get_string('loggedin', 'auth_oauth2'), 'notifysuccess');
// Try getting user info from the IDP.
$endpointurl = $client->get_issuer()->get_endpoint_url('userinfo');
$response = $client->get($endpointurl);
$userinfo = json_decode($response, true);
$templateinfo = [];
foreach ($userinfo as $key => $value) {
// We are just displaying the data from the IdP for testing purposes,
// so we are more interested in displaying it to the admin than
// processing it.
if (is_array($value)) {
$value = json_encode($value);
}
$templateinfo[] = ['name' => $key, 'value' => $value];
}
// Display user info.
if (!empty($templateinfo)) {
echo $OUTPUT->render_from_template('auth_oauth2/idpresponse', ['pairs' => $templateinfo]);
}
} else {
throw new moodle_exception('Could not get an OAuth client.');
}
echo $OUTPUT->footer();
+255
View File
@@ -0,0 +1,255 @@
<?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/>.
namespace auth_oauth2;
/**
* External auth oauth2 API tests.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class api_test extends \advanced_testcase {
/**
* Test the cleaning of orphaned linked logins for all issuers.
*/
public function test_clean_orphaned_linked_logins(): void {
$this->resetAfterTest();
$this->setAdminUser();
$issuer = \core\oauth2\api::create_standard_issuer('google');
\core\oauth2\api::create_standard_issuer('microsoft');
$user = $this->getDataGenerator()->create_user();
$info = [];
$info['username'] = 'banana';
$info['email'] = 'banana@example.com';
\auth_oauth2\api::link_login($info, $issuer, $user->id, false);
\core\oauth2\api::delete_issuer($issuer->get('id'));
$linkedlogins = \auth_oauth2\api::get_linked_logins($user->id, $issuer);
$this->assertCount(1, $linkedlogins);
\auth_oauth2\api::clean_orphaned_linked_logins();
$linkedlogins = \auth_oauth2\api::get_linked_logins($user->id, $issuer);
$this->assertCount(0, $linkedlogins);
$match = \auth_oauth2\api::match_username_to_user('banana', $issuer);
$this->assertFalse($match);
}
/**
* Test the cleaning of orphaned linked logins for a specific issuer.
*/
public function test_clean_orphaned_linked_logins_with_issuer_id(): void {
$this->resetAfterTest();
$this->setAdminUser();
$issuer1 = \core\oauth2\api::create_standard_issuer('google');
$issuer2 = \core\oauth2\api::create_standard_issuer('microsoft');
$user1 = $this->getDataGenerator()->create_user();
$info = [];
$info['username'] = 'banana';
$info['email'] = 'banana@example.com';
\auth_oauth2\api::link_login($info, $issuer1, $user1->id, false);
$user2 = $this->getDataGenerator()->create_user();
$info = [];
$info['username'] = 'apple';
$info['email'] = 'apple@example.com';
\auth_oauth2\api::link_login($info, $issuer2, $user2->id, false);
\core\oauth2\api::delete_issuer($issuer1->get('id'));
\auth_oauth2\api::clean_orphaned_linked_logins($issuer1->get('id'));
$linkedlogins = \auth_oauth2\api::get_linked_logins($user1->id, $issuer1);
$this->assertCount(0, $linkedlogins);
$linkedlogins = \auth_oauth2\api::get_linked_logins($user2->id, $issuer2);
$this->assertCount(1, $linkedlogins);
}
/**
* Test creating a new confirmed account.
* Including testing that user profile fields are correctly set.
*
* @covers \auth_oauth2\api::create_new_confirmed_account
*/
public function test_create_new_confirmed_account(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$issuer = \core\oauth2\api::create_standard_issuer('microsoft');
$info = [];
$info['username'] = 'apple';
$info['email'] = 'apple@example.com';
$info['firstname'] = 'Apple';
$info['lastname'] = 'Fruit';
$info['alternatename'] = 'Beatles';
$info['idnumber'] = '123456';
$info['city'] = 'Melbourne';
$info['country'] = 'AU';
$info['institution'] = 'ACME Inc';
$info['department'] = 'Misc Explosives';
$createduser = \auth_oauth2\api::create_new_confirmed_account($info, $issuer);
// Get actual user record from DB to check.
$userdata = $DB->get_record('user', ['id' => $createduser->id]);
// Confirm each value supplied from issuers is saved into the user record.
foreach ($info as $key => $value) {
$this->assertEquals($value, $userdata->$key);
}
// Explicitly test the user is confirmed.
$this->assertEquals(1, $userdata->confirmed);
}
/**
* Test auto-confirming linked logins.
*/
public function test_linked_logins(): void {
$this->resetAfterTest();
$this->setAdminUser();
$issuer = \core\oauth2\api::create_standard_issuer('google');
$user = $this->getDataGenerator()->create_user();
$this->setUser($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'));
}
/**
* Test that we cannot deleted a linked login for another user
*/
public function test_delete_linked_login_other_user(): void {
$this->resetAfterTest();
$this->setAdminUser();
$issuer = \core\oauth2\api::create_standard_issuer('google');
$user = $this->getDataGenerator()->create_user();
api::link_login([
'username' => 'banana',
'email' => 'banana@example.com',
], $issuer, $user->id);
/** @var linked_login $linkedlogin */
$linkedlogin = api::get_linked_logins($user->id)[0];
// We are logged in as a different user, so cannot delete this.
$this->expectException(\dml_missing_record_exception::class);
api::delete_linked_login($linkedlogin->get('id'));
}
/**
* Test that is_enabled correctly identifies when the plugin is enabled.
*/
public function test_is_enabled(): void {
$this->resetAfterTest();
set_config('auth', 'manual,oauth2');
$this->assertTrue(\auth_oauth2\api::is_enabled());
}
/**
* Test that is_enabled correctly identifies when the plugin is disabled.
*/
public function test_is_enabled_disabled(): void {
$this->resetAfterTest();
set_config('auth', 'manual');
$this->assertFalse(\auth_oauth2\api::is_enabled());
}
/**
* Test creating a user via the send confirm account email method.
* Including testing that user profile fields are correctly set.
*
* @covers \auth_oauth2\api::send_confirm_account_email
*/
public function test_send_confirm_account_email(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$issuer = \core\oauth2\api::create_standard_issuer('microsoft');
$info = [];
$info['username'] = 'apple';
$info['email'] = 'apple@example.com';
$info['firstname'] = 'Apple';
$info['lastname'] = 'Fruit';
$info['alternatename'] = 'Beatles';
$info['idnumber'] = '123456';
$info['city'] = 'Melbourne';
$info['country'] = 'AU';
$info['institution'] = 'ACME Inc';
$info['department'] = 'Misc Explosives';
$createduser = \auth_oauth2\api::send_confirm_account_email($info, $issuer);
// Get actual user record from DB to check.
$userdata = $DB->get_record('user', ['id' => $createduser->id]);
// Confirm each value supplied from issuers is saved into the user record.
foreach ($info as $key => $value) {
$this->assertEquals($value, $userdata->$key);
}
// Explicitly test the user is not yet confirmed.
$this->assertEquals(0, $userdata->confirmed);
}
}
+92
View File
@@ -0,0 +1,92 @@
<?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/>.
namespace auth_oauth2;
/**
* Auth oauth2 auth functions tests.
*
* @package auth_oauth2
* @category test
* @copyright 2019 Shamim Rezaie
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \auth_oauth2\auth
*/
class auth_test extends \advanced_testcase {
public function test_get_password_change_info(): void {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user(['auth' => 'oauth2']);
$auth = get_auth_plugin($user->auth);
$info = $auth->get_password_change_info($user);
$this->assertEqualsCanonicalizing(['subject', 'message'], array_keys($info));
$this->assertStringContainsString(
'your password cannot be reset because you are using your account on another site to log in',
$info['message']);
}
/**
* Test complete_login for oauth2.
* @covers ::complete_login
*/
public function test_oauth2_complete_login(): void {
global $CFG;
$this->resetAfterTest();
$this->setAdminUser();
$wantsurl = new \moodle_url('/');
$issuer = \core\oauth2\api::create_standard_issuer('microsoft');
$info = [];
$info['username'] = 'apple';
$info['email'] = 'apple@example.com';
$info['firstname'] = 'Apple';
$info['lastname'] = 'Fruit';
$info['url'] = 'http://apple.com/';
$info['alternamename'] = 'Beatles';
$info['auth'] = 'oauth2';
$user = \auth_oauth2\api::create_new_confirmed_account($info, $issuer);
$auth = get_auth_plugin($user->auth);
// Set up mock data.
$client = $this->createMock(\core\oauth2\client::class);
$client->expects($this->once())->method('get_raw_userinfo')->willReturn((object)$info);
$client->expects($this->once())->method('get_userinfo')->willReturn($info);
$client->expects($this->once())->method('get_issuer')->willReturn($issuer);
$sink = $this->redirectEvents();
try {
// Need @ as it will fail at \core\session\manager::login_user for session_regenerate_id.
@$auth->complete_login($client, $wantsurl);
} catch (\Exception $e) {
// This happens as complete login is using 'redirect'.
$this->assertInstanceOf(\moodle_exception::class, $e);
}
$events = $sink->get_events();
$sink->close();
// There are 2 events. First is core\event\user_updated and second is core\event\user_loggedin.
$event = $events[1];
$this->assertInstanceOf('core\event\user_loggedin', $event);
// Make sure the extra record is in the user_loggedin event.
$extrauserinfo = $event->other['extrauserinfo'];
$this->assertEquals($info, $extrauserinfo);
}
}
@@ -0,0 +1,26 @@
@auth @auth_oauth2 @javascript
Feature: OAuth2 settings test functionality
In order to use them later for authentication
As an administrator
I need to be able to test configured OAuth2 login services.
Background:
Given I log in as "admin"
And I change window size to "large"
Scenario: Test oAuth2 authentication settings with no configured service.
Given I navigate to "Plugins > Authentication > Manage authentication" in site administration
And I click on "Test settings" "link" in the "OAuth 2" "table_row"
Then I should see "There are no configured OAuth2 providers"
Scenario: Test oAuth2 authentication settings for a configured service.
Given I navigate to "Server > OAuth 2 services" in site administration
And I press "Google"
And I set the following fields to these values:
| Name | Testing service |
| Client ID | thisistheclientid |
| Client secret | supersecret |
And I press "Save changes"
And I navigate to "Plugins > Authentication > Manage authentication" in site administration
And I click on "Test settings" "link" in the "OAuth 2" "table_row"
Then I should see "Testing service"
+279
View File
@@ -0,0 +1,279 @@
<?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/>.
/**
* Privacy test for the authentication oauth2
*
* @package auth_oauth2
* @category test
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_oauth2\privacy;
defined('MOODLE_INTERNAL') || die();
use auth_oauth2\privacy\provider;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\writer;
use core_privacy\tests\provider_testcase;
use core_privacy\local\request\approved_userlist;
/**
* Privacy test for the authentication oauth2
*
* @package auth_oauth2
* @category test
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider_test extends provider_testcase {
/**
* Set up method.
*/
public function setUp(): void {
$this->resetAfterTest();
$this->setAdminUser();
}
/**
* Check that a user context is returned if there is any user data for this user.
*/
public function test_get_contexts_for_userid(): void {
$user = $this->getDataGenerator()->create_user();
$this->assertEmpty(provider::get_contexts_for_userid($user->id));
$issuer = \core\oauth2\api::create_standard_issuer('google');
$info = [];
$info['username'] = 'gina';
$info['email'] = 'gina@example.com';
\auth_oauth2\api::link_login($info, $issuer, $user->id, false);
$contextlist = provider::get_contexts_for_userid($user->id);
// Check that we only get back one context.
$this->assertCount(1, $contextlist);
// Check that a context is returned is the expected.
$usercontext = \context_user::instance($user->id);
$this->assertEquals($usercontext->id, $contextlist->get_contextids()[0]);
}
/**
* Test that user data is exported correctly.
*/
public function test_export_user_data(): void {
$user = $this->getDataGenerator()->create_user();
$issuer = \core\oauth2\api::create_standard_issuer('google');
$info = [];
$info['username'] = 'gina';
$info['email'] = 'gina@example.com';
\auth_oauth2\api::link_login($info, $issuer, $user->id, false);
$usercontext = \context_user::instance($user->id);
$writer = writer::with_context($usercontext);
$this->assertFalse($writer->has_any_data());
$approvedlist = new approved_contextlist($user, 'auth_oauth2', [$usercontext->id]);
provider::export_user_data($approvedlist);
$data = $writer->get_data([get_string('privacy:metadata:auth_oauth2', 'auth_oauth2'), $issuer->get('name')]);
$this->assertEquals($info['username'], $data->username);
$this->assertEquals($info['email'], $data->email);
}
/**
* Test deleting all user data for a specific context.
*/
public function test_delete_data_for_all_users_in_context(): void {
global $DB;
$user1 = $this->getDataGenerator()->create_user();
$issuer1 = \core\oauth2\api::create_standard_issuer('google');
$info = [];
$info['username'] = 'gina';
$info['email'] = 'gina@example.com';
\auth_oauth2\api::link_login($info, $issuer1, $user1->id, false);
$user1context = \context_user::instance($user1->id);
$user2 = $this->getDataGenerator()->create_user();
$issuer2 = \core\oauth2\api::create_standard_issuer('microsoft');
$info = [];
$info['username'] = 'jerry';
$info['email'] = 'jerry@example.com';
\auth_oauth2\api::link_login($info, $issuer2, $user2->id, false);
$user2context = \context_user::instance($user2->id);
// Get all oauth2 accounts.
$oauth2accounts = $DB->get_records('auth_oauth2_linked_login', array());
// There should be two.
$this->assertCount(2, $oauth2accounts);
// Delete everything for the first user context.
provider::delete_data_for_all_users_in_context($user1context);
// Get all oauth2 accounts match with user1.
$oauth2accounts = $DB->get_records('auth_oauth2_linked_login', ['userid' => $user1->id]);
$this->assertCount(0, $oauth2accounts);
// Get all oauth2 accounts.
$oauth2accounts = $DB->get_records('auth_oauth2_linked_login', array());
// There should be one.
$this->assertCount(1, $oauth2accounts);
}
/**
* This should work identical to the above test.
*/
public function test_delete_data_for_user(): void {
global $DB;
$user1 = $this->getDataGenerator()->create_user();
$issuer1 = \core\oauth2\api::create_standard_issuer('google');
$info = [];
$info['username'] = 'gina';
$info['email'] = 'gina@example.com';
\auth_oauth2\api::link_login($info, $issuer1, $user1->id, false);
$user1context = \context_user::instance($user1->id);
$user2 = $this->getDataGenerator()->create_user();
$issuer2 = \core\oauth2\api::create_standard_issuer('microsoft');
$info = [];
$info['username'] = 'jerry';
$info['email'] = 'jerry@example.com';
\auth_oauth2\api::link_login($info, $issuer2, $user2->id, false);
$user2context = \context_user::instance($user2->id);
// Get all oauth2 accounts.
$oauth2accounts = $DB->get_records('auth_oauth2_linked_login', array());
// There should be two.
$this->assertCount(2, $oauth2accounts);
// Delete everything for the first user.
$approvedlist = new approved_contextlist($user1, 'auth_oauth2', [$user1context->id]);
provider::delete_data_for_user($approvedlist);
// Get all oauth2 accounts match with user1.
$oauth2accounts = $DB->get_records('auth_oauth2_linked_login', ['userid' => $user1->id]);
$this->assertCount(0, $oauth2accounts);
// Get all oauth2 accounts.
$oauth2accounts = $DB->get_records('auth_oauth2_linked_login', array());
// There should be one user.
$this->assertCount(1, $oauth2accounts);
}
/**
* Test that only users with a user context are fetched.
*/
public function test_get_users_in_context(): void {
$this->resetAfterTest();
$component = 'auth_oauth2';
// Create a user.
$user = $this->getDataGenerator()->create_user();
$usercontext = \context_user::instance($user->id);
// The list of users should not return anything yet (related data still haven't been created).
$userlist = new \core_privacy\local\request\userlist($usercontext, $component);
provider::get_users_in_context($userlist);
$this->assertCount(0, $userlist);
$issuer = \core\oauth2\api::create_standard_issuer('google');
$info = [];
$info['username'] = 'gina';
$info['email'] = 'gina@example.com';
\auth_oauth2\api::link_login($info, $issuer, $user->id, false);
// The list of users for user context should return the user.
provider::get_users_in_context($userlist);
$this->assertCount(1, $userlist);
$expected = [$user->id];
$actual = $userlist->get_userids();
$this->assertEquals($expected, $actual);
// The list of users for system context should not return any users.
$systemcontext = \context_system::instance();
$userlist = new \core_privacy\local\request\userlist($systemcontext, $component);
provider::get_users_in_context($userlist);
$this->assertCount(0, $userlist);
}
/**
* Test that data for users in approved userlist is deleted.
*/
public function test_delete_data_for_users(): void {
$this->resetAfterTest();
$component = 'auth_oauth2';
// Create user1.
$user1 = $this->getDataGenerator()->create_user();
$usercontext1 = \context_user::instance($user1->id);
// Create user2.
$user2 = $this->getDataGenerator()->create_user();
$usercontext2 = \context_user::instance($user2->id);
$issuer1 = \core\oauth2\api::create_standard_issuer('google');
$info1 = [];
$info1['username'] = 'gina1';
$info1['email'] = 'gina@example1.com';
\auth_oauth2\api::link_login($info1, $issuer1, $user1->id, false);
$issuer2 = \core\oauth2\api::create_standard_issuer('google');
$info2 = [];
$info2['username'] = 'gina2';
$info2['email'] = 'gina@example2.com';
\auth_oauth2\api::link_login($info2, $issuer2, $user2->id, false);
// The list of users for usercontext1 should return user1.
$userlist1 = new \core_privacy\local\request\userlist($usercontext1, $component);
provider::get_users_in_context($userlist1);
$this->assertCount(1, $userlist1);
$expected = [$user1->id];
$actual = $userlist1->get_userids();
$this->assertEquals($expected, $actual);
// The list of users for usercontext2 should return user2.
$userlist2 = new \core_privacy\local\request\userlist($usercontext2, $component);
provider::get_users_in_context($userlist2);
$this->assertCount(1, $userlist2);
$expected = [$user2->id];
$actual = $userlist2->get_userids();
$this->assertEquals($expected, $actual);
// Add userlist1 to the approved user list.
$approvedlist = new approved_userlist($usercontext1, $component, $userlist1->get_userids());
// Delete user data using delete_data_for_user for usercontext1.
provider::delete_data_for_users($approvedlist);
// Re-fetch users in usercontext1 - The user list should now be empty.
$userlist1 = new \core_privacy\local\request\userlist($usercontext1, $component);
provider::get_users_in_context($userlist1);
$this->assertCount(0, $userlist1);
// Re-fetch users in usercontext2 - The user list should not be empty (user2).
$userlist2 = new \core_privacy\local\request\userlist($usercontext2, $component);
provider::get_users_in_context($userlist2);
$this->assertCount(1, $userlist2);
// User data should be only removed in the user context.
$systemcontext = \context_system::instance();
// Add userlist2 to the approved user list in the system context.
$approvedlist = new approved_userlist($systemcontext, $component, $userlist2->get_userids());
// Delete user1 data using delete_data_for_user.
provider::delete_data_for_users($approvedlist);
// Re-fetch users in usercontext2 - The user list should not be empty (user2).
$userlist2 = new \core_privacy\local\request\userlist($usercontext2, $component);
provider::get_users_in_context($userlist2);
$this->assertCount(1, $userlist2);
}
}
+29
View File
@@ -0,0 +1,29 @@
<?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/>.
/**
* Version information
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024042200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'auth_oauth2'; // Full name of the plugin (used for diagnostics).