Skip to content

Commit

Permalink
Merge branch 'MDL-65959-master' of git://github.com/peterRd/moodle
Browse files Browse the repository at this point in the history
  • Loading branch information
sarjona committed Oct 26, 2020
2 parents 3dcb4f5 + ef447fc commit 5deb0da
Show file tree
Hide file tree
Showing 20 changed files with 1,086 additions and 196 deletions.
133 changes: 96 additions & 37 deletions badges/backpack-add.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@
require_once(__DIR__ . '/../config.php');
require_once($CFG->libdir . '/badgeslib.php');

if (badges_open_badges_backpack_api() != OPEN_BADGES_V2) {
require_login();

$userbackpack = badges_get_user_backpack();
if (badges_open_badges_backpack_api($userbackpack->id) != OPEN_BADGES_V2) {
throw new coding_exception('No backpacks support Open Badges V2.');
}

require_login();

$id = required_param('hash', PARAM_ALPHANUM);

$PAGE->set_url('/badges/backpack-add.php', array('hash' => $id));
Expand All @@ -44,53 +45,111 @@
// * Create badge
// * Create assertion (Award the badge!)

// With the introduction OBv2.1 and MDL-65959 to allow cross region Badgr imports the above (old) procedure will
// only be completely performed if both the site and user backpacks conform to the same apiversion.
// Else we will attempt at pushing the assertion to the user's backpack. In this case, the id set against the assertion
// has to be a publicly accessible resource.

// Get the backpack.
$badgeid = $issuedbadge->badgeid;
$badge = new badge($badgeid);
$backpack = $DB->get_record('badge_backpack', array('userid' => $USER->id));
$sitebackpack = badges_get_site_backpack($backpack->externalbackpackid);
$assertion = new core_badges_assertion($id, $sitebackpack->apiversion);
$api = new \core_badges\backpack_api($sitebackpack);
$api->authenticate();
$userbackpack = badges_get_site_backpack($backpack->externalbackpackid, $USER->id);
$assertion = new core_badges_assertion($id, OPEN_BADGES_V2);
$assertiondata = $assertion->get_badge_assertion(false, false);
$assertionid = $assertion->get_assertion_hash();
$assertionentityid = $assertiondata['id'];
$badgeadded = false;
if (badges_open_badges_backpack_api() == OPEN_BADGES_V2) {
$sitebackpack = badges_get_site_primary_backpack();
$api = new \core_badges\backpack_api($sitebackpack);
$response = $api->authenticate();

// Create issuer.
$issuer = $assertion->get_issuer();
if (!($issuerentityid = badges_external_get_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ISSUER, $issuer['email']))) {
$response = $api->put_issuer($issuer);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
}
$issuerentityid = $response->id;
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ISSUER, $issuer['email'], $issuerentityid);
}
// Create badge.
$badge = $assertion->get_badge_class(false);
$badgeid = $assertion->get_badge_id();
if (!($badgeentityid = badges_external_get_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_BADGE, $badgeid))) {
$response = $api->put_badgeclass($issuerentityid, $badge);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
}
$badgeentityid = $response->id;
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_BADGE, $badgeid, $badgeentityid);
}
// A numeric response indicates a valid successful authentication. Else an error object will be returned.
if (is_numeric($response)) {
// Create issuer.
$issuer = $assertion->get_issuer();
if (!($issuerentityid = badges_external_get_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ISSUER, $issuer['email']))) {
$response = $api->put_issuer($issuer);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
}
$issuerentityid = $response->id;
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ISSUER, $issuer['email'],
$issuerentityid);
}
// Create badge.
$badge = $assertion->get_badge_class(false);
$badgeid = $assertion->get_badge_id();
if (!($badgeentityid = badges_external_get_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_BADGE, $badgeid))) {
$response = $api->put_badgeclass($issuerentityid, $badge);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
}
$badgeentityid = $response->id;
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_BADGE, $badgeid,
$badgeentityid);
}

// Create assertion (Award the badge!).
$assertiondata = $assertion->get_badge_assertion(false, false);
// Create assertion (Award the badge!).
$assertionentityid = badges_external_get_mapping(
$sitebackpack->id,
OPEN_BADGES_V2_TYPE_ASSERTION,
$assertionid
);

$assertionid = $assertion->get_assertion_hash();
if ($assertionentityid && strpos($sitebackpack->backpackapiurl, 'badgr')) {
$assertionentityid = badges_generate_badgr_open_url(
$sitebackpack,
OPEN_BADGES_V2_TYPE_ASSERTION,
$assertionentityid
);
}

