From 6f7fe5df9cc51ef4d5fc0d0c826c1c455bfb2d38 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 25 Sep 2019 12:38:53 +0800 Subject: [PATCH] MDL-65959 core_badges: Unrestricted user's badger account * Restructure the email to be backpack specific * Amended table defintion and functions --- badges/backpackemailverify.php | 18 ++-- badges/classes/backpack_api.php | 4 +- badges/classes/form/backpack.php | 55 +++++++----- badges/classes/form/external_backpack.php | 47 ++++++++-- badges/mybackpack.php | 13 ++- lib/badgeslib.php | 102 ++++++++++++++++++---- lib/db/upgrade.php | 14 +++ version.php | 2 +- 8 files changed, 187 insertions(+), 68 deletions(-) diff --git a/badges/backpackemailverify.php b/badges/backpackemailverify.php index 9b563ff9b78..62f0b750213 100644 --- a/badges/backpackemailverify.php +++ b/badges/backpackemailverify.php @@ -54,15 +54,15 @@ if (!is_null($storedsecret)) { null, \core\output\notification::NOTIFY_ERROR); } - $obj = new stdClass(); - $obj->userid = $USER->id; - $obj->email = $data->email; - $obj->externalbackpackid = $backpackid; - $obj->backpackuid = $backpackuid; - $obj->autosync = 0; - $obj->password = $password; - - $DB->insert_record('badge_backpack', $obj); + $values = [ + 'userid' => $USER->id, + 'backpackemail' => $data->email, + 'externalbackpackid' => $backpackid, + 'backpackuid' => $backpackuid, + 'autosync' => 0, + 'password' => $password + ]; + badges_save_backpack_credentials((object) $values); // Remove the verification vars and redirect to the mypackpack page. unset_user_preference('badges_email_verify_secret'); diff --git a/badges/classes/backpack_api.php b/badges/classes/backpack_api.php index 0916dea4752..b25b39f9ef1 100644 --- a/badges/classes/backpack_api.php +++ b/badges/classes/backpack_api.php @@ -570,9 +570,10 @@ class backpack_api { * * @param integer $userid The user in Moodle * @param integer $backpackid The backpack to disconnect + * @param integer $externalbackupid The external backpack to disconnect * @return boolean */ - public function disconnect_backpack($userid, $backpackid) { + public function disconnect_backpack($userid, $backpackid, $externalbackupid) { global $DB, $USER; if (\core\session\manager::is_loggedinas() || $userid != $USER->id) { @@ -584,6 +585,7 @@ class backpack_api { $DB->delete_records('badge_external', array('backpackid' => $backpackid)); $DB->delete_records('badge_backpack', array('userid' => $userid)); + $DB->delete_records('badge_external_backpack', array('id' => $externalbackupid)); $badgescache->delete($userid); return true; } diff --git a/badges/classes/form/backpack.php b/badges/classes/form/backpack.php index 6c7370c0c79..6689a4f7ac0 100644 --- a/badges/classes/form/backpack.php +++ b/badges/classes/form/backpack.php @@ -39,7 +39,7 @@ use stdClass; * Form to edit backpack initial details. * */ -class backpack extends moodleform { +class backpack extends external_backpack { /** * Defines the form @@ -53,24 +53,36 @@ class backpack extends moodleform { $mform->addHelpButton('backpackheader', 'backpackconnection', 'badges'); $mform->addElement('hidden', 'userid', $USER->id); $mform->setType('userid', PARAM_INT); - $sitebackpack = badges_get_site_backpack($CFG->badges_site_backpack); - + $mform->addElement('hidden', 'externalbackpackid'); + $mform->setType('externalbackpackid', PARAM_INT); if (isset($this->_customdata['email'])) { // Email will be passed in when we're in the process of verifying the user's email address, // so set the connection status, lock the email field, and provide options to resend the verification // email or cancel the verification process entirely and start over. - $mform->addElement('hidden', 'backpackid', $sitebackpack->id); - $mform->setType('backpackid', PARAM_INT); + $mform->hardFreeze(); $status = html_writer::tag('span', get_string('backpackemailverificationpending', 'badges'), array('class' => 'notconnected', 'id' => 'connection-status')); - $mform->addElement('static', 'status', get_string('status'), $status); - $mform->addElement('hidden', 'email', $this->_customdata['email']); - $mform->setType('email', PARAM_EMAIL); - $mform->hardFreeze(['email']); - $emailverify = html_writer::tag('span', s($this->_customdata['email']), []); - $mform->addElement('static', 'emailverify', get_string('email'), $emailverify); - $mform->addElement('hidden', 'backpackpassword', $this->_customdata['backpackpassword']); - $mform->setType('backpackpassword', PARAM_RAW); + } else { + $status = html_writer::tag('span', get_string('notconnected', 'badges'), + array('class' => 'notconnected', 'id' => 'connection-status')); + } + $mform->addElement('static', 'status', get_string('status'), $status); + + parent::definition(); + + $mform->setDefault('backpackemail', $USER->email); + $mform->setDisableShortforms(false); + } + + /** + * Override add_action_buttons + * + * @param bool $cancel + * @param null|text $submitlabel + */ + public function add_action_buttons($cancel = true, $submitlabel = null) { + if ($this->_customdata['email']) { + $mform = $this->_form; $buttonarray = []; $buttonarray[] = &$mform->createElement('submit', 'submitbutton', get_string('backpackconnectionresendemail', 'badges')); @@ -101,7 +113,7 @@ class backpack extends moodleform { $mform->setType('backpackpassword', PARAM_RAW); } } - $this->add_action_buttons(false, get_string('backpackconnectionconnect', 'badges')); + parent::add_action_buttons(false, get_string('backpackconnectionconnect', 'badges')); } } @@ -118,19 +130,18 @@ class backpack extends moodleform { // We don't need to verify the email address if we're clearing a pending email verification attempt. if (!isset($data['revertbutton'])) { $check = new stdClass(); - $backpack = badges_get_site_backpack($data['backpackid']); - $check->email = $data['email']; - $check->password = $data['backpackpassword']; - $check->externalbackpackid = $backpack->id; + $check->email = $data['backpackemail']; + $check->password = $data['password']; + $check->externalbackpackid = $data['externalbackpackid']; - $bp = new \core_badges\backpack_api($backpack, $check); + $bp = new \core_badges\backpack_api((object) $data, $check); $result = $bp->authenticate(); if ($result === false || !empty($result->error)) { - $errors['email'] = get_string('backpackconnectionunexpectedresult', 'badges'); + $errors['backpackemail'] = get_string('backpackconnectionunexpectedresult', 'badges'); $msg = $bp->get_authentication_error(); if (!empty($msg)) { - $errors['email'] .= '

