Passed
Push — master ( 17bffe...10bee5 )
by Stefan
06:13
created

SanityTests::eapol_test_test()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 9

Duplication

Lines 5
Ratio 45.45 %

Importance

Changes 0
Metric Value
dl 5
loc 11
c 0
b 0
f 0
rs 9.4285
cc 3
eloc 9
nc 3
nop 0
1
<?php
2
3
/*
4
 * ******************************************************************************
5
 * Copyright 2011-2017 DANTE Ltd. and GÉANT on behalf of the GN3, GN3+, GN4-1 
6
 * and GN4-2 consortia
7
 *
8
 * License: see the web/copyright.php file in the file structure
9
 * ******************************************************************************
10
 */
11
12
/**
13
 * 
14
 * 
15
 * This is the definition of the CAT class implementing various configuration
16
 * tests. 
17
 * Each test is implemented as a priviate method which needs to be named "test_name_test".
18
 * The test returns the results by calling the test_return method, this passing the return
19
 * code and the explanatory message. Multiple calls to test_return are allowed.
20
 *
21
 * An individual test can be run by the "test" method which takes the test name as an argument
22
 * multiple tests should be run by the run_all_tests method which takes an array as an argument
23
 * see method descriptions for more information.
24
 * 
25
 * The results of the tests are passed within the $test_result array
26
 *
27
 * Some configuration of this class is required, see further down.
28
 * @author Stefan Winter <[email protected]>
29
 * @author Tomasz Wolniewicz <[email protected]>
30
 *
31
 * @license see LICENSE file in root directory
32
 *
33
 * @package Utilities
34
 */
