BaseController::checkPostParamsExist()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 11
ccs 5
cts 5
cp 1
rs 10
cc 3
nc 3
nop 1
crap 3
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 Psr\Http\Message\ResponseInterface;
26
use Shieldon\Firewall\Firewall;
27
use Shieldon\Firewall\FirewallTrait;
28
use Shieldon\Firewall\Panel\DemoModeTrait;
29
use Shieldon\Firewall\Panel\ConfigMethodsTrait;
30
use Shieldon\Firewall\Panel\CsrfTrait;
31
use Shieldon\Firewall\Container;
32
use Shieldon\Firewall\Log\ActionLogParser;
33
use RuntimeException;
34
use function Shieldon\Firewall\__;
35
use function Shieldon\Firewall\get_request;
36
use function Shieldon\Firewall\get_response;
37
use function Shieldon\Firewall\get_session_instance;
38
use function Shieldon\Firewall\unset_superglobal;
39
use function Shieldon\Firewall\get_user_lang;
40
use function array_push;
41
use function define;
42
use function defined;
43
use function extract;
44
use function file_exists;
45
use function file_put_contents;
46
use function in_array;
47
use function is_array;
48
use function json_encode;
49
use function ob_end_clean;
50
use function ob_get_contents;
51
use function ob_start;
52
use function trim;
53
use const JSON_PRETTY_PRINT;
54
55
/**
56
 * Base controller.
57
 */