'; - $errors['email'] .= get_string('backpackconnectionunexpectedmessage', 'badges', $msg); + $errors['backpackemail'] .= '

'; + $errors['backpackemail'] .= get_string('backpackconnectionunexpectedmessage', 'badges', $msg); } } } diff --git a/badges/classes/form/external_backpack.php b/badges/classes/form/external_backpack.php index e5c495c0304..6fefec49cfb 100644 --- a/badges/classes/form/external_backpack.php +++ b/badges/classes/form/external_backpack.php @@ -53,11 +53,6 @@ class external_backpack extends \moodleform { $mform->addElement('hidden', 'action', 'edit'); $mform->setType('action', PARAM_ALPHA); - if ($backpack) { - $mform->addElement('hidden', 'id', $backpack->id); - $mform->setType('id', PARAM_INTEGER); - } - $mform->addElement('text', 'backpackapiurl', get_string('backpackapiurl', 'core_badges')); $mform->setType('backpackapiurl', PARAM_URL); $mform->addRule('backpackapiurl', null, 'required', null, 'client'); @@ -74,11 +69,24 @@ class external_backpack extends \moodleform { $mform->setDefault('apiversion', OPEN_BADGES_V2P1); $mform->addRule('apiversion', null, 'required', null, 'client'); - $issuername = $CFG->badges_defaultissuername; - $mform->addElement('static', 'issuerinfo', get_string('defaultissuername', 'core_badges'), $issuername); + $mform->addElement('hidden', 'id', ($backpack->id ?? null)); + $mform->setType('id', PARAM_INT); + $mform->addElement('hidden', 'badgebackpack', 0); + $mform->setType('badgebackpack', PARAM_INTEGER); + $mform->addElement('hidden', 'userid', 0); + $mform->setType('userid', PARAM_INTEGER); + $mform->addElement('hidden', 'backpackuid', 0); + $mform->setType('backpackuid', PARAM_INTEGER); + + $mform->addElement('advcheckbox', 'includeauthdetails', null, get_string('includeauthdetails', 'core_badges')); + if (!empty($backpack->backpackemail) || !empty($backpack->password)) { + $mform->setDefault('includeauthdetails', 1); + } $issuercontact = $CFG->badges_defaultissuercontact; - $mform->addElement('static', 'issuerinfo', get_string('defaultissuercontact', 'core_badges'), $issuercontact); + $mform->addElement('text', 'backpackemail', get_string('defaultissuercontact', 'core_badges')); + $mform->setType('backpackemail', PARAM_EMAIL); + $mform->setDefault('backpackemail', $issuercontact); $mform->addElement('passwordunmask', 'password', get_string('defaultissuerpassword', 'core_badges')); $mform->setType('password', PARAM_RAW); @@ -120,4 +128,27 @@ class external_backpack extends \moodleform { return $errors; } + + /** + * Return submitted data if properly submitted or returns NULL if validation fails or + * if there is no submitted data. + * + * @return object|void + */ + public function get_data() { + $data = parent::get_data(); + if ($data ) { + if ((isset($data->includeauthdetails) && !$data->includeauthdetails) + || (isset($data->apiversion) && $data->apiversion == 2.1)) { + $data->backpackemail = ""; + $data->password = ""; + } + + if ((isset($data->apiversion) && $data->apiversion == 1)) { + $data->password = ""; + } + } + + return $data; + } } diff --git a/badges/mybackpack.php b/badges/mybackpack.php index 61dd37408d9..11ce5ccede4 100644 --- a/badges/mybackpack.php +++ b/badges/mybackpack.php @@ -64,7 +64,7 @@ if ($disconnect && $backpack) { } else { // If backpack is connected, need to select collections. $bp = new \core_badges\backpack_api($sitebackpack, $backpack); - $bp->disconnect_backpack($USER->id, $backpack->id); + $bp->disconnect_backpack($USER->id, $backpack->id, $sitebackpack->id); redirect(new moodle_url('/badges/mybackpack.php')); } } @@ -73,10 +73,6 @@ if ($backpack) { $sitebackpack = badges_get_site_backpack($backpack->externalbackpackid); - if ($sitebackpack->id != $CFG->badges_site_backpack) { - $warning = $OUTPUT->notification(get_string('backpackneedsupdate', 'badges'), 'warning'); - } - // If backpack is connected, need to select collections. $bp = new \core_badges\backpack_api($sitebackpack, $backpack); $request = $bp->get_collections(); @@ -145,11 +141,12 @@ if ($backpack) { if (isset($data->revertbutton)) { badges_disconnect_user_backpack($USER->id); redirect(new moodle_url('/badges/mybackpack.php')); - } else if (isset($data->email)) { - if (badges_send_verification_email($data->email, $data->backpackid, $data->backpackpassword)) { + } else if (isset($data->backpackemail)) { + $newid = badges_create_site_backpack($data, true); + if (badges_send_verification_email($data->backpackemail, $newid, $data->password)) { $a = get_user_preferences('badges_email_verify_backpackid'); redirect(new moodle_url('/badges/mybackpack.php'), - get_string('backpackemailverifypending', 'badges', $data->email), + get_string('backpackemailverifypending', 'badges', $data->backpackemail), null, \core\output\notification::NOTIFY_INFO); } else { print_error ('backpackcannotsendverification', 'badges'); diff --git a/lib/badgeslib.php b/lib/badgeslib.php index 78228c05162..4b54cb6b7f3 100644 --- a/lib/badgeslib.php +++ b/lib/badgeslib.php @@ -762,7 +762,7 @@ function badges_local_backpack_js($checksite = false) { } /** - * Create the backpack with this data. + * Create the site backpack with this data. * * @param stdClass $data The new backpack data. * @return boolean @@ -773,14 +773,8 @@ function badges_create_site_backpack($data) { require_capability('moodle/badges:manageglobalsettings', $context); $count = $DB->count_records('badge_external_backpack'); - - $backpack = new stdClass(); - $backpack->apiversion = $data->apiversion; - $backpack->backpackapiurl = $data->backpackapiurl; - $backpack->backpackweburl = $data->backpackweburl; - $backpack->sortorder = $count; - $DB->insert_record('badge_external_backpack', $backpack); - return true; + $data->sortorder = $count; + return badges_save_external_backpack($data); } /** @@ -796,15 +790,8 @@ function badges_update_site_backpack($id, $data) { require_capability('moodle/badges:manageglobalsettings', $context); if ($backpack = badges_get_site_backpack($id)) { - $backpack = new stdClass(); - $backpack->id = $id; - $backpack->apiversion = $data->apiversion; - $backpack->backpackweburl = $data->backpackweburl; - $backpack->backpackapiurl = $data->backpackapiurl; - $backpack->password = !empty($data->password) ? $data->password : ''; - $backpack->oauth2_issuerid = !empty($data->oauth2_issuerid) ? $data->oauth2_issuerid : ''; - $DB->update_record('badge_external_backpack', $backpack); - return true; + $data->id = $id; + return badges_save_external_backpack($data); } return false; } @@ -850,6 +837,79 @@ function badges_delete_site_backpack($id) { return false; } +/** + * Perform the actual create/update of external bakpacks. Any checks on the validity of the id will need to be + * performed before it reaches this function. + * + * @param stdClass $data The backpack data we are updating/inserting + * @return int Returns the id of the new/updated record + */ +function badges_save_external_backpack(stdClass $data) { + global $DB; + $backpack = new stdClass(); + + $backpack->apiversion = $data->apiversion; + $backpack->backpackweburl = $data->backpackweburl; + $backpack->backpackapiurl = $data->backpackapiurl; + $backpack->backpackemail = $data->backpackemail; + $backpack->password = !empty($data->password) ? $data->password : ''; + $backpack->oauth2_issuerid = !empty($data->oauth2_issuerid) ? $data->oauth2_issuerid : ''; + if (isset($data->sortorder)) { + $backpack->sortorder = $data->sortorder; + } + + $method = 'insert_record'; + if (isset($data->id) && $data->id) { + $backpack->id = $data->id; + $method = 'update_record'; + } + $record = $DB->$method('badge_external_backpack', $backpack, true); + $data->externalbackpackid = $data->id ?? $record; + + unset($data->id); + badges_save_backpack_credentials($data); + + return $data->externalbackpackid; +} + +/** + * Create a backpack with the provided details. Stores the auth details of the backpack + * + * @param stdClass $data Backpack specific data. + * @return int The id of the external backpack that the credentials correspond to + */ +function badges_save_backpack_credentials(stdClass $data) { + global $DB; + + if (isset($data->backpackemail) && isset($data->password)) { + $backpack = new stdClass(); + + $backpack->email = $data->backpackemail; + $backpack->password = !empty($data->password) ? $data->password : ''; + $backpack->externalbackpackid = $data->externalbackpackid; + $backpack->userid = $data->userid ?? 0; + $backpack->backpackuid = $data->backpackuid ?? 0; + $backpack->autosync = $data->autosync ?? 0; + + $id = null; + if (isset($data->badgebackpack) && $data->badgebackpack) { + $id = $data->badgebackpack; + } else if (isset($data->id) && $data->id) { + $id = $data->id; + } + + $method = $id ? 'update_record' : 'insert_record'; + if ($id) { + $backpack->id = $id; + } + + $DB->$method('badge_backpack', $backpack); + return $backpack->externalbackpackid; + } + + return $data->externalbackpackid ?? 0; +} + /** * Is any backpack enabled that supports open badges V1? * @return boolean @@ -884,7 +944,11 @@ function badges_get_site_backpack($id) { function badges_get_site_backpacks() { global $DB, $CFG; - $all = $DB->get_records('badge_external_backpack'); + $sql = "SELECT beb.* + FROM {badge_external_backpack} beb + LEFT JOIN {badge_backpack} bb ON bb.externalbackpackid = beb.id + WHERE bb.id IS NULL"; + $all = $DB->get_records_sql($sql); foreach ($all as $key => $bp) { if ($bp->id == $CFG->badges_site_backpack) { diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index bd6770f77bc..c52e2f41b1a 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -2862,5 +2862,19 @@ function xmldb_main_upgrade($oldversion) { upgrade_main_savepoint(true, 2020102100.02); } + if ($oldversion < 2020102300.01) { + $table = new xmldb_table('badge_external_backpack'); + $field = new xmldb_field('backpackemail', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + $table->deleteKey('backpackapiurlkey'); + $table->deleteKey('backpackweburlkey'); + $table->add_key('backpackapiurlkey', XMLDB_KEY_UNIQUE, ['backpackapiurl', 'backpackemail']); + + // Main savepoint reached. + upgrade_main_savepoint(true, 2020102300.01); + } + return true; } diff --git a/version.php b/version.php index 3f09a9f1f30..3b91464230e 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2020102300.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2020102300.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. $release = '3.10dev+ (Build: 20201023)';// Human-friendly version name