From bd214de2825a2faa514f1351e254db33d791c1da Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Wed, 26 Apr 2023 14:27:21 +0800 Subject: [PATCH] MDL-75552 badges: move apiBase consumption to backpack The apiBase in .well-known/badgeconnect.json was ignored and it was causing some failures when connecting or sending badges to an external backpack. For OBv2.1, it has been changed to always use the apiBase defined in the badgeconnect.json backpack provider. --- .../oauth2/tests/behat/basic_settings.feature | 24 ++++----- badges/classes/backpack_api2p1.php | 54 +++++++++++++++++-- .../oauth2/discovery/imsbadgeconnect.php | 10 ++++ 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/admin/tool/oauth2/tests/behat/basic_settings.feature b/admin/tool/oauth2/tests/behat/basic_settings.feature index 53e6ec12b6324..565cc356d16a5 100644 --- a/admin/tool/oauth2/tests/behat/basic_settings.feature +++ b/admin/tool/oauth2/tests/behat/basic_settings.feature @@ -151,25 +151,25 @@ Feature: Basic OAuth2 functionality | Service base URL | https://dc.imsglobal.org/ | When I press "Save changes" Then I should see "Changes saved" - And I should see "Open Badges" - And "Allow services" "icon" should exist in the "Open Badges" "table_row" - And "Do not allow login" "icon" should exist in the "Open Badges" "table_row" - And "Service discovery successful" "icon" should exist in the "Open Badges" "table_row" + And I should see "IMS Global Reference Implementation" + And I click on "Edit" "link" in the "IMS Global Reference Implementation" "table_row" + And I set the following fields to these values: + | Name | IMS Global | + And I press "Save changes" + And I should see "Changes saved" + And I should see "IMS Global" + And "Allow services" "icon" should exist in the "IMS Global" "table_row" + And "Do not allow login" "icon" should exist in the "IMS Global" "table_row" + And "Service discovery successful" "icon" should exist in the "IMS Global" "table_row" And the "src" attribute of "table.admintable th img" "css_element" should contain "IMS-Global-Logo.png" - And I click on "Configure endpoints" "link" in the "Open Badges" "table_row" + And I click on "Configure endpoints" "link" in the "IMS Global" "table_row" And I should see "https://dc.imsglobal.org/.well-known/badgeconnect.json" in the "discovery_endpoint" "table_row" And I should see "authorization_endpoint" And I navigate to "Server > OAuth 2 services" in site administration - And I click on "Configure user field mappings" "link" in the "Open Badges" "table_row" + And I click on "Configure user field mappings" "link" in the "IMS Global" "table_row" And I should not see "given_name" And I should not see "middle_name" And I navigate to "Server > OAuth 2 services" in site administration - And I click on "Edit" "link" in the "Open Badges" "table_row" - And I set the following fields to these values: - | Name | IMS Global | - And I press "Save changes" - And I should see "Changes saved" - And I should see "IMS Global" And I click on "Delete" "link" in the "IMS Global" "table_row" And I should see "Are you sure you want to delete the identity issuer \"IMS Global\"?" And I press "Continue" diff --git a/badges/classes/backpack_api2p1.php b/badges/classes/backpack_api2p1.php index 1d90b420759b0..f382908925242 100644 --- a/badges/classes/backpack_api2p1.php +++ b/badges/classes/backpack_api2p1.php @@ -36,6 +36,9 @@ use core_badges\oauth2\client; use curl; use stdClass; +use core\oauth2\issuer; +use core\oauth2\endpoint; +use core\oauth2\discovery\imsbadgeconnect; /** * To process badges with backpack and control api request and this class using for Open Badge API v2.1 methods. @@ -61,8 +64,11 @@ class backpack_api2p1 { /** @var null version api of the backpack. */ protected $backpackapiversion; - /** @var null api URL of the backpack. */ - protected $backpackapiurl = ''; + /** @var issuer The OAuth2 Issuer for this backpack */ + protected issuer $issuer; + + /** @var endpoint The apiBase endpoint */ + protected endpoint $apibase; /** * backpack_api2p1 constructor. @@ -75,7 +81,6 @@ public function __construct($externalbackpack) { if (!empty($externalbackpack)) { $this->externalbackpack = $externalbackpack; $this->backpackapiversion = $externalbackpack->apiversion; - $this->backpackapiurl = $externalbackpack->backpackapiurl; $this->get_clientid = $this->get_clientid($externalbackpack->oauth2_issuerid); if (!($this->tokendata = $this->get_stored_token($externalbackpack->id)) @@ -87,6 +92,44 @@ public function __construct($externalbackpack) { $this->define_mappings(); } + /** + * Initialises or returns the OAuth2 issuer associated to this backpack. + * + * @return issuer + */ + protected function get_issuer(): issuer { + if (!isset($this->issuer)) { + $this->issuer = new \core\oauth2\issuer($this->externalbackpack->oauth2_issuerid); + } + return $this->issuer; + } + + /** + * Gets the apiBase url associated to this backpack. + * + * @return string + */ + protected function get_api_base_url(): string { + if (!isset($this->apibase)) { + $apibase = endpoint::get_record([ + 'issuerid' => $this->externalbackpack->oauth2_issuerid, + 'name' => 'apiBase', + ]); + + if (empty($apibase)) { + imsbadgeconnect::create_endpoints($this->get_issuer()); + $apibase = endpoint::get_record([ + 'issuerid' => $this->externalbackpack->oauth2_issuerid, + 'name' => 'apiBase', + ]); + } + + $this->apibase = $apibase; + } + + return $this->apibase->get('url'); + } + /** * Define the mappings supported by this usage and api version. @@ -157,7 +200,7 @@ public function curl_request($action, $postdata = null) { foreach ($this->mappings as $mapping) { if ($mapping->is_match($action)) { return $mapping->request( - $this->backpackapiurl, + $this->get_api_base_url(), $tokenkey, $postdata ); @@ -194,6 +237,7 @@ protected function get_stored_token($externalbackpackid) { private function get_clientid($issuerid) { $issuer = \core\oauth2\api::get_issuer($issuerid); if (!empty($issuer)) { + $this->issuer = $issuer; $this->clientid = $issuer->get('clientid'); } } @@ -212,7 +256,7 @@ public function put_assertions($hash) { return false; } - $issuer = new \core\oauth2\issuer($this->externalbackpack->oauth2_issuerid); + $issuer = $this->get_issuer(); $client = new client($issuer, new moodle_url('/badges/mybadges.php'), '', $this->externalbackpack); if (!$client->is_logged_in()) { $redirecturl = new moodle_url('/badges/mybadges.php', ['error' => 'backpackexporterror']); diff --git a/lib/classes/oauth2/discovery/imsbadgeconnect.php b/lib/classes/oauth2/discovery/imsbadgeconnect.php index 2885f91b90ebc..0c8489b9ec9d3 100644 --- a/lib/classes/oauth2/discovery/imsbadgeconnect.php +++ b/lib/classes/oauth2/discovery/imsbadgeconnect.php @@ -82,6 +82,16 @@ protected static function process_configuration_json(issuer $issuer, stdClass $i $issuer->set('image', $url); $issuer->update(); } + } else if ($key == 'apiBase') { + (new endpoint(0, (object) [ + 'issuerid' => $issuer->get('id'), + 'name' => $key, + 'url' => $value, + ]))->create(); + } else if ($key == 'name') { + // Get and update issuer name. + $issuer->set('name', $value); + $issuer->update(); } } }