See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]
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/>. 16 17 /** 18 * This is the external API for this tool. 19 * 20 * @package tool_mobile 21 * @copyright 2016 Juan Leyva 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace tool_mobile; 26 defined('MOODLE_INTERNAL') || die(); 27 28 require_once("$CFG->libdir/externallib.php"); 29 require_once("$CFG->dirroot/webservice/lib.php"); 30 31 use external_api; 32 use external_files; 33 use external_function_parameters; 34 use external_value; 35 use external_single_structure; 36 use external_multiple_structure; 37 use external_warnings; 38 use context_system; 39 use moodle_exception; 40 use moodle_url; 41 use core_text; 42 use core_user; 43 use coding_exception; 44 45 /** 46 * This is the external API for this tool. 47 * 48 * @copyright 2016 Juan Leyva 49 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 50 */ 51 class external extends external_api { 52 53 /** 54 * Returns description of get_plugins_supporting_mobile() parameters. 55 * 56 * @return external_function_parameters 57 * @since Moodle 3.1 58 */ 59 public static function get_plugins_supporting_mobile_parameters() { 60 return new external_function_parameters(array()); 61 } 62 63 /** 64 * Returns a list of Moodle plugins supporting the mobile app. 65 * 66 * @return array an array of warnings and objects containing the plugin information 67 * @since Moodle 3.1 68 */ 69 public static function get_plugins_supporting_mobile() { 70 return array( 71 'plugins' => api::get_plugins_supporting_mobile(), 72 'warnings' => array(), 73 ); 74 } 75 76 /** 77 * Returns description of get_plugins_supporting_mobile() result value. 78 * 79 * @return external_description 80 * @since Moodle 3.1 81 */ 82 public static function get_plugins_supporting_mobile_returns() { 83 return new external_single_structure( 84 array( 85 'plugins' => new external_multiple_structure( 86 new external_single_structure( 87 array( 88 'component' => new external_value(PARAM_COMPONENT, 'The plugin component name.'), 89 'version' => new external_value(PARAM_NOTAGS, 'The plugin version number.'), 90 'addon' => new external_value(PARAM_COMPONENT, 'The Mobile addon (package) name.'), 91 'dependencies' => new external_multiple_structure( 92 new external_value(PARAM_COMPONENT, 'Mobile addon name.'), 93 'The list of Mobile addons this addon depends on.' 94 ), 95 'fileurl' => new external_value(PARAM_URL, 'The addon package url for download 96 or empty if it doesn\'t exist.'), 97 'filehash' => new external_value(PARAM_RAW, 'The addon package hash or empty if it doesn\'t exist.'), 98 'filesize' => new external_value(PARAM_INT, 'The addon package size or empty if it doesn\'t exist.'), 99 'handlers' => new external_value(PARAM_RAW, 'Handlers definition (JSON)', VALUE_OPTIONAL), 100 'lang' => new external_value(PARAM_RAW, 'Language strings used by the handlers (JSON)', VALUE_OPTIONAL), 101 ) 102 ) 103 ), 104 'warnings' => new external_warnings(), 105 ) 106 ); 107 } 108 109 /** 110 * Returns description of get_public_config() parameters. 111 * 112 * @return external_function_parameters 113 * @since Moodle 3.2 114 */ 115 public static function get_public_config_parameters() { 116 return new external_function_parameters(array()); 117 } 118 119 /** 120 * Returns a list of the site public settings, those not requiring authentication. 121 * 122 * @return array with the settings and warnings 123 * @since Moodle 3.2 124 */ 125 public static function get_public_config() { 126 $result = api::get_public_config(); 127 $result['warnings'] = array(); 128 return $result; 129 } 130 131 /** 132 * Returns description of get_public_config() result value. 133 * 134 * @return external_description 135 * @since Moodle 3.2 136 */ 137 public static function get_public_config_returns() { 138 return new external_single_structure( 139 array( 140 'wwwroot' => new external_value(PARAM_RAW, 'Site URL.'), 141 'httpswwwroot' => new external_value(PARAM_RAW, 'Site https URL (if httpslogin is enabled).'), 142 'sitename' => new external_value(PARAM_RAW, 'Site name.'), 143 'guestlogin' => new external_value(PARAM_INT, 'Whether guest login is enabled.'), 144 'rememberusername' => new external_value(PARAM_INT, 'Values: 0 for No, 1 for Yes, 2 for optional.'), 145 'authloginviaemail' => new external_value(PARAM_INT, 'Whether log in via email is enabled.'), 146 'registerauth' => new external_value(PARAM_PLUGIN, 'Authentication method for user registration.'), 147 'forgottenpasswordurl' => new external_value(PARAM_URL, 'Forgotten password URL.'), 148 'authinstructions' => new external_value(PARAM_RAW, 'Authentication instructions.'), 149 'authnoneenabled' => new external_value(PARAM_INT, 'Whether auth none is enabled.'), 150 'enablewebservices' => new external_value(PARAM_INT, 'Whether Web Services are enabled.'), 151 'enablemobilewebservice' => new external_value(PARAM_INT, 'Whether the Mobile service is enabled.'), 152 'maintenanceenabled' => new external_value(PARAM_INT, 'Whether site maintenance is enabled.'), 153 'maintenancemessage' => new external_value(PARAM_RAW, 'Maintenance message.'), 154 'logourl' => new external_value(PARAM_URL, 'The site logo URL', VALUE_OPTIONAL), 155 'compactlogourl' => new external_value(PARAM_URL, 'The site compact logo URL', VALUE_OPTIONAL), 156 'typeoflogin' => new external_value(PARAM_INT, 'The type of login. 1 for app, 2 for browser, 3 for embedded.'), 157 'launchurl' => new external_value(PARAM_URL, 'SSO login launch URL.', VALUE_OPTIONAL), 158 'mobilecssurl' => new external_value(PARAM_URL, 'Mobile custom CSS theme', VALUE_OPTIONAL), 159 'tool_mobile_disabledfeatures' => new external_value(PARAM_RAW, 'Disabled features in the app', VALUE_OPTIONAL), 160 'identityproviders' => new external_multiple_structure( 161 new external_single_structure( 162 array( 163 'name' => new external_value(PARAM_TEXT, 'The identity provider name.'), 164 'iconurl' => new external_value(PARAM_URL, 'The icon URL for the provider.'), 165 'url' => new external_value(PARAM_URL, 'The URL of the provider.'), 166 ) 167 ), 168 'Identity providers', VALUE_OPTIONAL 169 ), 170 'country' => new external_value(PARAM_NOTAGS, 'Default site country', VALUE_OPTIONAL), 171 'agedigitalconsentverification' => new external_value(PARAM_BOOL, 'Whether age digital consent verification 172 is enabled.', VALUE_OPTIONAL), 173 'supportname' => new external_value(PARAM_NOTAGS, 'Site support contact name 174 (only if age verification is enabled).', VALUE_OPTIONAL), 175 'supportemail' => new external_value(PARAM_EMAIL, 'Site support contact email 176 (only if age verification is enabled).', VALUE_OPTIONAL), 177 'supportpage' => new external_value(PARAM_URL, 'Site support page link.', VALUE_OPTIONAL), 178 'supportavailability' => new external_value(PARAM_INT, 'Determines who has access to contact site support.', 179 VALUE_OPTIONAL), 180 'autolang' => new external_value(PARAM_INT, 'Whether to detect default language 181 from browser setting.', VALUE_OPTIONAL), 182 'lang' => new external_value(PARAM_LANG, 'Default language for the site.', VALUE_OPTIONAL), 183 'langmenu' => new external_value(PARAM_INT, 'Whether the language menu should be displayed.', VALUE_OPTIONAL), 184 'langlist' => new external_value(PARAM_RAW, 'Languages on language menu.', VALUE_OPTIONAL), 185 'locale' => new external_value(PARAM_RAW, 'Sitewide locale.', VALUE_OPTIONAL), 186 'tool_mobile_minimumversion' => new external_value(PARAM_NOTAGS, 'Minimum required version to access.', 187 VALUE_OPTIONAL), 188 'tool_mobile_iosappid' => new external_value(PARAM_ALPHANUM, 'iOS app\'s unique identifier.', 189 VALUE_OPTIONAL), 190 'tool_mobile_androidappid' => new external_value(PARAM_NOTAGS, 'Android app\'s unique identifier.', 191 VALUE_OPTIONAL), 192 'tool_mobile_setuplink' => new external_value(PARAM_URL, 'App download page.', VALUE_OPTIONAL), 193 'tool_mobile_qrcodetype' => new external_value(PARAM_INT, 'QR login configuration.', VALUE_OPTIONAL), 194 'warnings' => new external_warnings(), 195 ) 196 ); 197 } 198 199 /** 200 * Returns description of get_config() parameters. 201 * 202 * @return external_function_parameters 203 * @since Moodle 3.2 204 */ 205 public static function get_config_parameters() { 206 return new external_function_parameters( 207 array( 208 'section' => new external_value(PARAM_ALPHANUMEXT, 'Settings section name.', VALUE_DEFAULT, ''), 209 ) 210 ); 211 } 212 213 /** 214 * Returns a list of site settings, filtering by section. 215 * 216 * @param string $section settings section name 217 * @return array with the settings and warnings 218 * @since Moodle 3.2 219 */ 220 public static function get_config($section = '') { 221 222 $params = self::validate_parameters(self::get_config_parameters(), array('section' => $section)); 223 224 $settings = api::get_config($params['section']); 225 $result['settings'] = array(); 226 foreach ($settings as $name => $value) { 227 $result['settings'][] = array( 228 'name' => $name, 229 'value' => $value, 230 ); 231 } 232 233 $result['warnings'] = array(); 234 return $result; 235 } 236 237 /** 238 * Returns description of get_config() result value. 239 * 240 * @return external_description 241 * @since Moodle 3.2 242 */ 243 public static function get_config_returns() { 244 return new external_single_structure( 245 array( 246 'settings' => new external_multiple_structure( 247 new external_single_structure( 248 array( 249 'name' => new external_value(PARAM_RAW, 'The name of the setting'), 250 'value' => new external_value(PARAM_RAW, 'The value of the setting'), 251 ) 252 ), 253 'Settings' 254 ), 255 'warnings' => new external_warnings(), 256 ) 257 ); 258 } 259 260 /** 261 * Returns description of get_autologin_key() parameters. 262 * 263 * @return external_function_parameters 264 * @since Moodle 3.2 265 */ 266 public static function get_autologin_key_parameters() { 267 return new external_function_parameters ( 268 array( 269 'privatetoken' => new external_value(PARAM_ALPHANUM, 'Private token, usually generated by login/token.php'), 270 ) 271 ); 272 } 273 274 /** 275 * Creates an auto-login key for the current user. Is created only in https sites and is restricted by time and ip address. 276 * 277 * Please note that it only works if the request comes from the Moodle mobile or desktop app. 278 * 279 * @param string $privatetoken the user private token for validating the request 280 * @return array with the settings and warnings 281 * @since Moodle 3.2 282 */ 283 public static function get_autologin_key($privatetoken) { 284 global $CFG, $DB, $USER; 285 286 $params = self::validate_parameters(self::get_autologin_key_parameters(), array('privatetoken' => $privatetoken)); 287 $privatetoken = $params['privatetoken']; 288 289 $context = context_system::instance(); 290 291 // We must toletare these two exceptions: forcepasswordchangenotice and usernotfullysetup. 292 try { 293 self::validate_context($context); 294 } catch (moodle_exception $e) { 295 if ($e->errorcode != 'usernotfullysetup' && $e->errorcode != 'forcepasswordchangenotice') { 296 // In case we receive a different exception, throw it. 297 throw $e; 298 } 299 } 300 301 // Only requests from the Moodle mobile or desktop app. This enhances security to avoid any type of XSS attack. 302 // This code goes intentionally here and not inside the check_autologin_prerequisites() function because it 303 // is used by other PHP scripts that can be opened in any browser. 304 if (!\core_useragent::is_moodle_app()) { 305 throw new moodle_exception('apprequired', 'tool_mobile'); 306 } 307 api::check_autologin_prerequisites($USER->id); 308 309 if (isset($_GET['privatetoken']) or empty($privatetoken)) { 310 throw new moodle_exception('invalidprivatetoken', 'tool_mobile'); 311 } 312 313 // Check the request counter, we must limit the number of times the privatetoken is sent. 314 // Between each request 6 minutes are required. 315 $last = get_user_preferences('tool_mobile_autologin_request_last', 0, $USER); 316 // Check if we must reset the count. 317 $mintimereq = get_config('tool_mobile', 'autologinmintimebetweenreq'); 318 $mintimereq = empty($mintimereq) ? 6 * MINSECS : $mintimereq; 319 $timenow = time(); 320 if ($timenow - $last < $mintimereq) { 321 $minutes = $mintimereq / MINSECS; 322 throw new moodle_exception('autologinkeygenerationlockout', 'tool_mobile', '', $minutes); 323 } 324 set_user_preference('tool_mobile_autologin_request_last', $timenow, $USER); 325 326 // We are expecting a privatetoken linked to the current token being used. 327 // This WS is only valid when using mobile services via REST (this is intended). 328 $currenttoken = required_param('wstoken', PARAM_ALPHANUM); 329 $conditions = array( 330 'userid' => $USER->id, 331 'token' => $currenttoken, 332 'privatetoken' => $privatetoken, 333 ); 334 if (!$token = $DB->get_record('external_tokens', $conditions)) { 335 throw new moodle_exception('invalidprivatetoken', 'tool_mobile'); 336 } 337 338 $result = array(); 339 $result['key'] = api::get_autologin_key(); 340 $autologinurl = new moodle_url("/$CFG->admin/tool/mobile/autologin.php"); 341 $result['autologinurl'] = $autologinurl->out(false); 342 $result['warnings'] = array(); 343 return $result; 344 } 345 346 /** 347 * Returns description of get_autologin_key() result value. 348 * 349 * @return external_description 350 * @since Moodle 3.2 351 */ 352 public static function get_autologin_key_returns() { 353 return new external_single_structure( 354 array( 355 'key' => new external_value(PARAM_ALPHANUMEXT, 'Auto-login key for a single usage with time expiration.'), 356 'autologinurl' => new external_value(PARAM_URL, 'Auto-login URL.'), 357 'warnings' => new external_warnings(), 358 ) 359 ); 360 } 361 362 /** 363 * Returns description of get_content() parameters 364 * 365 * @return external_function_parameters 366 * @since Moodle 3.5 367 */ 368 public static function get_content_parameters() { 369 return new external_function_parameters( 370 array( 371 'component' => new external_value(PARAM_COMPONENT, 'Component where the class is e.g. mod_assign.'), 372 'method' => new external_value(PARAM_ALPHANUMEXT, 'Method to execute in class \$component\output\mobile.'), 373 'args' => new external_multiple_structure( 374 new external_single_structure( 375 array( 376 'name' => new external_value(PARAM_ALPHANUMEXT, 'Param name.'), 377 'value' => new external_value(PARAM_RAW, 'Param value.') 378 ) 379 ), 'Args for the method are optional.', VALUE_OPTIONAL 380 ) 381 ) 382 ); 383 } 384 385 /** 386 * Returns a piece of content to be displayed in the Mobile app, it usually returns a template, javascript and 387 * other structured data that will be used to render a view in the Mobile app. 388 * 389 * Callbacks (placed in \$component\output\mobile) that are called by this web service are responsible for doing the 390 * appropriate security checks to access the information to be returned. 391 * 392 * @param string $component name of the component. 393 * @param string $method function method name in class \$component\output\mobile. 394 * @param array $args optional arguments for the method. 395 * @return array HTML, JavaScript and other required data and information to create a view in the app. 396 * @since Moodle 3.5 397 * @throws coding_exception 398 */ 399 public static function get_content($component, $method, $args = array()) { 400 global $OUTPUT, $PAGE, $USER; 401 402 $params = self::validate_parameters(self::get_content_parameters(), 403 array( 404 'component' => $component, 405 'method' => $method, 406 'args' => $args 407 ) 408 ); 409 410 // Reformat arguments into something less unwieldy. 411 $arguments = array(); 412 foreach ($params['args'] as $paramargument) { 413 $arguments[$paramargument['name']] = $paramargument['value']; 414 } 415 416 // The component was validated via the PARAM_COMPONENT parameter type. 417 $classname = '\\' . $params['component'] .'\output\mobile'; 418 if (!method_exists($classname, $params['method'])) { 419 throw new coding_exception("Missing method in $classname"); 420 } 421 $result = call_user_func_array(array($classname, $params['method']), array($arguments)); 422 423 // Populate otherdata. 424 $otherdata = array(); 425 if (!empty($result['otherdata'])) { 426 $result['otherdata'] = (array) $result['otherdata']; 427 foreach ($result['otherdata'] as $name => $value) { 428 $otherdata[] = array( 429 'name' => $name, 430 'value' => $value 431 ); 432 } 433 } 434 435 return array( 436 'templates' => !empty($result['templates']) ? $result['templates'] : array(), 437 'javascript' => !empty($result['javascript']) ? $result['javascript'] : '', 438 'otherdata' => $otherdata, 439 'files' => !empty($result['files']) ? $result['files'] : array(), 440 'restrict' => !empty($result['restrict']) ? $result['restrict'] : array(), 441 'disabled' => !empty($result['disabled']) ? true : false, 442 ); 443 } 444 445 /** 446 * Returns description of get_content() result value 447 * 448 * @return array 449 * @since Moodle 3.5 450 */ 451 public static function get_content_returns() { 452 return new external_single_structure( 453 array( 454 'templates' => new external_multiple_structure( 455 new external_single_structure( 456 array( 457 'id' => new external_value(PARAM_TEXT, 'ID of the template.'), 458 'html' => new external_value(PARAM_RAW, 'HTML code.'), 459 ) 460 ), 461 'Templates required by the generated content.' 462 ), 463 'javascript' => new external_value(PARAM_RAW, 'JavaScript code.'), 464 'otherdata' => new external_multiple_structure( 465 new external_single_structure( 466 array( 467 'name' => new external_value(PARAM_RAW, 'Field name.'), 468 'value' => new external_value(PARAM_RAW, 'Field value.') 469 ) 470 ), 471 'Other data that can be used or manipulated by the template via 2-way data-binding.' 472 ), 473 'files' => new external_files('Files in the content.'), 474 'restrict' => new external_single_structure( 475 array( 476 'users' => new external_multiple_structure( 477 new external_value(PARAM_INT, 'user id'), 'List of allowed users.', VALUE_OPTIONAL 478 ), 479 'courses' => new external_multiple_structure( 480 new external_value(PARAM_INT, 'course id'), 'List of allowed courses.', VALUE_OPTIONAL 481 ), 482 ), 483 'Restrict this content to certain users or courses.' 484 ), 485 'disabled' => new external_value(PARAM_BOOL, 'Whether we consider this disabled or not.', VALUE_OPTIONAL), 486 ) 487 ); 488 } 489 490 /** 491 * Returns description of method parameters 492 * 493 * @return external_function_parameters 494 * @since Moodle 3.7 495 */ 496 public static function call_external_functions_parameters() { 497 return new external_function_parameters([ 498 'requests' => new external_multiple_structure( 499 new external_single_structure([ 500 'function' => new external_value(PARAM_ALPHANUMEXT, 'Function name'), 501 'arguments' => new external_value(PARAM_RAW, 'JSON-encoded object with named arguments', VALUE_DEFAULT, '{}'), 502 'settingraw' => new external_value(PARAM_BOOL, 'Return raw text', VALUE_DEFAULT, false), 503 'settingfilter' => new external_value(PARAM_BOOL, 'Filter text', VALUE_DEFAULT, false), 504 'settingfileurl' => new external_value(PARAM_BOOL, 'Rewrite plugin file URLs', VALUE_DEFAULT, true), 505 'settinglang' => new external_value(PARAM_LANG, 'Session language', VALUE_DEFAULT, ''), 506 ]) 507 ) 508 ]); 509 } 510 511 /** 512 * Call multiple external functions and return all responses. 513 * 514 * @param array $requests List of requests. 515 * @return array Responses. 516 * @since Moodle 3.7 517 */ 518 public static function call_external_functions($requests) { 519 global $SESSION; 520 521 $params = self::validate_parameters(self::call_external_functions_parameters(), ['requests' => $requests]); 522 523 // We need to check if the functions being called are included in the service of the current token. 524 // This function only works when using mobile services via REST (this is intended). 525 $webservicemanager = new \webservice; 526 $token = $webservicemanager->get_user_ws_token(required_param('wstoken', PARAM_ALPHANUM)); 527 528 $settings = \external_settings::get_instance(); 529 $defaultlang = current_language(); 530 $responses = []; 531 532 foreach ($params['requests'] as $request) { 533 // Some external functions modify _GET or $_POST data, we need to restore the original data after each call. 534 $originalget = fullclone($_GET); 535 $originalpost = fullclone($_POST); 536 537 // Set external settings and language. 538 $settings->set_raw($request['settingraw']); 539 $settings->set_filter($request['settingfilter']); 540 $settings->set_fileurl($request['settingfileurl']); 541 $settings->set_lang($request['settinglang']); 542 $SESSION->lang = $request['settinglang'] ?: $defaultlang; 543 544 // Parse arguments to an array, validation is done in external_api::call_external_function. 545 $args = @json_decode($request['arguments'], true); 546 if (!is_array($args)) { 547 $args = []; 548 } 549 550 if ($webservicemanager->service_function_exists($request['function'], $token->externalserviceid)) { 551 $response = external_api::call_external_function($request['function'], $args, false); 552 } else { 553 // Function not included in the service, return an access exception. 554 $response = [ 555 'error' => true, 556 'exception' => [ 557 'errorcode' => 'accessexception', 558 'module' => 'webservice' 559 ] 560 ]; 561 if (debugging('', DEBUG_DEVELOPER)) { 562 $response['exception']['debuginfo'] = 'Access to the function is not allowed.'; 563 } 564 } 565 566 if (isset($response['data'])) { 567 $response['data'] = json_encode($response['data']); 568 } 569 if (isset($response['exception'])) { 570 $response['exception'] = json_encode($response['exception']); 571 } 572 $responses[] = $response; 573 574 // Restore original $_GET and $_POST. 575 $_GET = $originalget; 576 $_POST = $originalpost; 577 578 if ($response['error']) { 579 // Do not process the remaining requests. 580 break; 581 } 582 } 583 584 return ['responses' => $responses]; 585 } 586 587 /** 588 * Returns description of method result value 589 * 590 * @return external_single_structure 591 * @since Moodle 3.7 592 */ 593 public static function call_external_functions_returns() { 594 return new external_function_parameters([ 595 'responses' => new external_multiple_structure( 596 new external_single_structure([ 597 'error' => new external_value(PARAM_BOOL, 'Whether an exception was thrown.'), 598 'data' => new external_value(PARAM_RAW, 'JSON-encoded response data', VALUE_OPTIONAL), 599 'exception' => new external_value(PARAM_RAW, 'JSON-encoed exception info', VALUE_OPTIONAL), 600 ]) 601 ) 602 ]); 603 } 604 605 /** 606 * Returns description of get_tokens_for_qr_login() parameters. 607 * 608 * @return external_function_parameters 609 * @since Moodle 3.9 610 */ 611 public static function get_tokens_for_qr_login_parameters() { 612 return new external_function_parameters ( 613 [ 614 'qrloginkey' => new external_value(PARAM_ALPHANUMEXT, 'The user key for validating the request.'), 615 'userid' => new external_value(PARAM_INT, 'The user the key belongs to.'), 616 ] 617 ); 618 } 619 620 /** 621 * Returns a WebService token (and private token) for QR login 622 * 623 * @param string $qrloginkey the user key generated and embedded into the QR code for validating the request 624 * @param int $userid the user the key belongs to 625 * @return array with the tokens and warnings 626 * @since Moodle 3.9 627 */ 628 public static function get_tokens_for_qr_login($qrloginkey, $userid) { 629 global $PAGE, $DB; 630 631 $params = self::validate_parameters(self::get_tokens_for_qr_login_parameters(), 632 ['qrloginkey' => $qrloginkey, 'userid' => $userid]); 633 634 $context = context_system::instance(); 635 // We need this to make work the format text functions. 636 $PAGE->set_context($context); 637 638 $qrcodetype = get_config('tool_mobile', 'qrcodetype'); 639 if ($qrcodetype != api::QR_CODE_LOGIN) { 640 throw new moodle_exception('qrcodedisabled', 'tool_mobile'); 641 } 642 643 // Only requests from the Moodle mobile or desktop app. This enhances security to avoid any type of XSS attack. 644 // This code goes intentionally here and not inside the check_autologin_prerequisites() function because it 645 // is used by other PHP scripts that can be opened in any browser. 646 if (!\core_useragent::is_moodle_app()) { 647 throw new moodle_exception('apprequired', 'tool_mobile'); 648 } 649 api::check_autologin_prerequisites($params['userid']); // Checks https, avoid site admins using this... 650 651 // Validate and delete the key. 652 $key = validate_user_key($params['qrloginkey'], 'tool_mobile', null); 653 delete_user_key('tool_mobile', $params['userid']); 654 655 // Double check key belong to user. 656 if ($key->userid != $params['userid']) { 657 throw new moodle_exception('invalidkey'); 658 } 659 660 // Key validated, check user. 661 $user = core_user::get_user($key->userid, '*', MUST_EXIST); 662 core_user::require_active_user($user, true, true); 663 664 // Generate WS tokens. 665 \core\session\manager::set_user($user); 666 667 // Check if the service exists and is enabled. 668 $service = $DB->get_record('external_services', ['shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE, 'enabled' => 1]); 669 if (empty($service)) { 670 // will throw exception if no token found 671 throw new moodle_exception('servicenotavailable', 'webservice'); 672 } 673 674 // Get an existing token or create a new one. 675 $token = external_generate_token_for_current_user($service); 676 $privatetoken = $token->privatetoken; // Save it here, the next function removes it. 677 external_log_token_request($token); 678 679 $result = [ 680 'token' => $token->token, 681 'privatetoken' => $privatetoken ?: '', 682 'warnings' => [], 683 ]; 684 return $result; 685 } 686 687 /** 688 * Returns description of get_tokens_for_qr_login() result value. 689 * 690 * @return external_description 691 * @since Moodle 3.9 692 */ 693 public static function get_tokens_for_qr_login_returns() { 694 return new external_single_structure( 695 [ 696 'token' => new external_value(PARAM_ALPHANUM, 'A valid WebService token for the official mobile app service.'), 697 'privatetoken' => new external_value(PARAM_ALPHANUM, 'Private token used for auto-login processes.'), 698 'warnings' => new external_warnings(), 699 ] 700 ); 701 } 702 703 /** 704 * Returns description of validate_subscription_key() parameters. 705 * 706 * @return external_function_parameters 707 * @since Moodle 3.9 708 */ 709 public static function validate_subscription_key_parameters() { 710 return new external_function_parameters( 711 [ 712 'key' => new external_value(PARAM_RAW, 'Site subscription temporary key.'), 713 ] 714 ); 715 } 716 717 /** 718 * Check if the given site subscription key is valid 719 * 720 * @param string $key subscriptiion temporary key 721 * @return array with the settings and warnings 722 * @since Moodle 3.9 723 */ 724 public static function validate_subscription_key(string $key): array { 725 global $CFG, $PAGE; 726 727 $params = self::validate_parameters(self::validate_subscription_key_parameters(), ['key' => $key]); 728 729 $context = context_system::instance(); 730 $PAGE->set_context($context); 731 732 $validated = false; 733 $sitesubscriptionkey = get_config('tool_mobile', 'sitesubscriptionkey'); 734 if (!empty($sitesubscriptionkey) && $CFG->enablemobilewebservice && empty($CFG->disablemobileappsubscription)) { 735 $sitesubscriptionkey = json_decode($sitesubscriptionkey); 736 $validated = time() < $sitesubscriptionkey->validuntil && $params['key'] === $sitesubscriptionkey->key; 737 // Delete existing, even if not validated to enforce security and attacks prevention. 738 unset_config('sitesubscriptionkey', 'tool_mobile'); 739 } 740 741 return [ 742 'validated' => $validated, 743 'warnings' => [], 744 ]; 745 } 746 747 /** 748 * Returns description of validate_subscription_key() result value. 749 * 750 * @return external_description 751 * @since Moodle 3.9 752 */ 753 public static function validate_subscription_key_returns() { 754 return new external_single_structure( 755 [ 756 'validated' => new external_value(PARAM_BOOL, 'Whether the key is validated or not.'), 757 'warnings' => new external_warnings(), 758 ] 759 ); 760 } 761 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body