MDL-65959 core_badges: Unrestricted user's badger account
authorPeter <peterrolanddias@gmail.com>
Wed, 25 Sep 2019 04:38:53 +0000 (12:38 +0800)
committerPeter Dias <peter@moodle.com>
Sun, 25 Oct 2020 15:03:59 +0000 (23:03 +0800)
* Restructure the email to be backpack specific
* Amended table defintion and functions

badges/backpackemailverify.php
badges/classes/backpack_api.php
badges/classes/form/backpack.php
badges/classes/form/external_backpack.php
badges/mybackpack.php
lib/badgeslib.php
lib/db/upgrade.php
version.php

index 9b563ff..62f0b75 100644 (file)
@@ -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');
index 0916dea..b25b39f 100644 (file)
@@ -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;
     }
index 6c7370c..6689a4f 100644 (file)
@@ -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'] .= '<br/><br/>';
-                    $errors['email'] .= get_string('backpackconnectionunexpectedmessage', 'badges', $msg);
+                    $errors['backpackemail'] .= '<br/><br/>';
+                    $errors['backpackemail'] .= get_string('backpackconnectionunexpectedmessage', 'badges', $msg);
                 }
             }
         }
index e5c495c..6fefec4 100644 (file)
@@ -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;
+    }
 }
index 61dd374..11ce5cc 100644 (file)
@@ -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');
index 78228c0..4b54cb6 100644 (file)
@@ -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) {
index b9c5d34..febd2fe 100644 (file)
@@ -2862,5 +2862,19 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2021052500.30);
     }
 
+    if ($oldversion < 2021052500.32) {
+        $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, 2021052500.32);
+    }
+
     return true;
 }
index 40a8acb..a1508dc 100644 (file)
@@ -29,7 +29,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$version  = 2021052500.31;              // YYYYMMDD      = weekly release date of this DEV branch.
+$version  = 2021052500.32;              // YYYYMMDD      = weekly release date of this DEV branch.
                                         //         RR    = release increments - 00 in DEV branches.
                                         //           .XX = incremental changes.
 $release  = '4.0dev (Build: 20201023)'; // Human-friendly version name