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