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
}

Read our documentation on viewing source code .

Loading