ConfigMethodsTrait::saveConfigCheckActionLogger()   A
last analyzed

Complexity

Conditions 5
Paths 6

Size

Total Lines 39
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 5

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 19
c 3
b 0
f 0
dl 0
loc 39
ccs 7
cts 7
cp 1
rs 9.3222
cc 5
nc 6
nop 1
crap 5
1
<?php
2
/**
3
 * This file is part of the Shieldon package.
4
 *
5
 * (c) Terry L. <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * php version 7.1.0
11
 *
12
 * @category  Web-security
13
 * @package   Shieldon
14
 * @author    Terry Lin <[email protected]>
15
 * @copyright 2019 terrylinooo
16
 * @license   https://github.com/terrylinooo/shieldon/blob/2.x/LICENSE MIT
17
 * @link      https://github.com/terrylinooo/shieldon
18
 * @see       https://shieldon.io
19
 */
20
21
declare(strict_types=1);
22
23
namespace Shieldon\Firewall\Panel;
24
25
use function Shieldon\Firewall\__;
26
27
use PDO;
28
use PDOException;
29
use Redis;
30
use Exception;
31
use function class_exists;
32
use function is_dir;
33
use function is_numeric;
34
use function is_string;
35
use function is_writable;
36
use function mkdir;
37
use function password_hash;
38
use function preg_split;
39
use function str_replace;
40
use function umask;
41
42
/*
43
 * @since 2.0.0
44
 */
