2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
20 * @copyright 2011 Dongsheng Cai <dongsheng@moodle.com>
21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 define('AJAX_SCRIPT', true);
25 define('REQUIRE_CORRECT_ACCESS', true);
26 define('NO_MOODLE_COOKIES', true);
28 require_once(dirname(dirname(__FILE__
)) . '/config.php');
30 // Allow CORS requests.
31 header('Access-Control-Allow-Origin: *');
33 $username = required_param('username', PARAM_USERNAME
);
34 $password = required_param('password', PARAM_RAW
);
35 $serviceshortname = required_param('service', PARAM_ALPHANUMEXT
);
37 echo $OUTPUT->header();
39 if (!$CFG->enablewebservices
) {
40 throw new moodle_exception('enablewsdescription', 'webservice');
42 $username = trim(core_text
::strtolower($username));
43 if (is_restored_user($username)) {
44 throw new moodle_exception('restoredaccountresetpassword', 'webservice');
46 $user = authenticate_user_login($username, $password);
49 //Non admin can not authenticate if maintenance mode
50 $hassiteconfig = has_capability('moodle/site:config', context_system
::instance(), $user);
51 if (!empty($CFG->maintenance_enabled
) and !$hassiteconfig) {
52 throw new moodle_exception('sitemaintenance', 'admin');
55 if (isguestuser($user)) {
56 throw new moodle_exception('noguest');
58 if (empty($user->confirmed
)) {
59 throw new moodle_exception('usernotconfirmed', 'moodle', '', $user->username
);
61 // check credential expiry
62 $userauth = get_auth_plugin($user->auth
);
63 if (!empty($userauth->config
->expiration
) and $userauth->config
->expiration
== 1) {
64 $days2expire = $userauth->password_expire($user->username
);
65 if (intval($days2expire) < 0 ) {
66 throw new moodle_exception('passwordisexpired', 'webservice');
70 // Check whether the user should be changing password.
71 if (get_user_preferences('auth_forcepasswordchange', false, $user)) {
72 if ($userauth->can_change_password()) {
73 throw new moodle_exception('forcepasswordchangenotice');
75 throw new moodle_exception('nopasswordchangeforced', 'auth');
79 // let enrol plugins deal with new enrolments if necessary
80 enrol_check_plugins($user);
82 // setup user session to check capability
83 \core\session\manager
::set_user($user);
85 //check if the service exists and is enabled
86 $service = $DB->get_record('external_services', array('shortname' => $serviceshortname, 'enabled' => 1));
87 if (empty($service)) {
88 // will throw exception if no token found
89 throw new moodle_exception('servicenotavailable', 'webservice');
92 //check if there is any required system capability
93 if ($service->requiredcapability
and !has_capability($service->requiredcapability
, context_system
::instance(), $user)) {
94 throw new moodle_exception('missingrequiredcapability', 'webservice', '', $service->requiredcapability
);
97 //specific checks related to user restricted service
98 if ($service->restrictedusers
) {
99 $authoriseduser = $DB->get_record('external_services_users',
100 array('externalserviceid' => $service->id
, 'userid' => $user->id
));
102 if (empty($authoriseduser)) {
103 throw new moodle_exception('usernotallowed', 'webservice', '', $serviceshortname);
106 if (!empty($authoriseduser->validuntil
) and $authoriseduser->validuntil
< time()) {
107 throw new moodle_exception('invalidtimedtoken', 'webservice');
110 if (!empty($authoriseduser->iprestriction
) and !address_in_subnet(getremoteaddr(), $authoriseduser->iprestriction
)) {
111 throw new moodle_exception('invalidiptoken', 'webservice');
115 //Check if a token has already been created for this user and this service
116 //Note: this could be an admin created or an user created token.
117 // It does not really matter we take the first one that is valid.
118 $tokenssql = "SELECT t.id, t.sid, t.token, t.validuntil, t.iprestriction
119 FROM {external_tokens} t
120 WHERE t.userid = ? AND t.externalserviceid = ? AND t.tokentype = ?
121 ORDER BY t.timecreated ASC";
122 $tokens = $DB->get_records_sql($tokenssql, array($user->id
, $service->id
, EXTERNAL_TOKEN_PERMANENT
));
124 //A bit of sanity checks
125 foreach ($tokens as $key=>$token) {
127 /// Checks related to a specific token. (script execution continue)
129 //if sid is set then there must be a valid associated session no matter the token type
130 if (!empty($token->sid
)) {
131 if (!\core\session\manager
::session_exists($token->sid
)){
132 //this token will never be valid anymore, delete it
133 $DB->delete_records('external_tokens', array('sid'=>$token->sid
));
138 //remove token if no valid anymore
139 //Also delete this wrong token (similar logic to the web service servers
140 // /webservice/lib.php/webservice_server::authenticate_by_token())
141 if (!empty($token->validuntil
) and $token->validuntil
< time()) {
142 $DB->delete_records('external_tokens', array('token'=>$token->token
, 'tokentype'=> EXTERNAL_TOKEN_PERMANENT
));
146 // remove token if its ip not in whitelist
147 if (isset($token->iprestriction
) and !address_in_subnet(getremoteaddr(), $token->iprestriction
)) {
152 unset($tokens[$key]);
156 // if some valid tokens exist then use the most recent
157 if (count($tokens) > 0) {
158 $token = array_pop($tokens);
160 if ( ($serviceshortname == MOODLE_OFFICIAL_MOBILE_SERVICE
and has_capability('moodle/webservice:createmobiletoken', context_system
::instance()))
161 //Note: automatically token generation is not available to admin (they must create a token manually)
162 or (!is_siteadmin($user) && has_capability('moodle/webservice:createtoken', context_system
::instance()))) {
163 // if service doesn't exist, dml will throw exception
164 $service_record = $DB->get_record('external_services', array('shortname'=>$serviceshortname, 'enabled'=>1), '*', MUST_EXIST
);
166 // Create a new token.
167 $token = new stdClass
;
168 $token->token
= md5(uniqid(rand(), 1));
169 $token->userid
= $user->id
;
170 $token->tokentype
= EXTERNAL_TOKEN_PERMANENT
;
171 $token->contextid
= context_system
::instance()->id
;
172 $token->creatorid
= $user->id
;
173 $token->timecreated
= time();
174 $token->externalserviceid
= $service_record->id
;
175 // MDL-43119 Token valid for 3 months (12 weeks).
176 $token->validuntil
= $token->timecreated +
12 * WEEKSECS
;
177 $token->id
= $DB->insert_record('external_tokens', $token);
180 'objectid' => $token->id
,
181 'relateduserid' => $user->id
,
186 $event = \core\event\webservice_token_created
::create($params);
187 $event->add_record_snapshot('external_tokens', $token);
190 throw new moodle_exception('cannotcreatetoken', 'webservice', '', $serviceshortname);
195 $DB->set_field('external_tokens', 'lastaccess', time(), array('id'=>$token->id
));
198 'objectid' => $token->id
,
200 $event = \core\event\webservice_token_sent
::create($params);
201 $event->add_record_snapshot('external_tokens', $token);
204 $usertoken = new stdClass
;
205 $usertoken->token
= $token->token
;
206 echo json_encode($usertoken);
208 throw new moodle_exception('invalidlogin');