if (!($assertionentityid = badges_external_get_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid))) {
$response = $api->put_badgeclass_assertion($badgeentityid, $assertiondata);
// Create an assertion for the recipient in the issuer's account.
if (!$assertionentityid) {
$response = $api->put_badgeclass_assertion($badgeentityid, $assertiondata);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
}
$assertionentityid = badges_generate_badgr_open_url($sitebackpack, OPEN_BADGES_V2_TYPE_ASSERTION, $response->id);
$badgeadded = true;
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid,
$response->id);
} else {
// An assertion already exists. Make sure it's up to date.
$internalid = badges_external_get_mapping(
$sitebackpack->id,
OPEN_BADGES_V2_TYPE_ASSERTION,
$assertionid,
'externalid'
);
$response = $api->update_assertion($internalid, $assertiondata);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
}
}
}
}

// Now award/upload the badge to the user's account.
// - If a user and site backpack have the same provider we can skip this as Badgr automatically maps recipients
// based on email address.
// - This is only needed when the backpacks are from different regions.
if ($assertionentityid && !badges_external_get_mapping($userbackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid)) {
$userapi = new \core_badges\backpack_api($userbackpack, $backpack);
$userapi->authenticate();
$response = $userapi->import_badge_assertion($assertionentityid);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
}
$assertionentityid = $response->id;
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid, $assertionentityid);
$response = ['success' => 'addedtobackpack'];
} else {
$response = ['warning' => 'existsinbackpack'];
$badgeadded = true;
badges_external_create_mapping($userbackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid,
$assertionentityid);
}

