diff --git a/lang/en/role.php b/lang/en/role.php
index cf4f832126d..933f2176614 100644
--- a/lang/en/role.php
+++ b/lang/en/role.php
@@ -372,6 +372,7 @@ $string['useshowadvancedtochange'] = 'Use \'Show advanced\' to change';
$string['viewingdefinitionofrolex'] = 'Viewing the definition of role \'{$a}\'';
$string['viewrole'] = 'View role details';
$string['webservice:createtoken'] = 'Create a web service token';
+$string['webservice:createmobiletoken'] = 'Create a web service token for mobile access';
$string['whydoesuserhavecap'] = 'Why does {$a->fullname} have capability {$a->capability} in context {$a->context}?';
$string['whydoesusernothavecap'] = 'Why does {$a->fullname} not have capability {$a->capability} in context {$a->context}?';
$string['xroleassignments'] = '{$a}\'s role assignments';
diff --git a/lang/en/webservice.php b/lang/en/webservice.php
index fc15adf2724..5d0690e082d 100644
--- a/lang/en/webservice.php
+++ b/lang/en/webservice.php
@@ -42,12 +42,14 @@ $string['apiexplorer'] = 'API explorer';
$string['apiexplorernotavalaible'] = 'API explorer not available yet.';
$string['arguments'] = 'Arguments';
$string['authmethod'] = 'Authentication method';
+$string['cannotcreatemobiletoken'] = 'No permission to create web service token for mobile access.';
$string['configwebserviceplugins'] = 'For security reasons, only protocols that are in use should be enabled.';
$string['context'] = 'Context';
$string['createservicedescription'] = 'A service is a set of web service functions. You will allow the user to access to a new service. On the Add service page check \'Enable\' and \'Authorised users\' options. Select \'No required capability\'.';
$string['createserviceforusersdescription'] = 'A service is a set of web service functions. You will allow users to access to a new service. On the Add service page check \'Enable\' and uncheck \'Authorised users\' options. Select \'No required capability\'.';
$string['createtoken'] = 'Create token';
$string['createtokenforuser'] = 'Create a token for a user';
+$string['createtokenforuserauto'] = 'Create a token for a user automatically';
$string['createtokenforuserdescription'] = 'Create a token for the web services user.';
$string['createuser'] = 'Create a specific user';
$string['createuserdescription'] = 'A web services user is required to represent the system controlling Moodle.';
@@ -125,6 +127,7 @@ $string['onesystemcontrolling'] = 'One system controlling Moodle with a token';
$string['onesystemcontrollingdescription'] = 'The following steps help you to set up the Moodle web service for a system to control Moodle. These steps also help to set up the recommended token (security keys) authentication method.';
$string['operation'] = 'Operation';
$string['optional'] = 'Optional';
+$string['passwordisexpired'] = 'Password is expired.';
$string['phpparam'] = 'XML-RPC (PHP structure)';
$string['phpresponse'] = 'XML-RPC (PHP structure)';
$string['postrestparam'] = 'PHP code for REST (POST request)';
@@ -147,6 +150,7 @@ $string['restexception'] = 'REST';
$string['restparam'] = 'REST (POST parameters)';
$string['restrictedusers'] = 'Authorised users only';
$string['restrictedusers_help'] = 'This setting determines whether all users with the permission to create a web services token can generate a token for this service via their security keys page or whether only authorised users can do so.';
+$string['restoredaccountresetpassword'] = 'Restored account need to reset password before getting a token.';
$string['securitykey'] = 'Security key (token)';
$string['securitykeys'] = 'Security keys';
$string['selectauthorisedusers'] = 'Select authorised users';
diff --git a/lib/db/access.php b/lib/db/access.php
index e3e6d68c8a6..544ed5cf464 100644
--- a/lib/db/access.php
+++ b/lib/db/access.php
@@ -1692,6 +1692,15 @@ $capabilities = array(
'manager' => CAP_ALLOW
)
),
+ 'moodle/webservice:createmobiletoken' => array(
+
+ 'riskbitmask' => RISK_CONFIG | RISK_DATALOSS | RISK_SPAM | RISK_PERSONAL | RISK_XSS,
+ 'captype' => 'write',
+ 'contextlevel' => CONTEXT_SYSTEM,
+ 'archetypes' => array(
+ 'user' => CAP_ALLOW
+ )
+ ),
'moodle/rating:view' => array(
'captype' => 'read',
diff --git a/login/token.php b/login/token.php
new file mode 100644
index 00000000000..3215d32a5a9
--- /dev/null
+++ b/login/token.php
@@ -0,0 +1,136 @@
+.
+
+/**
+ * Return token
+ * @package moodlecore
+ * @copyright 2011 Dongsheng Cai
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define('AJAX_SCRIPT', true);
+define('NO_MOODLE_COOKIES', true);
+
+require_once(dirname(dirname(__FILE__)) . '/config.php');
+
+$username = required_param('username', PARAM_USERNAME);
+$password = required_param('password', PARAM_RAW);
+$service = required_param('service', PARAM_ALPHANUMEXT);
+
+echo $OUTPUT->header();
+
+if (!$CFG->enablewebservices) {
+ throw new moodle_exception('enablewsdescription', 'webservice');
+}
+$username = trim(moodle_strtolower($username));
+if (is_restored_user($username)) {
+ throw new moodle_exception('restoredaccountresetpassword', 'webservice');
+}
+$user = authenticate_user_login($username, $password);
+if (!empty($user)) {
+ if (isguestuser($user)) {
+ throw new moodle_exception('noguest');
+ }
+ if (empty($user->confirmed)) {
+ throw new moodle_exception('usernotconfirmed', 'moodle', '', $user->username);
+ }
+ // check credential expiry
+ $userauth = get_auth_plugin($user->auth);
+ if (!empty($userauth->config->expiration) and $userauth->config->expiration == 1) {
+ $days2expire = $userauth->password_expire($user->username);
+ if (intval($days2expire) < 0 ) {
+ throw new moodle_exception('passwordisexpired', 'webservice');
+ }
+ }
+
+ // setup user session to check capability
+ session_set_user($user);
+
+ $admintokenssql = "SELECT t.*
+ FROM {external_tokens} t
+ JOIN {external_services} s
+ ON t.externalserviceid = s.id
+ WHERE s.shortname = ?
+ AND s.enabled = 1
+ AND t.userid = ?
+ AND (t.validuntil = 0 OR t.validuntil IS NULL OR t.validuntil > ?)
+ AND t.userid != t.creatorid
+ ORDER BY t.timecreated ASC";
+ $tokens = $DB->get_records_sql($admintokenssql, array($service, $user->id, time()));
+ foreach ($tokens as $key=>$admin_token) {
+ // remove token if its ip not in whitelist
+ if (isset($admin_token->iprestriction) and !address_in_subnet(getremoteaddr(), $admin_token->iprestriction)) {
+ unset($tokens[$key]);
+ }
+ }
+ // if admin created token then use the most recent created one over user created token
+ if (count($tokens) > 0) {
+ $token = array_pop($tokens);
+ } else {
+ // if no admin created tokens, try to use user created token
+ // NOTE user created token doesn't have valid date and ip limits
+ $usertokensql = "SELECT t.*
+ FROM {external_tokens} t
+ JOIN {external_services} s
+ ON t.externalserviceid = s.id
+ WHERE s.shortname = ?
+ AND s.enabled = 1
+ AND t.userid = ?
+ AND t.userid = t.creatorid";
+
+ $token = $DB->get_record_sql($usertokensql, array($service, $user->id));
+ // create token if not exists
+ if (!$token) {
+ // This is an exception for Moodle Mobiel App
+ // if user doesn't have token, we will create one on the fly
+ // even user doesn't have createtoken permission
+ if ($service == MOODLE_OFFICIAL_MOBILE_SERVICE) {
+ if (has_capability('moodle/webservice:createmobiletoken', get_system_context())) {
+ // if service doesn't exist, dml will throw exception
+ $service_record = $DB->get_record('external_services', array('shortname'=>$service, 'enabled'=>1), '*', MUST_EXIST);
+ // create a new token
+ $token = new stdClass;
+ $token->token = md5(uniqid(rand(), 1));
+ $token->userid = $user->id;
+ $token->tokentype = EXTERNAL_TOKEN_PERMANENT;
+ $token->contextid = get_context_instance(CONTEXT_SYSTEM)->id;
+ $token->creatorid = $user->id;
+ $token->timecreated = time();
+ $token->externalserviceid = $service_record->id;
+ $tokenid = $DB->insert_record('external_tokens', $token);
+ add_to_log(SITEID, 'webservice', get_string('createtokenforuserauto', 'webservice'), '' , 'User ID: ' . $user->id);
+ $token->id = $tokenid;
+ } else {
+ throw new moodle_exception('cannotcreatemobiletoken', 'webservice');
+ }
+ } else {
+ // will throw exception if no token found
+ throw new moodle_exception('invalidtoken', 'webservice');
+ }
+ }
+ }
+
+ // log token access
+ $DB->set_field('external_tokens', 'lastaccess', time(), array('id'=>$token->id));
+
+ add_to_log(SITEID, 'webservice', 'user request webservice token', '' , 'User ID: ' . $user->id);
+
+ $usertoken = new stdClass;
+ $usertoken->token = $token->token;
+ echo json_encode($usertoken);
+} else {
+ throw new moodle_exception('usernamenotfound', 'moodle');
+}
\ No newline at end of file
diff --git a/version.php b/version.php
index ec45c32837f..fa3d5715903 100644
--- a/version.php
+++ b/version.php
@@ -31,7 +31,7 @@ defined('MOODLE_INTERNAL') || die();
-$version = 2011060900.01; // YYYYMMDD = weekly release date of this DEV branch
+$version = 2011060900.02; // YYYYMMDD = weekly release date of this DEV branch
// RR = release increments - 00 in DEV branches
// .XX = incremental changes