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