MDL-68294 tool_mobile: WebService for validating subscription key

This commit is contained in:
Juan Leyva
2020-05-15 20:49:34 +02:00
parent 33dbc582e4
commit df2aa3480e
5 changed files with 137 additions and 1 deletions
+4
View File
@@ -688,6 +688,10 @@ class api {
if (\core\hub\registration::is_registered()) {
$credentials[] = ['type' => 'siteid', 'value' => $CFG->siteidentifier];
}
// Generate a hash key for validating that the request is coming from this site via WS.
$sitesubscriptionkey = json_encode(['validuntil' => time() + 10 * MINSECS, 'key' => complex_random_string(32)]);
set_config('sitesubscriptionkey', $sitesubscriptionkey, 'tool_mobile');
$credentials[] = ['type' => 'sitesubscriptionkey', 'value' => $sitesubscriptionkey];
// Parameters for the WebService returning site information.
$fnparams = (object) [
+59
View File
@@ -692,4 +692,63 @@ class external extends external_api {
]
);
}
/**
* Returns description of validate_subscription_key() parameters.
*
* @return external_function_parameters
* @since Moodle 3.9
*/
public static function validate_subscription_key_parameters() {
return new external_function_parameters(
[
'key' => new external_value(PARAM_RAW, 'Site subscription temporary key.'),
]
);
}
/**
* Check if the given site subscription key is valid
*
* @param string $key subscriptiion temporary key
* @return array with the settings and warnings
* @since Moodle 3.9
*/
public static function validate_subscription_key(string $key): array {
global $CFG, $PAGE;
$params = self::validate_parameters(self::validate_subscription_key_parameters(), ['key' => $key]);
$context = context_system::instance();
$PAGE->set_context($context);
$validated = false;
$sitesubscriptionkey = get_config('tool_mobile', 'sitesubscriptionkey');
if (!empty($sitesubscriptionkey) && $CFG->enablemobilewebservice && empty($CFG->disablemobileappsubscription)) {
$sitesubscriptionkey = json_decode($sitesubscriptionkey);
$validated = time() < $sitesubscriptionkey->validuntil && $params['key'] === $sitesubscriptionkey->key;
// Delete existing, even if not validated to enforce security and attacks prevention.
unset_config('sitesubscriptionkey', 'tool_mobile');
}
return [
'validated' => $validated,
'warnings' => [],
];
}
/**
* Returns description of validate_subscription_key() result value.
*
* @return external_description
* @since Moodle 3.9
*/
public static function validate_subscription_key_returns() {
return new external_single_structure(
[
'validated' => new external_value(PARAM_BOOL, 'Whether the key is validated or not.'),
'warnings' => new external_warnings(),
]
);
}
}
+10
View File
@@ -79,6 +79,16 @@ $functions = array(
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
'tool_mobile_validate_subscription_key' => array(
'classname' => 'tool_mobile\external',
'methodname' => 'validate_subscription_key',
'description' => 'Check if the given site subscription key is valid.',
'type' => 'write',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
'ajax' => true,
'loginrequired' => false,
),
'tool_mobile_get_tokens_for_qr_login' => array(
'classname' => 'tool_mobile\external',
'methodname' => 'get_tokens_for_qr_login',
@@ -725,4 +725,67 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
$this->expectExceptionMessage(get_string('apprequired', 'tool_mobile'));
$result = external::get_tokens_for_qr_login('', $USER->id);
}
/**
* Test validate subscription key.
*/
public function test_validate_subscription_key_valid() {
$this->resetAfterTest(true);
$sitesubscriptionkey = ['validuntil' => time() + MINSECS, 'key' => complex_random_string(32)];
set_config('sitesubscriptionkey', json_encode($sitesubscriptionkey), 'tool_mobile');
$result = external::validate_subscription_key($sitesubscriptionkey['key']);
$result = external_api::clean_returnvalue(external::validate_subscription_key_returns(), $result);
$this->assertEmpty($result['warnings']);
$this->assertTrue($result['validated']);
}
/**
* Test validate subscription key invalid first and then a valid one.
*/
public function test_validate_subscription_key_invalid_key_first() {
$this->resetAfterTest(true);
$sitesubscriptionkey = ['validuntil' => time() + MINSECS, 'key' => complex_random_string(32)];
set_config('sitesubscriptionkey', json_encode($sitesubscriptionkey), 'tool_mobile');
$result = external::validate_subscription_key('fakekey');
$result = external_api::clean_returnvalue(external::validate_subscription_key_returns(), $result);
$this->assertEmpty($result['warnings']);
$this->assertFalse($result['validated']);
// The valid one has been invalidated because the previous attempt.
$result = external::validate_subscription_key($sitesubscriptionkey['key']);
$result = external_api::clean_returnvalue(external::validate_subscription_key_returns(), $result);
$this->assertEmpty($result['warnings']);
$this->assertFalse($result['validated']);
}
/**
* Test validate subscription key invalid.
*/
public function test_validate_subscription_key_invalid_key() {
$this->resetAfterTest(true);
$result = external::validate_subscription_key('fakekey');
$result = external_api::clean_returnvalue(external::validate_subscription_key_returns(), $result);
$this->assertEmpty($result['warnings']);
$this->assertFalse($result['validated']);
}
/**
* Test validate subscription key invalid.
*/
public function test_validate_subscription_key_outdated() {
$this->resetAfterTest(true);
$sitesubscriptionkey = ['validuntil' => time() - MINSECS, 'key' => complex_random_string(32)];
set_config('sitesubscriptionkey', json_encode($sitesubscriptionkey), 'tool_mobile');
$result = external::validate_subscription_key($sitesubscriptionkey['key']);
$result = external_api::clean_returnvalue(external::validate_subscription_key_returns(), $result);
$this->assertEmpty($result['warnings']);
$this->assertFalse($result['validated']);
}
}
+1 -1
View File
@@ -23,7 +23,7 @@
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2019111801; // The current plugin version (Date: YYYYMMDDXX).
$plugin->version = 2019111802; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2019111200; // Requires this Moodle version.
$plugin->component = 'tool_mobile'; // Full name of the plugin (used for diagnostics).
$plugin->dependencies = array(