From 5d93de8b670ab247091149a3ae93d45409f7c75f Mon Sep 17 00:00:00 2001 From: Sara Arjona Date: Fri, 19 Mar 2021 13:54:04 +0100 Subject: [PATCH 1/4] MDL-70722 core_badges: minor fixes from MDL-70689 In MDL-70689, Eloy and Helen suggested some fixes to improve this patch. As this is a followup issue to move the pending services, this very first commit will fix pending things raised in the parent issue. --- admin/tool/oauth2/issuers.php | 12 +++++------ admin/tool/oauth2/lang/en/tool_oauth2.php | 4 ++-- .../oauth2/tests/behat/basic_settings.feature | 20 +++++++++---------- lib/classes/oauth2/issuer.php | 2 +- lib/classes/oauth2/service/imsobv2p1.php | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/admin/tool/oauth2/issuers.php b/admin/tool/oauth2/issuers.php index cc5da40997d..6383ef2c46b 100644 --- a/admin/tool/oauth2/issuers.php +++ b/admin/tool/oauth2/issuers.php @@ -53,7 +53,7 @@ if ($action == 'edit') { if ($issuer) { $PAGE->navbar->add(get_string('editissuer', 'tool_oauth2', s($issuer->get('name')))); } else { - $PAGE->navbar->add(get_string('createnewservice', 'tool_oauth2') . get_string('custom_service', 'tool_oauth2')); + $PAGE->navbar->add(get_string('createnewservice', 'tool_oauth2') . ' ' . get_string('custom_service', 'tool_oauth2')); } $showrequireconfirm = false; @@ -96,7 +96,7 @@ if ($mform && $mform->is_cancelled()) { if ($issuer) { echo $OUTPUT->heading(get_string('editissuer', 'tool_oauth2', s($issuer->get('name')))); } else { - echo $OUTPUT->heading(get_string('createnewservice', 'tool_oauth2') . get_string('custom_service', 'tool_oauth2')); + echo $OUTPUT->heading(get_string('createnewservice', 'tool_oauth2') . ' ' . get_string('custom_service', 'tool_oauth2')); } $mform->display(); echo $OUTPUT->footer(); @@ -115,7 +115,7 @@ if ($mform && $mform->is_cancelled()) { redirect($PAGE->url, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS); } else { echo $OUTPUT->header(); - echo $OUTPUT->heading(get_string('createnewservice', 'tool_oauth2') . get_string($type . '_service', 'tool_oauth2')); + echo $OUTPUT->heading(get_string('createnewservice', 'tool_oauth2') . ' ' . get_string($type . '_service', 'tool_oauth2')); $mform->display(); echo $OUTPUT->footer(); } @@ -130,9 +130,9 @@ if ($mform && $mform->is_cancelled()) { $mform = new \tool_oauth2\form\issuer(null, ['persistent' => $issuer, 'type' => $type, 'showrequireconfirm' => $showrequireconfirm]); - $PAGE->navbar->add(get_string('createnewservice', 'tool_oauth2') . get_string($type . '_service', 'tool_oauth2')); + $PAGE->navbar->add(get_string('createnewservice', 'tool_oauth2') . ' ' . get_string($type . '_service', 'tool_oauth2')); echo $OUTPUT->header(); - echo $OUTPUT->heading(get_string('createnewservice', 'tool_oauth2') . get_string($type . '_service', 'tool_oauth2')); + echo $OUTPUT->heading(get_string('createnewservice', 'tool_oauth2') . ' ' . get_string($type . '_service', 'tool_oauth2')); $mform->display(); echo $OUTPUT->footer(); @@ -199,7 +199,7 @@ if ($mform && $mform->is_cancelled()) { echo $renderer->issuers_table($issuers); echo $renderer->container_start(); - echo get_string('createnewservice', 'tool_oauth2'); + echo get_string('createnewservice', 'tool_oauth2') . ' '; // Google template. $docs = 'admin/tool/oauth2/issuers/google'; diff --git a/admin/tool/oauth2/lang/en/tool_oauth2.php b/admin/tool/oauth2/lang/en/tool_oauth2.php index 938c576bcba..9ff13af03a4 100644 --- a/admin/tool/oauth2/lang/en/tool_oauth2.php +++ b/admin/tool/oauth2/lang/en/tool_oauth2.php @@ -31,7 +31,7 @@ $string['connectsystemaccount'] = 'Connect to a system account'; $string['createfromtemplate'] = 'Create an OAuth 2 service from a template'; $string['createfromtemplatedesc'] = 'Choose one of the OAuth 2 service templates below to create an OAuth service with a valid configuration for one of the known service types. This will create the OAuth 2 service, with all the correct end points and parameters required for authentication, though you will still need to enter the client ID and secret for the new service before it can be used.'; $string['createnewendpoint'] = 'Create new endpoint for issuer "{$a}"'; -$string['createnewservice'] = 'Create new service: '; +$string['createnewservice'] = 'Create new service:'; $string['createnewuserfieldmapping'] = 'Create new user field mapping for issuer "{$a}"'; $string['custom_service'] = 'Custom'; $string['deleteconfirm'] = 'Are you sure you want to delete the identity issuer "{$a}"? Any plugins relying on this issuer will stop working.'; @@ -53,7 +53,7 @@ $string['endpointurl_help'] = 'URL for this endpoint. Must use https:// protocol $string['endpointurl'] = 'URL'; $string['facebook_service'] = 'Facebook'; $string['google_service'] = 'Google'; -$string['imsobv2p1_service'] = 'IMS OBv2.1'; +$string['imsobv2p1_service'] = 'OpenBadges'; $string['issuersetup'] = 'Detailed instructions on configuring the common OAuth 2 services'; $string['issuersetuptype'] = 'Detailed instructions on setting up the {$a} OAuth 2 provider'; $string['issueralloweddomains_help'] = 'If set, this setting is a comma separated list of domains that logins will be restricted to when using this provider.'; diff --git a/admin/tool/oauth2/tests/behat/basic_settings.feature b/admin/tool/oauth2/tests/behat/basic_settings.feature index 2eb33451ac6..429d3713e1b 100644 --- a/admin/tool/oauth2/tests/behat/basic_settings.feature +++ b/admin/tool/oauth2/tests/behat/basic_settings.feature @@ -142,29 +142,29 @@ Feature: Basic OAuth2 functionality And I should see "Identity issuer deleted" And I should not see "Testing service modified" - Scenario: Create, edit and delete standard service for IMS OBv2.1 - Given I press "IMS OBv2.1" - And I should see "Create new service: IMS OBv2.1" + Scenario: Create, edit and delete standard service for OpenBadges + Given I press "OpenBadges" + And I should see "Create new service: OpenBadges" And I set the following fields to these values: | Client ID | thisistheclientid | | Client secret | supersecret | | Service base URL | https://dc.imsglobal.org/ | When I press "Save changes" Then I should see "Changes saved" - And I should see "IMS OBv2.1" - And "Configured" "icon" should exist in the "IMS OBv2.1" "table_row" - And "Do not allow login" "icon" should exist in the "IMS OBv2.1" "table_row" - And "Service discovery successful" "icon" should exist in the "IMS OBv2.1" "table_row" + And I should see "OpenBadges" + And "Configured" "icon" should exist in the "OpenBadges" "table_row" + And "Do not allow login" "icon" should exist in the "OpenBadges" "table_row" + And "Service discovery successful" "icon" should exist in the "OpenBadges" "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 OBv2.1" "table_row" + And I click on "Configure endpoints" "link" in the "OpenBadges" "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 follow "OAuth 2 services" - And I click on "Configure user field mappings" "link" in the "IMS OBv2.1" "table_row" + And I click on "Configure user field mappings" "link" in the "OpenBadges" "table_row" And I should not see "given_name" And I should not see "middle_name" And I follow "OAuth 2 services" - And I click on "Edit" "link" in the "IMS OBv2.1" "table_row" + And I click on "Edit" "link" in the "OpenBadges" "table_row" And I set the following fields to these values: | Name | IMS Global | And I press "Save changes" diff --git a/lib/classes/oauth2/issuer.php b/lib/classes/oauth2/issuer.php index b7545752ce2..ce4d32f384b 100644 --- a/lib/classes/oauth2/issuer.php +++ b/lib/classes/oauth2/issuer.php @@ -111,7 +111,7 @@ class issuer extends persistent { 'default' => true ), 'servicetype' => array( - 'type' => PARAM_TEXT, + 'type' => PARAM_ALPHANUM, 'null' => NULL_ALLOWED, 'default' => null, ), diff --git a/lib/classes/oauth2/service/imsobv2p1.php b/lib/classes/oauth2/service/imsobv2p1.php index 1edd552aef5..80adb3a55c1 100644 --- a/lib/classes/oauth2/service/imsobv2p1.php +++ b/lib/classes/oauth2/service/imsobv2p1.php @@ -35,7 +35,7 @@ class imsobv2p1 extends imsbadgeconnect implements issuer_interface { */ public static function init(): ?issuer { $record = (object) [ - 'name' => 'IMS OBv2.1', + 'name' => 'OpenBadges', 'image' => '', 'servicetype' => 'imsobv2p1', ]; From 0b53d70ae916092635338aae705183f46519ef1a Mon Sep 17 00:00:00 2001 From: Sara Arjona Date: Wed, 27 Jan 2021 17:37:16 +0100 Subject: [PATCH 2/4] MDL-70722 oauth2: move Facebook methods to service class --- lib/classes/oauth2/api.php | 89 +------------------ lib/classes/oauth2/service/facebook.php | 111 ++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 88 deletions(-) create mode 100644 lib/classes/oauth2/service/facebook.php diff --git a/lib/classes/oauth2/api.php b/lib/classes/oauth2/api.php index 5f76b38ad23..6c1032d3297 100644 --- a/lib/classes/oauth2/api.php +++ b/lib/classes/oauth2/api.php @@ -40,85 +40,6 @@ use moodle_exception; */ class api { - /** - * Build a facebook ready OAuth 2 service. - * @return \core\oauth2\issuer - */ - private static function init_facebook() { - // Facebook is a custom setup. - $record = (object) [ - 'name' => 'Facebook', - 'image' => 'https://facebookbrand.com/wp-content/uploads/2016/05/flogo_rgb_hex-brc-site-250.png', - 'baseurl' => '', - 'loginscopes' => 'public_profile email', - 'loginscopesoffline' => 'public_profile email', - 'showonloginpage' => true, - 'servicetype' => 'facebook', - ]; - - $issuer = new issuer(0, $record); - return $issuer; - } - - /** - * Create endpoints for facebook issuers. - * @param issuer $issuer issuer the endpoints should be created for. - * @return mixed - * @throws \coding_exception - * @throws \core\invalid_persistent_exception - */ - private static function create_endpoints_for_facebook($issuer) { - // The Facebook API version. - $apiversion = '2.12'; - // The Graph API URL. - $graphurl = 'https://graph.facebook.com/v' . $apiversion; - // User information fields that we want to fetch. - $infofields = [ - 'id', - 'first_name', - 'last_name', - 'link', - 'picture.type(large)', - 'name', - 'email', - ]; - $endpoints = [ - 'authorization_endpoint' => sprintf('https://www.facebook.com/v%s/dialog/oauth', $apiversion), - 'token_endpoint' => $graphurl . '/oauth/access_token', - 'userinfo_endpoint' => $graphurl . '/me?fields=' . implode(',', $infofields) - ]; - - foreach ($endpoints as $name => $url) { - $record = (object) [ - 'issuerid' => $issuer->get('id'), - 'name' => $name, - 'url' => $url - ]; - $endpoint = new endpoint(0, $record); - $endpoint->create(); - } - - // Create the field mappings. - $mapping = [ - 'name' => 'alternatename', - 'last_name' => 'lastname', - 'email' => 'email', - 'first_name' => 'firstname', - 'picture-data-url' => 'picture', - 'link' => 'url', - ]; - foreach ($mapping as $external => $internal) { - $record = (object) [ - 'issuerid' => $issuer->get('id'), - 'externalfield' => $external, - 'internalfield' => $internal - ]; - $userfieldmapping = new user_field_mapping(0, $record); - $userfieldmapping->create(); - } - return $issuer; - } - /** * Build a microsoft ready OAuth 2 service. * @return \core\oauth2\issuer @@ -267,8 +188,6 @@ class api { // TODO: Move these methods to new service classes (to make this API easier to understand and maintain). if ($type == 'microsoft') { return self::init_microsoft(); - } else if ($type == 'facebook') { - return self::init_facebook(); } else if ($type == 'nextcloud') { return self::init_nextcloud(); } else { @@ -292,8 +211,6 @@ class api { // TODO: Move these methods to new service classes (to make this API easier to understand and maintain). if ($type == 'microsoft') { return self::create_endpoints_for_microsoft($issuer); - } else if ($type == 'facebook') { - return self::create_endpoints_for_facebook($issuer); } else if ($type == 'nextcloud') { return self::create_endpoints_for_nextcloud($issuer); } else { @@ -322,6 +239,7 @@ class api { throw new moodle_exception('IMS OBv2.1 service type requires the baseurl parameter.'); } case 'google': + case 'facebook': $classname = self::get_service_classname($type); $issuer = $classname::init(); if ($baseurl) { @@ -335,11 +253,6 @@ class api { $issuer->create(); return self::create_endpoints_for_microsoft($issuer); - case 'facebook': - $issuer = self::init_facebook(); - $issuer->create(); - return self::create_endpoints_for_facebook($issuer); - case 'nextcloud': if (!$baseurl) { throw new moodle_exception('Nextcloud service type requires the baseurl parameter.'); diff --git a/lib/classes/oauth2/service/facebook.php b/lib/classes/oauth2/service/facebook.php new file mode 100644 index 00000000000..92ec1f513d2 --- /dev/null +++ b/lib/classes/oauth2/service/facebook.php @@ -0,0 +1,111 @@ +. + +namespace core\oauth2\service; + +use core\oauth2\issuer; +use core\oauth2\endpoint; +use core\oauth2\user_field_mapping; +use core\oauth2\discovery\openidconnect; + +/** + * Class for Facebook oAuth service, with the specific methods related to it. + * + * @package core + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class facebook extends openidconnect implements issuer_interface { + + /** + * Build an OAuth2 issuer, with all the default values for this service. + * + * @return issuer The issuer initialised with proper default values. + */ + public static function init(): issuer { + $record = (object) [ + 'name' => 'Facebook', + 'image' => 'https://facebookbrand.com/wp-content/uploads/2016/05/flogo_rgb_hex-brc-site-250.png', + 'baseurl' => '', + 'loginscopes' => 'public_profile email', + 'loginscopesoffline' => 'public_profile email', + 'showonloginpage' => true, + 'servicetype' => 'facebook', + ]; + + $issuer = new issuer(0, $record); + return $issuer; + } + + /** + * Create endpoints for this issuer. + * + * @param issuer $issuer Issuer the endpoints should be created for. + * @return issuer + */ + public static function create_endpoints(issuer $issuer): issuer { + // The Facebook API version. + $apiversion = '2.12'; + // The Graph API URL. + $graphurl = 'https://graph.facebook.com/v' . $apiversion; + // User information fields that we want to fetch. + $infofields = [ + 'id', + 'first_name', + 'last_name', + 'link', + 'picture.type(large)', + 'name', + 'email', + ]; + $endpoints = [ + 'authorization_endpoint' => sprintf('https://www.facebook.com/v%s/dialog/oauth', $apiversion), + 'token_endpoint' => $graphurl . '/oauth/access_token', + 'userinfo_endpoint' => $graphurl . '/me?fields=' . implode(',', $infofields) + ]; + + foreach ($endpoints as $name => $url) { + $record = (object) [ + 'issuerid' => $issuer->get('id'), + 'name' => $name, + 'url' => $url + ]; + $endpoint = new endpoint(0, $record); + $endpoint->create(); + } + + // Create the field mappings. + $mapping = [ + 'name' => 'alternatename', + 'last_name' => 'lastname', + 'email' => 'email', + 'first_name' => 'firstname', + 'picture-data-url' => 'picture', + 'link' => 'url', + ]; + foreach ($mapping as $external => $internal) { + $record = (object) [ + 'issuerid' => $issuer->get('id'), + 'externalfield' => $external, + 'internalfield' => $internal + ]; + $userfieldmapping = new user_field_mapping(0, $record); + $userfieldmapping->create(); + } + + return $issuer; + } +} From fdaa958ff7c0882f891759e1aa86d138bb7d6647 Mon Sep 17 00:00:00 2001 From: Sara Arjona Date: Fri, 19 Mar 2021 14:32:15 +0100 Subject: [PATCH 3/4] MDL-70722 oauth2: move Microsoft methods to service class --- lib/classes/oauth2/api.php | 82 +------------------- lib/classes/oauth2/service/microsoft.php | 98 ++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 79 deletions(-) create mode 100644 lib/classes/oauth2/service/microsoft.php diff --git a/lib/classes/oauth2/api.php b/lib/classes/oauth2/api.php index 6c1032d3297..046570a1be5 100644 --- a/lib/classes/oauth2/api.php +++ b/lib/classes/oauth2/api.php @@ -40,74 +40,6 @@ use moodle_exception; */ class api { - /** - * Build a microsoft ready OAuth 2 service. - * @return \core\oauth2\issuer - */ - private static function init_microsoft() { - // Microsoft is a custom setup. - $record = (object) [ - 'name' => 'Microsoft', - 'image' => 'https://www.microsoft.com/favicon.ico', - 'baseurl' => '', - 'loginscopes' => 'openid profile email user.read', - 'loginscopesoffline' => 'openid profile email user.read offline_access', - 'showonloginpage' => true, - 'servicetype' => 'microsoft', - ]; - - $issuer = new issuer(0, $record); - return $issuer; - } - - /** - * Create endpoints for microsoft issuers. - * @param issuer $issuer issuer the endpoints should be created for. - * @return mixed - * @throws \coding_exception - * @throws \core\invalid_persistent_exception - */ - private static function create_endpoints_for_microsoft($issuer) { - - $endpoints = [ - 'authorization_endpoint' => 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize', - 'token_endpoint' => 'https://login.microsoftonline.com/common/oauth2/v2.0/token', - 'userinfo_endpoint' => 'https://graph.microsoft.com/v1.0/me/', - 'userpicture_endpoint' => 'https://graph.microsoft.com/v1.0/me/photo/$value', - ]; - - foreach ($endpoints as $name => $url) { - $record = (object) [ - 'issuerid' => $issuer->get('id'), - 'name' => $name, - 'url' => $url - ]; - $endpoint = new endpoint(0, $record); - $endpoint->create(); - } - - // Create the field mappings. - $mapping = [ - 'givenName' => 'firstname', - 'surname' => 'lastname', - 'userPrincipalName' => 'email', - 'displayName' => 'alternatename', - 'officeLocation' => 'address', - 'mobilePhone' => 'phone1', - 'preferredLanguage' => 'lang' - ]; - foreach ($mapping as $external => $internal) { - $record = (object) [ - 'issuerid' => $issuer->get('id'), - 'externalfield' => $external, - 'internalfield' => $internal - ]; - $userfieldmapping = new user_field_mapping(0, $record); - $userfieldmapping->create(); - } - return $issuer; - } - /** * Build a nextcloud ready OAuth 2 service. * @return \core\oauth2\issuer @@ -186,9 +118,7 @@ class api { require_capability('moodle/site:config', context_system::instance()); // TODO: Move these methods to new service classes (to make this API easier to understand and maintain). - if ($type == 'microsoft') { - return self::init_microsoft(); - } else if ($type == 'nextcloud') { + if ($type == 'nextcloud') { return self::init_nextcloud(); } else { $classname = self::get_service_classname($type); @@ -209,9 +139,7 @@ class api { require_capability('moodle/site:config', context_system::instance()); // TODO: Move these methods to new service classes (to make this API easier to understand and maintain). - if ($type == 'microsoft') { - return self::create_endpoints_for_microsoft($issuer); - } else if ($type == 'nextcloud') { + if ($type == 'nextcloud') { return self::create_endpoints_for_nextcloud($issuer); } else { $classname = self::get_service_classname($type); @@ -240,6 +168,7 @@ class api { } case 'google': case 'facebook': + case 'microsoft': $classname = self::get_service_classname($type); $issuer = $classname::init(); if ($baseurl) { @@ -248,11 +177,6 @@ class api { $issuer->create(); return self::create_endpoints_for_standard_issuer($type, $issuer); - case 'microsoft': - $issuer = self::init_microsoft(); - $issuer->create(); - return self::create_endpoints_for_microsoft($issuer); - case 'nextcloud': if (!$baseurl) { throw new moodle_exception('Nextcloud service type requires the baseurl parameter.'); diff --git a/lib/classes/oauth2/service/microsoft.php b/lib/classes/oauth2/service/microsoft.php new file mode 100644 index 00000000000..d5d6e7ff7d2 --- /dev/null +++ b/lib/classes/oauth2/service/microsoft.php @@ -0,0 +1,98 @@ +. + +namespace core\oauth2\service; + +use core\oauth2\issuer; +use core\oauth2\endpoint; +use core\oauth2\user_field_mapping; +use core\oauth2\discovery\openidconnect; + +/** + * Class for Microsoft oAuth service, with the specific methods related to it. + * + * @package core + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class microsoft extends openidconnect implements issuer_interface { + + /** + * Build an OAuth2 issuer, with all the default values for this service. + * + * @return issuer The issuer initialised with proper default values. + */ + public static function init(): issuer { + $record = (object) [ + 'name' => 'Microsoft', + 'image' => 'https://www.microsoft.com/favicon.ico', + 'baseurl' => '', + 'loginscopes' => 'openid profile email user.read', + 'loginscopesoffline' => 'openid profile email user.read offline_access', + 'showonloginpage' => true, + 'servicetype' => 'microsoft', + ]; + + $issuer = new issuer(0, $record); + return $issuer; + } + + /** + * Create endpoints for this issuer. + * + * @param issuer $issuer Issuer the endpoints should be created for. + * @return issuer + */ + public static function create_endpoints(issuer $issuer): issuer { + $endpoints = [ + 'authorization_endpoint' => 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize', + 'token_endpoint' => 'https://login.microsoftonline.com/common/oauth2/v2.0/token', + 'userinfo_endpoint' => 'https://graph.microsoft.com/v1.0/me/', + 'userpicture_endpoint' => 'https://graph.microsoft.com/v1.0/me/photo/$value', + ]; + foreach ($endpoints as $name => $url) { + $record = (object) [ + 'issuerid' => $issuer->get('id'), + 'name' => $name, + 'url' => $url + ]; + $endpoint = new endpoint(0, $record); + $endpoint->create(); + } + + // Create the field mappings. + $mapping = [ + 'givenName' => 'firstname', + 'surname' => 'lastname', + 'userPrincipalName' => 'email', + 'displayName' => 'alternatename', + 'officeLocation' => 'address', + 'mobilePhone' => 'phone1', + 'preferredLanguage' => 'lang' + ]; + foreach ($mapping as $external => $internal) { + $record = (object) [ + 'issuerid' => $issuer->get('id'), + 'externalfield' => $external, + 'internalfield' => $internal + ]; + $userfieldmapping = new user_field_mapping(0, $record); + $userfieldmapping->create(); + } + + return $issuer; + } +} From 7afda52e62d40353dc396bc2d5687ba13de0252f Mon Sep 17 00:00:00 2001 From: Sara Arjona Date: Fri, 19 Mar 2021 14:51:27 +0100 Subject: [PATCH 4/4] MDL-70722 oauth2: move Nextcloud methods to service class --- lib/classes/oauth2/api.php | 109 +++-------------------- lib/classes/oauth2/service/nextcloud.php | 100 +++++++++++++++++++++ 2 files changed, 113 insertions(+), 96 deletions(-) create mode 100644 lib/classes/oauth2/service/nextcloud.php diff --git a/lib/classes/oauth2/api.php b/lib/classes/oauth2/api.php index 046570a1be5..6219c8dc98c 100644 --- a/lib/classes/oauth2/api.php +++ b/lib/classes/oauth2/api.php @@ -40,74 +40,6 @@ use moodle_exception; */ class api { - /** - * Build a nextcloud ready OAuth 2 service. - * @return \core\oauth2\issuer - */ - private static function init_nextcloud() { - // Nextcloud has a custom baseurl. Thus, the creation of endpoints has to be done later. - $record = (object) [ - 'name' => 'Nextcloud', - 'image' => 'https://nextcloud.com/wp-content/themes/next/assets/img/common/favicon.png?x16328', - 'basicauth' => 1, - 'servicetype' => 'nextcloud', - ]; - - $issuer = new issuer(0, $record); - - return $issuer; - } - - /** - * Create endpoints for nextcloud issuers. - * @param issuer $issuer issuer the endpoints should be created for. - * @return mixed - * @throws \coding_exception - * @throws \core\invalid_persistent_exception - */ - private static function create_endpoints_for_nextcloud($issuer) { - $baseurl = $issuer->get('baseurl'); - // Add trailing slash to baseurl, if needed. - if (substr($baseurl, -1) !== '/') { - $baseurl .= '/'; - } - - $endpoints = [ - // Baseurl will be prepended later. - 'authorization_endpoint' => 'index.php/apps/oauth2/authorize', - 'token_endpoint' => 'index.php/apps/oauth2/api/v1/token', - 'userinfo_endpoint' => 'ocs/v2.php/cloud/user?format=json', - 'webdav_endpoint' => 'remote.php/webdav/', - 'ocs_endpoint' => 'ocs/v1.php/apps/files_sharing/api/v1/shares', - ]; - - foreach ($endpoints as $name => $url) { - $record = (object) [ - 'issuerid' => $issuer->get('id'), - 'name' => $name, - 'url' => $baseurl . $url, - ]; - $endpoint = new \core\oauth2\endpoint(0, $record); - $endpoint->create(); - } - - // Create the field mappings. - $mapping = [ - 'ocs-data-email' => 'email', - 'ocs-data-id' => 'username', - ]; - foreach ($mapping as $external => $internal) { - $record = (object) [ - 'issuerid' => $issuer->get('id'), - 'externalfield' => $external, - 'internalfield' => $internal - ]; - $userfieldmapping = new \core\oauth2\user_field_mapping(0, $record); - $userfieldmapping->create(); - } - return $issuer; - } - /** * Initializes a record for one of the standard issuers to be displayed in the settings. * The issuer is not yet created in the database. @@ -117,16 +49,11 @@ class api { public static function init_standard_issuer($type) { require_capability('moodle/site:config', context_system::instance()); - // TODO: Move these methods to new service classes (to make this API easier to understand and maintain). - if ($type == 'nextcloud') { - return self::init_nextcloud(); - } else { - $classname = self::get_service_classname($type); - if (class_exists($classname)) { - return $classname::init(); - } - throw new moodle_exception('OAuth 2 service type not recognised: ' . $type); + $classname = self::get_service_classname($type); + if (class_exists($classname)) { + return $classname::init(); } + throw new moodle_exception('OAuth 2 service type not recognised: ' . $type); } /** @@ -138,17 +65,12 @@ class api { public static function create_endpoints_for_standard_issuer($type, $issuer) { require_capability('moodle/site:config', context_system::instance()); - // TODO: Move these methods to new service classes (to make this API easier to understand and maintain). - if ($type == 'nextcloud') { - return self::create_endpoints_for_nextcloud($issuer); - } else { - $classname = self::get_service_classname($type); - if (class_exists($classname)) { - $classname::create_endpoints($issuer); - return $issuer; - } - throw new moodle_exception('OAuth 2 service type not recognised: ' . $type); + $classname = self::get_service_classname($type); + if (class_exists($classname)) { + $classname::create_endpoints($issuer); + return $issuer; } + throw new moodle_exception('OAuth 2 service type not recognised: ' . $type); } /** @@ -166,6 +88,10 @@ class api { if (!$baseurl) { throw new moodle_exception('IMS OBv2.1 service type requires the baseurl parameter.'); } + case 'nextcloud': + if (!$baseurl) { + throw new moodle_exception('Nextcloud service type requires the baseurl parameter.'); + } case 'google': case 'facebook': case 'microsoft': @@ -176,15 +102,6 @@ class api { } $issuer->create(); return self::create_endpoints_for_standard_issuer($type, $issuer); - - case 'nextcloud': - if (!$baseurl) { - throw new moodle_exception('Nextcloud service type requires the baseurl parameter.'); - } - $issuer = self::init_nextcloud(); - $issuer->set('baseurl', $baseurl); - $issuer->create(); - return self::create_endpoints_for_nextcloud($issuer); } throw new moodle_exception('OAuth 2 service type not recognised: ' . $type); diff --git a/lib/classes/oauth2/service/nextcloud.php b/lib/classes/oauth2/service/nextcloud.php new file mode 100644 index 00000000000..51784fa5e33 --- /dev/null +++ b/lib/classes/oauth2/service/nextcloud.php @@ -0,0 +1,100 @@ +. + +namespace core\oauth2\service; + +use core\oauth2\issuer; +use core\oauth2\endpoint; +use core\oauth2\user_field_mapping; +use core\oauth2\discovery\openidconnect; + +/** + * Class for Nextcloud oAuth service, with the specific methods related to it. + * + * @package core + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class nextcloud extends openidconnect implements issuer_interface { + + /** + * Build an OAuth2 issuer, with all the default values for this service. + * + * @return issuer The issuer initialised with proper default values. + */ + public static function init(): issuer { + $record = (object) [ + 'name' => 'Nextcloud', + 'image' => 'https://nextcloud.com/wp-content/themes/next/assets/img/common/favicon.png?x16328', + 'basicauth' => 1, + 'servicetype' => 'nextcloud', + ]; + + $issuer = new issuer(0, $record); + + return $issuer; + } + + /** + * Create endpoints for this issuer. + * + * @param issuer $issuer Issuer the endpoints should be created for. + * @return issuer + */ + public static function create_endpoints(issuer $issuer): issuer { + // Nextcloud has a custom baseurl. Thus, the creation of endpoints has to be done later. + $baseurl = $issuer->get('baseurl'); + // Add trailing slash to baseurl, if needed. + if (substr($baseurl, -1) !== '/') { + $baseurl .= '/'; + } + + $endpoints = [ + // Baseurl will be prepended later. + 'authorization_endpoint' => 'index.php/apps/oauth2/authorize', + 'token_endpoint' => 'index.php/apps/oauth2/api/v1/token', + 'userinfo_endpoint' => 'ocs/v2.php/cloud/user?format=json', + 'webdav_endpoint' => 'remote.php/webdav/', + 'ocs_endpoint' => 'ocs/v1.php/apps/files_sharing/api/v1/shares', + ]; + + foreach ($endpoints as $name => $url) { + $record = (object) [ + 'issuerid' => $issuer->get('id'), + 'name' => $name, + 'url' => $baseurl . $url, + ]; + $endpoint = new \core\oauth2\endpoint(0, $record); + $endpoint->create(); + } + + // Create the field mappings. + $mapping = [ + 'ocs-data-email' => 'email', + 'ocs-data-id' => 'username', + ]; + foreach ($mapping as $external => $internal) { + $record = (object) [ + 'issuerid' => $issuer->get('id'), + 'externalfield' => $external, + 'internalfield' => $internal + ]; + $userfieldmapping = new \core\oauth2\user_field_mapping(0, $record); + $userfieldmapping->create(); + } + return $issuer; + } +}