Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?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 linked login** Requires auth/oauth2:managelinkedlogins capability at the user context.** @param int $linkedloginid* @return boolean*/public static function delete_linked_login($linkedloginid) {$login = new linked_login($linkedloginid);$userid = $login->get('userid');if (\core\session\manager::is_loggedinas()) {throw new moodle_exception('notwhileloggedinas', 'auth_oauth2');}$context = context_user::instance($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;}}