Merge branch 'MDL-40050-master' of git://github.com/FMCorz/moodle
[moodle.git] / login / token.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
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.
8 //
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.
13 //
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/>.
17 /**
18  * Return token
19  * @package    moodlecore
20  * @copyright  2011 Dongsheng Cai <dongsheng@moodle.com>
21  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22  */
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 $username = required_param('username', PARAM_USERNAME);
31 $password = required_param('password', PARAM_RAW);
32 $serviceshortname  = required_param('service',  PARAM_ALPHANUMEXT);
34 echo $OUTPUT->header();
36 if (!$CFG->enablewebservices) {
37     throw new moodle_exception('enablewsdescription', 'webservice');
38 }
39 $username = trim(core_text::strtolower($username));
40 if (is_restored_user($username)) {
41     throw new moodle_exception('restoredaccountresetpassword', 'webservice');
42 }
43 $user = authenticate_user_login($username, $password);
44 if (!empty($user)) {
46     //Non admin can not authenticate if maintenance mode
47     $hassiteconfig = has_capability('moodle/site:config', context_system::instance(), $user);
48     if (!empty($CFG->maintenance_enabled) and !$hassiteconfig) {
49         throw new moodle_exception('sitemaintenance', 'admin');
50     }
52     if (isguestuser($user)) {
53         throw new moodle_exception('noguest');
54     }
55     if (empty($user->confirmed)) {
56         throw new moodle_exception('usernotconfirmed', 'moodle', '', $user->username);
57     }
58     // check credential expiry
59     $userauth = get_auth_plugin($user->auth);
60     if (!empty($userauth->config->expiration) and $userauth->config->expiration == 1) {
61         $days2expire = $userauth->password_expire($user->username);
62         if (intval($days2expire) < 0 ) {
63             throw new moodle_exception('passwordisexpired', 'webservice');
64         }
65     }
67     // let enrol plugins deal with new enrolments if necessary
68     enrol_check_plugins($user);
70     // setup user session to check capability
71     \core\session\manager::set_user($user);
73     //check if the service exists and is enabled
74     $service = $DB->get_record('external_services', array('shortname' => $serviceshortname, 'enabled' => 1));
75     if (empty($service)) {
76         // will throw exception if no token found
77         throw new moodle_exception('servicenotavailable', 'webservice');
78     }
80     //check if there is any required system capability
81     if ($service->requiredcapability and !has_capability($service->requiredcapability, context_system::instance(), $user)) {
82         throw new moodle_exception('missingrequiredcapability', 'webservice', '', $service->requiredcapability);
83     }
85     //specific checks related to user restricted service
86     if ($service->restrictedusers) {
87         $authoriseduser = $DB->get_record('external_services_users',
88             array('externalserviceid' => $service->id, 'userid' => $user->id));
90         if (empty($authoriseduser)) {
91             throw new moodle_exception('usernotallowed', 'webservice', '', $serviceshortname);
92         }
94         if (!empty($authoriseduser->validuntil) and $authoriseduser->validuntil < time()) {
95             throw new moodle_exception('invalidtimedtoken', 'webservice');
96         }
98         if (!empty($authoriseduser->iprestriction) and !address_in_subnet(getremoteaddr(), $authoriseduser->iprestriction)) {
99             throw new moodle_exception('invalidiptoken', 'webservice');
100         }
101     }
103     //Check if a token has already been created for this user and this service
104     //Note: this could be an admin created or an user created token.
105     //      It does not really matter we take the first one that is valid.
106     $tokenssql = "SELECT t.id, t.sid, t.token, t.validuntil, t.iprestriction
107               FROM {external_tokens} t
108              WHERE t.userid = ? AND t.externalserviceid = ? AND t.tokentype = ?
109           ORDER BY t.timecreated ASC";
110     $tokens = $DB->get_records_sql($tokenssql, array($user->id, $service->id, EXTERNAL_TOKEN_PERMANENT));
112     //A bit of sanity checks
113     foreach ($tokens as $key=>$token) {
115         /// Checks related to a specific token. (script execution continue)
116         $unsettoken = false;
117         //if sid is set then there must be a valid associated session no matter the token type
118         if (!empty($token->sid)) {
119             if (!\core\session\manager::session_exists($token->sid)){
120                 //this token will never be valid anymore, delete it
121                 $DB->delete_records('external_tokens', array('sid'=>$token->sid));
122                 $unsettoken = true;
123             }
124         }
126         //remove token if no valid anymore
127         //Also delete this wrong token (similar logic to the web service servers
128         //    /webservice/lib.php/webservice_server::authenticate_by_token())
129         if (!empty($token->validuntil) and $token->validuntil < time()) {
130             $DB->delete_records('external_tokens', array('token'=>$token->token, 'tokentype'=> EXTERNAL_TOKEN_PERMANENT));
131             $unsettoken = true;
132         }
134         // remove token if its ip not in whitelist
135         if (isset($token->iprestriction) and !address_in_subnet(getremoteaddr(), $token->iprestriction)) {
136             $unsettoken = true;
137         }
139         if ($unsettoken) {
140             unset($tokens[$key]);
141         }
142     }
144     // if some valid tokens exist then use the most recent
145     if (count($tokens) > 0) {
146         $token = array_pop($tokens);
147     } else {
148         if ( ($serviceshortname == MOODLE_OFFICIAL_MOBILE_SERVICE and has_capability('moodle/webservice:createmobiletoken', context_system::instance()))
149                 //Note: automatically token generation is not available to admin (they must create a token manually)
150                 or (!is_siteadmin($user) && has_capability('moodle/webservice:createtoken', context_system::instance()))) {
151             // if service doesn't exist, dml will throw exception
152             $service_record = $DB->get_record('external_services', array('shortname'=>$serviceshortname, 'enabled'=>1), '*', MUST_EXIST);
154             // Create a new token.
155             $token = new stdClass;
156             $token->token = md5(uniqid(rand(), 1));
157             $token->userid = $user->id;
158             $token->tokentype = EXTERNAL_TOKEN_PERMANENT;
159             $token->contextid = context_system::instance()->id;
160             $token->creatorid = $user->id;
161             $token->timecreated = time();
162             $token->externalserviceid = $service_record->id;
163             $token->id = $DB->insert_record('external_tokens', $token);
165             $params = array(
166                 'objectid' => $token->id,
167                 'relateduserid' => $user->id,
168                 'other' => array(
169                     'auto' => true
170                 )
171             );
172             $event = \core\event\webservice_token_created::create($params);
173             $event->add_record_snapshot('external_tokens', $token);
174             $event->trigger();
175         } else {
176             throw new moodle_exception('cannotcreatetoken', 'webservice', '', $serviceshortname);
177         }
178     }
180     // log token access
181     $DB->set_field('external_tokens', 'lastaccess', time(), array('id'=>$token->id));
183     $params = array(
184         'objectid' => $token->id,
185     );
186     $event = \core\event\webservice_token_sent::create($params);
187     $event->add_record_snapshot('external_tokens', $token);
188     $event->trigger();
190     $usertoken = new stdClass;
191     $usertoken->token = $token->token;
192     echo json_encode($usertoken);
193 } else {
194     throw new moodle_exception('usernamenotfound', 'moodle');