1
<?php
2
/**
3
 * This file is part of the Shieldon package.
4
 *
5
 * (c) Terry L. <contact@terryl.in>
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 <contact@terryl.in>
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
     * Parse the POST fields and set them into configuration data structure.
85
     * Used for saveConfig method only.
86
     *
87
     * @param array $postParams The PSR-7 variable of $_POST
88
     *
89
     * @return void
90
     */
91 3
    protected function saveConfigPrepareSettings(array $postParams): void
92
    {
93 3
        foreach ($postParams as $postKey => $postData) {
94

95 3
            if (is_string($postData)) {
96 3
                if ($postData === 'on') {
97 3
                    $this->setConfig(str_replace('__', '.', $postKey), true);
98

99 3
                } elseif ($postData === 'off') {
100 3
                    $this->setConfig(str_replace('__', '.', $postKey), false);
101

102 3
                } elseif ($postKey === 'ip_variable_source') {
103 3
                    $this->setConfig('ip_variable_source.REMOTE_ADDR', false);
104 3
                    $this->setConfig('ip_variable_source.HTTP_CF_CONNECTING_IP', false);
105 3
                    $this->setConfig('ip_variable_source.HTTP_X_FORWARDED_FOR', false);
106 3
                    $this->setConfig('ip_variable_source.HTTP_X_FORWARDED_HOST', false);
107 3
                    $this->setConfig('ip_variable_source.' . $postData, true);
108

109 3
                } elseif ($postKey === 'dialog_ui__shadow_opacity') {
110 3
                    $this->setConfig('dialog_ui.shadow_opacity', (string) $postData);
111

112 3
                } elseif ($postKey === 'admin__pass') {
113 3
                    if (strlen($postParams['admin__pass']) < 58) {
114 3
                        $this->setConfig('admin.pass', password_hash($postData, PASSWORD_BCRYPT));
115
                    }
116

117 3
                } else if (strpos($postKey, 'config__recipients') !== false) {
118
                    // For example: 
119
                    // messengers__sendgrid__config__recipients
120
                    // => messengers.sendgrid.config.recipients
121 3
                    $this->setConfig(
122 3
                        str_replace('__', '.', $postKey),
123 3
                        preg_split(
124 3
                            '/\r\n|[\r\n]/',
125 3
                            $postData
126
                        )
127
                    );
128

129 3
                } elseif (is_numeric($postData)) {
130 3
                    $this->setConfig(str_replace('__', '.', $postKey), (int) $postData);
131

132
                } else {
133 3
                    $this->setConfig(str_replace('__', '.', $postKey), $postData);
134
                }
135
            }
136
        }
137
    }
138

139
    /**
140
     * Check the settings of Action Logger.
141
     * 
142
     * @param bool $result The result passed from previous check.
143
     *
144
     * @return bool
145
     */
146 3
    protected function saveConfigCheckActionLogger(bool $result): bool
147
    {
148 3
        if (!$result) {
149 3
            return false;
150
        }
151

152
        // Check Action Logger settings.
153 3
        $enableActionLogger = $this->getConfig('loggers.action.enable');
154

155
        // $actionLogDir = rtrim($this->getConfig('loggers.action.config.directory_path'), '\\/ ');
156 3
        $actionLogDir = $this->directory . '/action_logs';
157

158 3
        if ($enableActionLogger) {
159 3
            $this->setConfig('loggers.action.config.directory_path', $actionLogDir);
160

161 3
            if (!is_dir($actionLogDir)) {
162
                // @codeCoverageIgnoreStart
163
                $originalUmask = umask(0);
164
                mkdir($actionLogDir, 0777, true);
165
                umask($originalUmask);
166
                // @codeCoverageIgnoreEnd
167
            }
168

169 3
            if (!is_writable($actionLogDir)) {
170
                // @codeCoverageIgnoreStart
171
                $result = false;
172
                $this->pushMessage(
173
                    'error',
174
                    __(
175
                        'panel',
176
                        'error_logger_directory_not_writable',
177
                        'Action Logger requires the storage directory writable.'
178
                    )
179
                );
180
                // @codeCoverageIgnoreEnd
181
            }
182
        }
183

184 3
        return $result;
185
    }
186

