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