Passed
Push — 2.x ( 887434...faa78c )
by Terry
01:48
created

ConfigMethodsTrait::saveConfigCheckActionLogger()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 39
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

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