Passed
Push — 2.x ( fd069b...31976b )
by Terry
02:06
created

Panel::checkAuth()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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