35
36
namespace core;
37
38
use GeoIp2\Database\Reader;
39
use \Exception;
40
41
require_once(dirname(dirname(__FILE__)) . "/config/_config.php");
42
require_once(dirname(dirname(__FILE__)) . "/core/PHPMailer/src/PHPMailer.php");
43
require_once(dirname(dirname(__FILE__)) . "/core/PHPMailer/src/SMTP.php");
44
45
class SanityTests extends CAT {
46
    /* in this section set current CAT requirements */
47
48
    /* $php_needversion sets the minumum required php version */
49
50
    // because of bug:
51
    // Fixed bug #74005 (mail.add_x_header causes RFC-breaking lone line feed).
52
    private $php_needversion = '7.0.17';
53
54
    /* List all required NSIS modules below */
55
    private $NSIS_Modules = [
56
        "nsArray.nsh",
57
        "FileFunc.nsh",
58
        "LogicLib.nsh",
59
        "WordFunc.nsh",
60
        "FileFunc.nsh",
61
        "x64.nsh",
62
    ];
63
64
    /* set $profile_option_ct to the number of rows returned by "SELECT * FROM profile_option_dict" */
65
    private $profile_option_ct;
66
    /* set $view_admin_ct to the number of rows returned by "desc view_admin" */
67
    private $view_admin_ct = 8;
68
69
    /* end of config */
70
    public $out;
71
    public $name;
72
73
    /** 
74
     * initialise the tests. Includes counting the number of expected rows in the profile_option_dict table.
75
     */
76
    public function __construct() {
77
        parent::__construct();
78
        $this->test_result = [];
79
        $this->test_result['global'] = 0;
80
        // parse the schema file to find out the number of expected rows...
81
        $schema = file(dirname(dirname(__FILE__)) . "/schema/schema.sql");
82
        $this->profile_option_ct = 0;
83
        $passedTheWindmill = FALSE;
84
        foreach ($schema as $schemaLine) {
85
            if (preg_match("/^INSERT INTO \`profile_option_dict\` VALUES/", $schemaLine)) {
86
                $passedTheWindmill = TRUE;
87
                continue;
88
            }
89
            if ($passedTheWindmill) {
90
                if (substr($schemaLine, 0, 1) == '(') { // a relevant line in schema
91
                    $this->profile_option_ct = $this->profile_option_ct+1;
92
                } else { // anything else, quit parsing
93
                    break;
94
                }
95
            }
96
        }
97
    }
98
99
    /**
100
     * The single test wrapper
101
     * @param string $test the test name
102
     */
103
    public function test($test) {
104
        $this->out[$test] = [];
105
        $this->name = $test;
106
        $m_name = $test . '_test';
107
        $this->test_result[$test] = 0;
108
        if (!method_exists($this, $m_name)) {
109
            $this->test_return(\core\common\Entity::L_ERROR, "Configuration error, no test configured for <strong>$test</strong>.");
110
            return;
111
        }
112
        $this->$m_name();
113
    }
114
115
    /**
116
     * The multiple tests wrapper
117
     * @param array $Tests the tests array.
118
     *
119
     * The $Tests is a simple string array, where each entry is a test name
120
     * the test names can also be given in the format "test=>subtest", which 
121
     * defines a conditional execution of the "subtest" if the "test" was run earier
122
     * and returned a success.
123
     */
124
    public function run_tests($Tests) {
125
        foreach ($Tests as $testName) {
126
            $matchArray = [];
127
            if (preg_match('/(.+)=>(.+)/', $testName, $matchArray)) {
128
                $tst = $matchArray[1];
129
                $subtst = $matchArray[2];
130
                if ($this->test_result[$tst] < \core\common\Entity::L_ERROR) {
131
                    $this->test($subtst);
132
                }
133
            } else {
134
                $this->test($testName);
135
            }
136
        }
137
    }
138
139
    /**
140
     * enumerates the tests which are defined
141
     * 
142
     * @return array
143
     */
144
    public function get_test_names() {
145
        $T = get_class_methods($this);
146
        $out = [];
147
        foreach ($T as $t) {
148
            if (preg_match('/^(.*)_test$/', $t, $m)) {
149
                $out[] = $m[1];
150
            }
151
        }
152
        return $out;
153
    }
154
155
    /**
156
     * This array is used to return the test results.
157
     * As the 'global' entry it returns the maximum return value
158
     * from all tests.
159
     * Individual tests results are teturned as separate entires
160
     * indexed by test names; each value is an array passing "level" and "message"
161
     * from each of the tests.
162
     * $test_result is set by the test_return method
163
     *
164
     * @var array $test_result
165
     */
166
    public $test_result;
167
168
    /**
169
     * stores the result of a given test in standardised format
170
     * 
171
     * @param int $level severity level of the result
172
     * @param string $message verbal description of the result
173
     */
174
    private function test_return($level, $message) {
175
        $this->out[$this->name][] = ['level' => $level, 'message' => $message];
176
        $this->test_result[$this->name] = max($this->test_result[$this->name], $level);
177
        $this->test_result['global'] = max($this->test_result['global'], $level);
178
    }
179
180
    /**
181
     * finds out if a path name is configured as an absolute path or only implicit (e.g. is in $PATH)
182
     * @param string $pathToCheck
183
     * @return array
184
     */
185
    private function get_exec_path($pathToCheck) {
186
        $the_path = "";
187
        $exec_is = "UNDEFINED";
188
        foreach ([CONFIG, CONFIG_CONFASSISTANT, CONFIG_DIAGNOSTICS] as $config) {
189
            if (!empty($config['PATHS'][$pathToCheck])) {
190
                $matchArray = [];
191
                preg_match('/([^ ]+) ?/', $config['PATHS'][$pathToCheck], $matchArray);
192
                $exe = $matchArray[1];
193
                $the_path = exec("which " . $config['PATHS'][$pathToCheck]);
194
                if ($the_path == $exe) {
195
                    $exec_is = "EXPLICIT";
196
                } else {
197
                    $exec_is = "IMPLICIT";
198
                }
199
                return(['exec' => $the_path, 'exec_is' => $exec_is]);
200
            }
201
        }
202
        return(['exec' => $the_path, 'exec_is' => $exec_is]);
203
    }
204
205
    /**
206
     *  Test for php version
207
     */
208
    private function php_test() {
209
        if (version_compare(phpversion(), $this->php_needversion, '>=')) {
210
            $this->test_return(\core\common\Entity::L_OK, "<strong>PHP</strong> is sufficiently recent. You are running " . phpversion() . ".");
211
        } else {
212
            $this->test_return(\core\common\Entity::L_ERROR, "<strong>PHP</strong> is too old. We need at least $this->php_needversion, but you only have " . phpversion() . ".");
213
        }
214
    }
215
216
    /**
217
     * set for cat_base_url setting
218
     */
219
    private function cat_base_url_test() {
220
        $rootUrl = substr(CONFIG['PATHS']['cat_base_url'], -1) === '/' ? substr(CONFIG['PATHS']['cat_base_url'], 0, -1) : CONFIG['PATHS']['cat_base_url'];
221
        preg_match('/(^.*)\/admin\/112365365321.php/', $_SERVER['SCRIPT_NAME'], $m);
222
        if ($rootUrl === $m[1]) {
223
            $this->test_return(\core\common\Entity::L_OK, "<strong>cat_base_url</strong> set correctly");
224
        } else {
225
            $rootFromScript = $m[1] === '' ? '/' : $m[1];
226
            $this->test_return(\core\common\Entity::L_ERROR, "<strong>cat_base_url</strong> is set to <strong>" . CONFIG['PATHS']['cat_base_url'] . "</strong> and should be <strong>$rootFromScript</strong>");
227
        }
228
    }
229
230
    /**
231
     * test for simpleSAMLphp
232
     */
233
    private function ssp_test() {
234
        if (!is_file(CONFIG['AUTHENTICATION']['ssp-path-to-autoloader'])) {
235
            $this->test_return(\core\common\Entity::L_ERROR, "SS<strong>simpleSAMLphp</strong> not found!");
236
        } else {
237
            $this->test_return(\core\common\Entity::L_OK, "<strong>simpleSAMLphp</strong> autoloader found.");
238
        }
239
    }
240
241
    /**
242
     * test for security setting
243
     */
244
    private function security_test() {
245
        if (in_array("I do not care about security!", CONFIG['SUPERADMINS'])) {
246
            $this->test_return(\core\common\Entity::L_WARN, "You do not care about security. This page should be made accessible to the CAT admin only! See config.php 'Superadmins'!");
247
        }
248
    }
249
250
    /**
251
     * test if zip is available
252
     */
253
    private function zip_test() {
254 View Code Duplication
        if (exec("which zip") != "") {
255
            $this->test_return(\core\common\Entity::L_OK, "<strong>zip</strong> binary found.");
256
        } else {
257
            $this->test_return(\core\common\Entity::L_ERROR, "<strong>zip</strong> not found in your \$PATH!");
258
        }
259
    }
260
261
    /**
262
     * test if eapol_test is availabe and reacent enough
263
     */
264
    private function eapol_test_test() {
265
        exec(CONFIG_DIAGNOSTICS['PATHS']['eapol_test'], $out, $retval);
266
        if ($retval == 255) {
267
            $o = preg_grep('/-o<server cert/', $out);
268 View Code Duplication
            if (count($o) > 0) {
269
                $this->test_return(\core\common\Entity::L_OK, "<strong>eapol_test</strong> script found.");
270
            } else {
271
                $this->test_return(\core\common\Entity::L_ERROR, "<strong>eapol_test</strong> found, but is too old!");
272
            }
273
        } else {
274
            $this->test_return(\core\common\Entity::L_ERROR, "<strong>eapol_test</strong> not found!");
275
        }
276
    }
277
278
    /**
279
     * test if logdir exists and is writable
280
     */
281
    private function logdir_test() {
282
        if (fopen(CONFIG['PATHS']['logdir'] . "/debug.log", "a") == FALSE) {
283
            $this->test_return(\core\common\Entity::L_WARN, "Log files in <strong>" . CONFIG['PATHS']['logdir'] . "</strong> are not writable!");
284
        } else {
285
            $this->test_return(\core\common\Entity::L_OK, "Log directory is writable.");
286
        }
287
    }
288
289
    /**
290
     * test for required PHP modules
291
     */
292
    private function phpModules_test() {
293
        if (function_exists('idn_to_ascii')) {
294
            $this->test_return(\core\common\Entity::L_OK, "PHP can handle internationalisation.");
295
        } else {
296
            $this->test_return(\core\common\Entity::L_ERROR, "PHP can <strong>NOT</strong> handle internationalisation (idn_to_ascii() from php7.0-intl).");
297
        }
298
299
        if (function_exists('gettext')) {
300
            $this->test_return(\core\common\Entity::L_OK, "PHP extension <strong>GNU Gettext</strong> is installed.");
301
        } else {
302
            $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GNU Gettext</strong> not found!");
303
        }
304
305
        if (function_exists('openssl_sign')) {
306
            $this->test_return(\core\common\Entity::L_OK, "PHP extension <strong>OpenSSL</strong> is installed.");
307
        } else {
308
            $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>OpenSSL</strong> not found!");
309
        }
310
311
        if (class_exists('\Imagick')) {
312
            $this->test_return(\core\common\Entity::L_OK, "PHP extension <strong>Imagick</strong> is installed.");
313
        } else {
314
            $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>Imagick</strong> not found! Get it from your distribution or <a href='http://pecl.php.net/package/imagick'>here</a>.");
315
        }
316
317
        if (function_exists('ImageCreate')) {
318
            $this->test_return(\core\common\Entity::L_OK, "PHP extension <strong>GD</strong> is installed.");
319
        } else {
320
            $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GD</strong> not found!</a>.");
321
        }
322
323
        if (function_exists('mysqli_connect')) {
324
            $this->test_return(\core\common\Entity::L_OK, "PHP extension <strong>MySQL</strong> is installed.");
325
        } else {
326
            $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>MySQL</strong> not found!");
327
        }
328
    }
329
330
    /**
331
     * test if GeoIP is installed correctly
332
     */
333
    private function geoip_test() {
334
        $host_4 = '145.0.2.50';
335
        $host_6 = '2001:610:188:444::50';
336
        switch (CONFIG['GEOIP']['version']) {
337
            case 0:
338
                $this->test_return(\core\common\Entity::L_REMARK, "As set in the config, no geolocation service will be used");
339
                break;
340
            case 1:
341
                if (!function_exists('geoip_record_by_name')) {
342
                    $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GeoIP</strong> (legacy) not found! Get it from your distribution or <a href='http://pecl.php.net/package/geoip'>here</a> or better install GeoIP2 from <a href='https://github.com/maxmind/GeoIP2-php'>here</a>.");
343
                    return;
344
                }
345
                $record = geoip_record_by_name($host_4);
346
                if ($record === FALSE) {
347
                    $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GeoIP</strong> (legacy) found but not working properly, perhaps you need to download the databases. See utils/GeoIP-update.sh in the CAT distribution and use it tu update the GeoIP database regularly.");
348
                    return;
349
                }
350
                if ($record['city'] != 'Utrecht') {
351
                    $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GeoIP</strong> (legacy) found but not working properly, perhaps you need to download the databases. See utils/GeoIP-update.sh in the CAT distribution and use it tu update the GeoIP database regularly.");
352
                    return;
353
                }
354
                $this->test_return(\core\common\Entity::L_REMARK, "PHP extension <strong>GeoIP</strong> (legacy) is installed and working. See utils/GeoIP-update.sh in the CAT distribution and use it tu update the GeoIP database regularly. We stronly advise to replace the legacy GeoIP with GeoIP2 from <a href='https://github.com/maxmind/GeoIP2-php'>here</a>.");
355
                break;
356
            case 2:
357 View Code Duplication
                if (!is_file(CONFIG['GEOIP']['geoip2-path-to-autoloader'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
358
                    $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GeoIP2</strong> not found! Get it from <a href='https://github.com/maxmind/GeoIP2-php'>here</a>.");
359
                    return;
360
                }
361 View Code Duplication
                if (!is_file(CONFIG['GEOIP']['geoip2-path-to-db'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
362
                    $this->test_return(\core\common\Entity::L_ERROR, "<strong>GeoIP2 database</strong> not found! See utils/GeoIP-update.sh in the CAT distribution and use it tu update the GeoIP database regularly.");
363
                    return;
364
                }
365
                require_once CONFIG['GEOIP']['geoip2-path-to-autoloader'];
366
                $reader = new Reader(CONFIG['GEOIP']['geoip2-path-to-db']);
367
                try {
368
                    $record = $reader->city($host_4);
369
                } catch (Exception $e) {
370
                    $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GeoIP2</strong> found but not working properly, perhaps you need to download the databases. See utils/GeoIP-update.sh in the CAT distribution and use it tu update the GeoIP database regularly.");
371
                    return;
372
                }
373
                if ($record->city->name != 'Utrecht') {
374
                    $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GeoIP2</strong> found but not working properly, perhaps you need to download the databases. See utils/GeoIP-update.sh in the CAT distribution and use it tu update the GeoIP database regularly.");
375
                    return;
376
                }
377
                try {
378
                    $record = $reader->city($host_6);
379
                } catch (Exception $e) {
380
                    $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GeoIP2</strong> found but not working properly with IPv6, perhaps you need to download the databases. See utils/GeoIP-update.sh in the CAT distribution and use it tu update the GeoIP database regularly.");
381
                    return;
382
                }
383
                if ($record->city->name != 'Utrecht') {
384
                    $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GeoIP2</strong> found but not working properly with IPv6, perhaps you need to download the databases. See utils/GeoIP-update.sh in the CAT distribution and use it tu update the GeoIP database regularly.");
385
                    return;
386
                }
387
                $this->test_return(\core\common\Entity::L_OK, "PHP extension <strong>GeoIP2</strong> is installed and working. See utils/GeoIP-update.sh in the CAT distribution and use it tu update the GeoIP database regularly.");
388
                break;
389
            default:
390
                $this->test_return(\core\common\Entity::L_ERROR, 'Check CONFIG[\'GEOIP\'][\'version\'], it must be set to either 1 or 2');
391
                break;
392
        }
393
    }
394
395
    /**
396
     * test if openssl is available
397
     */
398
    private function openssl_test() {
399
        $A = $this->get_exec_path('openssl');
400
        if ($A['exec'] != "") {
401
            $t = exec($A['exec'] . ' version');
402 View Code Duplication
            if ($A['exec_is'] == "EXPLICIT") {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
403
                $this->test_return(\core\common\Entity::L_OK, "<strong>$t</strong> was found and is configured explicitly in your config.");
404
            } else {
405
                $this->test_return(\core\common\Entity::L_WARN, "<strong>$t</strong> was found, but is not configured with an absolute path in your config.");
406
            }
407
        } else {
408
            $this->test_return(\core\common\Entity::L_ERROR, "<strong>openssl</strong> was not found on your system!");
409
        }
410
    }
411
412
    /**
413
     * test if makensis is available
414
     */
415
    private function makensis_test() {
416
        if (!is_numeric(CONFIG_CONFASSISTANT['NSIS_VERSION'])) {
417
            $this->test_return(\core\common\Entity::L_ERROR, "NSIS_VERSION needs to be numeric!");
418
            return;
419
        }
420
        if (CONFIG_CONFASSISTANT['NSIS_VERSION'] < 2) {
421
            $this->test_return(\core\common\Entity::L_ERROR, "NSIS_VERSION needs to be at least 2!");
422
            return;
423
        }
424
        $A = $this->get_exec_path('makensis');
425
        if ($A['exec'] != "") {
426
            $t = exec($A['exec'] . ' -VERSION');
427 View Code Duplication
            if ($A['exec_is'] == "EXPLICIT") {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
428
                $this->test_return(\core\common\Entity::L_OK, "<strong>makensis $t</strong> was found and is configured explicitly in your config.");
429
            } else {
430
                $this->test_return(\core\common\Entity::L_WARN, "<strong>makensis $t</strong> was found, but is not configured with an absolute path in your config.");
431
            }
432
            exec($A['exec'] . ' -HELP', $t);
433
            $t1 = count(preg_grep('/INPUTCHARSET/', $t));
434 View Code Duplication
            if ($t1 == 1 && CONFIG_CONFASSISTANT['NSIS_VERSION'] == 2) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
435
                $this->test_return(\core\common\Entity::L_ERROR, "Declared NSIS_VERSION does not seem to match the file pointed to by PATHS['makensis']!");
436
            }
437 View Code Duplication
            if ($t1 == 0 && CONFIG_CONFASSISTANT['NSIS_VERSION'] >= 3) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
438
                $this->test_return(\core\common\Entity::L_ERROR, "Declared NSIS_VERSION does not seem to match the file pointed to by PATHS['makensis']!");
439
            }
440
        } else {
441
            $this->test_return(\core\common\Entity::L_ERROR, "<strong>makensis</strong> was not found on your system!");
442
        }
443
    }
444
445
    /**
446
     * test if all required NSIS modules are available
447
     */
448
    private function NSISmodules_test() {
449
        $tmp_dir = $this->createTemporaryDirectory('installer', 0)['dir'];
450
        if (!chdir($tmp_dir)) {
451
            $this->loggerInstance->debug(2, "Cannot chdir to $tmp_dir\n");
452
            $this->test_return(\core\common\Entity::L_ERROR, "NSIS modules test - problem with temporary directory permissions, cannot continue");
453
            return;
454
        }
455
        $exe = 'tt.exe';
456
        $NSIS_Module_status = [];
457
        foreach ($this->NSIS_Modules as $module) {
458
            unset($out);
459
            exec(CONFIG_CONFASSISTANT['PATHS']['makensis'] . " -V1 '-X!include $module' '-XOutFile $exe' '-XSection X' '-XSectionEnd'", $out, $retval);
460
            if ($retval > 0) {
461
                $NSIS_Module_status[$module] = 0;
462
            } else {
463
                $NSIS_Module_status[$module] = 1;
464
            }
465
        }
466
        if (is_file($exe)) {
467
            unlink($exe);
468
        }
469
        foreach ($NSIS_Module_status as $module => $status) {
470
            if ($status == 1) {
471
                $this->test_return(\core\common\Entity::L_OK, "NSIS module <strong>$module</strong> was found.");
472
            } else {
473
                $this->test_return(\core\common\Entity::L_ERROR, "NSIS module <strong>$module</strong> was not found or is not working correctly.");
474
            }
475
        }
476
    }
477
478
    /**
479
     * test access to dowloads directories
480
     */
481
    private function directories_test() {
482
        $Dir1 = $this->createTemporaryDirectory('installer', 0);
483
        $dir1 = $Dir1['dir'];
484
        $base1 = $Dir1['base'];
485
        if ($dir1) {
486
            $this->test_return(\core\common\Entity::L_OK, "Installer cache directory is writable.");
487
            \core\common\Entity::rrmdir($dir1);
488
        } else {
489
            $this->test_return(\core\common\Entity::L_ERROR, "Installer cache directory $base1 does not exist or is not writable!");
490
        }
491
        $Dir2 = $this->createTemporaryDirectory('test', 0);
492
        $dir2 = $Dir2['dir'];
493
        $base2 = $Dir2['base'];
494
        if ($dir2) {
495
            $this->test_return(\core\common\Entity::L_OK, "Test directory is writable.");
496
            \core\common\Entity::rrmdir($dir2);
497
        } else {
498
            $this->test_return(\core\common\Entity::L_ERROR, "Test directory $base2 does not exist or is not writable!");
499
        }
500
        $Dir3 = $this->createTemporaryDirectory('logo', 0);
501
        $dir3 = $Dir3['dir'];
502
        $base3 = $Dir3['base'];
503
        if ($dir3) {
504
            $this->test_return(\core\common\Entity::L_OK, "Logos cache directory is writable.");
505
            \core\common\Entity::rrmdir($dir3);
506
        } else {
507
            $this->test_return(\core\common\Entity::L_ERROR, "Logos cache directory $base3 does not exist or is not writable!");
508
        }
509
    }
510
511
    /**
512
     * test if all required locales are enabled
513
     */
514
    private function locales_test() {
515
        $locales = shell_exec("locale -a");
516
        $allthere = "";
517
        foreach (CONFIG['LANGUAGES'] as $onelanguage) {
518
            if (preg_match("/" . $onelanguage['locale'] . "/", $locales) == 0) {
519
                $allthere .= $onelanguage['locale'] . " ";
520
            }
521
        }
522 View Code Duplication
        if ($allthere == "") {
523
            $this->test_return(\core\common\Entity::L_OK, "All of your configured locales are available on your system.");
524
        } else {
525
            $this->test_return(\core\common\Entity::L_WARN, "Some of your configured locales (<strong>$allthere</strong>) are not installed and will not be displayed correctly!");
526
        }
527
    }
528
529
    const DEFAULTS = [
530
        ["SETTING" => CONFIG['APPEARANCE']['from-mail'],
531
            "DEFVALUE" => "[email protected]",
532
            "COMPLAINTSTRING" => "APPEARANCE/from-mail "],
533
        ["SETTING" => CONFIG['APPEARANCE']['support-contact']['url'],
534
            "DEFVALUE" => "[email protected]?body=Only%20English%20language%20please!",
535
            "COMPLAINTSTRING" => "APPEARANCE/support-contact/url "],
536
        ["SETTING" => CONFIG['APPEARANCE']['support-contact']['display'],
537
            "DEFVALUE" => "[email protected]",
538
            "COMPLAINTSTRING" => "APPEARANCE/support-contact/display "],
539
        ["SETTING" => CONFIG['APPEARANCE']['support-contact']['developer-mail'],
540
            "DEFVALUE" => "[email protected]",
541
            "COMPLAINTSTRING" => "APPEARANCE/support-contact/mail "],
542
        ["SETTING" => CONFIG['APPEARANCE']['abuse-mail'],
543
            "DEFVALUE" => "[email protected]",
544
            "COMPLAINTSTRING" => "APPEARANCE/abuse-mail "],
545
        ["SETTING" => CONFIG['APPEARANCE']['MOTD'],
546
            "DEFVALUE" => "Release Candidate. All bugs to be shot on sight!",
547
            "COMPLAINTSTRING" => "APPEARANCE/MOTD "],
548
        ["SETTING" => CONFIG['APPEARANCE']['webcert_CRLDP'],
549
            "DEFVALUE" => ['list', 'of', 'CRL', 'pointers'],
550
            "COMPLAINTSTRING" => "APPEARANCE/webcert_CRLDP "],
551
        ["SETTING" => CONFIG['DB']['INST']['host'],
552
            "DEFVALUE" => "db.host.example",
553
            "COMPLAINTSTRING" => "DB/INST "],
554
        ["SETTING" => CONFIG['DB']['INST']['host'],
555
            "DEFVALUE" => "db.host.example",
556
            "COMPLAINTSTRING" => "DB/USER "],
557
    ];
558
559
    /**
560
     * test if defaults in the config have been replaced with some real values
561
     */
562
    private function defaults_test() {
563
        $defaultvalues = "";
564
        $missingvalues = "";
565
        // all the checks for equality with a shipped default value
566
        foreach (SanityTests::DEFAULTS as $oneCheckItem) {
567
            if ($oneCheckItem['SETTING'] == $oneCheckItem["DEFVALUE"]) {
568
                $defaultvalues .= $oneCheckItem["COMPLAINTSTRING"];
569
            }
570
        }
571
        // additional checks for defaults, which are not simple equality checks
572
        if (empty(CONFIG['APPEARANCE']['webcert_OCSP'])) {
573
            $missingvalues .= "APPEARANCE/webcert_OCSP ";
574
        } elseif (CONFIG['APPEARANCE']['webcert_OCSP'] == ['list', 'of', 'OCSP', 'pointers']) {
575
            $defaultvalues .= "APPEARANCE/webcert_OCSP ";
576
        }
577
        if (isset(CONFIG_DIAGNOSTICS['RADIUSTESTS']['UDP-hosts'][0]) && CONFIG_DIAGNOSTICS['RADIUSTESTS']['UDP-hosts'][0]['ip'] == "192.0.2.1") {
578
            $defaultvalues .= "RADIUSTESTS/UDP-hosts ";
579
        }
580
        if (!empty(CONFIG['DB']['EXTERNAL']) && CONFIG['DB']['EXTERNAL']['host'] == "customerdb.otherhost.example") {
581
            $defaultvalues .= "DB/EXTERNAL ";
582
        }
583
        $files = [];
584
        foreach (CONFIG_DIAGNOSTICS['RADIUSTESTS']['TLS-clientcerts'] as $cadata) {
585
            foreach ($cadata['certificates'] as $cert_files) {
586
                $files[] = $cert_files['public'];
587
                $files[] = $cert_files['private'];
588
            }
589
        }
590
591
        foreach ($files as $file) {
592
            $handle = fopen(ROOT . "/config/cli-certs/" . $file, 'r');
593
            if (!$handle) {
594
                $defaultvalues .= "CERTIFICATE/$file ";
595
            } else {
596
                fclose($handle);
597
            }
598
        }
599 View Code Duplication
        if ($defaultvalues != "") {
600
            $this->test_return(\core\common\Entity::L_WARN, "Your configuration in config/config.php contains unchanged default values or links to inexistent files: <strong>$defaultvalues</strong>!");
601
        } else {
602
            $this->test_return(\core\common\Entity::L_OK, "Your configuration does not contain any unchanged defaults, which is a good sign.");
603
        }
604
    }
605
606
    /**
607
     * test access to databases
608
     */
609
    private function databases_test() {
610
        $databaseName1 = 'INST';
611
        $db1 = mysqli_connect(CONFIG['DB'][$databaseName1]['host'], CONFIG['DB'][$databaseName1]['user'], CONFIG['DB'][$databaseName1]['pass'], CONFIG['DB'][$databaseName1]['db']);
612 View Code Duplication
        if (!$db1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
613
            $this->test_return(\core\common\Entity::L_ERROR, "Connection to the  $databaseName1 database failed");
614
        } else {
615
            $r = mysqli_query($db1, 'select * from profile_option_dict');
616
            if ($r->num_rows == $this->profile_option_ct) {
617
                $this->test_return(\core\common\Entity::L_OK, "The $databaseName1 database appears to be OK.");
618
            } else {
619
                $this->test_return(\core\common\Entity::L_ERROR, "The $databaseName1 database is reacheable but probably not updated to this version of CAT.");
620
            }
621
        }
622
        $databaseName2 = 'USER';
623
        $db2 = mysqli_connect(CONFIG['DB'][$databaseName2]['host'], CONFIG['DB'][$databaseName2]['user'], CONFIG['DB'][$databaseName2]['pass'], CONFIG['DB'][$databaseName2]['db']);
624 View Code Duplication
        if (!$db2) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
625
            $this->test_return(\core\common\Entity::L_ERROR, "Connection to the  $databaseName2 database failed");
626
        } else {
627
            $r = mysqli_query($db2, 'desc view_admin');
628
            if ($r->num_rows == $this->view_admin_ct) {
629
                $this->test_return(\core\common\Entity::L_OK, "The $databaseName2 database appears to be OK.");
630
            } else {
631
                $this->test_return(\core\common\Entity::L_ERROR, "The $databaseName2 is reacheable but there is something wrong with the schema");
632
            }
633
        }
634
        $databaseName3 = 'EXTERNAL';
635
        if (!empty(CONFIG['DB'][$databaseName3])) {
636
            $db3 = mysqli_connect(CONFIG['DB'][$databaseName3]['host'], CONFIG['DB'][$databaseName3]['user'], CONFIG['DB'][$databaseName3]['pass'], CONFIG['DB'][$databaseName3]['db']);
637 View Code Duplication
            if (!$db3) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
638
                $this->test_return(\core\common\Entity::L_ERROR, "Connection to the  $databaseName3 database failed");
639
            } else {
640
                $r = mysqli_query($db3, 'desc view_admin');
641
                if ($r->num_rows == $this->view_admin_ct) {
642
                    $this->test_return(\core\common\Entity::L_OK, "The $databaseName3 database appears to be OK.");
643
                } else {
644
                    $this->test_return(\core\common\Entity::L_ERROR, "The $databaseName3 is reacheable but there is something wrong with the schema");
645
                }
646
            }
647
        }
648
    }
649
650
    /**
651
     * test devices.php for the no_cache option
652
     */
653
    private function device_cache_test() {
654
        if ((!empty(\devices\Devices::$Options['no_cache'])) && \devices\Devices::$Options['no_cache']) {
655
            $global_no_cache = 1;
656
        } else {
657
            $global_no_cache = 0;
658
        }
659
660
        if ($global_no_cache == 1) {
661
            $this->test_return(\core\common\Entity::L_WARN, "Devices no_cache global option is set, this is not a good idea in a production setting\n");
662
        }
663
        $Devs = \devices\Devices::listDevices();
664
        $no_cache_dev = '';
665
        $no_cache_dev_count = 0;
666
        if ($global_no_cache) {
667 View Code Duplication
            foreach ($Devs as $dev => $D) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
668
                if (empty($D['options']['no_cache']) || $D['options']['no_cache'] != 0) {
669
                    $no_cache_dev .= $dev . " ";
670
                    $no_cache_dev_count++;
671
                }
672
            }
673
        } else {
674 View Code Duplication
            foreach ($Devs as $dev => $D) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
675
                if (!empty($D['options']['no_cache']) && $D['options']['no_cache'] != 0) {
676
                    $no_cache_dev .= $dev . " ";
677
                    $no_cache_dev_count++;
678
                }
679
            }
680
        }
