Completed
Push — feature/6.x ( 5f23eb...e064bf )
by Schlaefer
03:44
created

Application   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 202
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 1
Metric Value
eloc 74
c 5
b 0
f 1
dl 0
loc 202
rs 10
wmc 15

7 Methods

Rating   Name   Duplication   Size   Complexity  
A middleware() 0 60 3
A bootstrapCli() 0 9 2
A loadDefaultThemePlugin() 0 11 3
A getAuthenticationService() 0 7 2
A isApiRoute() 0 5 1
A bootstrap() 0 45 3
A __construct() 0 7 1
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
6
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
7
 *
8
 * Licensed under The MIT License
9
 * For full copyright and license information, please see the LICENSE.txt
10
 * Redistributions of files must retain the above copyright notice.
11
 *
12
 * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
13
 * @link      https://cakephp.org CakePHP(tm) Project
14
 * @since     3.3.0
15
 * @license   https://opensource.org/licenses/mit-license.php MIT License
16
 */
17
18
namespace App;
19
20
use App\Auth\AuthenticationServiceFactory;
21
use App\Middleware\SaitoBootstrapMiddleware;
22
use Authentication\AuthenticationServiceInterface;
23
use Authentication\AuthenticationServiceProviderInterface;
24
use Authentication\Middleware\AuthenticationMiddleware;
25
use Authentication\UrlChecker\DefaultUrlChecker;
26
use Cake\Core\Configure;
27
use Cake\Core\Exception\MissingPluginException;
28
use Cake\Core\Plugin;
29
use Cake\Error\Middleware\ErrorHandlerMiddleware;
30
use Cake\Event\EventManagerInterface;
31
use Cake\Http\BaseApplication;
32
use Cake\Http\Middleware\BodyParserMiddleware;
33
use Cake\Http\Middleware\CsrfProtectionMiddleware;
34
use Cake\Http\Middleware\EncryptedCookieMiddleware;
35
use Cake\Http\Middleware\SecurityHeadersMiddleware;
36
use Cake\Http\ServerRequest;
37
use Cake\Routing\Middleware\AssetMiddleware;
38
use Cake\Routing\Middleware\RoutingMiddleware;
39
use Psr\Http\Message\ServerRequestInterface;
40
use Saito\App\Registry;
41
use Saito\RememberTrait;
42
use Stopwatch\Lib\Stopwatch;
43
44
/**
45
 * Application setup class.
46
 *
47
 * This defines the bootstrapping logic and middleware layers you
48
 * want to use in your application.
49
 */