58
class BaseController
59
{
60
    /**
61
     *   Public methods       | Desctiotion
62
     *  ----------------------|---------------------------------------------
63
     *                        | No public methods.
64
     *  ----------------------|---------------------------------------------
65
     */
66
67
    /**
68
     *   Public methods       | Desctiotion
69
     *  ----------------------|---------------------------------------------
70
     *                        | No public methods.
71
     *  ----------------------|---------------------------------------------
72
     */
73
    use ConfigMethodsTrait;
74
75
    /**
76
     *   Public methods       | Desctiotion
77
     *  ----------------------|---------------------------------------------
78
     *   csrf                 | Receive the CSRF name and token from the App.
79
     *   setCsrfField         | Set CSRF input fields.
80
     *   fieldCsrf            | Output HTML input element with CSRF token.
81
     *  ----------------------|---------------------------------------------
82
     */
83
    use CsrfTrait;
84
85
    /**
86
     *   Public methods       | Desctiotion
87
     *  ----------------------|---------------------------------------------
88
     *   demo                 | Start a demo mode. Setting fields are hidden.
89
     *  ----------------------|---------------------------------------------
90
     */
91
    use DemoModeTrait;
92
93
    /**
94
     *   Public methods       | Desctiotion
95
     *  ----------------------|---------------------------------------------
96
     *   getKernel            | Get the Shieldon Kernel instance.
97
     *   getConfiguration     | Get the configuration data.
98
     *   getDirectory         | Get the dictionary where the data is stored.
99
     *   getFileName          | Get the path of the configuration file.
100
     *   getConfig            | Get the value by identification string.
101
     *   setConfig            | Set the value by identification string.
102
     *  ----------------------|---------------------------------------------
103
     */
104
    use FirewallTrait;
105
106
    /**
107
     * LogPaeser instance.
108
     *
109
     * @var object
110
     */
111
    protected $parser;
112
113
    /**
114
     * Messages.
115
     *
116
     * @var array
117
     */
118
    protected $messages = [];
119
120
    /**
121
     * Check page availability.
122
     *
123
     * @var array
124
     */
125
    protected $pageAvailability = [
126
127
        // Need to implement Action Logger to make it true.
128
        'logs' => false,
129
    ];
130
131
    /**
132
     * Language code.
133
     *
134
     * @var string
135
     */
136
    protected $locate = 'en';
137
138
    /**
139
     * Captcha modules.
140
     *
141
     * @var array
142
     */
143
    protected $captcha = [];
144
145
    /**
146
     * The base URL of the firewall panel.
147
     *
148
     * @var string
149
     */
150
    public $base = '';
151
152
    /**
153
     * Firewall panel base controller.
154
     */
155 85
    public function __construct()
156
    {
157 85
        $firewall = Container::get('firewall');
158
159 85
        if (!($firewall instanceof Firewall)) {
160 1
            throw new RuntimeException(
161 1
                'The Firewall instance should be initialized first.'
162 1
            );
163
        }
164
165 84
        $this->mode          = 'managed';
166 84
        $this->kernel        = $firewall->getKernel();
167 84
        $this->configuration = $firewall->getConfiguration();
168 84
        $this->directory     = $firewall->getDirectory();
169 84
        $this->filename      = $firewall->getFilename();
170 84
        $this->base          = SHIELDON_PANEL_BASE;
171
172 84
        if (!empty($this->kernel->logger)) {
173
            // We need to know where the logs stored in.
174
            $logDirectory = $this->kernel->logger->getDirectory();
175 67
176
            // Load ActionLogParser for parsing log files.
177
            $this->parser = new ActionLogParser($logDirectory);
178 67
179
            $this->pageAvailability['logs'] = true;
180 67
        }
181
182
        $flashMessage = get_session_instance()->get('flash_messages');
183 84
184
        // Flash message, use it when redirecting page.
185
        if (!empty($flashMessage) && is_array($flashMessage)) {
186 84
            $this->messages = $flashMessage;
187 17
            get_session_instance()->remove('flash_messages');
188 17
        }
189
190
        $this->locate = get_user_lang();
191 84
    }
192
193
    /**
194
     * Load view file.
195
     *
196
     * @param string $page The page type. (filename)
197
     * @param array  $data The variables passed to that page.
198
     *
199
     * @return string
200
     */
201
    protected function loadView(string $page, array $data = []): string
202 45
    {
203
        if (!defined('SHIELDON_VIEW')) {
204 45
            define('SHIELDON_VIEW', true);
205 37
        }
206
207
        $viewFilePath = __DIR__ . '/../../../templates/' . $page . '.php';
208 45
    
209
        if (!empty($data)) {
210 45
            extract($data);
211 45
        }
212
213
        $output = '';
214 45
    
215
        if (file_exists($viewFilePath)) {
216 45
            ob_start();
217 45
            include $viewFilePath;
218 45
            $output = ob_get_contents();
219 45
            ob_end_clean();
220 45
        }
221
222
        return $output;
223 45
    }
224
225
    /**
226
     * Render the web page with full layout.
227
     *
228
     * @param string $page The page type. (filename)
229
     * @param array  $data The variables passed to that page.
230
     *
231
     * @return ResponseInterface
232
     */
233
    protected function renderPage(string $page, array $data): ResponseInterface
234 39
    {
235
        $channelName = $this->kernel->driver->getChannel();
236 39
        $body = [];
237 39
238
        if (empty($channelName)) {
239 39
            $channelName = 'default';
240 39
        }
241
242
        $body['title'] = $data['title'] ?? '';
243 39
        $body['title'] .= ' - ' . __('panel', 'title_site_wide', 'Shieldon Firewall');
244 39
        $body['title'] .= ' v' . SHIELDON_FIREWALL_VERSION;
245 39
246
        $body['channel_name'] = $channelName;
247 39
        $body['mode_name'] = $this->mode;
248 39
        $body['page_url'] = $this->url();
249 39
        $body['content'] = $this->loadView($page, $data);
250 39
251
        $body['js_url'] = $this->url('asset/js');
252 39
        $body['css_url'] = $this->url('asset/css');
253 39
        $body['favicon_url'] = $this->url('asset/favicon');
254 39
        $body['logo_url'] = $this->url('asset/logo');
255 39
256
        if ($this->mode === 'demo') {
257 39
            $body['title'] .= ' (DEMO)';
258
        }
259 39
260
        $page = $this->loadView('panel/template', $body);
261
262
        return $this->respond($page);
263
    }
264
265
    /**
266
     * Return the response instance.
267
     *
268
     * @param string $body The content body.
269 45
     *
270
     * @return ResponseInterface
271 45
     */
272 45
    protected function respond(string $body): ResponseInterface
273 45
    {
274 45
        $response = get_response();
275
        $stream = $response->getBody();
276 45
        $stream->write($body);
277
        $stream->rewind();
278
279
        return $response->withBody($stream);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response->withBody($stream) returns the type Psr\Http\Message\MessageInterface which includes types incompatible with the type-hinted return Psr\Http\Message\ResponseInterface.
Loading history...
280
    }
281
282
    /**
283
     * Include a view file.
284
     * This method is used in a template loading other templates.
285
     *
286
     * @param string $page The page type. (filename)
287
     * @param array  $data The variables passed to that page.
288 7
     *
289
     * @return void
290 7
     */
291 1
    protected function loadViewPart(string $page, array $data = []): void
292
    {
293
        if (!defined('SHIELDON_VIEW')) {
294 7
            define('SHIELDON_VIEW', true);
295 3
        }
296
297
        foreach ($data as $k => $v) {
298 7
            ${$k} = $v;
299
        }
300
301
        include __DIR__ . '/../../../templates/' . $page . '.php';
302
    }
303
304
    /**
305
     * Response message to front.
306
     *
307
     * @param string $type The message status type. error|success
308
     * @param string $text The message body.
309 32
     *
310
     * @return void
311 32
     */
312
    protected function pushMessage(string $type, string $text): void
313 32
    {
314 5
        $class = $type;
315
316
        if ($type == 'error') {
317 32
            $class = 'danger';
318 32
        }
319 32
320 32
        array_push(
321 32
            $this->messages,
322 32
            [
323 32
                'type' => $type,
324 32
                'text' => $text,
325
                'class' => $class,
326
            ]
327
        );
328
    }
329
330
    /**
331
     * Return the relative URL.
332
     *
333
     * @param string $path The page's path.
334 45
     *
335
     * @return string
336 45
     */
337
    protected function url(string $path = ''): string
338
    {
339
        return '/' . trim($this->base, '/') . '/' . $path . '/';
340
    }
341
342
    /**
343
     * Save the configuration settings to the JSON file.
344 26
     *
345
     * @return void
346 26
     */
347 1
    protected function saveConfig(): void
348
    {
349
        if ($this->mode !== 'managed') {
350 25
            return;
351
        }
352 25
353
        $postParams = (array) get_request()->getParsedBody();
354 25
355
        $configFilePath = $this->directory . '/' . $this->filename;
356
357
        foreach ($this->csrfField as $csrfInfo) {
358
            // @codeCoverageIgnoreStart
359
            if (!empty($csrfInfo['name'])) {
360
                unset_superglobal($csrfInfo['name'], 'post');
361
            }
362 25
            // @codeCoverageIgnoreEnd
363
        }
364
365 25
        $this->saveConfigPrepareSettings($postParams);
366 25
367 25
        //  Start checking the availibility of the data driver settings.
368 25
        $result = true;
369
        $result = $this->saveConfigCheckDataDriver($result);
370
        $result = $this->saveConfigCheckActionLogger($result);
371 25
        $result = $this->saveConfigCheckIptables($result);
372 25
373
        // Only update settings while data driver is correctly connected.
374 25
        if ($result) {
375 25
            file_put_contents($configFilePath, json_encode($this->configuration, JSON_PRETTY_PRINT));
376 25
377 25
            $this->pushMessage(
378 25
                'success',
379 25
                __(
380 25
                    'panel',
381 25
                    'success_settings_saved',
382
                    'Settings saved.'
383
                )
384
            );
385
        }
386
    }
387
388
    /**
389
     * Echo the setting string to the template.
390
     *
391
     * @param string $field   Field.
392
     * @param mixed  $default Default value.
393 7
     *
394
     * @return void
395 7
     */
396
    protected function _(string $field, $default = ''): void
397
    {
398 2
        if ($this->mode === 'demo') {
399 2
            // Hide sensitive data because of security concerns.
400 2
            $hiddenForDemo = [
401 2
                'drivers.redis.auth',
402 2
                'drivers.file.directory_path',
403 2
                'drivers.sqlite.directory_path',
404 2
                'drivers.mysql.dbname',
405 2
                'drivers.mysql.user',
406 2
                'drivers.mysql.pass',
407 2
                'captcha_modules.recaptcha.config.site_key',
408 2
                'captcha_modules.recaptcha.config.secret_key',
409 2
                'loggers.action.config.directory_path',
410 2
                'admin.user',
411 2
                'admin.pass',
412 2
                'admin.last_modified',
413 2
                'messengers.telegram.config.api_key',
414 2
                'messengers.telegram.config.channel',
415 2
                'messengers.sendgrid.config.api_key',
416 2
                'messengers.sendgrid.config.sender',
417 2
                'messengers.sendgrid.config.recipients',
418 2
                'messengers.line_notify.config.access_token',
419 2
                'iptables.config.watching_folder',
420 2
                'ip6tables.config.watching_folder',
421
                'messengers.sendgrid.config.recipients', // array
422 2
            ];
423 1
424 1
            if (in_array($field, $hiddenForDemo)) {
425
                echo __('panel', 'field_not_visible', 'This field cannot be viewed in demonstration mode.');
426
                return;
427
            }
428 6
        }
429
430 6
        $fieldtype = gettype($this->getConfig($field));
431 2
432 2
        if ($fieldtype === 'array') {
433
            echo implode("\n", $this->getConfig($field));
434
            return;
435 6
        }
436
437
        echo $this->getConfig($field) ?: $default;
438
    }
439
440
    /**
441
     * Use on HTML checkbox and radio elements.
442
     *
443
     * @param string $value        The variable or configuation field.
444
     * @param mixed  $valueChecked The value.
445
     * @param bool   $isConfig     Is it a configuration field or not.
446
     *
447 9
     * @return void
448
     */
449 9
    protected function checked(string $value, $valueChecked, bool $isConfig = true): void
450 8
    {
451 8
        if ($isConfig) {
452 8
            if ($this->getConfig($value) === $valueChecked) {
453
                echo 'checked';
454
                return;
455 5
            }
456 5
        } else {
457 5
            if ($value === $valueChecked) {
458
                echo 'checked';
459
                return;
460
            }
461 8
        }
462
463
        echo '';
464
    }
465
466
    /**
467
     * Echo correspondence string on Messenger setting page.
468
     *
469
     * @param string $moduleName The messenger module's name.
470
     * @param string $echoType   Value: css | icon
471
     *
472 2
     * @return void
473
     */
474 2
    protected function messengerAjaxStatus(string $moduleName, string $echoType = 'css'): void
475
    {
476 2
        $echo = [];
477 2
478 2
        $echo['css'] = $this->getConfig('messengers.' . $moduleName . '.confirm_test') ?
479
            'success' :
480 2
            '';
481 2
        
482 2
        $echo['icon'] = $this->getConfig('messengers.' . $moduleName . '.confirm_test') ?
483
            '<i class="fas fa-check"></i>' :
484 2
            '<i class="fas fa-exclamation"></i>';
485
486
        echo $echo[$echoType];
487
    }
488
489
    /**
490
     * Check the required fields.
491
     *
492
     * @param array $fields The fields from POST form.
493
     *
494 8
     * @return bool
495
     */
496 8
    protected function checkPostParamsExist(...$fields): bool
497
    {
498 8
        $postParams = (array) get_request()->getParsedBody();
499 8
500 8
        foreach ($fields as $field) {
501
            if (empty($postParams[$field])) {
502
                return false;
503
            }
504 6
        }
505
506
        return true;
507
    }
508
}
509