Proyectos de Subversion Moodle

Rev

Ir a la última revisión | | 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 core;
18
 
19
use admin_category;
20
use admin_externalpage;
21
use admin_root;
22
use admin_settingpage;
23
use admin_setting_configdirectory;
24
use admin_setting_configduration;
25
use admin_setting_configexecutable;
26
use admin_setting_configfile;
27
use admin_setting_configmixedhostiplist;
28
use admin_setting_configpasswordunmask;
29
use admin_setting_configtext;
30
 
31
defined('MOODLE_INTERNAL') || die();
32
 
33
global $CFG;
34
require_once($CFG->libdir.'/adminlib.php');
35
 
36
/**
37
 * Provides the unit tests for admin tree functionality.
38
 *
39
 * @package     core
40
 * @category    test
41
 * @copyright   2013 David Mudrak <david@moodle.com>
42
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
43
 */
44
class admintree_test extends \advanced_testcase {
45
 
46
    /**
47
     * Adding nodes into the admin tree.
48
     */
49
    public function test_add_nodes() {
50
 
51
        $tree = new admin_root(true);
52
        $tree->add('root', $one = new admin_category('one', 'One'));
53
        $tree->add('root', new admin_category('three', 'Three'));
54
        $tree->add('one', new admin_category('one-one', 'One-one'));
55
        $tree->add('one', new admin_category('one-three', 'One-three'));
56
 
57
        // Check the order of nodes in the root.
58
        $map = array();
59
        foreach ($tree->children as $child) {
60
            $map[] = $child->name;
61
        }
62
        $this->assertEquals(array('one', 'three'), $map);
63
 
64
        // Insert a node into the middle.
65
        $tree->add('root', new admin_category('two', 'Two'), 'three');
66
        $map = array();
67
        foreach ($tree->children as $child) {
68
            $map[] = $child->name;
69
        }
70
        $this->assertEquals(array('one', 'two', 'three'), $map);
71
 
72
        // Non-existing sibling.
73
        $tree->add('root', new admin_category('four', 'Four'), 'five');
74
        $this->assertDebuggingCalled('Sibling five not found', DEBUG_DEVELOPER);
75
 
76
        $tree->add('root', new admin_category('five', 'Five'));
77
        $map = array();
78
        foreach ($tree->children as $child) {
79
            $map[] = $child->name;
80
        }
81
        $this->assertEquals(array('one', 'two', 'three', 'four', 'five'), $map);
82
 
83
        // Insert a node into the middle of the subcategory.
84
        $tree->add('one', new admin_category('one-two', 'One-two'), 'one-three');
85
        $map = array();
86
        foreach ($one->children as $child) {
87
            $map[] = $child->name;
88
        }
89
        $this->assertEquals(array('one-one', 'one-two', 'one-three'), $map);
90
 
91
        // Check just siblings, not parents or children.
92
        $tree->add('one', new admin_category('one-four', 'One-four'), 'one');
93
        $this->assertDebuggingCalled('Sibling one not found', DEBUG_DEVELOPER);
94
 
95
        $tree->add('root', new admin_category('six', 'Six'), 'one-two');
96
        $this->assertDebuggingCalled('Sibling one-two not found', DEBUG_DEVELOPER);
97
 
98
        // Me! Me! I wanna be first!
99
        $tree->add('root', new admin_externalpage('zero', 'Zero', 'http://foo.bar'), 'one');
100
        $map = array();
101
        foreach ($tree->children as $child) {
102
            $map[] = $child->name;
103
        }
104
        $this->assertEquals(array('zero', 'one', 'two', 'three', 'four', 'five', 'six'), $map);
105
    }
106
 
107
    public function test_add_nodes_before_invalid1() {
108
        $tree = new admin_root(true);
109
        $this->expectException(\coding_exception::class);
110
        $tree->add('root', new admin_externalpage('foo', 'Foo', 'http://foo.bar'), array('moodle:site/config'));
111
    }
112
 
113
    public function test_add_nodes_before_invalid2() {
114
        $tree = new admin_root(true);
115
        $this->expectException(\coding_exception::class);
116
        $tree->add('root', new admin_category('bar', 'Bar'), '');
117
    }
118
 
119
    /**
120
     * Test that changes to config trigger events.
121
     */
122
    public function test_config_log_created_event() {
123
        global $DB;
124
        $this->resetAfterTest();
125
        $this->setAdminUser();
126
 
127
        $adminroot = new admin_root(true);
128
        $adminroot->add('root', $one = new admin_category('one', 'One'));
129
        $page = new admin_settingpage('page', 'Page');
130
        $page->add(new admin_setting_configtext('text1', 'Text 1', '', ''));
131
        $page->add(new admin_setting_configpasswordunmask('pass1', 'Password 1', '', ''));
132
        $adminroot->add('one', $page);
133
 
134
        $sink = $this->redirectEvents();
135
        $data = array('s__text1' => 'sometext', 's__pass1' => '');
136
        $this->save_config_data($adminroot, $data);
137
 
138
        $events = $sink->get_events();
139
        $sink->close();
140
        $event = array_pop($events);
141
        $this->assertInstanceOf('\core\event\config_log_created', $event);
142
 
143
        $sink = $this->redirectEvents();
144
        $data = array('s__text1'=>'other', 's__pass1'=>'nice password');
145
        $count = $this->save_config_data($adminroot, $data);
146
 
147
        $events = $sink->get_events();
148
        $sink->close();
149
        $event = array_pop($events);
150
        $this->assertInstanceOf('\core\event\config_log_created', $event);
151
        // Verify password was nuked.
152
        $this->assertNotEquals($event->other['value'], 'nice password');
153
 
154
    }
155
 
156
    /**
157
     * Testing whether a configexecutable setting is executable.
158
     */
159
    public function test_admin_setting_configexecutable() {
160
        global $CFG;
161
        $this->resetAfterTest();
162
 
163
        $CFG->theme = 'classic';
164
        $executable = new admin_setting_configexecutable('test1', 'Text 1', 'Help Path', '');
165
 
166
        // Check for an invalid path.
167
        $result = $executable->output_html($CFG->dirroot . '/lib/tests/other/file_does_not_exist');
168
        $this->assertMatchesRegularExpression('/class="text-danger"/', $result);
169
 
170
        // Check for a directory.
171
        $result = $executable->output_html($CFG->dirroot);
172
        $this->assertMatchesRegularExpression('/class="text-danger"/', $result);
173
 
174
        // Check for a file which is not executable.
175
        $result = $executable->output_html($CFG->dirroot . '/filter/tex/readme_moodle.txt');
176
        $this->assertMatchesRegularExpression('/class="text-danger"/', $result);
177
 
178
        // Check for an executable file.
179
        if ($CFG->ostype == 'WINDOWS') {
180
            $filetocheck = 'mimetex.exe';
181
        } else {
182
            $filetocheck = 'mimetex.darwin';
183
        }
184
        $result = $executable->output_html($CFG->dirroot . '/filter/tex/' . $filetocheck);
185
        $this->assertMatchesRegularExpression('/class="text-success"/', $result);
186
 
187
        // Check for no file specified.
188
        $result = $executable->output_html('');
189
        $this->assertMatchesRegularExpression('/name="s__test1"/', $result);
190
        $this->assertMatchesRegularExpression('/value=""/', $result);
191
    }
192
 
193
    /**
194
     * Saving of values.
195
     */
196
    public function test_config_logging() {
197
        global $DB;
198
        $this->resetAfterTest();
199
        $this->setAdminUser();
200
 
201
        $DB->delete_records('config_log', array());
202
 
203
        $adminroot = new admin_root(true);
204
        $adminroot->add('root', $one = new admin_category('one', 'One'));
205
        $page = new admin_settingpage('page', 'Page');
206
        $page->add(new admin_setting_configtext('text1', 'Text 1', '', ''));
207
        $page->add(new admin_setting_configpasswordunmask('pass1', 'Password 1', '', ''));
208
        $adminroot->add('one', $page);
209
 
210
        $this->assertEmpty($DB->get_records('config_log'));
211
        $data = array('s__text1'=>'sometext', 's__pass1'=>'');
212
        $count = $this->save_config_data($adminroot, $data);
213
 
214
        $this->assertEquals(2, $count);
215
        $records = $DB->get_records('config_log', array(), 'id asc');
216
        $this->assertCount(2, $records);
217
        reset($records);
218
        $record = array_shift($records);
219
        $this->assertNull($record->plugin);
220
        $this->assertSame('text1', $record->name);
221
        $this->assertNull($record->oldvalue);
222
        $this->assertSame('sometext', $record->value);
223
        $record = array_shift($records);
224
        $this->assertNull($record->plugin);
225
        $this->assertSame('pass1', $record->name);
226
        $this->assertNull($record->oldvalue);
227
        $this->assertSame('', $record->value);
228
 
229
        $DB->delete_records('config_log', array());
230
        $data = array('s__text1'=>'other', 's__pass1'=>'nice password');
231
        $count = $this->save_config_data($adminroot, $data);
232
 
233
        $this->assertEquals(2, $count);
234
        $records = $DB->get_records('config_log', array(), 'id asc');
235
        $this->assertCount(2, $records);
236
        reset($records);
237
        $record = array_shift($records);
238
        $this->assertNull($record->plugin);
239
        $this->assertSame('text1', $record->name);
240
        $this->assertSame('sometext', $record->oldvalue);
241
        $this->assertSame('other', $record->value);
242
        $record = array_shift($records);
243
        $this->assertNull($record->plugin);
244
        $this->assertSame('pass1', $record->name);
245
        $this->assertSame('', $record->oldvalue);
246
        $this->assertSame('********', $record->value);
247
 
248
        $DB->delete_records('config_log', array());
249
        $data = array('s__text1'=>'', 's__pass1'=>'');
250
        $count = $this->save_config_data($adminroot, $data);
251
 
252
        $this->assertEquals(2, $count);
253
        $records = $DB->get_records('config_log', array(), 'id asc');
254
        $this->assertCount(2, $records);
255
        reset($records);
256
        $record = array_shift($records);
257
        $this->assertNull($record->plugin);
258
        $this->assertSame('text1', $record->name);
259
        $this->assertSame('other', $record->oldvalue);
260
        $this->assertSame('', $record->value);
261
        $record = array_shift($records);
262
        $this->assertNull($record->plugin);
263
        $this->assertSame('pass1', $record->name);
264
        $this->assertSame('********', $record->oldvalue);
265
        $this->assertSame('', $record->value);
266
    }
267
 
268
    protected function save_config_data(admin_root $adminroot, array $data) {
269
        $adminroot->errors = array();
270
 
271
        $settings = admin_find_write_settings($adminroot, $data);
272
 
273
        $count = 0;
274
        foreach ($settings as $fullname=>$setting) {
275
            /** @var $setting admin_setting */
276
            $original = $setting->get_setting();
277
            $error = $setting->write_setting($data[$fullname]);
278
            if ($error !== '') {
279
                $adminroot->errors[$fullname] = new \stdClass();
280
                $adminroot->errors[$fullname]->data  = $data[$fullname];
281
                $adminroot->errors[$fullname]->id    = $setting->get_id();
282
                $adminroot->errors[$fullname]->error = $error;
283
            } else {
284
                $setting->write_setting_flags($data);
285
            }
286
            if ($setting->post_write_settings($original)) {
287
                $count++;
288
            }
289
        }
290
 
291
        return $count;
292
    }
293
 
294
    public function test_preventexecpath() {
295
        $this->resetAfterTest();
296
 
297
        set_config('preventexecpath', 0);
298
        set_config('execpath', null, 'abc_cde');
299
        $this->assertFalse(get_config('abc_cde', 'execpath'));
300
        $setting = new admin_setting_configexecutable('abc_cde/execpath', 'some desc', '', '/xx/yy');
301
        $setting->write_setting('/oo/pp');
302
        $this->assertSame('/oo/pp', get_config('abc_cde', 'execpath'));
303
 
304
        // Prevent changes.
305
        set_config('preventexecpath', 1);
306
        $setting->write_setting('/mm/nn');
307
        $this->assertSame('/oo/pp', get_config('abc_cde', 'execpath'));
308
 
309
        // Use default in install.
310
        set_config('execpath', null, 'abc_cde');
311
        $setting->write_setting('/mm/nn');
312
        $this->assertSame('/xx/yy', get_config('abc_cde', 'execpath'));
313
 
314
        // Use empty value if no default.
315
        $setting = new admin_setting_configexecutable('abc_cde/execpath', 'some desc', '', null);
316
        set_config('execpath', null, 'abc_cde');
317
        $setting->write_setting('/mm/nn');
318
        $this->assertSame('', get_config('abc_cde', 'execpath'));
319
 
320
        // This also affects admin_setting_configfile and admin_setting_configdirectory.
321
 
322
        set_config('preventexecpath', 0);
323
        set_config('execpath', null, 'abc_cde');
324
        $this->assertFalse(get_config('abc_cde', 'execpath'));
325
        $setting = new admin_setting_configfile('abc_cde/execpath', 'some desc', '', '/xx/yy');
326
        $setting->write_setting('/oo/pp');
327
        $this->assertSame('/oo/pp', get_config('abc_cde', 'execpath'));
328
 
329
        // Prevent changes.
330
        set_config('preventexecpath', 1);
331
        $setting->write_setting('/mm/nn');
332
        $this->assertSame('/oo/pp', get_config('abc_cde', 'execpath'));
333
 
334
        // Use default in install.
335
        set_config('execpath', null, 'abc_cde');
336
        $setting->write_setting('/mm/nn');
337
        $this->assertSame('/xx/yy', get_config('abc_cde', 'execpath'));
338
 
339
        // Use empty value if no default.
340
        $setting = new admin_setting_configfile('abc_cde/execpath', 'some desc', '', null);
341
        set_config('execpath', null, 'abc_cde');
342
        $setting->write_setting('/mm/nn');
343
        $this->assertSame('', get_config('abc_cde', 'execpath'));
344
 
345
        set_config('preventexecpath', 0);
346
        set_config('execpath', null, 'abc_cde');
347
        $this->assertFalse(get_config('abc_cde', 'execpath'));
348
        $setting = new admin_setting_configdirectory('abc_cde/execpath', 'some desc', '', '/xx/yy');
349
        $setting->write_setting('/oo/pp');
350
        $this->assertSame('/oo/pp', get_config('abc_cde', 'execpath'));
351
 
352
        // Prevent changes.
353
        set_config('preventexecpath', 1);
354
        $setting->write_setting('/mm/nn');
355
        $this->assertSame('/oo/pp', get_config('abc_cde', 'execpath'));
356
 
357
        // Use default in install.
358
        set_config('execpath', null, 'abc_cde');
359
        $setting->write_setting('/mm/nn');
360
        $this->assertSame('/xx/yy', get_config('abc_cde', 'execpath'));
361
 
362
        // Use empty value if no default.
363
        $setting = new admin_setting_configdirectory('abc_cde/execpath', 'some desc', '', null);
364
        set_config('execpath', null, 'abc_cde');
365
        $setting->write_setting('/mm/nn');
366
        $this->assertSame('', get_config('abc_cde', 'execpath'));
367
    }
368
 
369
    /**
370
     * Test setting an empty duration displays the correct validation message.
371
     */
372
    public function test_emptydurationvalue() {
373
        $this->resetAfterTest();
374
        $adminsetting = new admin_setting_configduration('abc_cde/duration', 'some desc', '', '');
375
 
376
        // A value that isn't a number is treated as a zero, so we expect to see no error message.
377
        $this->assertEmpty($adminsetting->write_setting(['u' => '3600', 'v' => 'abc']));
378
    }
379
 
380
    /**
381
     * Test setting for blocked hosts
382
     *
383
     * For testing the admin settings element only. Test for blocked hosts functionality can be found
384
     * in lib/tests/curl_security_helper_test.php
385
     */
386
    public function test_mixedhostiplist() {
387
        $this->resetAfterTest();
388
 
389
        $adminsetting = new admin_setting_configmixedhostiplist('abc_cde/hostiplist', 'some desc', '', '');
390
 
391
        // Test valid settings.
392
        $validsimplesettings = [
393
            'localhost',
394
            "localhost\n127.0.0.1",
395
            '192.168.10.1',
396
            '0:0:0:0:0:0:0:1',
397
            '::1',
398
            'fe80::',
399
            '231.54.211.0/20',
400
            'fe80::/64',
401
            '231.3.56.10-20',
402
            'fe80::1111-bbbb',
403
            '*.example.com',
404
            '*.sub.example.com',
405
        ];
406
 
407
        foreach ($validsimplesettings as $setting) {
408
            $errormessage = $adminsetting->write_setting($setting);
409
            $this->assertEmpty($errormessage, $errormessage);
410
            $this->assertSame($setting, get_config('abc_cde', 'hostiplist'));
411
            $this->assertSame($setting, $adminsetting->get_setting());
412
        }
413
 
414
        // Test valid international site names.
415
        $valididnsettings = [
416
            'правительство.рф' => 'xn--80aealotwbjpid2k.xn--p1ai',
417
            'faß.de' => 'xn--fa-hia.de',
418
            'ß.ß' => 'xn--zca.xn--zca',
419
            '*.tharkûn.com' => '*.xn--tharkn-0ya.com',
420
        ];
421
 
422
        foreach ($valididnsettings as $setting => $encodedsetting) {
423
            $errormessage = $adminsetting->write_setting($setting);
424
            $this->assertEmpty($errormessage, $errormessage);
425
            $this->assertSame($encodedsetting, get_config('abc_cde', 'hostiplist'));
426
            $this->assertSame($setting, $adminsetting->get_setting());
427
        }
428
 
429
        // Invalid settings.
430
        $this->assertEquals('These entries are invalid: nonvalid site name', $adminsetting->write_setting('nonvalid site name'));
431
        $this->assertEquals('Empty lines are not valid', $adminsetting->write_setting("localhost\n"));
432
    }
433
 
434
    /**
435
     * Verifies the $ADMIN global (adminroot cache) is properly reset when changing users, which might occur naturally during cron.
436
     */
437
    public function test_adminroot_cache_reset() {
438
        $this->resetAfterTest();
439
        global $DB;
440
        // Current user is a manager at site context, which won't have access to the 'debugging' section of the admin tree.
441
        $manageruser = $this->getDataGenerator()->create_user();
442
        $context = \context_system::instance();
443
        $managerrole = $DB->get_record('role', array('shortname' => 'manager'));
444
        role_assign($managerrole->id, $manageruser->id, $context->id);
445
        $this->setUser($manageruser);
446
        $adminroot = admin_get_root();
447
        $section = $adminroot->locate('debugging');
448
        $this->assertEmpty($section);
449
 
450
        // Now, change the user to an admin user and confirm we get a new copy of the admin tree when next we ask for it.
451
        $adminuser = get_admin();
452
        $this->setUser($adminuser);
453
        $adminroot = admin_get_root();
454
        $section = $adminroot->locate('debugging');
455
        $this->assertInstanceOf('\admin_settingpage', $section);
456
    }
457
}