Commit 69cbe359 authored by Juan Leyva's avatar Juan Leyva
Browse files

MDL-45639 webservice: Support private tokens

Private tokens are generated at the same time that the token.
They must be stored safely by the ws client, and they must be transmitted only via  https.
parent 6a69cda9
......@@ -2577,6 +2577,7 @@
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="token" TYPE="char" LENGTH="128" NOTNULL="true" SEQUENCE="false" COMMENT="security token, aka private access key"/>
<FIELD NAME="privatetoken" TYPE="char" LENGTH="64" NOTNULL="false" SEQUENCE="false" COMMENT="private token, generated at the same time that the token, must be stored safely by the ws client, to be transmitted only via https"/>
<FIELD NAME="tokentype" TYPE="int" LENGTH="4" NOTNULL="true" SEQUENCE="false" COMMENT="type of token: 0=permanent, no session; 1=linked to current browser session via sid; 2=permanent, with emulated session"/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="owner of the token"/>
<FIELD NAME="externalserviceid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
......
......@@ -2250,6 +2250,7 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2016101100.00);
}
if ($oldversion < 2016101101.00) {
// Define field component to be added to message_read.
$table = new xmldb_table('message_read');
......@@ -2272,5 +2273,18 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2016101101.00);
}
if ($oldversion < 2016101400.01) {
$table = new xmldb_table('external_tokens');
$field = new xmldb_field('privatetoken', XMLDB_TYPE_CHAR, '64', null, null, null, null);
// Conditionally add privatetoken field to the external_tokens table.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Main savepoint reached.
upgrade_main_savepoint(true, 2016101400.01);
}
return true;
}
......@@ -724,6 +724,7 @@ function external_generate_token($tokentype, $serviceorid, $userid, $contextorid
if (!empty($iprestriction)) {
$newtoken->iprestriction = $iprestriction;
}
$newtoken->privatetoken = null;
$DB->insert_record('external_tokens', $newtoken);
return $newtoken->token;
}
......@@ -1053,6 +1054,8 @@ function external_generate_token_for_current_user($service) {
$token->externalserviceid = $service->id;
// MDL-43119 Token valid for 3 months (12 weeks).
$token->validuntil = $token->timecreated + 12 * WEEKSECS;
// Generate the private token, it must be transmitted only via https.
$token->privatetoken = random_string(64);
$token->id = $DB->insert_record('external_tokens', $token);
$params = array(
......
......@@ -44,11 +44,14 @@ $username = trim(core_text::strtolower($username));
if (is_restored_user($username)) {
throw new moodle_exception('restoredaccountresetpassword', 'webservice');
}
$systemcontext = context_system::instance();
$user = authenticate_user_login($username, $password);
if (!empty($user)) {
// Cannot authenticate unless maintenance access is granted.
$hasmaintenanceaccess = has_capability('moodle/site:maintenanceaccess', context_system::instance(), $user);
$hasmaintenanceaccess = has_capability('moodle/site:maintenanceaccess', $systemcontext, $user);
if (!empty($CFG->maintenance_enabled) and !$hasmaintenanceaccess) {
throw new moodle_exception('sitemaintenance', 'admin');
}
......@@ -92,6 +95,8 @@ if (!empty($user)) {
// Get an existing token or create a new one.
$token = external_generate_token_for_current_user($service);
$privatetoken = $token->privatetoken;
$token->privatetoken = null;
// log token access
$DB->set_field('external_tokens', 'lastaccess', time(), array('id'=>$token->id));
......@@ -103,8 +108,16 @@ if (!empty($user)) {
$event->add_record_snapshot('external_tokens', $token);
$event->trigger();
$siteadmin = has_capability('moodle/site:config', $systemcontext, $USER->id) || is_siteadmin($USER->id);
$usertoken = new stdClass;
$usertoken->token = $token->token;
// Private token, only transmitted to https sites and non-admin users.
if (is_https() and !$siteadmin) {
$usertoken->privatetoken = $privatetoken;
} else {
$usertoken->privatetoken = null;
}
echo json_encode($usertoken);
} else {
throw new moodle_exception('invalidlogin');
......
......@@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2016101400.00; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2016101400.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.
......
......@@ -340,6 +340,7 @@ class webservice {
$newtoken->contextid = context_system::instance()->id;
$newtoken->creatorid = $userid;
$newtoken->timecreated = time();
$newtoken->privatetoken = null;
$DB->insert_record('external_tokens', $newtoken);
}
......
......@@ -12,6 +12,11 @@ This information is intended for authors of webservices, not people writing webs
In some contexts those parameteres are not necessary because is not required to do a file rewrite via
file_rewrite_pluginfile_urls.
* External function get_site_info now returns the site course ID. This new field is marked as VALUE_OPTIONAL for backwards compatibility.
* A new field "privatetoken" has been added to the "external_tokens" table.
This private token must be safely stored (or not stored at all) by the client because it will be used in places where a request
must be double-checked.
This token should not be passed via GET paramaters and it must be transmitted only via https.
This token is generated only in login/token.php after the user credential has been confirmed. It can't be generated by admins.
=== 3.1 ===
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment