From 50d1e69df6a834662519275bbb90bfa75662022f 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 | 28 +++++----- badges/classes/backpack_api2p1.php | 54 +++++++++++++++++-- .../oauth2/discovery/imsbadgeconnect.php | 10 ++++ 3 files changed, 73 insertions(+), 19 deletions(-) diff --git a/admin/tool/oauth2/tests/behat/basic_settings.feature b/admin/tool/oauth2/tests/behat/basic_settings.feature index 53e6ec12b63..565cc356d16 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 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 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 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 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 "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 "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 "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 055a3c393b9..fe85d9a1d39 100644 --- a/badges/classes/backpack_api2p1.php +++ b/badges/classes/backpack_api2p1.php @@ -36,6 +36,9 @@ use core_badges\backpack_api2p1_mapping; 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 @@ class backpack_api2p1 { 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 @@ class backpack_api2p1 { $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 @@ class backpack_api2p1 { 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 @@ class backpack_api2p1 { 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 @@ class backpack_api2p1 { 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 2885f91b90e..0c8489b9ec9 100644 --- a/lib/classes/oauth2/discovery/imsbadgeconnect.php +++ b/lib/classes/oauth2/discovery/imsbadgeconnect.php @@ -82,6 +82,16 @@ class imsbadgeconnect extends base_definition { $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(); } } }