Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 90 |
|
0.00% |
0 / 5 |
CRAP | |
0.00% |
0 / 1 |
AbstractApp | |
0.00% |
0 / 90 |
|
0.00% |
0 / 5 |
110 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 64 |
|
0.00% |
0 / 1 |
20 | |||
configureSlim | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
configureIoc | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
configureView | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
configureRoutes | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
run | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
redirect | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
template | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
configureHeaderMiddleware | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * @section LICENSE |
4 | * This file is part of Wikimedia Slim application library |
5 | * |
6 | * Wikimedia Slim application library is free software: you can |
7 | * redistribute it and/or modify it under the terms of the GNU General Public |
8 | * License as published by the Free Software Foundation, either version 3 of |
9 | * the License, or (at your option) any later version. |
10 | * |
11 | * Wikimedia Slim application library is distributed in the hope that it |
12 | * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty |
13 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License along |
17 | * with Wikimedia Grants Review application. If not, see |
18 | * <http://www.gnu.org/licenses/>. |
19 | * |
20 | * @file |
21 | * @copyright © 2015 Bryan Davis, Wikimedia Foundation and contributors. |
22 | */ |
23 | |
24 | namespace Wikimedia\Slimapp; |
25 | |
26 | use Monolog\Formatter\LogstashFormatter; |
27 | use Monolog\Handler\Udp2logHandler; |
28 | use Monolog\Logger; |
29 | use Monolog\Processor\ProcessIdProcessor; |
30 | use Monolog\Processor\PsrLogMessageProcessor; |
31 | use Monolog\Processor\UidProcessor; |
32 | use Monolog\Processor\WebProcessor; |
33 | use Psr\Log\LogLevel; |
34 | use Slim\Helper\Set; |
35 | use Slim\Slim; |
36 | use Slim\View; |
37 | use Slim\Views\Twig; |
38 | |
39 | /** |
40 | * Grants review application. |
41 | * |
42 | * @author Bryan Davis <bd808@wikimedia.org> |
43 | * @copyright © 2015 Bryan Davis, Wikimedia Foundation and contributors. |
44 | */ |
45 | abstract class AbstractApp { |
46 | |
47 | /** |
48 | * @var string |
49 | */ |
50 | protected $deployDir; |
51 | |
52 | /** |
53 | * @var Slim |
54 | */ |
55 | protected $slim; |
56 | |
57 | /** |
58 | * @param string $deployDir Full path to code deployment |
59 | * @param array $settings Associative array of application settings |
60 | */ |
61 | public function __construct( $deployDir, array $settings = [] ) { |
62 | $this->deployDir = $deployDir; |
63 | |
64 | $this->slim = new Slim( array_merge( |
65 | [ |
66 | 'mode' => 'production', |
67 | 'debug' => false, |
68 | 'log.channel' => Config::getStr( 'LOG_CHANNEL', 'app' ), |
69 | 'log.level' => Config::getStr( |
70 | 'LOG_LEVEL', LogLevel::NOTICE |
71 | ), |
72 | 'log.file' => Config::getStr( 'LOG_FILE', 'php://stderr' ), |
73 | 'view' => new Twig(), |
74 | 'view.cache' => Config::getStr( |
75 | 'CACHE_DIR', "{$this->deployDir}/data/cache" |
76 | ), |
77 | 'templates.path' => Config::getStr( |
78 | 'TEMPLATE_DIR', "{$this->deployDir}/data/templates" |
79 | ), |
80 | 'i18n.path' => Config::getStr( |
81 | 'I18N_DIR', "{$this->deployDir}/data/i18n" |
82 | ), |
83 | 'i18n.default' => Config::getstr( 'DEFAULT_LANG', 'en' ), |
84 | 'smtp.host' => Config::getStr( 'SMTP_HOST', 'localhost' ), |
85 | ], |
86 | $settings |
87 | ) ); |
88 | |
89 | // Slim does not natively understand being behind a proxy. If not |
90 | // corrected template links created via siteUrl() may use the wrong |
91 | // Protocol (http instead of https). |
92 | if ( getenv( 'HTTP_X_FORWARDED_PROTO' ) ) { |
93 | $proto = getenv( 'HTTP_X_FORWARDED_PROTO' ); |
94 | $this->slim->environment['slim.url_scheme'] = $proto; |
95 | |
96 | $port = getenv( 'HTTP_X_FORWARDED_PORT' ); |
97 | if ( $port === false ) { |
98 | $port = ( $proto == 'https' ) ? '443' : '80'; |
99 | } |
100 | $this->slim->environment['SERVER_PORT'] = $port; |
101 | } |
102 | |
103 | $this->configureSlim( $this->slim ); |
104 | |
105 | // Replace default logger with monolog. |
106 | // Done before configureIoc() so subclasses can easily switch it again |
107 | // if desired. |
108 | $this->slim->container->singleton( 'log', static function ( $c ) { |
109 | // Convert string level to Monolog integer value |
110 | $level = strtoupper( $c->settings['log.level'] ); |
111 | $level = constant( "\Monolog\Logger::{$level}" ); |
112 | |
113 | $log = new Logger( $c->settings['log.channel'] ); |
114 | $handler = new Udp2logHandler( |
115 | $c->settings['log.file'], |
116 | $level |
117 | ); |
118 | $handler->setFormatter( new LogstashFormatter( |
119 | $c->settings['log.channel'], null, null, '', |
120 | LogstashFormatter::V1 |
121 | ) ); |
122 | $handler->pushProcessor( |
123 | new PsrLogMessageProcessor() |
124 | ); |
125 | $handler->pushProcessor( |
126 | new ProcessIdProcessor() |
127 | ); |
128 | $handler->pushProcessor( new UidProcessor() ); |
129 | $handler->pushProcessor( new WebProcessor() ); |
130 | $log->pushHandler( $handler ); |
131 | return $log; |
132 | } ); |
133 | |
134 | $this->configureIoc( $this->slim->container ); |
135 | $this->configureView( $this->slim->view ); |
136 | |
137 | $this->slim->add( |
138 | new HeaderMiddleware( $this->configureHeaderMiddleware() ) |
139 | ); |
140 | |
141 | // Add CSRF protection for POST requests |
142 | $this->slim->add( new CsrfMiddleware() ); |
143 | |
144 | $this->configureRoutes( $this->slim ); |
145 | } |
146 | |
147 | /** |
148 | * Apply settings to the Slim application. |
149 | * |
150 | * @param Slim $slim Application |
151 | */ |
152 | abstract protected function configureSlim( Slim $slim ); |
153 | |
154 | /** |
155 | * Configure inversion of control/dependency injection container. |
156 | * |
157 | * @param Set $container IOC container |
158 | */ |
159 | abstract protected function configureIoc( Set $container ); |
160 | |
161 | /** |
162 | * Configure view behavior. |
163 | * |
164 | * @param View $view Default view |
165 | */ |
166 | abstract protected function configureView( View $view ); |
167 | |
168 | /** |
169 | * Configure routes to be handled by application. |
170 | * |
171 | * @param Slim $slim Application |
172 | */ |
173 | abstract protected function configureRoutes( Slim $slim ); |
174 | |
175 | /** |
176 | * Main entry point for all requests. |
177 | */ |
178 | public function run() { |
179 | session_name( '_s' ); |
180 | session_cache_limiter( false ); |
181 | ini_set( 'session.cookie_httponly', true ); |
182 | session_start(); |
183 | register_shutdown_function( 'session_write_close' ); |
184 | $this->slim->run(); |
185 | } |
186 | |
187 | /** |
188 | * Add a redirect route to the app. |
189 | * @param Slim $slim App |
190 | * @param string $name Page name |
191 | * @param string $to Redirect target route name |
192 | * @param string $routeName Name for the route |
193 | */ |
194 | public static function redirect( |
195 | Slim $slim, $name, $to, $routeName = null |
196 | ) { |
197 | $routeName = $routeName ?: $name; |
198 | |
199 | $slim->get( $name, static function () use ( $slim, $to ) { |
200 | $slim->flashKeep(); |
201 | $slim->redirect( $slim->urlFor( $to ) ); |
202 | } )->name( $routeName ); |
203 | } |
204 | |
205 | /** |
206 | * Add a static template route to the app. |
207 | * @param Slim $slim App |
208 | * @param string $name Page name |
209 | * @param string $routeName Name for the route |
210 | */ |
211 | public static function template( |
212 | Slim $slim, $name, $routeName = null |
213 | ) { |
214 | $routeName = $routeName ?: $name; |
215 | |
216 | $slim->get( $name, static function () use ( $slim, $name ) { |
217 | $slim->render( "{$name}.html" ); |
218 | } )->name( $routeName ); |
219 | } |
220 | |
221 | /** |
222 | * Configure the default HeaderMiddleware installed for all routes. |
223 | * |
224 | * Default configuration adds these headers: |
225 | * - "Vary: Cookie" to help upstream caches to the right thing |
226 | * - "X-Frame-Options: DENY" |
227 | * - A fairly strict 'self' only Content-Security-Policy to help protect |
228 | * against XSS attacks |
229 | * - "Content-Type: text/html; charset=UTF-8" |
230 | * |
231 | * @return array |
232 | */ |
233 | protected function configureHeaderMiddleware() { |
234 | // Add headers to all responses: |
235 | return [ |
236 | 'Vary' => 'Cookie', |
237 | 'X-Frame-Options' => 'DENY', |
238 | 'Content-Security-Policy' => |
239 | "default-src 'self'; " . |
240 | "frame-src 'none'; " . |
241 | "object-src 'none'; " . |
242 | // Needed for css data:... sprites |
243 | "img-src 'self' data:; " . |
244 | // Needed for jQuery and Modernizr feature detection |
245 | "style-src 'self' 'unsafe-inline'", |
246 | // Don't forget to override this for any content that is not |
247 | // actually HTML (e.g. json) |
248 | 'Content-Type' => 'text/html; charset=UTF-8', |
249 | ]; |
250 | } |
251 | } |