681
682
683
        if ($no_cache_dev_count > 1) {
684
            $this->test_return(\core\common\Entity::L_WARN, "The following devices will not be cached: $no_cache_dev");
685
        }
686
        if ($no_cache_dev_count == 1) {
687
            $this->test_return(\core\common\Entity::L_WARN, "The following device will not be cached: $no_cache_dev");
688
        }
689
    }
690
691
    /**
692
     * test if mailer works
693
     */
694
    private function mailer_test() {
695
        if (empty(CONFIG['APPEARANCE']['abuse-mail']) || CONFIG['APPEARANCE']['abuse-mail'] == "[email protected]") {
696
            $this->test_return(\core\common\Entity::L_ERROR, "Your abuse-mail has not been set, cannot continue with mailer tests.");
697
            return;
698
        }
699
        $mail = new \PHPMailer\PHPMailer\PHPMailer();
700
        $mail->isSMTP();
701
        $mail->Port = 587;
702
        $mail->SMTPAuth = true;
703
        $mail->SMTPSecure = 'tls';
704
        $mail->Host = CONFIG['MAILSETTINGS']['host'];
705
        $mail->Username = CONFIG['MAILSETTINGS']['user'];
706
        $mail->Password = CONFIG['MAILSETTINGS']['pass'];
707
        $mail->WordWrap = 72;
708
        $mail->isHTML(FALSE);
709
        $mail->CharSet = 'UTF-8';
710
        $mail->From = CONFIG['APPEARANCE']['from-mail'];
711
        $mail->FromName = CONFIG['APPEARANCE']['productname'] . " Invitation System";
712
        $mail->addAddress(CONFIG['APPEARANCE']['abuse-mail']);
713
        $mail->Subject = "testing CAT configuration mail";
714
        $mail->Body = "Testing CAT mailing\n";
715
        $sent = $mail->send();
716
        if ($sent) {
717
            $this->test_return(\core\common\Entity::L_OK, "mailer settings appear to be working, check " . CONFIG['APPEARANCE']['abuse-mail'] . " mailbox if the message was receiced.");
718
        } else {
719
            $this->test_return(\core\common\Entity::L_ERROR, "mailer settings failed, check the Config::MAILSETTINGS");
720
        }
721
    }
722
723
    /**
724
     * TODO test if RADIUS connections work
725
     */
726
    private function UDPhosts_test() {
727
//        if(empty)
728
    }
729
730
}
731