50
class Application extends BaseApplication implements AuthenticationServiceProviderInterface
51
{
52
    use RememberTrait;
53
54
    /**
55
     * {@inheritDoc}
56
     */
57
    public function __construct($configDir, ?EventManagerInterface $eventManager = null)
58
    {
59
        Stopwatch::init();
60
        Stopwatch::enable();
61
        Stopwatch::start('Application::__construct');
62
        parent::__construct($configDir, $eventManager);
63
        Stopwatch::stop('Application::__construct');
64
    }
65
66
    /**
67
     * Load all the application configuration and bootstrap logic.
68
     *
69
     * @return void
70
     */
71
    public function bootstrap(): void
72
    {
73
        Stopwatch::start('Application::bootstrap');
74
75
        parent::bootstrap();
76
77
        if (PHP_SAPI === 'cli') {
78
            $this->bootstrapCli();
79
        }
80
        /*
81
         * Only try to load DebugKit in development mode
82
         * Debug Kit should not be installed on a production system
83
         */
84
        if (Configure::read('debug')) {
85
            // $this->addPlugin('DebugKit');
86
        }
87
        // Load more plugins here
88
89
        Registry::initialize();
90
91
        $this->addPlugin('Authentication');
92
        $this->addPlugin(\Admin\Plugin::class, ['routes' => true]);
93
        $this->addPlugin(\Api\Plugin::class, ['bootstrap' => true, 'routes' => true]);
94
        $this->addPlugin(\Bookmarks\Plugin::class, ['routes' => true]);
95
        $this->addPlugin(\BbcodeParser\Plugin::class);
96
        $this->addPlugin(\Feeds\Plugin::class, ['routes' => true]);
97
        $this->addPlugin(\Installer\Plugin::class);
98
        $this->addPlugin(\SaitoHelp\Plugin::class, ['routes' => true]);
99
        $this->addPlugin(\SaitoSearch\Plugin::class, ['routes' => true]);
100
        $this->addPlugin(\Sitemap\Plugin::class, ['bootstrap' => true, 'routes' => true]);
101
        $this->addPlugin(\ImageUploader\Plugin::class, ['routes' => true]);
102
103
        $this->addPlugin(\Cron\Plugin::class);
104
        $this->addPlugin(\Commonmark\Plugin::class);
105
        $this->addPlugin(\Detectors\Plugin::class);
106
        $this->addPlugin(\MailObfuscator\Plugin::class);
107
        $this->addPlugin(\SpectrumColorpicker\Plugin::class);
108
        $this->addPlugin(\Stopwatch\Plugin::class);
109
110
        // TODO
111
        // $this->addPlugin('Proffer');
112
113
        $this->loadDefaultThemePlugin();
114
115
        Stopwatch::stop('Application::bootstrap');
116
    }
117
118
    /**
119
     * Setup the middleware queue your application will use.
120
     *
121
     * @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to setup.
122
     * @return \Cake\Http\MiddlewareQueue The updated middleware queue.
123
     */
124
    public function middleware($middlewareQueue): \Cake\Http\MiddlewareQueue
125
    {
126
        $middlewareQueue
127
            // Catch any exceptions in the lower layers,
128
            // and make an error page/response
129
            ->add(new ErrorHandlerMiddleware(Configure::read('Error')))
130
131
            // Handle plugin/theme assets like CakePHP normally does.
132
            ->add(new AssetMiddleware([
133
                'cacheTime' => Configure::read('Asset.cacheTime'),
134
            ]))
135
136
            // Add routing middleware.
137
            // If you have a large number of routes connected, turning on routes
138
            // caching in production could improve performance. For that when
139
            // creating the middleware instance specify the cache config name by
140
            // using it's second constructor argument:
141
            // `new RoutingMiddleware($this, '_cake_routes_')`
142
            ->add(new RoutingMiddleware($this, '_cake_routes_'))
143
144
            // Parse various types of encoded request bodies so that they are
145
            // available as array through $request->getData()
146
            // https://book.cakephp.org/4/en/controllers/middleware.html#body-parser-middleware
147
            ->add(new BodyParserMiddleware())
148
149
            ->insertAfter(RoutingMiddleware::class, new SaitoBootstrapMiddleware())
150
151
            ->add(new EncryptedCookieMiddleware(
152
                // Names of cookies to protect
153
                [Configure::read('Security.cookieAuthName')],
154
                Configure::read('Security.cookieSalt')
155
            ))
156
157
            // CakePHP authentication provider
158
            ->insertAfter(
159
                EncryptedCookieMiddleware::class,
160
                new AuthenticationMiddleware($this)
161
            );
162
163
        /// CSRF
164
        $csrf = new CsrfProtectionMiddleware([
165
            'expiry' => time() + 10800,
166
            'cookieName' => PHP_SAPI !== 'cli'
167
                // Nice looking Saito-CSRF cookie name
168
                ? Configure::read('Session.cookie') . '-CSRF'
169
                // The security mock in testing doesn't allow seeting
170
                // a different cookie-name.
171
                : 'csrfToken',
172
        ]);
173
        $csrf->whitelistCallback(function (ServerRequest $request) {
174
            return $this->isApiRoute($request) || $request->getParam('plugin') === 'Installer';
175
        });
176
        $middlewareQueue->insertAfter(EncryptedCookieMiddleware::class, $csrf);
177
178
        /// Security Header X-Frame
179
        $security = (new SecurityHeadersMiddleware())
180
            ->setXFrameOptions(strtolower(Configure::read('Saito.X-Frame-Options')));
181
        $middlewareQueue->add($security);
182
183
        return $middlewareQueue;
184
    }
185
186
    /**
187
     * Get authentication service.
188
     *
189
     * Part of AuthenticationServiceProviderInterface.
190
     *
191
     * {@inheritDoc}
192
     */
193
    public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
194
    {
195
        if ($this->isApiRoute($request)) {
196
            return AuthenticationServiceFactory::buildJwt();
197
        }
198
199
        return AuthenticationServiceFactory::buildApp();
200
    }
201
202
    /**
203
     * Load the plugin for Saito's default theme
204
     *
205
     * @return void
206
     */
207
    private function loadDefaultThemePlugin()
208
    {
209
        $defaultTheme = Configure::read('Saito.themes.default');
210
        if (empty($defaultTheme)) {
211
            throw new \RuntimeException(
212
                'Could not resolve default theme for plugin loading.',
213
                1556562215
214
            );
215
        }
216
        if (Plugin::isLoaded($defaultTheme) !== true) {
217
            $this->addPlugin($defaultTheme);
218
        }
219
    }
220
221
    /**
222
     * Bootrapping for CLI application.
223
     *
224
     * That is when running commands.
225
     *
226
     * @return void
227
     */
228
    protected function bootstrapCli(): void
229
    {
230
        try {
231
            $this->addPlugin('Bake');
232
        } catch (MissingPluginException $e) {
233
            // Do not halt if the plugin is missing
234
        }
235
236
        $this->addPlugin('Migrations');
237
238
        // Load more plugins here
239
    }
240
241
    /**
242
     * Check if current request is on an API route
243
     *
244
     * @param \Psr\Http\Message\ServerRequestInterface $request Current request
245
     * @return bool
246
     */
247
    protected function isApiRoute(ServerRequestInterface $request): bool
248
    {
249
        return $this->remember('isApiRoute', function () use ($request) {
250
            return (new DefaultUrlChecker())
251
                ->check($request, ['#api/v2#'], ['useRegex' => true]);
252
        });
253
    }
254
}
255