Passed
Push — 2.x ( c35924...704f2a )
by Terry
01:54
created

Panel::entry()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 61
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 5
eloc 27
c 4
b 0
f 0
nc 6
nop 0
dl 0
loc 61
rs 9.1768

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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;
24
25
use Psr\Http\Message\ResponseInterface;
26
use Shieldon\Firewall\HttpResolver;
27
use Shieldon\Firewall\Panel\CsrfTrait;
28
use Shieldon\Firewall\Panel\DemoModeTrait;
29
use Shieldon\Firewall\Panel\User;
30
use Shieldon\Firewall\Utils\Container;
31
use Shieldon\Firewall\Firewall;
32
use function Shieldon\Firewall\get_request;
33
use function Shieldon\Firewall\get_response;
34
35
use RuntimeException;
36
use function call_user_func;
37
use function explode;
38
use function in_array;
39
use function property_exists;
40
use function str_replace;
41
use function trim;
42
use function ucfirst;
43
44
/**
45
 * Firewall's Control Panel
46
 *
47
 * Display a Control Panel UI for developers or administrators.
48
 */
49
class Panel
50
{
51
    /**
52
     *   Public methods       | Desctiotion
53
     *  ----------------------|---------------------------------------------
54
     *   __call               | Magic method. Let property can run as a method.
55
     *   entry                | Initialize the entry point of the control panel
56
     *  ----------------------|---------------------------------------------
57
     */
58
59
    /**
60
     *   Public methods       | Desctiotion
61
     *  ----------------------|---------------------------------------------
62
     *   demo                 | Start a demo mode. Setting fields are hidden.
63
     *  ----------------------|---------------------------------------------
64
     */
65
    use DemoModeTrait;
66
67
    /**
68
     *   Public methods       | Desctiotion
69
     *  ----------------------|---------------------------------------------
70
     *   csrf                 | Receive the CSRF name and token from the App.
71
     *   setCsrfField         | Set CSRF input fields.
72
     *   fieldCsrf            | Output HTML input element with CSRF token.
73
     *  ----------------------|---------------------------------------------
74
     */
75
    use CsrfTrait;
76
77
    /**
78
     * Route map.
79
     *
80
     * @var array
81
     */
82
    protected $registerRoutes;
83
84
    /**
85
     * The HTTP resolver.
86
     * 
87
     * We need to resolve the HTTP result by ourselves to prevent conficts
88
     * with other frameworks.
89
     *
90
     * @var \Shieldon\Firewall\HttpResolver
91
     */
92
    protected $resolver = null;
93
94
    /**
95
     * Firewall panel constructor.                         
96
     */
97
    public function __construct() 
98
    {
99
        $this->registerRoutes = [
100
            'ajax/changeLocale',
101
            'ajax/tryMessenger',
102
            'circle/filter',
103
            'circle/rule',
104
            'circle/session',
105
            'home/index',
106
            'home/overview',
107
            'iptables/ip4',
108
            'iptables/ip4status',
109
            'iptables/ip6',
110
            'iptables/ip6status',
111
            'report/actionLog',
112
            'report/operation',
113
            'security/authentication',
114
            'security/xssProtection',
115
            'setting/basic',
116
            'setting/exclusion',
117
            'setting/export',
118
            'setting/import',
119
            'setting/ipManager',
120
            'setting/messenger',
121
            'user/login',
122
            'user/logout',
123
            // Render the static asset files for embedding.
124
            // Since 2.0, not link to shieldon-io.github.io anymore.
125
            'asset/css',
126
            'asset/js',
127
            'asset/favicon',
128
            'asset/logo',
129
        ];
130
131
        $this->resolver = new HttpResolver();
132
    }
133
134
    /**
135
     * Display pages.
136
     *
137
     * @return void
138
     */
139
    public function entry(): void
140
    {
141
        $firewall = $this->getFirewallInstance();
142
143
        $response = get_response();
144
145
        /**
146
         * Ex: /firewall/panel/user/login/
147
         *   => firewall/panel/user/login
148
         */
149
        $path = $firewall->getKernel()->getCurrentUrl();
150
        $path = trim($path, '/');
151
152
        /**
153
         * Ex: /firewall/panel/
154
         *   => firewall/panel
155
         */
156
        $base = $firewall->controlPanel();
157
        $base = trim($base, '/');
158
159
        /**
160
         * Ex: /user/login
161
         *   => user/login
162
         */
163
        $urlSegment = str_replace($base, '', $path);
164
        $urlSegment = trim($urlSegment, '/');
165
166
        if ($urlSegment === $base || $urlSegment === '') {
167
            $urlSegment = 'home/index';
168
        }
169
170
        $urlParts = explode('/', $urlSegment);
171
172
        $controller = $urlParts[0] ?? 'home';
173
        $method = $urlParts[1] ?? 'index';
174
175
        if (in_array($controller . '/' . $method, $this->registerRoutes)) {
176
177
            $this->setRouteBase($base);
178
            $this->checkAuth();
179
180
            $controller = __CLASS__ . '\\' . ucfirst($controller);
181
            $controllerClass = new $controller();
182
            $controllerClass->setCsrfField($this->getCsrfField());
183
184
            if ('demo' === $this->mode) {
185
                // For security reasons, the POST method is not allowed 
186
                // in the Demo mode.
187
                set_request(get_request()->withParsedBody([])->withMethod('GET'));
188
                unset_superglobal(null, 'post');
189
190
                $controllerClass->demo(
191
                    $this->demoUser['user'],
192
                    $this->demoUser['pass']
193
                );
194
            }
195
196
            $this->resolver(call_user_func([$controllerClass, $method]));
197
        }
198
199
        $this->resolver($response->withStatus(404));
200
    }
201
202
    /**
203
     * Set the base route for the panel.
204
     *
205
     * @param string $base The base path.
206
     *
207
     * @return void
208
     */
209
    protected function setRouteBase(string $base)
210
    {
211
        if (!defined('SHIELDON_PANEL_BASE')) {
212
            // @codeCoverageIgnoreStart
213
            define('SHIELDON_PANEL_BASE', $base);
214
            // @codeCoverageIgnoreEnd
215
        }
216
    }
217
218
    /**
219
     * Prompt an authorization login.
220
     *
221
     * @return void
222
     */
223
    protected function checkAuth(): void
224
    {
225
        $check = get_session()->get('shieldon_user_login');
226
227
        if (empty($check)) {
228
            $user = new User();
229
            $user->setCsrfField($this->getCsrfField());
230
231
            if ($this->mode === 'demo') {
232
                $user->demo(
233
                    $this->demoUser['user'],
234
                    $this->demoUser['pass']
235
                );
236
            }
237
238
            $this->resolver($user->login());
239
        }
240
    }
241
242
    /**
243
     * Magic method.
244
     * 
245
     * Helps the property `$resolver` to work like a function.
246
     * 
247
     * @param string $method The method name.
248
     * @param array  $args   The arguments.
249
     *
250
     * @return mixed
251
     */
252
    public function __call($method, $args)
253
    {
254
        if (property_exists($this, $method)) {
255
            $callable = $this->{$method};
256
257
            if (isset($args[0]) && $args[0] instanceof ResponseInterface) {
258
                return $callable($args[0]);
259
            }
260
        }
261
        // @codeCoverageIgnoreStart
262
    }
263
    // @codeCoverageIgnoreEnd
264
265
    /**
266
     * Get Firewall instance.
267
     *
268
     * @return Firewall
269
     */
270
    private function getFirewallInstance(): Firewall
271
    {
272
        $firewall = Container::get('firewall');
273
274
        if (!($firewall instanceof Firewall)) {
275
            throw new RuntimeException(
276
                'The Firewall instance should be initialized first.'
277
            );
278
        }
279
280
        return $firewall;
281
    }
282
}
283