Skip to content

Commit

Permalink
MDL-45639 webservice: Support private tokens
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jleyva committed Oct 14, 2016
1 parent 6a69cda commit 69cbe35
Show file tree
Hide file tree
Showing 7 changed files with 39 additions and 2 deletions.
1 change: 1 addition & 0 deletions lib/db/install.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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"/>
Expand Down
14 changes: 14 additions & 0 deletions lib/db/upgrade.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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;
}
3 changes: 3 additions & 0 deletions lib/externallib.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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(
Expand Down
15 changes: 14 additions & 1 deletion login/token.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,14 @@
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');
}
Expand Down Expand Up @@ -92,6 +95,8 @@

// 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));
Expand All @@ -103,8 +108,16 @@
$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');
Expand Down
2 changes: 1 addition & 1 deletion version.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
1 change: 1 addition & 0 deletions webservice/lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ public function generate_user_ws_tokens($userid) {
$newtoken->contextid = context_system::instance()->id;
$newtoken->creatorid = $userid;
$newtoken->timecreated = time();
$newtoken->privatetoken = null;

$DB->insert_record('external_tokens', $newtoken);
}
Expand Down
5 changes: 5 additions & 0 deletions webservice/upgrade.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 ===

Expand Down

0 comments on commit 69cbe35

Please sign in to comment.