187
    /**
188
     * Check the settings of iptables.
189
     * 
190
     * @param bool $result The result passed from previous check.
191
     *
192
     * @return bool
193
     */
194 3
    protected function saveConfigCheckIptables(bool $result): bool
195
    {
196 3
        if (!$result) {
197 3
            return false;
198
        }
199

200
        // System firewall.
201 3
        $enableiptables = $this->getConfig('iptables.enable');
202

203
        // $iptablesWatchingFolder = rtrim($this->getConfig('iptables.config.watching_folder'), '\\/ ');
204 3
        $iptablesWatchingFolder = $this->directory . '/iptables';
205
        
206 3
        if ($enableiptables) {
207 3
            $this->setConfig('iptables.config.watching_folder', $iptablesWatchingFolder);
208

209 3
            if (!is_dir($iptablesWatchingFolder)) {
210
                // @codeCoverageIgnoreStart
211
                $originalUmask = umask(0);
212
                mkdir($iptablesWatchingFolder, 0777, true);
213
                umask($originalUmask);
214
                // @codeCoverageIgnoreEnd
215
            }
216
    
217
            // Create default log files.
218 3
            if (is_writable($iptablesWatchingFolder)) {
219 3
                fopen($iptablesWatchingFolder . '/iptables_queue.log', 'w+');
220 3
                fopen($iptablesWatchingFolder . '/ipv4_status.log',    'w+');
221 3
                fopen($iptablesWatchingFolder . '/ipv6_status.log',    'w+');
222 3
                fopen($iptablesWatchingFolder . '/ipv4_command.log',   'w+');
223 3
                fopen($iptablesWatchingFolder . '/ipv6_command.log',   'w+');
224

225 3
                return $result;
226
            }
227

228
            // @codeCoverageIgnoreStart
229
            $result = false;
230

231
            $this->pushMessage(
232
                'error',
233
                __(
234
                    'panel',
235
                    'error_ip6tables_directory_not_writable',
236
                    'iptables watching folder requires the storage directory writable.'
237
                )
238
            );
239
            // @codeCoverageIgnoreEnd
240
        }
241

242 3
        return $result;
243
    }
244

245
    /**
246
     * Check the settings of Data drivers.
247
     *
248
     * @param bool $result The result passed from previous check.
249
     *
250
     * @return bool
251
     */
252 3
    protected function saveConfigCheckDataDriver(bool $result): bool
253
    {
254 3
        if (!$result) {
255 3
            return false;
256
        }
257

258 3
        $type = $this->getConfig('driver_type');
259

260
        $methods = [
261 3
            'mysql'  => 'saveCofigCheckDataDriverMySql',
262
            'sqlite' => 'saveCofigCheckDataDriverSqlLite',
263
            'redis'  => 'saveCofigCheckDataDriverRedis',
264
            'file'   => 'saveCofigCheckDataDriverFile',
265
        ];
266

267 3
        $method = $methods[$type];
268 3
        $result = $this->{$method}($result);
269

270 3
        return $result;
271
    }
272

273
    /**
274
     * Check the settings of Data drivers.
275
     *
276
     * @param bool $result The result passed from previous check.
277
     *
278
     * @return bool
279
     */
280 3
    protected function saveCofigCheckDataDriverMySql(bool $result): bool
281
    {
282 3
        if (class_exists('PDO')) {
283

284
            $db = [
285 3
                'host'    => $this->getConfig('drivers.mysql.host'),
286 3
                'dbname'  => $this->getConfig('drivers.mysql.dbname'),
287 3
                'user'    => $this->getConfig('drivers.mysql.user'),
288 3
                'pass'    => $this->getConfig('drivers.mysql.pass'),
289 3
                'charset' => $this->getConfig('drivers.mysql.charset'),
290
            ];
291

292
            try {
293 3
                new PDO(
294 3
                    'mysql:host=' . $db['host'] . ';dbname=' . $db['dbname'] . ';charset=' . $db['charset'],
295 3
                    (string) $db['user'],
296 3
                    (string) $db['pass']
297
                );
298 3
            } catch (PDOException $e) {
299

300 3
                $result = false;
301

302 3
                $this->pushMessage(
303 3
                    'error', 
304 3
                    __(
305 3
                        'panel',
306 3
                        'error_mysql_connection',
307 3
                        'Cannot access to your MySQL database, please check your settings.'
308
                    )
309
                );
310
            }
311 3
            return $result;
312
        }
313

314
        // @codeCoverageIgnoreStart
315

316
        $result = false;
317

318
        $this->pushMessage(
319
            'error',
320
            __(
321
                'panel',
322
                'error_mysql_driver_not_supported',
323
                'Your system doesn’t support MySQL driver.'
324
            )
325
        );
326

327
        return $result;
328

329
        // @codeCoverageIgnoreEnd
330
    }