45
trait ConfigMethodsTrait
46
{
47
    /**
48
     *   Public methods       | Desctiotion
49
     *  ----------------------|---------------------------------------------
50
     *                        | No public methods.
51
     *  ----------------------|---------------------------------------------
52
     */
53
54
    /**
55
     * Get a variable from configuration.
56
     *
57
     * @param string $field The field of the configuration.
58
     *
59
     * @return mixed
60
     */
61
    abstract public function getConfig(string $field);
62
63
    /**
64
     * Set a variable to the configuration.
65
     *
66
     * @param string $field The field of the configuration.
67
     * @param mixed  $value The vale of a field in the configuration.
68
     *
69
     * @return void
70
     */
71
    abstract public function setConfig(string $field, $value): void;
72
73
    /**
74
     * Response message to front.
75
     *
76
     * @param string $type The message status type. error|success
77
     * @param string $text The message body.
78
     *
79
     * @return void
80
     */
81
    abstract protected function pushMessage(string $type, string $text): void;
82
83
    /**
84
     * Handle to update settings.
85
     *
86
     * @param string $postKey  The key of the post data.
87
     * @param string $postData The value of the post data.
88
     * @param array $postParams The post data.
89
     * @return void
90
     */
91 25
    private function handleUpdatingSettings(string $postKey, string $postData, array $postParams): void
92
    {
93 25
        if ($postData === 'on') {
94
            $this->setConfig(str_replace('__', '.', $postKey), true);
95 14
        } elseif ($postData === 'off') {
96 14
            $this->setConfig(str_replace('__', '.', $postKey), false);
97 2
        } elseif ($postKey === 'ip_variable_source') {
98
            $this->setConfig('ip_variable_source.REMOTE_ADDR', false);
99 14
            $this->setConfig('ip_variable_source.HTTP_CF_CONNECTING_IP', false);
100 14
            $this->setConfig('ip_variable_source.HTTP_X_FORWARDED_FOR', false);
101
            $this->setConfig('ip_variable_source.HTTP_X_FORWARDED_HOST', false);
102 2
            $this->setConfig('ip_variable_source.' . $postData, true);
103 1
        } elseif ($postKey === 'dialog_ui__shadow_opacity') {
104 1
            $this->setConfig('dialog_ui.shadow_opacity', (string) $postData);
105 1
        } elseif ($postKey === 'admin__pass') {
106 1
            if (strlen($postParams['admin__pass']) < 58) {
107 1
                $this->setConfig('admin.pass', password_hash($postData, PASSWORD_BCRYPT));
108
            }
109 2
        } elseif (strpos($postKey, 'config__recipients') !== false) {
110 1
            // For example:
111
            // messengers__sendgrid__config__recipients
112 2
            // => messengers.sendgrid.config.recipients
113 1
            $this->setConfig(
114 1
                str_replace('__', '.', $postKey),
115
                preg_split(
116
                    '/\r\n|[\r\n]/',
117 2
                    $postData
118
                )
119
            );
120
        } elseif (is_numeric($postData)) {
121 1
            $this->setConfig(str_replace('__', '.', $postKey), (int) $postData);
122 1
        } else {
123 1
            $this->setConfig(str_replace('__', '.', $postKey), $postData);
124 1
        }
125 1
    }
126 1
127 1
    /**
128
     * Parse the POST fields and set them into configuration data structure.
129 2
     * Used for saveConfig method only.
130 1
     *
131
     * @param array $postParams The PSR-7 variable of $_POST
132
     *
133 14
     * @return void
134
     */
135
    protected function saveConfigPrepareSettings(array $postParams): void
136
    {
137
        foreach ($postParams as $postKey => $postData) {
138
            if (!is_string($postData)) {
139
                continue;
140
            }
141
            $this->handleUpdatingSettings($postKey, $postData, $postParams);
142
        }
143
    }
144
145
    /**
146 27
     * Check the settings of Action Logger.
147
     *
148 27
     * @param bool $result The result passed from previous check.
149 1
     *
150
     * @return bool
151
     */
152
    protected function saveConfigCheckActionLogger(bool $result): bool
153 26
    {
154
        if (!$result) {
155
            return false;
156 26
        }
157
158 26
        // Check Action Logger settings.
159 25
        $enableActionLogger = $this->getConfig('loggers.action.enable');
160
161 25
        // $actionLogDir = rtrim($this->getConfig('loggers.action.config.directory_path'), '\\/ ');
162
        $actionLogDir = $this->directory . '/action_logs';
163
164
        if ($enableActionLogger) {
165
            $this->setConfig('loggers.action.config.directory_path', $actionLogDir);
166
167
            if (!is_dir($actionLogDir)) {
168
                // @codeCoverageIgnoreStart
169 25
                $originalUmask = umask(0);
170
                mkdir($actionLogDir, 0777, true);
171
                umask($originalUmask);
172
                // @codeCoverageIgnoreEnd
173
            }
174
175
            if (!is_writable($actionLogDir)) {
176
                // @codeCoverageIgnoreStart
177
                $result = false;
178
                $this->pushMessage(
179
                    'error',
180
                    __(
181
                        'panel',
182
                        'error_logger_directory_not_writable',
183
                        'Action Logger requires the storage directory writable.'
184 26
                    )
185
                );
186
                // @codeCoverageIgnoreEnd
187
            }
188
        }
189
190
        return $result;
191
    }
192
193
    /**
194 27
     * Check the settings of iptables.
195
     *
196 27
     * @param bool $result The result passed from previous check.
197 1
     *
198
     * @return bool
199
     */
200
    protected function saveConfigCheckIptables(bool $result): bool
201 26
    {
202
        if (!$result) {
203
            return false;
204 26
        }
205
206 26
        // System firewall.
207 25
        $enableiptables = $this->getConfig('iptables.enable');
208
209 25
        // $iptablesWatchingFolder = rtrim($this->getConfig('iptables.config.watching_folder'), '\\/ ');
210
        $iptablesWatchingFolder = $this->directory . '/iptables';
211
        
212
        if ($enableiptables) {
213
            $this->setConfig('iptables.config.watching_folder', $iptablesWatchingFolder);
214
215
            if (!is_dir($iptablesWatchingFolder)) {
216
                // @codeCoverageIgnoreStart
217
                $originalUmask = umask(0);
218 25
                mkdir($iptablesWatchingFolder, 0777, true);
219 25
                umask($originalUmask);
220 25
                // @codeCoverageIgnoreEnd
221 25
            }
222 25
    
223 25
            // Create default log files.
224
            if (is_writable($iptablesWatchingFolder)) {
225 25
                fopen($iptablesWatchingFolder . '/iptables_queue.log', 'w+');
226
                fopen($iptablesWatchingFolder . '/ipv4_status.log', 'w+');
227
                fopen($iptablesWatchingFolder . '/ipv6_status.log', 'w+');
228
                fopen($iptablesWatchingFolder . '/ipv4_command.log', 'w+');
229
                fopen($iptablesWatchingFolder . '/ipv6_command.log', 'w+');
230
231
                return $result;
232
            }
233
234
            // @codeCoverageIgnoreStart
235
            $result = false;
236
237
            $this->pushMessage(
238
                'error',
239
                __(
240
                    'panel',
241
                    'error_ip6tables_directory_not_writable',
242 1
                    'iptables watching folder requires the storage directory writable.'
243
                )
244
            );
245
            // @codeCoverageIgnoreEnd
246
        }
247
248
        return $result;
249
    }
250
251
    /**
252 27
     * Check the settings of Data drivers.
253
     *
254 27
     * @param bool $result The result passed from previous check.
255 1
     *
256
     * @return bool
257
     */
258 26
    protected function saveConfigCheckDataDriver(bool $result): bool
259
    {
260 26
        if (!$result) {
261 26
            return false;
262 26
        }
263 26
264 26
        $type = $this->getConfig('driver_type');
265 26
266
        $methods = [
267 26
            'mysql'  => 'saveCofigCheckDataDriverMySql',
268 26
            'sqlite' => 'saveCofigCheckDataDriverSqlLite',
269
            'redis'  => 'saveCofigCheckDataDriverRedis',
270 26
            'file'   => 'saveCofigCheckDataDriverFile',
271
        ];
272
273
        $method = $methods[$type];
274
        $result = $this->{$method}($result);
275
276
        return $result;
277
    }
278
279
    /**
280 2
     * Check the settings of Data drivers.
281
     *
282 2
     * @param bool $result The result passed from previous check.
283
     *
284 2
     * @return bool
285 2
     */
286 2
    protected function saveCofigCheckDataDriverMySql(bool $result): bool
287 2
    {
288 2
        if (class_exists('PDO')) {
289 2
            $db = [
290 2
                'host'    => $this->getConfig('drivers.mysql.host'),
291
                'dbname'  => $this->getConfig('drivers.mysql.dbname'),
292
                'user'    => $this->getConfig('drivers.mysql.user'),
293 2
                'pass'    => $this->getConfig('drivers.mysql.pass'),
294 2
                'charset' => $this->getConfig('drivers.mysql.charset'),
295 2
            ];
296 2
297 2
            try {
298 1
                new PDO(
299
                    'mysql:host=' . $db['host'] . ';dbname=' . $db['dbname'] . ';charset=' . $db['charset'],
300 1
                    (string) $db['user'],
301
                    (string) $db['pass']
302 1
                );
303 1
            } catch (PDOException $e) {
304 1
                $result = false;
305 1
306 1
                $this->pushMessage(
307 1
                    'error',
308 1
                    __(
309 1
                        'panel',
310
                        'error_mysql_connection',
311 2
                        'Cannot connect to your MySQL database, please check your settings.'
312
                    )
313
                );
314
            }
315
            return $result;
316
        }
317
318
        // @codeCoverageIgnoreStart
319
320
        $result = false;
321
322
        $this->pushMessage(
323
            'error',
324
            __(
325
                'panel',
326
                'error_mysql_driver_not_supported',
327
                'Your system doesn’t support MySQL driver.'
328
            )
329
        );
330
331
        return $result;
332
333
        // @codeCoverageIgnoreEnd
334
    }
335
336
    /**
337
     * Check the settings of Data drivers.
338
     *
339 1
     * @param bool $result The result passed from previous check.
340
     *
341
     * @return bool
342 1
     */
343
    protected function saveCofigCheckDataDriverSqlLite(bool $result): bool
344 1
    {
345
        // $sqliteDir = rtrim($this->getConfig('drivers.sqlite.directory_path'), '\\/ ');
346 1
        $sqliteDir = $this->directory . '/data_driver_sqlite';
347
348 1
        $this->setConfig('drivers.sqlite.directory_path', $sqliteDir);
349
350
        $sqliteFilePath = $sqliteDir . '/shieldon.sqlite3';
351
352
        if (!is_dir($sqliteDir)) {
353
            // @codeCoverageIgnoreStart
354
            $originalUmask = umask(0);
355
            mkdir($sqliteDir, 0777, true);
356 1
            umask($originalUmask);
357
            // @codeCoverageIgnoreEnd
358
        }
359 1
360
        if (class_exists('PDO')) {
361
            try {
362
                new PDO('sqlite:' . $sqliteFilePath);
363
364
                // @codeCoverageIgnoreStart
365
            } catch (PDOException $e) {
366
                $this->pushMessage('error', $e->getMessage());
367
                $result = false;
368
            }
369 1
370
            // @codeCoverageIgnoreEnd
371
372
            if (!is_writable($sqliteFilePath)) {
373
                // @codeCoverageIgnoreStart
374
                $this->pushMessage(
375
                    'error',
376
                    __(
377
                        'panel',
378
                        'error_sqlite_directory_not_writable',
379
                        'SQLite data driver requires the storage directory writable.'
380
                    )
381
                );
382
                $result = false;
383
                // @codeCoverageIgnoreEnd
384 1
            }
385
386
            return $result;
387
        }
388
389
        // @codeCoverageIgnoreStart
390
391
        $result = false;
392
393
        $this->pushMessage(
394
            'error',
395
            __(
396
                'panel',
397
                'error_sqlite_driver_not_supported',
398
                'Your system doesn’t support SQLite driver.'
399
            )
400
        );
401
402
        return $result;
403
404
        // @codeCoverageIgnoreEnd
405
    }
406
407
    /**
408
     * Check the settings of Data drivers.
409
     *
410
     * @param bool $result The result passed from previous check.
411
     *
412 2
     * @return bool
413
     */
414 2
    protected function saveCofigCheckDataDriverRedis(bool $result): bool
415
    {
416
        if (class_exists('Redis')) {
417 2
            try {
418 2
                $redis = new Redis();
419 2
                $redis->connect(
420 2
                    (string) $this->getConfig('drivers.redis.host'),
421 2
                    (int) $this->getConfig('drivers.redis.port')
422 1
                );
423
                unset($redis);
424 1
            } catch (Exception $e) {
425 1
                $this->pushMessage('error', $e->getMessage());
426 1
                $result = false;
427
            }
428
429 2
            return $result;
430
        }
431
432
        // @codeCoverageIgnoreStart
433
434
        $result = false;
435
436
        $this->pushMessage(
437
            'error',
438
            __(
439
                'panel',
440
                'error_redis_driver_not_supported',
441
                'Your system doesn’t support Redis driver.'
442
            )
443
        );
444
445
        return $result;
446
447
        // @codeCoverageIgnoreEnd
448
    }
449
450
    /**
451
     * Check the settings of Data drivers.
452
     *
453
     * @param bool $result The result passed from previous check.
454
     *
455
     * @return bool
456
     */
457 27
    protected function saveCofigCheckDataDriverFile(bool $result): bool
458
    {
459
        //$fileDir = rtrim($this->getConfig('drivers.file.directory_path'), '\\/ ');
460 27
        $fileDir = $this->directory . '/data_driver_file';
461
462 27
        $this->setConfig('drivers.file.directory_path', $fileDir);
463
464 27
        if (!is_dir($fileDir)) {
465
            // @codeCoverageIgnoreStart
466
            $originalUmask = umask(0);
467
            mkdir($fileDir, 0777, true);
468
            umask($originalUmask);
469
            // @codeCoverageIgnoreEnd
470
        }
471
472 27
        if (!is_writable($fileDir)) {
473
            // @codeCoverageIgnoreStart
474
            $result = false;
475
            $this->pushMessage(
476
                'error',
477
                __(
478
                    'panel',
479
                    'error_file_directory_not_writable',
480
                    'File data driver requires the storage directory writable.'
481
                )
482
            );
483
            // @codeCoverageIgnoreEnd
484
        }
485
486 27
        return $result;
487
    }
488
}
489