Differences Between: [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 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 'autolang' => new external_value(PARAM_INT, 'Whether to detect default language 178 from browser setting.', VALUE_OPTIONAL), 179 'lang' => new external_value(PARAM_LANG, 'Default language for the site.', VALUE_OPTIONAL), 180 'langmenu' => new external_value(PARAM_INT, 'Whether the language menu should be displayed.', VALUE_OPTIONAL), 181 'langlist' => new external_value(PARAM_RAW, 'Languages on language menu.', VALUE_OPTIONAL), 182 'locale' => new external_value(PARAM_RAW, 'Sitewide locale.', VALUE_OPTIONAL), 183 'tool_mobile_minimumversion' => new external_value(PARAM_NOTAGS, 'Minimum required version to access.', 184 VALUE_OPTIONAL), 185 'tool_mobile_iosappid' => new external_value(PARAM_ALPHANUM, 'iOS app\'s unique identifier.', 186 VALUE_OPTIONAL), 187 'tool_mobile_androidappid' => new external_value(PARAM_NOTAGS, 'Android app\'s unique identifier.', 188 VALUE_OPTIONAL), 189 'tool_mobile_setuplink' => new external_value(PARAM_URL, 'App download page.', VALUE_OPTIONAL), 190 'warnings' => new external_warnings(), 191 ) 192 ); 193 } 194 195 /** 196 * Returns description of get_config() parameters. 197 * 198 * @return external_function_parameters 199 * @since Moodle 3.2 200 */ 201 public static function get_config_parameters() { 202 return new external_function_parameters( 203 array( 204 'section' => new external_value(PARAM_ALPHANUMEXT, 'Settings section name.', VALUE_DEFAULT, ''), 205 ) 206 ); 207 } 208 209 /** 210 * Returns a list of site settings, filtering by section. 211 * 212 * @param string $section settings section name 213 * @return array with the settings and warnings 214 * @since Moodle 3.2 215 */ 216 public static function get_config($section = '') { 217 218 $params = self::validate_parameters(self::get_config_parameters(), array('section' => $section)); 219 220 $settings = api::get_config($params['section']); 221 $result['settings'] = array(); 222 foreach ($settings as $name => $value) { 223 $result['settings'][] = array( 224 'name' => $name, 225 'value' => $value, 226 ); 227 } 228 229 $result['warnings'] = array(); 230 return $result; 231 } 232 233 /** 234 * Returns description of get_config() result value. 235 * 236 * @return external_description 237 * @since Moodle 3.2 238 */ 239 public static function get_config_returns() { 240 return new external_single_structure( 241 array( 242 'settings' => new external_multiple_structure( 243 new external_single_structure( 244 array( 245 'name' => new external_value(PARAM_RAW, 'The name of the setting'), 246 'value' => new external_value(PARAM_RAW, 'The value of the setting'), 247 ) 248 ), 249 'Settings' 250 ), 251 'warnings' => new external_warnings(), 252 ) 253 ); 254 } 255 256 /** 257 * Returns description of get_autologin_key() parameters. 258 * 259 * @return external_function_parameters 260 * @since Moodle 3.2 261 */ 262 public static function get_autologin_key_parameters() { 263 return new external_function_parameters ( 264 array( 265 'privatetoken' => new external_value(PARAM_ALPHANUM, 'Private token, usually generated by login/token.php'), 266 ) 267 ); 268 } 269 270 /** 271 * Creates an auto-login key for the current user. Is created only in https sites and is restricted by time and ip address. 272 * 273 * Please note that it only works if the request comes from the Moodle mobile or desktop app. 274 * 275 * @param string $privatetoken the user private token for validating the request 276 * @return array with the settings and warnings 277 * @since Moodle 3.2 278 */ 279 public static function get_autologin_key($privatetoken) { 280 global $CFG, $DB, $USER; 281 282 $params = self::validate_parameters(self::get_autologin_key_parameters(), array('privatetoken' => $privatetoken)); 283 $privatetoken = $params['privatetoken']; 284 285 $context = context_system::instance(); 286 287 // We must toletare these two exceptions: forcepasswordchangenotice and usernotfullysetup. 288 try { 289 self::validate_context($context); 290 } catch (moodle_exception $e) { 291 if ($e->errorcode != 'usernotfullysetup' && $e->errorcode != 'forcepasswordchangenotice') { 292 // In case we receive a different exception, throw it. 293 throw $e; 294 } 295 } 296 297 // Only requests from the Moodle mobile or desktop app. This enhances security to avoid any type of XSS attack. 298 // This code goes intentionally here and not inside the check_autologin_prerequisites() function because it 299 // is used by other PHP scripts that can be opened in any browser. 300 if (!\core_useragent::is_moodle_app()) { 301 throw new moodle_exception('apprequired', 'tool_mobile'); 302 } 303 api::check_autologin_prerequisites($USER->id); 304 305 if (isset($_GET['privatetoken']) or empty($privatetoken)) { 306 throw new moodle_exception('invalidprivatetoken', 'tool_mobile'); 307 } 308 309 // Check the request counter, we must limit the number of times the privatetoken is sent. 310 // Between each request 6 minutes are required. 311 $last = get_user_preferences('tool_mobile_autologin_request_last', 0, $USER); 312 // Check if we must reset the count. 313 $timenow = time(); 314 if ($timenow - $last < 6 * MINSECS) { 315 throw new moodle_exception('autologinkeygenerationlockout', 'tool_mobile'); 316 } 317 set_user_preference('tool_mobile_autologin_request_last', $timenow, $USER); 318 319 // We are expecting a privatetoken linked to the current token being used. 320 // This WS is only valid when using mobile services via REST (this is intended). 321 $currenttoken = required_param('wstoken', PARAM_ALPHANUM); 322 $conditions = array( 323 'userid' => $USER->id, 324 'token' => $currenttoken, 325 'privatetoken' => $privatetoken, 326 ); 327 if (!$token = $DB->get_record('external_tokens', $conditions)) { 328 throw new moodle_exception('invalidprivatetoken', 'tool_mobile'); 329 } 330 331 $result = array(); 332 $result['key'] = api::get_autologin_key(); 333 $autologinurl = new moodle_url("/$CFG->admin/tool/mobile/autologin.php"); 334 $result['autologinurl'] = $autologinurl->out(false); 335 $result['warnings'] = array(); 336 return $result; 337 } 338 339 /** 340 * Returns description of get_autologin_key() result value. 341 * 342 * @return external_description 343 * @since Moodle 3.2 344 */ 345 public static function get_autologin_key_returns() { 346 return new external_single_structure( 347 array( 348 'key' => new external_value(PARAM_ALPHANUMEXT, 'Auto-login key for a single usage with time expiration.'), 349 'autologinurl' => new external_value(PARAM_URL, 'Auto-login URL.'), 350 'warnings' => new external_warnings(), 351 ) 352 ); 353 } 354 355 /** 356 * Returns description of get_content() parameters 357 * 358 * @return external_function_parameters 359 * @since Moodle 3.5 360 */ 361 public static function get_content_parameters() { 362 return new external_function_parameters( 363 array( 364 'component' => new external_value(PARAM_COMPONENT, 'Component where the class is e.g. mod_assign.'), 365 'method' => new external_value(PARAM_ALPHANUMEXT, 'Method to execute in class \$component\output\mobile.'), 366 'args' => new external_multiple_structure( 367 new external_single_structure( 368 array( 369 'name' => new external_value(PARAM_ALPHANUMEXT, 'Param name.'), 370 'value' => new external_value(PARAM_RAW, 'Param value.') 371 ) 372 ), 'Args for the method are optional.', VALUE_OPTIONAL 373 ) 374 ) 375 ); 376 } 377 378 /** 379 * Returns a piece of content to be displayed in the Mobile app, it usually returns a template, javascript and 380 * other structured data that will be used to render a view in the Mobile app. 381 * 382 * Callbacks (placed in \$component\output\mobile) that are called by this web service are responsible for doing the 383 * appropriate security checks to access the information to be returned. 384 * 385 * @param string $component name of the component. 386 * @param string $method function method name in class \$component\output\mobile. 387 * @param array $args optional arguments for the method. 388 * @return array HTML, JavaScript and other required data and information to create a view in the app. 389 * @since Moodle 3.5 390 * @throws coding_exception 391 */ 392 public static function get_content($component, $method, $args = array()) { 393 global $OUTPUT, $PAGE, $USER; 394 395 $params = self::validate_parameters(self::get_content_parameters(), 396 array( 397 'component' => $component, 398 'method' => $method, 399 'args' => $args 400 ) 401 ); 402 403 // Reformat arguments into something less unwieldy. 404 $arguments = array(); 405 foreach ($params['args'] as $paramargument) { 406 $arguments[$paramargument['name']] = $paramargument['value']; 407 } 408 409 // The component was validated via the PARAM_COMPONENT parameter type. 410 $classname = '\\' . $params['component'] .'\output\mobile'; 411 if (!method_exists($classname, $params['method'])) { 412 throw new coding_exception("Missing method in $classname"); 413 } 414 $result = call_user_func_array(array($classname, $params['method']), array($arguments)); 415 416 // Populate otherdata. 417 $otherdata = array(); 418 if (!empty($result['otherdata'])) { 419 $result['otherdata'] = (array) $result['otherdata']; 420 foreach ($result['otherdata'] as $name => $value) { 421 $otherdata[] = array( 422 'name' => $name, 423 'value' => $value 424 ); 425 } 426 } 427 428 return array( 429 'templates' => !empty($result['templates']) ? $result['templates'] : array(), 430 'javascript' => !empty($result['javascript']) ? $result['javascript'] : '', 431 'otherdata' => $otherdata, 432 'files' => !empty($result['files']) ? $result['files'] : array(), 433 'restrict' => !empty($result['restrict']) ? $result['restrict'] : array(), 434 'disabled' => !empty($result['disabled']) ? true : false, 435 ); 436 } 437 438 /** 439 * Returns description of get_content() result value 440 * 441 * @return array 442 * @since Moodle 3.5 443 */ 444 public static function get_content_returns() { 445 return new external_single_structure( 446 array( 447 'templates' => new external_multiple_structure( 448 new external_single_structure( 449 array( 450 'id' => new external_value(PARAM_TEXT, 'ID of the template.'), 451 'html' => new external_value(PARAM_RAW, 'HTML code.'), 452 ) 453 ), 454 'Templates required by the generated content.' 455 ), 456 'javascript' => new external_value(PARAM_RAW, 'JavaScript code.'), 457 'otherdata' => new external_multiple_structure( 458 new external_single_structure( 459 array( 460 'name' => new external_value(PARAM_RAW, 'Field name.'), 461 'value' => new external_value(PARAM_RAW, 'Field value.') 462 ) 463 ), 464 'Other data that can be used or manipulated by the template via 2-way data-binding.' 465 ), 466 'files' => new external_files('Files in the content.'), 467 'restrict' => new external_single_structure( 468 array( 469 'users' => new external_multiple_structure( 470 new external_value(PARAM_INT, 'user id'), 'List of allowed users.', VALUE_OPTIONAL 471 ), 472 'courses' => new external_multiple_structure( 473 new external_value(PARAM_INT, 'course id'), 'List of allowed courses.', VALUE_OPTIONAL 474 ), 475 ), 476 'Restrict this content to certain users or courses.' 477 ), 478 'disabled' => new external_value(PARAM_BOOL, 'Whether we consider this disabled or not.', VALUE_OPTIONAL), 479 ) 480 ); 481 } 482 483 /** 484 * Returns description of method parameters 485 * 486 * @return external_function_parameters 487 * @since Moodle 3.7 488 */ 489 public static function call_external_functions_parameters() { 490 return new external_function_parameters([ 491 'requests' => new external_multiple_structure( 492 new external_single_structure([ 493 'function' => new external_value(PARAM_ALPHANUMEXT, 'Function name'), 494 'arguments' => new external_value(PARAM_RAW, 'JSON-encoded object with named arguments', VALUE_DEFAULT, '{}'), 495 'settingraw' => new external_value(PARAM_BOOL, 'Return raw text', VALUE_DEFAULT, false), 496 'settingfilter' => new external_value(PARAM_BOOL, 'Filter text', VALUE_DEFAULT, false), 497 'settingfileurl' => new external_value(PARAM_BOOL, 'Rewrite plugin file URLs', VALUE_DEFAULT, true), 498 'settinglang' => new external_value(PARAM_LANG, 'Session language', VALUE_DEFAULT, ''), 499 ]) 500 ) 501 ]); 502 } 503 504 /** 505 * Call multiple external functions and return all responses. 506 * 507 * @param array $requests List of requests. 508 * @return array Responses. 509 * @since Moodle 3.7 510 */ 511 public static function call_external_functions($requests) { 512 global $SESSION; 513 514 $params = self::validate_parameters(self::call_external_functions_parameters(), ['requests' => $requests]); 515 516 // We need to check if the functions being called are included in the service of the current token. 517 // This function only works when using mobile services via REST (this is intended). 518 $webservicemanager = new \webservice; 519 $token = $webservicemanager->get_user_ws_token(required_param('wstoken', PARAM_ALPHANUM)); 520 521 $settings = \external_settings::get_instance(); 522 $defaultlang = current_language(); 523 $responses = []; 524 525 foreach ($params['requests'] as $request) { 526 // Some external functions modify _GET or $_POST data, we need to restore the original data after each call. 527 $originalget = fullclone($_GET); 528 $originalpost = fullclone($_POST); 529 530 // Set external settings and language. 531 $settings->set_raw($request['settingraw']); 532 $settings->set_filter($request['settingfilter']); 533 $settings->set_fileurl($request['settingfileurl']); 534 $settings->set_lang($request['settinglang']); 535 $SESSION->lang = $request['settinglang'] ?: $defaultlang; 536 537 // Parse arguments to an array, validation is done in external_api::call_external_function. 538 $args = @json_decode($request['arguments'], true); 539 if (!is_array($args)) { 540 $args = []; 541 } 542 543 if ($webservicemanager->service_function_exists($request['function'], $token->externalserviceid)) { 544 $response = external_api::call_external_function($request['function'], $args, false); 545 } else { 546 // Function not included in the service, return an access exception. 547 $response = [ 548 'error' => true, 549 'exception' => [ 550 'errorcode' => 'accessexception', 551 'module' => 'webservice' 552 ] 553 ]; 554 if (debugging('', DEBUG_DEVELOPER)) { 555 $response['exception']['debuginfo'] = 'Access to the function is not allowed.'; 556 } 557 } 558 559 if (isset($response['data'])) { 560 $response['data'] = json_encode($response['data']); 561 } 562 if (isset($response['exception'])) { 563 $response['exception'] = json_encode($response['exception']); 564 } 565 $responses[] = $response; 566 567 // Restore original $_GET and $_POST. 568 $_GET = $originalget; 569 $_POST = $originalpost; 570 571 if ($response['error']) { 572 // Do not process the remaining requests. 573 break; 574 } 575 } 576 577 return ['responses' => $responses]; 578 } 579 580 /** 581 * Returns description of method result value 582 * 583 * @return external_single_structure 584 * @since Moodle 3.7 585 */ 586 public static function call_external_functions_returns() { 587 return new external_function_parameters([ 588 'responses' => new external_multiple_structure( 589 new external_single_structure([ 590 'error' => new external_value(PARAM_BOOL, 'Whether an exception was thrown.'), 591 'data' => new external_value(PARAM_RAW, 'JSON-encoded response data', VALUE_OPTIONAL), 592 'exception' => new external_value(PARAM_RAW, 'JSON-encoed exception info', VALUE_OPTIONAL), 593 ]) 594 ) 595 ]); 596 } 597 598 /** 599 * Returns description of get_tokens_for_qr_login() parameters. 600 * 601 * @return external_function_parameters 602 * @since Moodle 3.9 603 */ 604 public static function get_tokens_for_qr_login_parameters() { 605 return new external_function_parameters ( 606 [ 607 'qrloginkey' => new external_value(PARAM_ALPHANUMEXT, 'The user key for validating the request.'), 608 'userid' => new external_value(PARAM_INT, 'The user the key belongs to.'), 609 ] 610 ); 611 } 612 613 /** 614 * Returns a WebService token (and private token) for QR login 615 * 616 * @param string $qrloginkey the user key generated and embedded into the QR code for validating the request 617 * @param int $userid the user the key belongs to 618 * @return array with the tokens and warnings 619 * @since Moodle 3.9 620 */ 621 public static function get_tokens_for_qr_login($qrloginkey, $userid) { 622 global $PAGE, $DB; 623 624 $params = self::validate_parameters(self::get_tokens_for_qr_login_parameters(), 625 ['qrloginkey' => $qrloginkey, 'userid' => $userid]); 626 627 $context = context_system::instance(); 628 // We need this to make work the format text functions. 629 $PAGE->set_context($context); 630 631 $qrcodetype = get_config('tool_mobile', 'qrcodetype'); 632 if ($qrcodetype != api::QR_CODE_LOGIN) { 633 throw new moodle_exception('qrcodedisabled', 'tool_mobile'); 634 } 635 636 // Only requests from the Moodle mobile or desktop app. This enhances security to avoid any type of XSS attack. 637 // This code goes intentionally here and not inside the check_autologin_prerequisites() function because it 638 // is used by other PHP scripts that can be opened in any browser. 639 if (!\core_useragent::is_moodle_app()) { 640 throw new moodle_exception('apprequired', 'tool_mobile'); 641 } 642 api::check_autologin_prerequisites($params['userid']); // Checks https, avoid site admins using this... 643 644 // Validate and delete the key. 645 $key = validate_user_key($params['qrloginkey'], 'tool_mobile', null); 646 delete_user_key('tool_mobile', $params['userid']); 647 648 // Double check key belong to user. 649 if ($key->userid != $params['userid']) { 650 throw new moodle_exception('invalidkey'); 651 } 652 653 // Key validated, check user. 654 $user = core_user::get_user($key->userid, '*', MUST_EXIST); 655 core_user::require_active_user($user, true, true); 656 657 // Generate WS tokens. 658 \core\session\manager::set_user($user); 659 660 // Check if the service exists and is enabled. 661 $service = $DB->get_record('external_services', ['shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE, 'enabled' => 1]); 662 if (empty($service)) { 663 // will throw exception if no token found 664 throw new moodle_exception('servicenotavailable', 'webservice'); 665 } 666 667 // Get an existing token or create a new one. 668 $token = external_generate_token_for_current_user($service); 669 $privatetoken = $token->privatetoken; // Save it here, the next function removes it. 670 external_log_token_request($token); 671 672 $result = [ 673 'token' => $token->token, 674 'privatetoken' => $privatetoken ?: '', 675 'warnings' => [], 676 ]; 677 return $result; 678 } 679 680 /** 681 * Returns description of get_tokens_for_qr_login() result value. 682 * 683 * @return external_description 684 * @since Moodle 3.9 685 */ 686 public static function get_tokens_for_qr_login_returns() { 687 return new external_single_structure( 688 [ 689 'token' => new external_value(PARAM_ALPHANUM, 'A valid WebService token for the official mobile app service.'), 690 'privatetoken' => new external_value(PARAM_ALPHANUM, 'Private token used for auto-login processes.'), 691 'warnings' => new external_warnings(), 692 ] 693 ); 694 } 695 696 /** 697 * Returns description of validate_subscription_key() parameters. 698 * 699 * @return external_function_parameters 700 * @since Moodle 3.9 701 */ 702 public static function validate_subscription_key_parameters() { 703 return new external_function_parameters( 704 [ 705 'key' => new external_value(PARAM_RAW, 'Site subscription temporary key.'), 706 ] 707 ); 708 } 709 710 /** 711 * Check if the given site subscription key is valid 712 * 713 * @param string $key subscriptiion temporary key 714 * @return array with the settings and warnings 715 * @since Moodle 3.9 716 */ 717 public static function validate_subscription_key(string $key): array { 718 global $CFG, $PAGE; 719 720 $params = self::validate_parameters(self::validate_subscription_key_parameters(), ['key' => $key]); 721 722 $context = context_system::instance(); 723 $PAGE->set_context($context); 724 725 $validated = false; 726 $sitesubscriptionkey = get_config('tool_mobile', 'sitesubscriptionkey'); 727 if (!empty($sitesubscriptionkey) && $CFG->enablemobilewebservice && empty($CFG->disablemobileappsubscription)) { 728 $sitesubscriptionkey = json_decode($sitesubscriptionkey); 729 $validated = time() < $sitesubscriptionkey->validuntil && $params['key'] === $sitesubscriptionkey->key; 730 // Delete existing, even if not validated to enforce security and attacks prevention. 731 unset_config('sitesubscriptionkey', 'tool_mobile'); 732 } 733 734 return [ 735 'validated' => $validated, 736 'warnings' => [], 737 ]; 738 } 739 740 /** 741 * Returns description of validate_subscription_key() result value. 742 * 743 * @return external_description 744 * @since Moodle 3.9 745 */ 746 public static function validate_subscription_key_returns() { 747 return new external_single_structure( 748 [ 749 'validated' => new external_value(PARAM_BOOL, 'Whether the key is validated or not.'), 750 'warnings' => new external_warnings(), 751 ] 752 ); 753 } 754 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body