diff --git a/auth/oauth2/classes/api.php b/auth/oauth2/classes/api.php new file mode 100644 index 00000000000..ce02ce39c89 --- /dev/null +++ b/auth/oauth2/classes/api.php @@ -0,0 +1,147 @@ +. + +/** + * 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; + +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 { + + /** + * 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]); + } + + /** + * 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 + ]; + $match = linked_login::get_record($params); + + if ($match) { + $user = get_complete_user_data('id', $match->get('userid')); + + return $user; + } + 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) + * @return boolean + */ + public static function link_login($userinfo, $issuer, $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); + + + $record = new stdClass(); + $record->issuerid = $issuer->get('id'); + $record->username = $userinfo['username']; + $record->email = $userinfo['email']; + $record->userid = $userid; + $existing = linked_login::get_record((array)$record); + if ($existing) { + return $existing; + } + $linkedlogin = new linked_login(0, $record); + return $linkedlogin->create(); + } + + /** + * 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(); + } +} diff --git a/auth/oauth2/classes/auth.php b/auth/oauth2/classes/auth.php index 22906567a9d..677ec630b2e 100644 --- a/auth/oauth2/classes/auth.php +++ b/auth/oauth2/classes/auth.php @@ -156,14 +156,8 @@ class auth extends \auth_plugin_base { * @param array $userfields */ public function config_form($config, $err, $userfields) { - echo get_string('plugindescription', 'auth_oauth2'); + include(__DIR__ . "/../config.html"); - // Force all fields updated on login and locked. - - foreach ($userfields as $field) { - set_config('field_updatelocal_' . $field, 'onlogin', 'auth_oauth2'); - set_config('field_lock_' . $field, 'unlockedifempty', 'auth_oauth2'); - } return; } @@ -312,6 +306,14 @@ class auth extends \auth_plugin_base { return true; } + public function process_config($config) { + // Set to defaults if undefined + if (!isset($config->allowlinkedlogins)) { + $config->allowlinkedlogins = false; + } + set_config('allowlinkedlogins', trim($config->allowlinkedlogins), 'auth_oauth2'); + } + /** * Complete the login process after oauth handshake is complete. * @param \core\oauth2\client $client @@ -336,20 +338,33 @@ class auth extends \auth_plugin_base { $userinfo['username'] = trim(core_text::strtolower($userinfo['username'])); - if (!empty($userinfo['picture'])) { - $this->set_static_user_picture($userinfo['picture']); - unset($userinfo['picture']); - } + $userwasmapped = false; + if (get_config('auth_oauth2', 'allowlinkedlogins')) { + $mappeduser = api::match_username_to_user($userinfo['username'], $client->get_issuer()); - 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']); + if ($mappeduser) { + $userinfo = (array) $mappeduser; + $userwasmapped = true; } } + + if (!$userwasmapped) { + 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']); + } + } + } + $this->set_static_user_info($userinfo); - $user = authenticate_user_login($userinfo['username'], ''); + $user = get_complete_user_data('username', $userinfo['username']); if ($user) { complete_user_login($user); diff --git a/auth/oauth2/classes/linked_login.php b/auth/oauth2/classes/linked_login.php new file mode 100644 index 00000000000..93dfd5702cf --- /dev/null +++ b/auth/oauth2/classes/linked_login.php @@ -0,0 +1,63 @@ +. + +/** + * Class for loading/storing issuers from the DB. + * + * @package core_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 */ + 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 + ) + ); + } + +} diff --git a/auth/oauth2/classes/output/renderer.php b/auth/oauth2/classes/output/renderer.php new file mode 100644 index 00000000000..dbe2048810e --- /dev/null +++ b/auth/oauth2/classes/output/renderer.php @@ -0,0 +1,96 @@ +. + +/** + * 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 \auth\oauth2\linked_login[] $linkedlogins - list of all linked logins. + * @return string HTML to output. + */ + public function linked_logins_table($linkedlogins) { + global $CFG, $OUTPUT; + + $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, $OUTPUT->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); + } +} diff --git a/auth/oauth2/config.html b/auth/oauth2/config.html new file mode 100644 index 00000000000..b78655a748d --- /dev/null +++ b/auth/oauth2/config.html @@ -0,0 +1,32 @@ + +
+ + +allowlinkedlogins)) { + $config->allowlinkedlogins = true; +} +?> + ++ + | ++ allowlinkedlogins) { echo 'checked="checked"'; } ?> + > + error_text($err['allowlinkedlogins']); } ?> + | ++ + | +