From cf66500557062ac23282c42e999e010ca503444f Mon Sep 17 00:00:00 2001 From: Peter Dias Date: Tue, 11 May 2021 14:18:26 +0800 Subject: [PATCH] MDL-70271 repository_dropbox: Switch to core\oauth2 API Dropbox is updating their OAuth2 authentication to issue short-term tokens, which are already supported by core\oauth2\client. This commit updates the dropbox repository to use the core\oauth2 API, which has built-in support for tokens and offline access. --- repository/dropbox/classes/dropbox.php | 26 +++++--- repository/dropbox/db/upgrade.php | 45 ++++++++++++- repository/dropbox/lang/en/deprecated.txt | 3 + .../dropbox/lang/en/repository_dropbox.php | 10 ++- repository/dropbox/lib.php | 63 ++++++------------- repository/dropbox/tests/generator/lib.php | 7 +-- repository/dropbox/version.php | 2 +- repository/tests/generator_test.php | 14 ++++- repository/tests/repositorylib_test.php | 3 +- 9 files changed, 106 insertions(+), 67 deletions(-) create mode 100644 repository/dropbox/lang/en/deprecated.txt diff --git a/repository/dropbox/classes/dropbox.php b/repository/dropbox/classes/dropbox.php index 1c3a5f8c6d5..099e2acd729 100644 --- a/repository/dropbox/classes/dropbox.php +++ b/repository/dropbox/classes/dropbox.php @@ -25,9 +25,8 @@ namespace repository_dropbox; -defined('MOODLE_INTERNAL') || die(); - -require_once($CFG->libdir . '/oauthlib.php'); +use core\oauth2\client; +use core\oauth2\issuer; /** * Dropbox V2 API. @@ -36,7 +35,7 @@ require_once($CFG->libdir . '/oauthlib.php'); * @copyright Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class dropbox extends \oauth2_client { +class dropbox extends client { /** * @var array Custom continue endpoints that differ from the standard. @@ -48,12 +47,23 @@ class dropbox extends \oauth2_client { /** * Create the DropBox API Client. * - * @param string $key The API key - * @param string $secret The API secret + * @param issuer $issuer The dropbox issuer * @param string $callback The callback URL */ - public function __construct($key, $secret, $callback) { - parent::__construct($key, $secret, $callback, ''); + public function __construct(issuer $issuer, $callback) { + parent::__construct($issuer, $callback, '', false, true); + } + + /** + * Override - Return an empty string to override parent function. + * + * Dropbox does not require scopes to be provided and can function without them. + * Additional information MDL-70268 + * + * @return string + */ + protected function get_login_scopes() { + return ''; } /** diff --git a/repository/dropbox/db/upgrade.php b/repository/dropbox/db/upgrade.php index 51d2ca7d3c7..b2418105c50 100644 --- a/repository/dropbox/db/upgrade.php +++ b/repository/dropbox/db/upgrade.php @@ -21,7 +21,7 @@ defined('MOODLE_INTERNAL') || die(); * @return bool result */ function xmldb_repository_dropbox_upgrade($oldversion) { - global $CFG; + global $CFG, $DB; // Automatically generated Moodle v3.6.0 release upgrade line. // Put any upgrade step following this. @@ -35,5 +35,48 @@ function xmldb_repository_dropbox_upgrade($oldversion) { // Automatically generated Moodle v3.9.0 release upgrade line. // Put any upgrade step following this. + if ($oldversion < 2021052501) { + $key = get_config('dropbox', 'dropbox_key'); + $secret = get_config('dropbox', 'dropbox_secret'); + + if ($key && $secret) { + $params = [ + 'name' => 'Dropbox', + 'clientid' => $key, + 'clientsecret' => $secret, + 'loginparamsoffline' => 'token_access_type=offline', + 'image' => '', + 'showonloginpage' => 0, // Internal services only. + ]; + $record = $DB->get_record('oauth2_issuer', ['name' => 'Dropbox'], 'id'); + if (!$record) { + $params = array_merge($params, [ + 'timecreated' => time(), + 'timemodified' => time(), + 'usermodified' => time(), + 'baseurl' => 0, + 'sortorder' => '', + 'loginparams' => '', + 'requireconfirmation' => 1, + 'alloweddomains' => '', + 'loginscopes' => 'openid profile email', + 'loginscopesoffline' => 'openid profile email', + ]); + $id = $DB->insert_record('oauth2_issuer', $params); + } else { + $id = $record->id; + $params['id'] = $id; + $DB->update_record('oauth2_issuer', $params); + } + + set_config('dropbox_issuerid', $id, 'dropbox'); + unset_config('dropbox_key', 'dropbox'); + unset_config('dropbox_secret', 'dropbox'); + } + + // Dropbox savepoint reached. + upgrade_plugin_savepoint(true, 2021052501, 'repository', 'dropbox'); + } + return true; } diff --git a/repository/dropbox/lang/en/deprecated.txt b/repository/dropbox/lang/en/deprecated.txt new file mode 100644 index 00000000000..2808d9f8722 --- /dev/null +++ b/repository/dropbox/lang/en/deprecated.txt @@ -0,0 +1,3 @@ +apikey,repository_dropbox +secret,repository_dropbox +instruction,repository_dropbox diff --git a/repository/dropbox/lang/en/repository_dropbox.php b/repository/dropbox/lang/en/repository_dropbox.php index 78ee4bb4173..c80c73e461b 100644 --- a/repository/dropbox/lang/en/repository_dropbox.php +++ b/repository/dropbox/lang/en/repository_dropbox.php @@ -27,10 +27,9 @@ $string['crontask'] = 'Background processing for Dropbox repository'; $string['notitle'] = 'notitle'; $string['remember'] = 'Remember me'; $string['pluginname'] = 'Dropbox'; -$string['apikey'] = 'Dropbox API key'; $string['dropbox'] = 'Dropbox'; -$string['secret'] = 'Dropbox secret'; -$string['instruction'] = 'You can get your API Key and secret from Dropbox developers. When setting up your key please select "Full Dropbox" as the "Access level".'; +$string['issuer'] = 'OAuth2 service'; +$string['issuer_help'] = 'Select the OAuth2 service that is configured to talk to the Dropbox API. If the service does not exist yet, you will need to create it.'; $string['cachelimit'] = 'Cache limit'; $string['cachelimit_info'] = 'Enter the maximum size of files (in bytes) to be cached on server for Dropbox aliases/shortcuts. Cached files will be served when the source is no longer available. Empty value or zero mean caching of all files regardless of size.'; $string['dropbox:view'] = 'View a Dropbox folder'; @@ -38,3 +37,8 @@ $string['logoutdesc'] = '(Logout when you finish using Dropbox)'; $string['oauth2redirecturi'] = 'OAuth 2 Redirect URI'; $string['privacy:metadata:repository_dropbox'] = 'The Dropbox repository plugin does not store any personal data, but does transmit user data from Moodle to the remote system.'; $string['privacy:metadata:repository_dropbox:query'] = 'The Dropbox repository user search text query.'; + +// Deprecated since Moodle 4.0. +$string['apikey'] = 'Dropbox API key'; +$string['secret'] = 'Dropbox secret'; +$string['instruction'] = 'You can get your API Key and secret from Dropbox developers. When setting up your key please select "Full Dropbox" as the "Access level".'; \ No newline at end of file diff --git a/repository/dropbox/lib.php b/repository/dropbox/lib.php index e67b18a50db..249440861dc 100644 --- a/repository/dropbox/lib.php +++ b/repository/dropbox/lib.php @@ -59,13 +59,8 @@ class repository_dropbox extends repository { ]); // Create the dropbox API instance. - $key = get_config('dropbox', 'dropbox_key'); - $secret = get_config('dropbox', 'dropbox_secret'); - $this->dropbox = new repository_dropbox\dropbox( - $key, - $secret, - $returnurl - ); + $issuer = \core\oauth2\api::get_issuer(get_config('dropbox', 'dropbox_issuerid')); + $this->dropbox = new repository_dropbox\dropbox($issuer, $returnurl); } /** @@ -436,7 +431,7 @@ class repository_dropbox extends repository { } /** - * Check if moodle has got access token and secret. + * Check if the dropbox is logged in via the oauth process. * * @inheritDocs */ @@ -507,29 +502,15 @@ class repository_dropbox extends repository { */ public static function type_config_form($mform, $classname = 'repository') { parent::type_config_form($mform); - $key = get_config('dropbox', 'dropbox_key'); - $secret = get_config('dropbox', 'dropbox_secret'); - - if (empty($key)) { - $key = ''; + $options = []; + $issuers = \core\oauth2\api::get_all_issuers(); + foreach ($issuers as $issuer) { + $options[$issuer->get('id')] = s($issuer->get('name')); } - if (empty($secret)) { - $secret = ''; - } - - $mform->addElement('text', 'dropbox_key', get_string('apikey', 'repository_dropbox'), array('value'=>$key,'size' => '40')); - $mform->setType('dropbox_key', PARAM_RAW_TRIMMED); - $mform->addElement('text', 'dropbox_secret', get_string('secret', 'repository_dropbox'), array('value'=>$secret,'size' => '40')); - - $mform->addRule('dropbox_key', get_string('required'), 'required', null, 'client'); - $mform->addRule('dropbox_secret', get_string('required'), 'required', null, 'client'); - $mform->setType('dropbox_secret', PARAM_RAW_TRIMMED); - $mform->addElement('static', null, '', get_string('instruction', 'repository_dropbox')); - $mform->addElement('static', null, - get_string('oauth2redirecturi', 'repository_dropbox'), - self::get_oauth2callbackurl()->out() - ); - + $strrequired = get_string('required'); + $mform->addElement('select', 'dropbox_issuerid', get_string('issuer', 'repository_dropbox'), $options); + $mform->addHelpButton('dropbox_issuerid', 'issuer', 'repository_dropbox'); + $mform->addRule('dropbox_issuerid', $strrequired, 'required', null, 'client'); $mform->addElement('text', 'dropbox_cachelimit', get_string('cachelimit', 'repository_dropbox'), array('size' => '40')); $mform->addRule('dropbox_cachelimit', null, 'numeric', null, 'client'); $mform->setType('dropbox_cachelimit', PARAM_INT); @@ -544,13 +525,9 @@ class repository_dropbox extends repository { * @return mixed */ public function set_option($options = []) { - if (!empty($options['dropbox_key'])) { - set_config('dropbox_key', trim($options['dropbox_key']), 'dropbox'); - unset($options['dropbox_key']); - } - if (!empty($options['dropbox_secret'])) { - set_config('dropbox_secret', trim($options['dropbox_secret']), 'dropbox'); - unset($options['dropbox_secret']); + if (!empty($options['dropbox_issuerid'])) { + set_config('dropbox_issuerid', trim($options['dropbox_issuerid']), 'dropbox'); + unset($options['dropbox_issuerid']); } if (!empty($options['dropbox_cachelimit'])) { $this->cachelimit = (int) trim($options['dropbox_cachelimit']); @@ -567,16 +544,13 @@ class repository_dropbox extends repository { * @return mixed */ public function get_option($config = '') { - if ($config === 'dropbox_key') { - return trim(get_config('dropbox', 'dropbox_key')); - } else if ($config === 'dropbox_secret') { - return trim(get_config('dropbox', 'dropbox_secret')); + if ($config === 'dropbox_issuerid') { + return trim(get_config('dropbox', 'dropbox_issuerid')); } else if ($config === 'dropbox_cachelimit') { return $this->max_cache_bytes(); } else { $options = parent::get_option(); - $options['dropbox_key'] = trim(get_config('dropbox', 'dropbox_key')); - $options['dropbox_secret'] = trim(get_config('dropbox', 'dropbox_secret')); + $options['dropbox_issuerid'] = trim(get_config('dropbox', 'dropbox_issuerid')); $options['dropbox_cachelimit'] = $this->max_cache_bytes(); } @@ -601,8 +575,7 @@ class repository_dropbox extends repository { */ public static function get_type_option_names() { return [ - 'dropbox_key', - 'dropbox_secret', + 'dropbox_issuerid', 'pluginname', 'dropbox_cachelimit', ]; diff --git a/repository/dropbox/tests/generator/lib.php b/repository/dropbox/tests/generator/lib.php index 1f4db858265..4ccae22e5f8 100644 --- a/repository/dropbox/tests/generator/lib.php +++ b/repository/dropbox/tests/generator/lib.php @@ -41,11 +41,8 @@ class repository_dropbox_generator extends testing_repository_generator { */ protected function prepare_type_record(array $record) { $record = parent::prepare_type_record($record); - if (!isset($record['dropbox_key'])) { - $record['dropbox_key'] = 'key'; - } - if (!isset($record['dropbox_secret'])) { - $record['dropbox_secret'] = 'secret'; + if (!isset($record['dropbox_issuerid'])) { + $record['dropbox_issuerid'] = 0; } if (!isset($record['dropbox_cachelimit'])) { $record['dropbox_cachelimit'] = 0; diff --git a/repository/dropbox/version.php b/repository/dropbox/version.php index 8f1c40e6629..f881c308683 100644 --- a/repository/dropbox/version.php +++ b/repository/dropbox/version.php @@ -25,6 +25,6 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2021052500; // The current plugin version (Date: YYYYMMDDXX). +$plugin->version = 2021052501; // The current plugin version (Date: YYYYMMDDXX). $plugin->requires = 2021052500; // Requires this Moodle version. $plugin->component = 'repository_dropbox'; // Full name of the plugin (used for diagnostics) diff --git a/repository/tests/generator_test.php b/repository/tests/generator_test.php index b1417eb25ee..a94ef34bf8e 100644 --- a/repository/tests/generator_test.php +++ b/repository/tests/generator_test.php @@ -100,10 +100,20 @@ class core_repository_generator_testcase extends advanced_testcase { $this->assertEquals('Custom Flickr', $DB->get_field('repository_instances', 'name', array('typeid' => $flickr->id), MUST_EXIST)); + // Create a dropbox oauth issuer. + $this->setAdminUser(); + $params = [ + 'name' => 'Dropbox', + 'clientid' => 'key', + 'clientsecret' => 'secret', + 'loginparamsoffline' => 'token_access_type=offline', + 'image' => '', + 'showonloginpage' => 1, + ]; + $issuer = \core\oauth2\api::create_issuer((object)$params); $record = new stdClass(); $record->pluginname = 'Custom Dropbox'; - $record->dropbox_key = '12345'; - $record->dropbox_secret = '67890'; + $record->dropbox_issuerid = $issuer->get('id'); $record->dropbox_cachelimit = '123'; $dropbox = $this->getDataGenerator()->create_repository_type('dropbox', $record); diff --git a/repository/tests/repositorylib_test.php b/repository/tests/repositorylib_test.php index 00211c9f37f..d618ae6ee36 100644 --- a/repository/tests/repositorylib_test.php +++ b/repository/tests/repositorylib_test.php @@ -480,8 +480,7 @@ class core_repositorylib_testcase extends advanced_testcase { // Check that a user can view SOME repositories when logged in as someone else. $params = new stdClass(); $params->name = 'Dropbox'; - $params->dropbox_key = 'key'; - $params->dropbox_secret = 'secret'; + $params->dropbox_issuerid = '2'; $privaterepoid = $this->getDataGenerator()->create_repository('dropbox')->id; $notprivaterepoid = $this->getDataGenerator()->create_repository('upload')->id;