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 namespace tool_mobile; 18 19 use externallib_advanced_testcase; 20 21 defined('MOODLE_INTERNAL') || die(); 22 23 global $CFG; 24 25 require_once($CFG->dirroot . '/webservice/tests/helpers.php'); 26 require_once($CFG->dirroot . '/admin/tool/mobile/tests/fixtures/output/mobile.php'); 27 require_once($CFG->dirroot . '/webservice/lib.php'); 28 29 /** 30 * Moodle Mobile admin tool external functions tests. 31 * 32 * @package tool_mobile 33 * @copyright 2016 Juan Leyva 34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 * @since Moodle 3.1 36 */ 37 class externallib_test extends externallib_advanced_testcase { 38 39 /** 40 * Test get_plugins_supporting_mobile. 41 * This is a very basic test because currently there aren't plugins supporting Mobile in core. 42 */ 43 public function test_get_plugins_supporting_mobile() { 44 $result = external::get_plugins_supporting_mobile(); 45 $result = \external_api::clean_returnvalue(external::get_plugins_supporting_mobile_returns(), $result); 46 $this->assertCount(0, $result['warnings']); 47 $this->assertArrayHasKey('plugins', $result); 48 $this->assertTrue(is_array($result['plugins'])); 49 } 50 51 public function test_get_public_config() { 52 global $CFG, $SITE, $OUTPUT; 53 54 $this->resetAfterTest(true); 55 $result = external::get_public_config(); 56 $result = \external_api::clean_returnvalue(external::get_public_config_returns(), $result); 57 58 // Test default values. 59 $context = \context_system::instance(); 60 list($authinstructions, $notusedformat) = external_format_text($CFG->auth_instructions, FORMAT_MOODLE, $context->id); 61 list($maintenancemessage, $notusedformat) = external_format_text($CFG->maintenance_message, FORMAT_MOODLE, $context->id); 62 63 $expected = array( 64 'wwwroot' => $CFG->wwwroot, 65 'httpswwwroot' => $CFG->wwwroot, 66 'sitename' => external_format_string($SITE->fullname, $context->id, true), 67 'guestlogin' => $CFG->guestloginbutton, 68 'rememberusername' => $CFG->rememberusername, 69 'authloginviaemail' => $CFG->authloginviaemail, 70 'registerauth' => $CFG->registerauth, 71 'forgottenpasswordurl' => $CFG->forgottenpasswordurl, 72 'authinstructions' => $authinstructions, 73 'authnoneenabled' => (int) is_enabled_auth('none'), 74 'enablewebservices' => $CFG->enablewebservices, 75 'enablemobilewebservice' => $CFG->enablemobilewebservice, 76 'maintenanceenabled' => $CFG->maintenance_enabled, 77 'maintenancemessage' => $maintenancemessage, 78 'typeoflogin' => api::LOGIN_VIA_APP, 79 'mobilecssurl' => '', 80 'tool_mobile_disabledfeatures' => '', 81 'launchurl' => "$CFG->wwwroot/$CFG->admin/tool/mobile/launch.php", 82 'country' => $CFG->country, 83 'agedigitalconsentverification' => \core_auth\digital_consent::is_age_digital_consent_verification_enabled(), 84 'autolang' => $CFG->autolang, 85 'lang' => $CFG->lang, 86 'langmenu' => $CFG->langmenu, 87 'langlist' => $CFG->langlist, 88 'locale' => $CFG->locale, 89 'tool_mobile_minimumversion' => '', 90 'tool_mobile_iosappid' => get_config('tool_mobile', 'iosappid'), 91 'tool_mobile_androidappid' => get_config('tool_mobile', 'androidappid'), 92 'tool_mobile_setuplink' => get_config('tool_mobile', 'setuplink'), 93 'tool_mobile_qrcodetype' => get_config('tool_mobile', 'qrcodetype'), 94 'supportpage' => $CFG->supportpage, 95 'warnings' => array() 96 ); 97 $this->assertEquals($expected, $result); 98 99 $this->setAdminUser(); 100 // Change some values. 101 set_config('registerauth', 'email'); 102 $authinstructions = 'Something with <b>html tags</b>'; 103 set_config('auth_instructions', $authinstructions); 104 set_config('typeoflogin', api::LOGIN_VIA_BROWSER, 'tool_mobile'); 105 set_config('logo', 'mock.png', 'core_admin'); 106 set_config('logocompact', 'mock.png', 'core_admin'); 107 set_config('forgottenpasswordurl', 'mailto:fake@email.zy'); // Test old hack. 108 set_config('agedigitalconsentverification', 1); 109 set_config('autolang', 1); 110 set_config('lang', 'a_b'); // Set invalid lang. 111 set_config('disabledfeatures', 'myoverview', 'tool_mobile'); 112 set_config('minimumversion', '3.8.0', 'tool_mobile'); 113 set_config('supportemail', 'test@test.com'); 114 115 // Enable couple of issuers. 116 $issuer = \core\oauth2\api::create_standard_issuer('google'); 117 $irecord = $issuer->to_record(); 118 $irecord->clientid = 'mock'; 119 $irecord->clientsecret = 'mock'; 120 \core\oauth2\api::update_issuer($irecord); 121 122 set_config('hostname', 'localhost', 'auth_cas'); 123 set_config('auth_logo', 'http://invalidurl.com//invalid/', 'auth_cas'); 124 set_config('auth_name', 'CAS', 'auth_cas'); 125 set_config('auth', 'oauth2,cas'); 126 127 list($authinstructions, $notusedformat) = external_format_text($authinstructions, FORMAT_MOODLE, $context->id); 128 $expected['registerauth'] = 'email'; 129 $expected['authinstructions'] = $authinstructions; 130 $expected['typeoflogin'] = api::LOGIN_VIA_BROWSER; 131 $expected['forgottenpasswordurl'] = ''; // Expect empty when it's not an URL. 132 $expected['agedigitalconsentverification'] = true; 133 $expected['supportname'] = $CFG->supportname; 134 $expected['supportemail'] = $CFG->supportemail; 135 $expected['autolang'] = '1'; 136 $expected['lang'] = ''; // Expect empty because it was set to an invalid lang. 137 $expected['tool_mobile_disabledfeatures'] = 'myoverview'; 138 $expected['tool_mobile_minimumversion'] = '3.8.0'; 139 140 if ($logourl = $OUTPUT->get_logo_url()) { 141 $expected['logourl'] = $logourl->out(false); 142 } 143 if ($compactlogourl = $OUTPUT->get_compact_logo_url()) { 144 $expected['compactlogourl'] = $compactlogourl->out(false); 145 } 146 147 $result = external::get_public_config(); 148 $result = \external_api::clean_returnvalue(external::get_public_config_returns(), $result); 149 // First check providers. 150 $identityproviders = $result['identityproviders']; 151 unset($result['identityproviders']); 152 153 $this->assertEquals('Google', $identityproviders[0]['name']); 154 $this->assertEquals($irecord->image, $identityproviders[0]['iconurl']); 155 $this->assertStringContainsString($CFG->wwwroot, $identityproviders[0]['url']); 156 157 $this->assertEquals('CAS', $identityproviders[1]['name']); 158 $this->assertEmpty($identityproviders[1]['iconurl']); 159 $this->assertStringContainsString($CFG->wwwroot, $identityproviders[1]['url']); 160 161 $this->assertEquals($expected, $result); 162 163 // Change providers img. 164 $newurl = 'validimage.png'; 165 set_config('auth_logo', $newurl, 'auth_cas'); 166 $result = external::get_public_config(); 167 $result = \external_api::clean_returnvalue(external::get_public_config_returns(), $result); 168 $this->assertStringContainsString($newurl, $result['identityproviders'][1]['iconurl']); 169 } 170 171 /** 172 * Test get_config 173 */ 174 public function test_get_config() { 175 global $CFG, $SITE; 176 require_once($CFG->dirroot . '/course/format/lib.php'); 177 178 $this->resetAfterTest(true); 179 180 $mysitepolicy = 'http://mysite.is/policy/'; 181 set_config('sitepolicy', $mysitepolicy); 182 set_config('supportemail', 'test@test.com'); 183 184 $result = external::get_config(); 185 $result = \external_api::clean_returnvalue(external::get_config_returns(), $result); 186 187 // SITE summary is null in phpunit which gets transformed to an empty string by format_text. 188 list($sitesummary, $unused) = external_format_text($SITE->summary, $SITE->summaryformat, \context_system::instance()->id); 189 190 // Test default values. 191 $context = \context_system::instance(); 192 $expected = array( 193 array('name' => 'fullname', 'value' => $SITE->fullname), 194 array('name' => 'shortname', 'value' => $SITE->shortname), 195 array('name' => 'summary', 'value' => $sitesummary), 196 array('name' => 'summaryformat', 'value' => FORMAT_HTML), 197 array('name' => 'frontpage', 'value' => $CFG->frontpage), 198 array('name' => 'frontpageloggedin', 'value' => $CFG->frontpageloggedin), 199 array('name' => 'maxcategorydepth', 'value' => $CFG->maxcategorydepth), 200 array('name' => 'frontpagecourselimit', 'value' => $CFG->frontpagecourselimit), 201 array('name' => 'numsections', 'value' => course_get_format($SITE)->get_last_section_number()), 202 array('name' => 'newsitems', 'value' => $SITE->newsitems), 203 array('name' => 'commentsperpage', 'value' => $CFG->commentsperpage), 204 array('name' => 'sitepolicy', 'value' => $mysitepolicy), 205 array('name' => 'sitepolicyhandler', 'value' => ''), 206 array('name' => 'disableuserimages', 'value' => $CFG->disableuserimages), 207 array('name' => 'mygradesurl', 'value' => user_mygrades_url()->out(false)), 208 array('name' => 'tool_mobile_forcelogout', 'value' => 0), 209 array('name' => 'tool_mobile_customlangstrings', 'value' => ''), 210 array('name' => 'tool_mobile_disabledfeatures', 'value' => ''), 211 array('name' => 'tool_mobile_filetypeexclusionlist', 'value' => ''), 212 array('name' => 'tool_mobile_custommenuitems', 'value' => ''), 213 array('name' => 'tool_mobile_apppolicy', 'value' => ''), 214 array('name' => 'tool_mobile_autologinmintimebetweenreq', 'value' => 6 * MINSECS), 215 array('name' => 'calendartype', 'value' => $CFG->calendartype), 216 array('name' => 'calendar_site_timeformat', 'value' => $CFG->calendar_site_timeformat), 217 array('name' => 'calendar_startwday', 'value' => $CFG->calendar_startwday), 218 array('name' => 'calendar_adminseesall', 'value' => $CFG->calendar_adminseesall), 219 array('name' => 'calendar_lookahead', 'value' => $CFG->calendar_lookahead), 220 array('name' => 'calendar_maxevents', 'value' => $CFG->calendar_maxevents), 221 ); 222 $colornumbers = range(1, 10); 223 foreach ($colornumbers as $number) { 224 $expected[] = [ 225 'name' => 'core_admin_coursecolor' . $number, 226 'value' => get_config('core_admin', 'coursecolor' . $number) 227 ]; 228 } 229 $expected[] = ['name' => 'supportname', 'value' => $CFG->supportname]; 230 $expected[] = ['name' => 'supportemail', 'value' => $CFG->supportemail]; 231 $expected[] = ['name' => 'supportpage', 'value' => $CFG->supportpage]; 232 233 $expected[] = ['name' => 'coursegraceperiodafter', 'value' => $CFG->coursegraceperiodafter]; 234 $expected[] = ['name' => 'coursegraceperiodbefore', 'value' => $CFG->coursegraceperiodbefore]; 235 236 $expected[] = ['name' => 'enabledashboard', 'value' => $CFG->enabledashboard]; 237 238 $this->assertCount(0, $result['warnings']); 239 $this->assertEquals($expected, $result['settings']); 240 241 // Change a value and retrieve filtering by section. 242 set_config('commentsperpage', 1); 243 $expected[10]['value'] = 1; 244 // Remove not expected elements. 245 array_splice($expected, 11); 246 247 $result = external::get_config('frontpagesettings'); 248 $result = \external_api::clean_returnvalue(external::get_config_returns(), $result); 249 $this->assertCount(0, $result['warnings']); 250 $this->assertEquals($expected, $result['settings']); 251 } 252 253 /* 254 * Test get_autologin_key. 255 */ 256 public function test_get_autologin_key() { 257 global $DB, $CFG, $USER; 258 259 $this->resetAfterTest(true); 260 261 $user = $this->getDataGenerator()->create_user(); 262 $this->setUser($user); 263 $service = $DB->get_record('external_services', array('shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE)); 264 265 $token = external_generate_token_for_current_user($service); 266 267 // Check we got the private token. 268 $this->assertTrue(isset($token->privatetoken)); 269 270 // Enable requeriments. 271 $_GET['wstoken'] = $token->token; // Mock parameters. 272 273 // Fake the app. 274 \core_useragent::instance(true, 'Mozilla/5.0 (Linux; Android 7.1.1; Moto G Play Build/NPIS26.48-43-2; wv) ' . 275 'AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36 MoodleMobile'); 276 277 // Even if we force the password change for the current user we should be able to retrieve the key. 278 set_user_preference('auth_forcepasswordchange', 1, $user->id); 279 280 $this->setCurrentTimeStart(); 281 $result = external::get_autologin_key($token->privatetoken); 282 $result = \external_api::clean_returnvalue(external::get_autologin_key_returns(), $result); 283 // Validate the key. 284 $this->assertEquals(32, \core_text::strlen($result['key'])); 285 $key = $DB->get_record('user_private_key', array('value' => $result['key'])); 286 $this->assertEquals($USER->id, $key->userid); 287 $this->assertTimeCurrent($key->validuntil - api::LOGIN_KEY_TTL); 288 289 // Now, try with an invalid private token. 290 set_user_preference('tool_mobile_autologin_request_last', time() - HOURSECS, $USER); 291 292 $this->expectException('moodle_exception'); 293 $this->expectExceptionMessage(get_string('invalidprivatetoken', 'tool_mobile')); 294 $result = external::get_autologin_key(random_string('64')); 295 } 296 297 /** 298 * Test get_autologin_key missing ws. 299 */ 300 public function test_get_autologin_key_missing_ws() { 301 global $CFG; 302 $this->resetAfterTest(true); 303 304 // Fake the app. 305 \core_useragent::instance(true, 'Mozilla/5.0 (Linux; Android 7.1.1; Moto G Play Build/NPIS26.48-43-2; wv) ' . 306 'AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36 MoodleMobile'); 307 308 // Need to disable webservices to verify that's checked. 309 $CFG->enablewebservices = 0; 310 $CFG->enablemobilewebservice = 0; 311 312 $this->setAdminUser(); 313 $this->expectException('moodle_exception'); 314 $this->expectExceptionMessage(get_string('enablewsdescription', 'webservice')); 315 $result = external::get_autologin_key(''); 316 } 317 318 /** 319 * Test get_autologin_key missing https. 320 */ 321 public function test_get_autologin_key_missing_https() { 322 global $CFG; 323 324 // Fake the app. 325 \core_useragent::instance(true, 'Mozilla/5.0 (Linux; Android 7.1.1; Moto G Play Build/NPIS26.48-43-2; wv) ' . 326 'AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36 MoodleMobile'); 327 328 // Need to simulate a non HTTPS site here. 329 $CFG->wwwroot = str_replace('https:', 'http:', $CFG->wwwroot); 330 331 $this->resetAfterTest(true); 332 $this->setAdminUser(); 333 334 $this->expectException('moodle_exception'); 335 $this->expectExceptionMessage(get_string('httpsrequired', 'tool_mobile')); 336 $result = external::get_autologin_key(''); 337 } 338 339 /** 340 * Test get_autologin_key missing admin. 341 */ 342 public function test_get_autologin_key_missing_admin() { 343 global $CFG; 344 345 $this->resetAfterTest(true); 346 $this->setAdminUser(); 347 348 // Fake the app. 349 \core_useragent::instance(true, 'Mozilla/5.0 (Linux; Android 7.1.1; Moto G Play Build/NPIS26.48-43-2; wv) ' . 350 'AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36 MoodleMobile'); 351 352 $this->expectException('moodle_exception'); 353 $this->expectExceptionMessage(get_string('autologinnotallowedtoadmins', 'tool_mobile')); 354 $result = external::get_autologin_key(''); 355 } 356 357 /** 358 * Test get_autologin_key locked. 359 */ 360 public function test_get_autologin_key_missing_locked() { 361 global $CFG, $DB, $USER; 362 363 $this->resetAfterTest(true); 364 $user = $this->getDataGenerator()->create_user(); 365 $this->setUser($user); 366 367 $service = $DB->get_record('external_services', array('shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE)); 368 369 $token = external_generate_token_for_current_user($service); 370 $_GET['wstoken'] = $token->token; // Mock parameters. 371 372 // Fake the app. 373 \core_useragent::instance(true, 'Mozilla/5.0 (Linux; Android 7.1.1; Moto G Play Build/NPIS26.48-43-2; wv) ' . 374 'AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36 MoodleMobile'); 375 376 $result = external::get_autologin_key($token->privatetoken); 377 $result = \external_api::clean_returnvalue(external::get_autologin_key_returns(), $result); 378 379 // Mock last time request. 380 $mocktime = time() - 7 * MINSECS; 381 set_user_preference('tool_mobile_autologin_request_last', $mocktime, $USER); 382 $result = external::get_autologin_key($token->privatetoken); 383 $result = \external_api::clean_returnvalue(external::get_autologin_key_returns(), $result); 384 385 // Change min time between requests to 30 seconds. 386 set_config('autologinmintimebetweenreq', 30, 'tool_mobile'); 387 388 // Mock a previous request, 60 seconds ago. 389 $mocktime = time() - MINSECS; 390 set_user_preference('tool_mobile_autologin_request_last', $mocktime, $USER); 391 $result = external::get_autologin_key($token->privatetoken); // All good, we were expecint 30 seconds or more. 392 $result = \external_api::clean_returnvalue(external::get_autologin_key_returns(), $result); 393 394 // We just requested one token, we must wait. 395 $this->expectException('moodle_exception'); 396 $this->expectExceptionMessage(get_string('autologinkeygenerationlockout', 'tool_mobile')); 397 $result = external::get_autologin_key($token->privatetoken); 398 } 399 400 /** 401 * Test get_autologin_key missing app_request. 402 */ 403 public function test_get_autologin_key_missing_app_request() { 404 global $CFG; 405 406 $this->resetAfterTest(true); 407 $this->setAdminUser(); 408 409 $this->expectException('moodle_exception'); 410 $this->expectExceptionMessage(get_string('apprequired', 'tool_mobile')); 411 $result = external::get_autologin_key(''); 412 } 413 414 /** 415 * Test get_content. 416 */ 417 public function test_get_content() { 418 419 $paramval = 16; 420 $result = external::get_content('tool_mobile', 'test_view', array(array('name' => 'param1', 'value' => $paramval))); 421 $result = \external_api::clean_returnvalue(external::get_content_returns(), $result); 422 $this->assertCount(1, $result['templates']); 423 $this->assertCount(1, $result['otherdata']); 424 $this->assertCount(2, $result['restrict']['users']); 425 $this->assertCount(2, $result['restrict']['courses']); 426 $this->assertEquals('alert();', $result['javascript']); 427 $this->assertEquals('main', $result['templates'][0]['id']); 428 $this->assertEquals('The HTML code', $result['templates'][0]['html']); 429 $this->assertEquals('otherdata1', $result['otherdata'][0]['name']); 430 $this->assertEquals($paramval, $result['otherdata'][0]['value']); 431 $this->assertEquals(array(1, 2), $result['restrict']['users']); 432 $this->assertEquals(array(3, 4), $result['restrict']['courses']); 433 $this->assertEmpty($result['files']); 434 $this->assertFalse($result['disabled']); 435 } 436 437 /** 438 * Test get_content disabled. 439 */ 440 public function test_get_content_disabled() { 441 442 $paramval = 16; 443 $result = external::get_content('tool_mobile', 'test_view_disabled', 444 array(array('name' => 'param1', 'value' => $paramval))); 445 $result = \external_api::clean_returnvalue(external::get_content_returns(), $result); 446 $this->assertTrue($result['disabled']); 447 } 448 449 /** 450 * Test get_content non existent function in valid component. 451 */ 452 public function test_get_content_non_existent_function() { 453 454 $this->expectException('coding_exception'); 455 $result = external::get_content('tool_mobile', 'test_blahblah'); 456 } 457 458 /** 459 * Test get_content incorrect component. 460 */ 461 public function test_get_content_invalid_component() { 462 463 $this->expectException('moodle_exception'); 464 $result = external::get_content('tool_mobile\hack', 'test_view'); 465 } 466 467 /** 468 * Test get_content non existent component. 469 */ 470 public function test_get_content_non_existent_component() { 471 472 $this->expectException('moodle_exception'); 473 $result = external::get_content('tool_blahblahblah', 'test_view'); 474 } 475 476 public function test_call_external_functions() { 477 global $SESSION; 478 479 $this->resetAfterTest(true); 480 481 $category = self::getDataGenerator()->create_category(array('name' => 'Category 1')); 482 $course = self::getDataGenerator()->create_course([ 483 'category' => $category->id, 484 'shortname' => 'c1', 485 'summary' => '<span lang="en" class="multilang">Course summary</span>' 486 . '<span lang="eo" class="multilang">Kurso resumo</span>' 487 . '@@PLUGINFILE@@/filename.txt' 488 . '<!-- Comment stripped when formatting text -->', 489 'summaryformat' => FORMAT_MOODLE 490 ]); 491 $user1 = self::getDataGenerator()->create_user(['username' => 'user1', 'lastaccess' => time()]); 492 $user2 = self::getDataGenerator()->create_user(['username' => 'user2', 'lastaccess' => time()]); 493 494 self::setUser($user1); 495 496 // Setup WS token. 497 $webservicemanager = new \webservice; 498 $service = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE); 499 $token = external_generate_token_for_current_user($service); 500 $_POST['wstoken'] = $token->token; 501 502 // Workaround for \external_api::call_external_function requiring sesskey. 503 $_POST['sesskey'] = sesskey(); 504 505 // Call some functions. 506 507 $requests = [ 508 [ 509 'function' => 'core_course_get_courses_by_field', 510 'arguments' => json_encode(['field' => 'id', 'value' => $course->id]) 511 ], 512 [ 513 'function' => 'core_user_get_users_by_field', 514 'arguments' => json_encode(['field' => 'id', 'values' => [$user1->id]]) 515 ], 516 [ 517 'function' => 'core_user_get_user_preferences', 518 'arguments' => json_encode(['name' => 'some_setting', 'userid' => $user2->id]) 519 ], 520 [ 521 'function' => 'core_course_get_courses_by_field', 522 'arguments' => json_encode(['field' => 'shortname', 'value' => $course->shortname]) 523 ], 524 ]; 525 $result = external::call_external_functions($requests); 526 527 // We need to execute the return values cleaning process to simulate the web service server. 528 $result = \external_api::clean_returnvalue(external::call_external_functions_returns(), $result); 529 530 // Only 3 responses, the 4th request is not executed because the 3rd throws an exception. 531 $this->assertCount(3, $result['responses']); 532 533 $this->assertFalse($result['responses'][0]['error']); 534 $coursedata = \external_api::clean_returnvalue( 535 \core_course_external::get_courses_by_field_returns(), 536 \core_course_external::get_courses_by_field('id', $course->id)); 537 $this->assertEquals(json_encode($coursedata), $result['responses'][0]['data']); 538 539 $this->assertFalse($result['responses'][1]['error']); 540 $userdata = \external_api::clean_returnvalue( 541 \core_user_external::get_users_by_field_returns(), 542 \core_user_external::get_users_by_field('id', [$user1->id])); 543 $this->assertEquals(json_encode($userdata), $result['responses'][1]['data']); 544 545 $this->assertTrue($result['responses'][2]['error']); 546 $exception = json_decode($result['responses'][2]['exception'], true); 547 $this->assertEquals('nopermissions', $exception['errorcode']); 548 549 // Call a function not included in the external service. 550 551 $_POST['wstoken'] = $token->token; 552 $functions = $webservicemanager->get_not_associated_external_functions($service->id); 553 $requests = [['function' => current($functions)->name]]; 554 $result = external::call_external_functions($requests); 555 556 $this->assertTrue($result['responses'][0]['error']); 557 $exception = json_decode($result['responses'][0]['exception'], true); 558 $this->assertEquals('accessexception', $exception['errorcode']); 559 $this->assertEquals('webservice', $exception['module']); 560 561 // Call a function with different external settings. 562 563 filter_set_global_state('multilang', TEXTFILTER_ON); 564 $_POST['wstoken'] = $token->token; 565 $SESSION->lang = 'eo'; // Change default language, so we can test changing it to "en". 566 $requests = [ 567 [ 568 'function' => 'core_course_get_courses_by_field', 569 'arguments' => json_encode(['field' => 'id', 'value' => $course->id]), 570 ], 571 [ 572 'function' => 'core_course_get_courses_by_field', 573 'arguments' => json_encode(['field' => 'id', 'value' => $course->id]), 574 'settingraw' => '1' 575 ], 576 [ 577 'function' => 'core_course_get_courses_by_field', 578 'arguments' => json_encode(['field' => 'id', 'value' => $course->id]), 579 'settingraw' => '1', 580 'settingfileurl' => '0' 581 ], 582 [ 583 'function' => 'core_course_get_courses_by_field', 584 'arguments' => json_encode(['field' => 'id', 'value' => $course->id]), 585 'settingfilter' => '1', 586 'settinglang' => 'en' 587 ], 588 ]; 589 $result = external::call_external_functions($requests); 590 591 $this->assertCount(4, $result['responses']); 592 593 $context = \context_course::instance($course->id); 594 $pluginfile = 'webservice/pluginfile.php'; 595 596 $this->assertFalse($result['responses'][0]['error']); 597 $data = json_decode($result['responses'][0]['data']); 598 $expected = file_rewrite_pluginfile_urls($course->summary, $pluginfile, $context->id, 'course', 'summary', null); 599 $expected = format_text($expected, $course->summaryformat, ['para' => false, 'filter' => false]); 600 $this->assertEquals($expected, $data->courses[0]->summary); 601 602 $this->assertFalse($result['responses'][1]['error']); 603 $data = json_decode($result['responses'][1]['data']); 604 $expected = file_rewrite_pluginfile_urls($course->summary, $pluginfile, $context->id, 'course', 'summary', null); 605 $this->assertEquals($expected, $data->courses[0]->summary); 606 607 $this->assertFalse($result['responses'][2]['error']); 608 $data = json_decode($result['responses'][2]['data']); 609 $this->assertEquals($course->summary, $data->courses[0]->summary); 610 611 $this->assertFalse($result['responses'][3]['error']); 612 $data = json_decode($result['responses'][3]['data']); 613 $expected = file_rewrite_pluginfile_urls($course->summary, $pluginfile, $context->id, 'course', 'summary', null); 614 $SESSION->lang = 'en'; // We expect filtered text in english. 615 $expected = format_text($expected, $course->summaryformat, ['para' => false, 'filter' => true]); 616 $this->assertEquals($expected, $data->courses[0]->summary); 617 } 618 619 /* 620 * Test get_tokens_for_qr_login. 621 */ 622 public function test_get_tokens_for_qr_login() { 623 global $DB, $CFG, $USER; 624 625 $this->resetAfterTest(true); 626 627 $user = $this->getDataGenerator()->create_user(); 628 $this->setUser($user); 629 630 $mobilesettings = get_config('tool_mobile'); 631 $qrloginkey = api::get_qrlogin_key($mobilesettings); 632 633 // Generate new tokens, the ones we expect to receive. 634 $service = $DB->get_record('external_services', array('shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE)); 635 $token = external_generate_token_for_current_user($service); 636 637 // Fake the app. 638 \core_useragent::instance(true, 'Mozilla/5.0 (Linux; Android 7.1.1; Moto G Play Build/NPIS26.48-43-2; wv) ' . 639 'AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36 MoodleMobile'); 640 641 $result = external::get_tokens_for_qr_login($qrloginkey, $USER->id); 642 $result = \external_api::clean_returnvalue(external::get_tokens_for_qr_login_returns(), $result); 643 644 $this->assertEmpty($result['warnings']); 645 $this->assertEquals($token->token, $result['token']); 646 $this->assertEquals($token->privatetoken, $result['privatetoken']); 647 648 // Now, try with an invalid key. 649 $this->expectException('moodle_exception'); 650 $this->expectExceptionMessage(get_string('invalidkey', 'error')); 651 $result = external::get_tokens_for_qr_login(random_string('64'), $user->id); 652 } 653 654 /** 655 * Test get_tokens_for_qr_login missing QR code enabled. 656 */ 657 public function test_get_tokens_for_qr_login_missing_enableqr() { 658 global $CFG, $USER; 659 $this->resetAfterTest(true); 660 $this->setAdminUser(); 661 662 set_config('qrcodetype', api::QR_CODE_DISABLED, 'tool_mobile'); 663 664 $this->expectExceptionMessage(get_string('qrcodedisabled', 'tool_mobile')); 665 $result = external::get_tokens_for_qr_login('', $USER->id); 666 } 667 668 /** 669 * Test get_tokens_for_qr_login missing ws. 670 */ 671 public function test_get_tokens_for_qr_login_missing_ws() { 672 global $CFG; 673 $this->resetAfterTest(true); 674 675 $user = $this->getDataGenerator()->create_user(); 676 $this->setUser($user); 677 678 // Fake the app. 679 \core_useragent::instance(true, 'Mozilla/5.0 (Linux; Android 7.1.1; Moto G Play Build/NPIS26.48-43-2; wv) ' . 680 'AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36 MoodleMobile'); 681 682 // Need to disable webservices to verify that's checked. 683 $CFG->enablewebservices = 0; 684 $CFG->enablemobilewebservice = 0; 685 686 $this->setAdminUser(); 687 $this->expectException('moodle_exception'); 688 $this->expectExceptionMessage(get_string('enablewsdescription', 'webservice')); 689 $result = external::get_tokens_for_qr_login('', $user->id); 690 } 691 692 /** 693 * Test get_tokens_for_qr_login missing https. 694 */ 695 public function test_get_tokens_for_qr_login_missing_https() { 696 global $CFG, $USER; 697 698 // Fake the app. 699 \core_useragent::instance(true, 'Mozilla/5.0 (Linux; Android 7.1.1; Moto G Play Build/NPIS26.48-43-2; wv) ' . 700 'AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36 MoodleMobile'); 701 702 // Need to simulate a non HTTPS site here. 703 $CFG->wwwroot = str_replace('https:', 'http:', $CFG->wwwroot); 704 705 $this->resetAfterTest(true); 706 $this->setAdminUser(); 707 708 $this->expectException('moodle_exception'); 709 $this->expectExceptionMessage(get_string('httpsrequired', 'tool_mobile')); 710 $result = external::get_tokens_for_qr_login('', $USER->id); 711 } 712 713 /** 714 * Test get_tokens_for_qr_login missing admin. 715 */ 716 public function test_get_tokens_for_qr_login_missing_admin() { 717 global $CFG, $USER; 718 719 $this->resetAfterTest(true); 720 $this->setAdminUser(); 721 722 // Fake the app. 723 \core_useragent::instance(true, 'Mozilla/5.0 (Linux; Android 7.1.1; Moto G Play Build/NPIS26.48-43-2; wv) ' . 724 'AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36 MoodleMobile'); 725 726 $this->expectException('moodle_exception'); 727 $this->expectExceptionMessage(get_string('autologinnotallowedtoadmins', 'tool_mobile')); 728 $result = external::get_tokens_for_qr_login('', $USER->id); 729 } 730 731 /** 732 * Test get_tokens_for_qr_login missing app_request. 733 */ 734 public function test_get_tokens_for_qr_login_missing_app_request() { 735 global $CFG, $USER; 736 737 $this->resetAfterTest(true); 738 $this->setAdminUser(); 739 740 $this->expectException('moodle_exception'); 741 $this->expectExceptionMessage(get_string('apprequired', 'tool_mobile')); 742 $result = external::get_tokens_for_qr_login('', $USER->id); 743 } 744 745 /** 746 * Test validate subscription key. 747 */ 748 public function test_validate_subscription_key_valid() { 749 $this->resetAfterTest(true); 750 751 $sitesubscriptionkey = ['validuntil' => time() + MINSECS, 'key' => complex_random_string(32)]; 752 set_config('sitesubscriptionkey', json_encode($sitesubscriptionkey), 'tool_mobile'); 753 754 $result = external::validate_subscription_key($sitesubscriptionkey['key']); 755 $result = \external_api::clean_returnvalue(external::validate_subscription_key_returns(), $result); 756 $this->assertEmpty($result['warnings']); 757 $this->assertTrue($result['validated']); 758 } 759 760 /** 761 * Test validate subscription key invalid first and then a valid one. 762 */ 763 public function test_validate_subscription_key_invalid_key_first() { 764 $this->resetAfterTest(true); 765 766 $sitesubscriptionkey = ['validuntil' => time() + MINSECS, 'key' => complex_random_string(32)]; 767 set_config('sitesubscriptionkey', json_encode($sitesubscriptionkey), 'tool_mobile'); 768 769 $result = external::validate_subscription_key('fakekey'); 770 $result = \external_api::clean_returnvalue(external::validate_subscription_key_returns(), $result); 771 $this->assertEmpty($result['warnings']); 772 $this->assertFalse($result['validated']); 773 774 // The valid one has been invalidated because the previous attempt. 775 $result = external::validate_subscription_key($sitesubscriptionkey['key']); 776 $result = \external_api::clean_returnvalue(external::validate_subscription_key_returns(), $result); 777 $this->assertEmpty($result['warnings']); 778 $this->assertFalse($result['validated']); 779 } 780 781 /** 782 * Test validate subscription key invalid. 783 */ 784 public function test_validate_subscription_key_invalid_key() { 785 $this->resetAfterTest(true); 786 787 $result = external::validate_subscription_key('fakekey'); 788 $result = \external_api::clean_returnvalue(external::validate_subscription_key_returns(), $result); 789 $this->assertEmpty($result['warnings']); 790 $this->assertFalse($result['validated']); 791 } 792 793 /** 794 * Test validate subscription key invalid. 795 */ 796 public function test_validate_subscription_key_outdated() { 797 $this->resetAfterTest(true); 798 799 $sitesubscriptionkey = ['validuntil' => time() - MINSECS, 'key' => complex_random_string(32)]; 800 set_config('sitesubscriptionkey', json_encode($sitesubscriptionkey), 'tool_mobile'); 801 802 $result = external::validate_subscription_key($sitesubscriptionkey['key']); 803 $result = \external_api::clean_returnvalue(external::validate_subscription_key_returns(), $result); 804 $this->assertEmpty($result['warnings']); 805 $this->assertFalse($result['validated']); 806 } 807 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body