Proyectos de Subversion Moodle

Rev

Rev 11 | | Comparar con el anterior | Ultima modificación | Ver Log |

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