$response = $badgeadded ? ['success' => 'addedtobackpack'] : ['warning' => 'existsinbackpack'];
redirect(new moodle_url('/badges/mybadges.php', $response));
} else {
redirect(new moodle_url('/badges/mybadges.php'));
Expand Down
7 changes: 4 additions & 3 deletions badges/backpack-connect.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,22 @@
require_once(__DIR__ . '/../config.php');
require_once($CFG->libdir . '/badgeslib.php');

$backpackid = required_param('backpackid', PARAM_INT);
$scope = optional_param('scope', '', PARAM_RAW);
$action = optional_param('action', null, PARAM_RAW);

if (badges_open_badges_backpack_api() != OPEN_BADGES_V2P1) {
if (badges_open_badges_backpack_api($backpackid) != OPEN_BADGES_V2P1) {
throw new coding_exception('backpacks only support Open Badges V2.1');
}

require_login();

$externalbackpack = badges_get_site_backpack($CFG->badges_site_backpack);
$externalbackpack = badges_get_site_backpack($backpackid);
$persistedissuer = \core\oauth2\issuer::get_record(['id' => $externalbackpack->oauth2_issuerid]);
if ($persistedissuer) {
$issuer = new \core\oauth2\issuer($externalbackpack->oauth2_issuerid);
$returnurl = new moodle_url('/badges/backpack-connect.php',
['action' => 'authorization', 'sesskey' => sesskey()]);
['action' => 'authorization', 'sesskey' => sesskey(), 'backpackid' => $backpackid]);

$client = new core_badges\oauth2\client($issuer, $returnurl, $scope, $externalbackpack);
if ($client) {
Expand Down
11 changes: 6 additions & 5 deletions badges/backpack-export.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@
require_once(__DIR__ . '/../config.php');
require_once($CFG->libdir . '/badgeslib.php');

if (badges_open_badges_backpack_api() != OPEN_BADGES_V2P1) {
throw new coding_exception('backpacks only support Open Badges V2.1');
}
$hash = optional_param('hash', null, PARAM_RAW);

$PAGE->set_pagelayout('admin');
Expand All @@ -37,7 +34,12 @@
if (empty($CFG->badges_allowexternalbackpack) || empty($CFG->enablebadges)) {
redirect($CFG->wwwroot);
}
$backpack = badges_get_site_backpack($CFG->badges_site_backpack);

$backpack = badges_get_user_backpack();
if (badges_open_badges_backpack_api($backpack->id) != OPEN_BADGES_V2P1) {
throw new coding_exception('backpacks only support Open Badges V2.1');
}

$userbadges = badges_get_user_badges($USER->id);
$context = context_user::instance($USER->id);

Expand All @@ -50,7 +52,6 @@

$redirecturl = new moodle_url('/badges/mybadges.php');
if ($hash) {
$backpack = badges_get_site_backpack($CFG->badges_site_backpack);
$api = new core_badges\backpack_api2p1($backpack);
$notify = $api->put_assertions($hash);
if (!empty($notify['status']) && $notify['status'] == \core\output\notification::NOTIFY_SUCCESS) {
Expand Down
20 changes: 11 additions & 9 deletions badges/backpackemailverify.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,21 @@
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');
unset_user_preference('badges_email_verify_address');
unset_user_preference('badges_email_verify_backpackid');
unset_user_preference('badges_email_verify_password');
redirect(new moodle_url($redirect), get_string('backpackemailverifysuccess', 'badges'),
null, \core\output\notification::NOTIFY_SUCCESS);
} else {
Expand Down
6 changes: 5 additions & 1 deletion badges/backpacks.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@
} else if ($data = $form->get_data()) {
require_sesskey();
if (!empty($data->id)) {
badges_update_site_backpack($data->id, $data);
$id = $data->id;
badges_update_site_backpack($id, $data);
// Apart from the password, any change here would result in an error in other parts of the badge systems.
// In order to negate this, we restart any further mapping from scratch.
badges_external_delete_mappings($id);
} else {
badges_create_site_backpack($data);
}
Expand Down
64 changes: 59 additions & 5 deletions badges/classes/backpack_api.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,13 @@ public function __construct($sitebackpack, $userbackpack = false) {
global $CFG;
$admin = get_admin();

$this->backpackapiurl = $sitebackpack->backpackapiurl;
$this->backpackapiurl = $sitebackpack->backpackapiurl;
$this->backpackapiversion = $sitebackpack->apiversion;
$this->password = $sitebackpack->password;
$this->email = !empty($CFG->badges_defaultissuercontact) ? $CFG->badges_defaultissuercontact : '';
$this->email = $sitebackpack->backpackemail;
$this->isuserbackpack = false;
$this->backpackid = $sitebackpack->id;
if (!empty($userbackpack)) {
if ($userbackpack->externalbackpackid != $sitebackpack->id) {
throw new coding_exception('Incorrect backpack');
}
$this->isuserbackpack = true;
$this->password = $userbackpack->password;
$this->email = $userbackpack->email;
Expand Down Expand Up @@ -152,6 +148,21 @@ private function define_mappings() {
true, // JSON Encoded.
true // Auth required.
];
$mapping[] = [
'importbadge', // Action.
// Badgr.io does not return the public information about a badge
// if the issuer is associated with another user. We need to pass
// the expand parameters which are not in any specification to get
// additional information about the assertion in a single request.
'[URL]/backpack/import',
['url' => '[PARAM]'], // Post params.
'', // Request exporter.
'core_badges\external\assertion_exporter', // Response exporter.
false, // Multiple.
'post', // Method.
true, // JSON Encoded.
true // Auth required.
];
$mapping[] = [
'badges', // Action.
'[URL]/backpack/collections/[PARAM1]', // URL
Expand Down Expand Up @@ -214,6 +225,17 @@ private function define_mappings() {
true, // JSON Encoded.
true // Auth required.
];
$mapping[] = [
'updateassertion', // Action.
'[URL]/assertions/[PARAM2]?expand=badgeclass&expand=issuer',
'[PARAM]', // Post params.
'core_badges\external\assertion_exporter', // Request exporter.
'core_badges\external\assertion_exporter', // Response exporter.
false, // Multiple.
'put', // Method.
true, // JSON Encoded.
true // Auth required.
];
foreach ($mapping as $map) {
$map[] = false; // Site api function.
$map[] = OPEN_BADGES_V2; // V2 function.
Expand Down Expand Up @@ -408,6 +430,38 @@ public function put_badgeclass_assertion($entityid, $data) {
return $this->curl_request('assertions', null, $entityid, $data);
}

/**
* Update a badgeclass assertion.
*
* @param string $entityid The id of the badge class.
* @param array $data The structure of the badge class assertion.
* @return mixed
*/
public function update_assertion(string $entityid, array $data) {
// V2 Only.
if ($this->backpackapiversion == OPEN_BADGES_V1) {
throw new coding_exception('Not supported in this backpack API');
}

return $this->curl_request('updateassertion', null, $entityid, $data);
}

/**
* Import a badge assertion into a backpack. This is used to handle cross domain backpacks.
*
* @param string $data The structure of the badge class assertion.
* @return mixed
* @throws coding_exception
*/
public function import_badge_assertion(string $data) {
// V2 Only.
if ($this->backpackapiversion == OPEN_BADGES_V1) {
throw new coding_exception('Not supported in this backpack API');
}

return $this->curl_request('importbadge', null, null, $data);
}

/**
* Select collections from a backpack.
*
Expand Down
5 changes: 5 additions & 0 deletions badges/classes/backpack_api_mapping.php
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ private function get_post_params($email, $password, $param) {
} else if ($value == '[PASSWORD]') {
$value = $password;
$request[$key] = $value;
} else if ($value == '[PARAM]') {
$request[$key] = is_array($param) ? $param[0] : $param;
}
}
}
Expand Down Expand Up @@ -312,6 +314,7 @@ private function get_curl_options() {
return array(
'FRESH_CONNECT' => true,
'RETURNTRANSFER' => true,
'FOLLOWLOCATION' => true,
'FORBID_REUSE' => true,
'HEADER' => 0,
'CONNECTTIMEOUT' => 3,
Expand Down Expand Up @@ -359,6 +362,8 @@ public function request($apiurl, $urlparam1, $urlparam2, $email, $password, $pos
$response = $curl->get($url, $post, $options);
} else if ($this->method == 'post') {
$response = $curl->post($url, $post, $options);
} else if ($this->method == 'put') {
$response = $curl->put($url, $post, $options);
}
$response = json_decode($response);
if (isset($response->result)) {
Expand Down
Loading

0 comments on commit 5deb0da

Please sign in to comment.