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