331

332
    /**
333
     * Check the settings of Data drivers.
334
     *
335
     * @param bool $result The result passed from previous check.
336
     *
337
     * @return bool
338
     */
339 3
    protected function saveCofigCheckDataDriverSqlLite(bool $result): bool
340
    {
341
        // $sqliteDir = rtrim($this->getConfig('drivers.sqlite.directory_path'), '\\/ ');
342 3
        $sqliteDir = $this->directory . '/data_driver_sqlite';
343

344 3
        $this->setConfig('drivers.sqlite.directory_path', $sqliteDir);
345

346 3
        $sqliteFilePath = $sqliteDir . '/shieldon.sqlite3';
347

348 3
        if (!is_dir($sqliteDir)) {
349
            // @codeCoverageIgnoreStart
350
            $originalUmask = umask(0);
351
            mkdir($sqliteDir, 0777, true);
352
            umask($originalUmask);
353
            // @codeCoverageIgnoreEnd
354
        }
355

356 3
        if (class_exists('PDO')) {
357

358
            try {
359 3
                new PDO('sqlite:' . $sqliteFilePath);
360

361
                // @codeCoverageIgnoreStart
362
            } catch (PDOException $e) { 
363
                $this->pushMessage('error', $e->getMessage());
364
                $result = false;
365
            }
366

367
            // @codeCoverageIgnoreEnd
368

369 3
            if (!is_writable($sqliteFilePath)) {
370

371
                // @codeCoverageIgnoreStart
372
                $this->pushMessage(
373
                    'error',
374
                    __(
375
                        'panel',
376
                        'error_sqlite_directory_not_writable',
377
                        'SQLite data driver requires the storage directory writable.'
378
                    )
379
                );
380
                $result = false;
381
                // @codeCoverageIgnoreEnd
382
            }
383

384 3
            return $result;
385
        }
386

387
        // @codeCoverageIgnoreStart
388

389
        $result = false;
390

391
        $this->pushMessage(
392
            'error',
393
            __(
394
                'panel',
395
                'error_sqlite_driver_not_supported',
396
                'Your system doesn’t support SQLite driver.'
397
            )
398
        );
399

400
        return $result;
401

402
        // @codeCoverageIgnoreEnd
403
    }
404

405
    /**
406
     * Check the settings of Data drivers.
407
     *
408
     * @param bool $result The result passed from previous check.
409
     *
410
     * @return bool
411
     */
412 3
    protected function saveCofigCheckDataDriverRedis(bool $result): bool
413
    {
414 3
        if (class_exists('Redis')) {
415

416
            try {
417 3
                $redis = new Redis();
418 3
                $redis->connect(
419 3
                    (string) $this->getConfig('drivers.redis.host'), 
420 3
                    (int)    $this->getConfig('drivers.redis.port')
421
                );
422 3
                unset($redis);
423

424 3
            } catch (Exception $e) {
425 3
                $this->pushMessage('error', $e->getMessage());
426 3
                $result = false;
427
            }
428

429 3
            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 3
    protected function saveCofigCheckDataDriverFile(bool $result): bool
458
    {
459
        //$fileDir = rtrim($this->getConfig('drivers.file.directory_path'), '\\/ ');
460 3
        $fileDir = $this->directory . '/data_driver_file';
461

462 3
        $this->setConfig('drivers.file.directory_path', $fileDir);
463

464 3
        if (!is_dir($fileDir)) {
465
            // @codeCoverageIgnoreStart
466
            $originalUmask = umask(0);
467
            mkdir($fileDir, 0777, true);
468
            umask($originalUmask);
469
            // @codeCoverageIgnoreEnd
470
        }
471

472 3
        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 3
        return $result;
487
    }
488
}

Read our documentation on viewing source code .

Loading