Failed Conditions
Push — newinternal-releasecandidate ( 327c61...a30d14 )
by Simon
15:28 queued 05:26
created
includes/Router/RequestRouter.php 1 patch
Indentation   +441 added lines, -441 removed lines patch added patch discarded remove patch
@@ -63,445 +63,445 @@
 block discarded – undo
63 63
  */
64 64
 class RequestRouter implements IRequestRouter
65 65
 {
66
-    /**
67
-     * This is the core routing table for the application. The basic idea is:
68
-     *
69
-     *      array(
70
-     *          "foo" =>
71
-     *              array(
72
-     *                  "class"   => PageFoo::class,
73
-     *                  "actions" => array("bar", "other")
74
-     *              ),
75
-     * );
76
-     *
77
-     * Things to note:
78
-     *     - If no page is requested, we go to PageMain. PageMain can't have actions defined.
79
-     *
80
-     *     - If a page is defined and requested, but no action is requested, go to that page's main() method
81
-     *     - If a page is defined and requested, and an action is defined and requested, go to that action's method.
82
-     *     - If a page is defined and requested, and an action NOT defined and requested, go to Page404 and it's main()
83
-     *       method.
84
-     *     - If a page is NOT defined and requested, go to Page404 and it's main() method.
85
-     *
86
-     *     - Query parameters are ignored.
87
-     *
88
-     * The key point here is request routing with validation that this is allowed, before we start hitting the
89
-     * filesystem through the AutoLoader, and opening random files. Also, so that we validate the action requested
90
-     * before we start calling random methods through the web UI.
91
-     *
92
-     * Examples:
93
-     * /internal.php                => returns instance of PageMain, routed to main()
94
-     * /internal.php?query          => returns instance of PageMain, routed to main()
95
-     * /internal.php/foo            => returns instance of PageFoo, routed to main()
96
-     * /internal.php/foo?query      => returns instance of PageFoo, routed to main()
97
-     * /internal.php/foo/bar        => returns instance of PageFoo, routed to bar()
98
-     * /internal.php/foo/bar?query  => returns instance of PageFoo, routed to bar()
99
-     * /internal.php/foo/baz        => returns instance of Page404, routed to main()
100
-     * /internal.php/foo/baz?query  => returns instance of Page404, routed to main()
101
-     * /internal.php/bar            => returns instance of Page404, routed to main()
102
-     * /internal.php/bar?query      => returns instance of Page404, routed to main()
103
-     * /internal.php/bar/baz        => returns instance of Page404, routed to main()
104
-     * /internal.php/bar/baz?query  => returns instance of Page404, routed to main()
105
-     *
106
-     * Take care when changing this - a lot of places rely on the array key for redirects and other links. If you need
107
-     * to change the key, then you'll likely have to update a lot of files.
108
-     *
109
-     * @var array
110
-     */
111
-    private $routeMap = array(
112
-
113
-        //////////////////////////////////////////////////////////////////////////////////////////////////
114
-        // Login and registration
115
-        'logout'                      =>
116
-            array(
117
-                'class'   => PageLogout::class,
118
-                'actions' => array(),
119
-            ),
120
-        'login'                       =>
121
-            array(
122
-                'class'   => PagePasswordLogin::class,
123
-                'actions' => array(),
124
-            ),
125
-        'login/otp'                   =>
126
-            array(
127
-                'class'   => PageOtpLogin::class,
128
-                'actions' => array(),
129
-            ),
130
-        'login/u2f'                   =>
131
-            array(
132
-                'class'   => PageU2FLogin::class,
133
-                'actions' => array(),
134
-            ),
135
-        'forgotPassword'              =>
136
-            array(
137
-                'class'   => PageForgotPassword::class,
138
-                'actions' => array('reset'),
139
-            ),
140
-        'register'                    =>
141
-            array(
142
-                'class'   => PageRegisterOption::class,
143
-                'actions' => array(),
144
-            ),
145
-        'register/standard'           =>
146
-            array(
147
-                'class'   => PageRegisterStandard::class,
148
-                'actions' => array('done'),
149
-            ),
150
-
151
-        //////////////////////////////////////////////////////////////////////////////////////////////////
152
-        // Discovery
153
-        'search'                      =>
154
-            array(
155
-                'class'   => PageSearch::class,
156
-                'actions' => array(),
157
-            ),
158
-        'logs'                        =>
159
-            array(
160
-                'class'   => PageLog::class,
161
-                'actions' => array(),
162
-            ),
163
-
164
-        //////////////////////////////////////////////////////////////////////////////////////////////////
165
-        // Administration
166
-        'bans'                        =>
167
-            array(
168
-                'class'   => PageBan::class,
169
-                'actions' => array('set', 'remove'),
170
-            ),
171
-        'userManagement'              =>
172
-            array(
173
-                'class'   => PageUserManagement::class,
174
-                'actions' => array(
175
-                    'approve',
176
-                    'decline',
177
-                    'rename',
178
-                    'editUser',
179
-                    'suspend',
180
-                    'editRoles',
181
-                ),
182
-            ),
183
-        'siteNotice'                  =>
184
-            array(
185
-                'class'   => PageSiteNotice::class,
186
-                'actions' => array(),
187
-            ),
188
-        'emailManagement'             =>
189
-            array(
190
-                'class'   => PageEmailManagement::class,
191
-                'actions' => array('create', 'edit', 'view'),
192
-            ),
193
-        'jobQueue'                    =>
194
-            array(
195
-                'class'   => PageJobQueue::class,
196
-                'actions' => array('acknowledge', 'requeue', 'view', 'all'),
197
-            ),
198
-
199
-        //////////////////////////////////////////////////////////////////////////////////////////////////
200
-        // Personal preferences
201
-        'preferences'                 =>
202
-            array(
203
-                'class'   => PagePreferences::class,
204
-                'actions' => array(
205
-                    'refreshOAuth'
206
-                ),
207
-            ),
208
-        'changePassword'              =>
209
-            array(
210
-                'class'   => PageChangePassword::class,
211
-                'actions' => array(),
212
-            ),
213
-        'multiFactor'                 =>
214
-            array(
215
-                'class'   => PageMultiFactor::class,
216
-                'actions' => array(
217
-                    'scratch',
218
-                    'enableYubikeyOtp',
219
-                    'disableYubikeyOtp',
220
-                    'enableTotp',
221
-                    'disableTotp',
222
-                    'enableU2F',
223
-                    'disableU2F',
224
-                ),
225
-            ),
226
-        'oauth'                       =>
227
-            array(
228
-                'class'   => PageOAuth::class,
229
-                'actions' => array('detach', 'attach'),
230
-            ),
231
-        'oauth/callback'              =>
232
-            array(
233
-                'class'   => PageOAuthCallback::class,
234
-                'actions' => array('authorise', 'create'),
235
-            ),
236
-
237
-        //////////////////////////////////////////////////////////////////////////////////////////////////
238
-        // Welcomer configuration
239
-        'welcomeTemplates'            =>
240
-            array(
241
-                'class'   => PageWelcomeTemplateManagement::class,
242
-                'actions' => array('select', 'edit', 'delete', 'add', 'view'),
243
-            ),
244
-
245
-        //////////////////////////////////////////////////////////////////////////////////////////////////
246
-        // Statistics
247
-        'statistics'                  =>
248
-            array(
249
-                'class'   => StatsMain::class,
250
-                'actions' => array(),
251
-            ),
252
-        'statistics/fastCloses'       =>
253
-            array(
254
-                'class'   => StatsFastCloses::class,
255
-                'actions' => array(),
256
-            ),
257
-        'statistics/inactiveUsers'    =>
258
-            array(
259
-                'class'   => StatsInactiveUsers::class,
260
-                'actions' => array(),
261
-            ),
262
-        'statistics/monthlyStats'     =>
263
-            array(
264
-                'class'   => StatsMonthlyStats::class,
265
-                'actions' => array(),
266
-            ),
267
-        'statistics/reservedRequests' =>
268
-            array(
269
-                'class'   => StatsReservedRequests::class,
270
-                'actions' => array(),
271
-            ),
272
-        'statistics/templateStats'    =>
273
-            array(
274
-                'class'   => StatsTemplateStats::class,
275
-                'actions' => array(),
276
-            ),
277
-        'statistics/topCreators'      =>
278
-            array(
279
-                'class'   => StatsTopCreators::class,
280
-                'actions' => array(),
281
-            ),
282
-        'statistics/users'            =>
283
-            array(
284
-                'class'   => StatsUsers::class,
285
-                'actions' => array('detail'),
286
-            ),
287
-
288
-        //////////////////////////////////////////////////////////////////////////////////////////////////
289
-        // Zoom page
290
-        'viewRequest'                 =>
291
-            array(
292
-                'class'   => PageViewRequest::class,
293
-                'actions' => array(),
294
-            ),
295
-        'viewRequest/reserve'         =>
296
-            array(
297
-                'class'   => PageReservation::class,
298
-                'actions' => array(),
299
-            ),
300
-        'viewRequest/breakReserve'    =>
301
-            array(
302
-                'class'   => PageBreakReservation::class,
303
-                'actions' => array(),
304
-            ),
305
-        'viewRequest/defer'           =>
306
-            array(
307
-                'class'   => PageDeferRequest::class,
308
-                'actions' => array(),
309
-            ),
310
-        'viewRequest/comment'         =>
311
-            array(
312
-                'class'   => PageComment::class,
313
-                'actions' => array(),
314
-            ),
315
-        'viewRequest/sendToUser'      =>
316
-            array(
317
-                'class'   => PageSendToUser::class,
318
-                'actions' => array(),
319
-            ),
320
-        'viewRequest/close'           =>
321
-            array(
322
-                'class'   => PageCloseRequest::class,
323
-                'actions' => array(),
324
-            ),
325
-        'viewRequest/create'          =>
326
-            array(
327
-                'class'   => PageCreateRequest::class,
328
-                'actions' => array(),
329
-            ),
330
-        'viewRequest/drop'            =>
331
-            array(
332
-                'class'   => PageDropRequest::class,
333
-                'actions' => array(),
334
-            ),
335
-        'viewRequest/custom'          =>
336
-            array(
337
-                'class'   => PageCustomClose::class,
338
-                'actions' => array(),
339
-            ),
340
-        'editComment'                 =>
341
-            array(
342
-                'class'   => PageEditComment::class,
343
-                'actions' => array(),
344
-            ),
345
-
346
-        //////////////////////////////////////////////////////////////////////////////////////////////////
347
-        // Misc stuff
348
-        'team'                        =>
349
-            array(
350
-                'class'   => PageTeam::class,
351
-                'actions' => array(),
352
-            ),
353
-        'requestList'                 =>
354
-            array(
355
-                'class'   => PageExpandedRequestList::class,
356
-                'actions' => array(),
357
-            ),
358
-        'xffdemo'                     =>
359
-            array(
360
-                'class'   => PageXffDemo::class,
361
-                'actions' => array(),
362
-            ),
363
-    );
364
-
365
-    /**
366
-     * @return IRoutedTask
367
-     * @throws Exception
368
-     */
369
-    final public function route()
370
-    {
371
-        $pathInfo = WebRequest::pathInfo();
372
-
373
-        list($pageClass, $action) = $this->getRouteFromPath($pathInfo);
374
-
375
-        /** @var IRoutedTask $page */
376
-        $page = new $pageClass();
377
-
378
-        // Dynamic creation, so we've got to be careful here. We can't use built-in language type protection, so
379
-        // let's use our own.
380
-        if (!($page instanceof IRoutedTask)) {
381
-            throw new Exception('Expected a page, but this is not a page.');
382
-        }
383
-
384
-        // OK, I'm happy at this point that we know we're running a page, and we know it's probably what we want if it
385
-        // inherits PageBase and has been created from the routing map.
386
-        $page->setRoute($action);
387
-
388
-        return $page;
389
-    }
390
-
391
-    /**
392
-     * @param $pathInfo
393
-     *
394
-     * @return array
395
-     */
396
-    protected function getRouteFromPath($pathInfo)
397
-    {
398
-        if (count($pathInfo) === 0) {
399
-            // No pathInfo, so no page to load. Load the main page.
400
-            return $this->getDefaultRoute();
401
-        }
402
-        elseif (count($pathInfo) === 1) {
403
-            // Exactly one path info segment, it's got to be a page.
404
-            $classSegment = $pathInfo[0];
405
-
406
-            return $this->routeSinglePathSegment($classSegment);
407
-        }
408
-
409
-        // OK, we have two or more segments now.
410
-        if (count($pathInfo) > 2) {
411
-            // Let's handle more than two, and collapse it down into two.
412
-            $requestedAction = array_pop($pathInfo);
413
-            $classSegment = implode('/', $pathInfo);
414
-        }
415
-        else {
416
-            // Two path info segments.
417
-            $classSegment = $pathInfo[0];
418
-            $requestedAction = $pathInfo[1];
419
-        }
420
-
421
-        $routeMap = $this->routePathSegments($classSegment, $requestedAction);
422
-
423
-        if ($routeMap[0] === Page404::class) {
424
-            $routeMap = $this->routeSinglePathSegment($classSegment . '/' . $requestedAction);
425
-        }
426
-
427
-        return $routeMap;
428
-    }
429
-
430
-    /**
431
-     * @param $classSegment
432
-     *
433
-     * @return array
434
-     */
435
-    final protected function routeSinglePathSegment($classSegment)
436
-    {
437
-        $routeMap = $this->getRouteMap();
438
-        if (array_key_exists($classSegment, $routeMap)) {
439
-            // Route exists, but we don't have an action in path info, so default to main.
440
-            $pageClass = $routeMap[$classSegment]['class'];
441
-            $action = 'main';
442
-
443
-            return array($pageClass, $action);
444
-        }
445
-        else {
446
-            // Doesn't exist in map. Fall back to 404
447
-            $pageClass = Page404::class;
448
-            $action = "main";
449
-
450
-            return array($pageClass, $action);
451
-        }
452
-    }
453
-
454
-    /**
455
-     * @param $classSegment
456
-     * @param $requestedAction
457
-     *
458
-     * @return array
459
-     */
460
-    final protected function routePathSegments($classSegment, $requestedAction)
461
-    {
462
-        $routeMap = $this->getRouteMap();
463
-        if (array_key_exists($classSegment, $routeMap)) {
464
-            // Route exists, but we don't have an action in path info, so default to main.
465
-
466
-            if (isset($routeMap[$classSegment]['actions'])
467
-                && array_search($requestedAction, $routeMap[$classSegment]['actions']) !== false
468
-            ) {
469
-                // Action exists in allowed action list. Allow both the page and the action
470
-                $pageClass = $routeMap[$classSegment]['class'];
471
-                $action = $requestedAction;
472
-
473
-                return array($pageClass, $action);
474
-            }
475
-            else {
476
-                // Valid page, invalid action. 404 our way out.
477
-                $pageClass = Page404::class;
478
-                $action = 'main';
479
-
480
-                return array($pageClass, $action);
481
-            }
482
-        }
483
-        else {
484
-            // Class doesn't exist in map. Fall back to 404
485
-            $pageClass = Page404::class;
486
-            $action = 'main';
487
-
488
-            return array($pageClass, $action);
489
-        }
490
-    }
491
-
492
-    /**
493
-     * @return array
494
-     */
495
-    protected function getRouteMap()
496
-    {
497
-        return $this->routeMap;
498
-    }
499
-
500
-    /**
501
-     * @return callable
502
-     */
503
-    protected function getDefaultRoute()
504
-    {
505
-        return array(PageMain::class, "main");
506
-    }
66
+	/**
67
+	 * This is the core routing table for the application. The basic idea is:
68
+	 *
69
+	 *      array(
70
+	 *          "foo" =>
71
+	 *              array(
72
+	 *                  "class"   => PageFoo::class,
73
+	 *                  "actions" => array("bar", "other")
74
+	 *              ),
75
+	 * );
76
+	 *
77
+	 * Things to note:
78
+	 *     - If no page is requested, we go to PageMain. PageMain can't have actions defined.
79
+	 *
80
+	 *     - If a page is defined and requested, but no action is requested, go to that page's main() method
81
+	 *     - If a page is defined and requested, and an action is defined and requested, go to that action's method.
82
+	 *     - If a page is defined and requested, and an action NOT defined and requested, go to Page404 and it's main()
83
+	 *       method.
84
+	 *     - If a page is NOT defined and requested, go to Page404 and it's main() method.
85
+	 *
86
+	 *     - Query parameters are ignored.
87
+	 *
88
+	 * The key point here is request routing with validation that this is allowed, before we start hitting the
89
+	 * filesystem through the AutoLoader, and opening random files. Also, so that we validate the action requested
90
+	 * before we start calling random methods through the web UI.
91
+	 *
92
+	 * Examples:
93
+	 * /internal.php                => returns instance of PageMain, routed to main()
94
+	 * /internal.php?query          => returns instance of PageMain, routed to main()
95
+	 * /internal.php/foo            => returns instance of PageFoo, routed to main()
96
+	 * /internal.php/foo?query      => returns instance of PageFoo, routed to main()
97
+	 * /internal.php/foo/bar        => returns instance of PageFoo, routed to bar()
98
+	 * /internal.php/foo/bar?query  => returns instance of PageFoo, routed to bar()
99
+	 * /internal.php/foo/baz        => returns instance of Page404, routed to main()
100
+	 * /internal.php/foo/baz?query  => returns instance of Page404, routed to main()
101
+	 * /internal.php/bar            => returns instance of Page404, routed to main()
102
+	 * /internal.php/bar?query      => returns instance of Page404, routed to main()
103
+	 * /internal.php/bar/baz        => returns instance of Page404, routed to main()
104
+	 * /internal.php/bar/baz?query  => returns instance of Page404, routed to main()
105
+	 *
106
+	 * Take care when changing this - a lot of places rely on the array key for redirects and other links. If you need
107
+	 * to change the key, then you'll likely have to update a lot of files.
108
+	 *
109
+	 * @var array
110
+	 */
111
+	private $routeMap = array(
112
+
113
+		//////////////////////////////////////////////////////////////////////////////////////////////////
114
+		// Login and registration
115
+		'logout'                      =>
116
+			array(
117
+				'class'   => PageLogout::class,
118
+				'actions' => array(),
119
+			),
120
+		'login'                       =>
121
+			array(
122
+				'class'   => PagePasswordLogin::class,
123
+				'actions' => array(),
124
+			),
125
+		'login/otp'                   =>
126
+			array(
127
+				'class'   => PageOtpLogin::class,
128
+				'actions' => array(),
129
+			),
130
+		'login/u2f'                   =>
131
+			array(
132
+				'class'   => PageU2FLogin::class,
133
+				'actions' => array(),
134
+			),
135
+		'forgotPassword'              =>
136
+			array(
137
+				'class'   => PageForgotPassword::class,
138
+				'actions' => array('reset'),
139
+			),
140
+		'register'                    =>
141
+			array(
142
+				'class'   => PageRegisterOption::class,
143
+				'actions' => array(),
144
+			),
145
+		'register/standard'           =>
146
+			array(
147
+				'class'   => PageRegisterStandard::class,
148
+				'actions' => array('done'),
149
+			),
150
+
151
+		//////////////////////////////////////////////////////////////////////////////////////////////////
152
+		// Discovery
153
+		'search'                      =>
154
+			array(
155
+				'class'   => PageSearch::class,
156
+				'actions' => array(),
157
+			),
158
+		'logs'                        =>
159
+			array(
160
+				'class'   => PageLog::class,
161
+				'actions' => array(),
162
+			),
163
+
164
+		//////////////////////////////////////////////////////////////////////////////////////////////////
165
+		// Administration
166
+		'bans'                        =>
167
+			array(
168
+				'class'   => PageBan::class,
169
+				'actions' => array('set', 'remove'),
170
+			),
171
+		'userManagement'              =>
172
+			array(
173
+				'class'   => PageUserManagement::class,
174
+				'actions' => array(
175
+					'approve',
176
+					'decline',
177
+					'rename',
178
+					'editUser',
179
+					'suspend',
180
+					'editRoles',
181
+				),
182
+			),
183
+		'siteNotice'                  =>
184
+			array(
185
+				'class'   => PageSiteNotice::class,
186
+				'actions' => array(),
187
+			),
188
+		'emailManagement'             =>
189
+			array(
190
+				'class'   => PageEmailManagement::class,
191
+				'actions' => array('create', 'edit', 'view'),
192
+			),
193
+		'jobQueue'                    =>
194
+			array(
195
+				'class'   => PageJobQueue::class,
196
+				'actions' => array('acknowledge', 'requeue', 'view', 'all'),
197
+			),
198
+
199
+		//////////////////////////////////////////////////////////////////////////////////////////////////
200
+		// Personal preferences
201
+		'preferences'                 =>
202
+			array(
203
+				'class'   => PagePreferences::class,
204
+				'actions' => array(
205
+					'refreshOAuth'
206
+				),
207
+			),
208
+		'changePassword'              =>
209
+			array(
210
+				'class'   => PageChangePassword::class,
211
+				'actions' => array(),
212
+			),
213
+		'multiFactor'                 =>
214
+			array(
215
+				'class'   => PageMultiFactor::class,
216
+				'actions' => array(
217
+					'scratch',
218
+					'enableYubikeyOtp',
219
+					'disableYubikeyOtp',
220
+					'enableTotp',
221
+					'disableTotp',
222
+					'enableU2F',
223
+					'disableU2F',
224
+				),
225
+			),
226
+		'oauth'                       =>
227
+			array(
228
+				'class'   => PageOAuth::class,
229
+				'actions' => array('detach', 'attach'),
230
+			),
231
+		'oauth/callback'              =>
232
+			array(
233
+				'class'   => PageOAuthCallback::class,
234
+				'actions' => array('authorise', 'create'),
235
+			),
236
+
237
+		//////////////////////////////////////////////////////////////////////////////////////////////////
238
+		// Welcomer configuration
239
+		'welcomeTemplates'            =>
240
+			array(
241
+				'class'   => PageWelcomeTemplateManagement::class,
242
+				'actions' => array('select', 'edit', 'delete', 'add', 'view'),
243
+			),
244
+
245
+		//////////////////////////////////////////////////////////////////////////////////////////////////
246
+		// Statistics
247
+		'statistics'                  =>
248
+			array(
249
+				'class'   => StatsMain::class,
250
+				'actions' => array(),
251
+			),
252
+		'statistics/fastCloses'       =>
253
+			array(
254
+				'class'   => StatsFastCloses::class,
255
+				'actions' => array(),
256
+			),
257
+		'statistics/inactiveUsers'    =>
258
+			array(
259
+				'class'   => StatsInactiveUsers::class,
260
+				'actions' => array(),
261
+			),
262
+		'statistics/monthlyStats'     =>
263
+			array(
264
+				'class'   => StatsMonthlyStats::class,
265
+				'actions' => array(),
266
+			),
267
+		'statistics/reservedRequests' =>
268
+			array(
269
+				'class'   => StatsReservedRequests::class,
270
+				'actions' => array(),
271
+			),
272
+		'statistics/templateStats'    =>
273
+			array(
274
+				'class'   => StatsTemplateStats::class,
275
+				'actions' => array(),
276
+			),
277
+		'statistics/topCreators'      =>
278
+			array(
279
+				'class'   => StatsTopCreators::class,
280
+				'actions' => array(),
281
+			),
282
+		'statistics/users'            =>
283
+			array(
284
+				'class'   => StatsUsers::class,
285
+				'actions' => array('detail'),
286
+			),
287
+
288
+		//////////////////////////////////////////////////////////////////////////////////////////////////
289
+		// Zoom page
290
+		'viewRequest'                 =>
291
+			array(
292
+				'class'   => PageViewRequest::class,
293
+				'actions' => array(),
294
+			),
295
+		'viewRequest/reserve'         =>
296
+			array(
297
+				'class'   => PageReservation::class,
298
+				'actions' => array(),
299
+			),
300
+		'viewRequest/breakReserve'    =>
301
+			array(
302
+				'class'   => PageBreakReservation::class,
303
+				'actions' => array(),
304
+			),
305
+		'viewRequest/defer'           =>
306
+			array(
307
+				'class'   => PageDeferRequest::class,
308
+				'actions' => array(),
309
+			),
310
+		'viewRequest/comment'         =>
311
+			array(
312
+				'class'   => PageComment::class,
313
+				'actions' => array(),
314
+			),
315
+		'viewRequest/sendToUser'      =>
316
+			array(
317
+				'class'   => PageSendToUser::class,
318
+				'actions' => array(),
319
+			),
320
+		'viewRequest/close'           =>
321
+			array(
322
+				'class'   => PageCloseRequest::class,
323
+				'actions' => array(),
324
+			),
325
+		'viewRequest/create'          =>
326
+			array(
327
+				'class'   => PageCreateRequest::class,
328
+				'actions' => array(),
329
+			),
330
+		'viewRequest/drop'            =>
331
+			array(
332
+				'class'   => PageDropRequest::class,
333
+				'actions' => array(),
334
+			),
335
+		'viewRequest/custom'          =>
336
+			array(
337
+				'class'   => PageCustomClose::class,
338
+				'actions' => array(),
339
+			),
340
+		'editComment'                 =>
341
+			array(
342
+				'class'   => PageEditComment::class,
343
+				'actions' => array(),
344
+			),
345
+
346
+		//////////////////////////////////////////////////////////////////////////////////////////////////
347
+		// Misc stuff
348
+		'team'                        =>
349
+			array(
350
+				'class'   => PageTeam::class,
351
+				'actions' => array(),
352
+			),
353
+		'requestList'                 =>
354
+			array(
355
+				'class'   => PageExpandedRequestList::class,
356
+				'actions' => array(),
357
+			),
358
+		'xffdemo'                     =>
359
+			array(
360
+				'class'   => PageXffDemo::class,
361
+				'actions' => array(),
362
+			),
363
+	);
364
+
365
+	/**
366
+	 * @return IRoutedTask
367
+	 * @throws Exception
368
+	 */
369
+	final public function route()
370
+	{
371
+		$pathInfo = WebRequest::pathInfo();
372
+
373
+		list($pageClass, $action) = $this->getRouteFromPath($pathInfo);
374
+
375
+		/** @var IRoutedTask $page */
376
+		$page = new $pageClass();
377
+
378
+		// Dynamic creation, so we've got to be careful here. We can't use built-in language type protection, so
379
+		// let's use our own.
380
+		if (!($page instanceof IRoutedTask)) {
381
+			throw new Exception('Expected a page, but this is not a page.');
382
+		}
383
+
384
+		// OK, I'm happy at this point that we know we're running a page, and we know it's probably what we want if it
385
+		// inherits PageBase and has been created from the routing map.
386
+		$page->setRoute($action);
387
+
388
+		return $page;
389
+	}
390
+
391
+	/**
392
+	 * @param $pathInfo
393
+	 *
394
+	 * @return array
395
+	 */
396
+	protected function getRouteFromPath($pathInfo)
397
+	{
398
+		if (count($pathInfo) === 0) {
399
+			// No pathInfo, so no page to load. Load the main page.
400
+			return $this->getDefaultRoute();
401
+		}
402
+		elseif (count($pathInfo) === 1) {
403
+			// Exactly one path info segment, it's got to be a page.
404
+			$classSegment = $pathInfo[0];
405
+
406
+			return $this->routeSinglePathSegment($classSegment);
407
+		}
408
+
409
+		// OK, we have two or more segments now.
410
+		if (count($pathInfo) > 2) {
411
+			// Let's handle more than two, and collapse it down into two.
412
+			$requestedAction = array_pop($pathInfo);
413
+			$classSegment = implode('/', $pathInfo);
414
+		}
415
+		else {
416
+			// Two path info segments.
417
+			$classSegment = $pathInfo[0];
418
+			$requestedAction = $pathInfo[1];
419
+		}
420
+
421
+		$routeMap = $this->routePathSegments($classSegment, $requestedAction);
422
+
423
+		if ($routeMap[0] === Page404::class) {
424
+			$routeMap = $this->routeSinglePathSegment($classSegment . '/' . $requestedAction);
425
+		}
426
+
427
+		return $routeMap;
428
+	}
429
+
430
+	/**
431
+	 * @param $classSegment
432
+	 *
433
+	 * @return array
434
+	 */
435
+	final protected function routeSinglePathSegment($classSegment)
436
+	{
437
+		$routeMap = $this->getRouteMap();
438
+		if (array_key_exists($classSegment, $routeMap)) {
439
+			// Route exists, but we don't have an action in path info, so default to main.
440
+			$pageClass = $routeMap[$classSegment]['class'];
441
+			$action = 'main';
442
+
443
+			return array($pageClass, $action);
444
+		}
445
+		else {
446
+			// Doesn't exist in map. Fall back to 404
447
+			$pageClass = Page404::class;
448
+			$action = "main";
449
+
450
+			return array($pageClass, $action);
451
+		}
452
+	}
453
+
454
+	/**
455
+	 * @param $classSegment
456
+	 * @param $requestedAction
457
+	 *
458
+	 * @return array
459
+	 */
460
+	final protected function routePathSegments($classSegment, $requestedAction)
461
+	{
462
+		$routeMap = $this->getRouteMap();
463
+		if (array_key_exists($classSegment, $routeMap)) {
464
+			// Route exists, but we don't have an action in path info, so default to main.
465
+
466
+			if (isset($routeMap[$classSegment]['actions'])
467
+				&& array_search($requestedAction, $routeMap[$classSegment]['actions']) !== false
468
+			) {
469
+				// Action exists in allowed action list. Allow both the page and the action
470
+				$pageClass = $routeMap[$classSegment]['class'];
471
+				$action = $requestedAction;
472
+
473
+				return array($pageClass, $action);
474
+			}
475
+			else {
476
+				// Valid page, invalid action. 404 our way out.
477
+				$pageClass = Page404::class;
478
+				$action = 'main';
479
+
480
+				return array($pageClass, $action);
481
+			}
482
+		}
483
+		else {
484
+			// Class doesn't exist in map. Fall back to 404
485
+			$pageClass = Page404::class;
486
+			$action = 'main';
487
+
488
+			return array($pageClass, $action);
489
+		}
490
+	}
491
+
492
+	/**
493
+	 * @return array
494
+	 */
495
+	protected function getRouteMap()
496
+	{
497
+		return $this->routeMap;
498
+	}
499
+
500
+	/**
501
+	 * @return callable
502
+	 */
503
+	protected function getDefaultRoute()
504
+	{
505
+		return array(PageMain::class, "main");
506
+	}
507 507
 }
Please login to merge, or discard this patch.
includes/Security/ContentSecurityPolicyManager.php 2 patches
Indentation   +85 added lines, -85 removed lines patch added patch discarded remove patch
@@ -12,100 +12,100 @@
 block discarded – undo
12 12
 
13 13
 class ContentSecurityPolicyManager
14 14
 {
15
-    private $policy = [
16
-        'default-src'     => [],
17
-        'script-src'      => ['self', 'nonce'],
18
-        'script-src-elem' => ['self', 'nonce'],
19
-        'script-src-attr' => [],
20
-        'connect-src'     => ['self'],
21
-        'style-src'       => ['self'],
22
-        'style-src-elem'  => ['self'],
23
-        'style-src-attr'  => [],
24
-        'img-src'         => ['self', 'data:', 'https://upload.wikimedia.org', 'https://accounts-dev.wmflabs.org/'],
25
-        'font-src'        => ['self'],
26
-        'form-action'     => ['self', 'oauth'],
27
-        'frame-ancestors' => [],
28
-    ];
29
-    private $nonce = null;
30
-    private $reportOnly = false;
31
-    /**
32
-     * @var SiteConfiguration
33
-     */
34
-    private $configuration;
15
+	private $policy = [
16
+		'default-src'     => [],
17
+		'script-src'      => ['self', 'nonce'],
18
+		'script-src-elem' => ['self', 'nonce'],
19
+		'script-src-attr' => [],
20
+		'connect-src'     => ['self'],
21
+		'style-src'       => ['self'],
22
+		'style-src-elem'  => ['self'],
23
+		'style-src-attr'  => [],
24
+		'img-src'         => ['self', 'data:', 'https://upload.wikimedia.org', 'https://accounts-dev.wmflabs.org/'],
25
+		'font-src'        => ['self'],
26
+		'form-action'     => ['self', 'oauth'],
27
+		'frame-ancestors' => [],
28
+	];
29
+	private $nonce = null;
30
+	private $reportOnly = false;
31
+	/**
32
+	 * @var SiteConfiguration
33
+	 */
34
+	private $configuration;
35 35
 
36
-    /**
37
-     * ContentSecurityPolicyManager constructor.
38
-     *
39
-     * @param SiteConfiguration $configuration
40
-     */
41
-    public function __construct(SiteConfiguration $configuration)
42
-    {
43
-        $this->configuration = $configuration;
44
-    }
36
+	/**
37
+	 * ContentSecurityPolicyManager constructor.
38
+	 *
39
+	 * @param SiteConfiguration $configuration
40
+	 */
41
+	public function __construct(SiteConfiguration $configuration)
42
+	{
43
+		$this->configuration = $configuration;
44
+	}
45 45
 
46
-    public function getNonce()
47
-    {
48
-        if ($this->nonce === null) {
49
-            $this->nonce = base64_encode(openssl_random_pseudo_bytes(32));
50
-        }
46
+	public function getNonce()
47
+	{
48
+		if ($this->nonce === null) {
49
+			$this->nonce = base64_encode(openssl_random_pseudo_bytes(32));
50
+		}
51 51
 
52
-        return $this->nonce;
53
-    }
52
+		return $this->nonce;
53
+	}
54 54
 
55
-    public function getHeader(): string
56
-    {
57
-        $reportOnly = '';
58
-        if ($this->reportOnly) {
59
-            $reportOnly = '-Report-Only';
60
-        }
55
+	public function getHeader(): string
56
+	{
57
+		$reportOnly = '';
58
+		if ($this->reportOnly) {
59
+			$reportOnly = '-Report-Only';
60
+		}
61 61
 
62
-        $constructedPolicy = "Content-Security-Policy{$reportOnly}: ";
62
+		$constructedPolicy = "Content-Security-Policy{$reportOnly}: ";
63 63
 
64
-        foreach ($this->policy as $item => $values) {
65
-            $constructedPolicy .= $item . ' ';
66
-            $policyIsSet = false;
64
+		foreach ($this->policy as $item => $values) {
65
+			$constructedPolicy .= $item . ' ';
66
+			$policyIsSet = false;
67 67
 
68
-            if (count($values) > 0) {
69
-                foreach ($values as $value) {
70
-                    switch ($value) {
71
-                        case 'none':
72
-                        case 'self':
73
-                        case 'strict-dynamic':
74
-                            $policyIsSet = true;
75
-                            $constructedPolicy .= "'{$value}' ";
76
-                            break;
77
-                        case 'nonce':
78
-                            if ($this->nonce !== null) {
79
-                                $policyIsSet = true;
80
-                                $constructedPolicy .= "'nonce-{$this->nonce}' ";
81
-                            }
82
-                            break;
83
-                        case 'oauth':
84
-                            $policyIsSet = true;
85
-                            $constructedPolicy .= "{$this->configuration->getOauthMediaWikiCanonicalServer()} ";
86
-                            break;
87
-                        default:
88
-                            $policyIsSet = true;
89
-                            $constructedPolicy .= $value . ' ';
90
-                            break;
91
-                    }
92
-                }
68
+			if (count($values) > 0) {
69
+				foreach ($values as $value) {
70
+					switch ($value) {
71
+						case 'none':
72
+						case 'self':
73
+						case 'strict-dynamic':
74
+							$policyIsSet = true;
75
+							$constructedPolicy .= "'{$value}' ";
76
+							break;
77
+						case 'nonce':
78
+							if ($this->nonce !== null) {
79
+								$policyIsSet = true;
80
+								$constructedPolicy .= "'nonce-{$this->nonce}' ";
81
+							}
82
+							break;
83
+						case 'oauth':
84
+							$policyIsSet = true;
85
+							$constructedPolicy .= "{$this->configuration->getOauthMediaWikiCanonicalServer()} ";
86
+							break;
87
+						default:
88
+							$policyIsSet = true;
89
+							$constructedPolicy .= $value . ' ';
90
+							break;
91
+					}
92
+				}
93 93
 
94
-                if (!$policyIsSet) {
95
-                    $constructedPolicy .= "'none' ";
96
-                }
97
-            }
98
-            else {
99
-                $constructedPolicy .= "'none' ";
100
-            }
94
+				if (!$policyIsSet) {
95
+					$constructedPolicy .= "'none' ";
96
+				}
97
+			}
98
+			else {
99
+				$constructedPolicy .= "'none' ";
100
+			}
101 101
 
102
-            $constructedPolicy .= '; ';
103
-        }
102
+			$constructedPolicy .= '; ';
103
+		}
104 104
 
105
-        if ($this->configuration->getCspReportUri() !== null) {
106
-            $constructedPolicy .= 'report-uri ' . $this->configuration->getCspReportUri();
107
-        }
105
+		if ($this->configuration->getCspReportUri() !== null) {
106
+			$constructedPolicy .= 'report-uri ' . $this->configuration->getCspReportUri();
107
+		}
108 108
 
109
-        return $constructedPolicy;
110
-    }
109
+		return $constructedPolicy;
110
+	}
111 111
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -62,7 +62,7 @@  discard block
 block discarded – undo
62 62
         $constructedPolicy = "Content-Security-Policy{$reportOnly}: ";
63 63
 
64 64
         foreach ($this->policy as $item => $values) {
65
-            $constructedPolicy .= $item . ' ';
65
+            $constructedPolicy .= $item.' ';
66 66
             $policyIsSet = false;
67 67
 
68 68
             if (count($values) > 0) {
@@ -86,7 +86,7 @@  discard block
 block discarded – undo
86 86
                             break;
87 87
                         default:
88 88
                             $policyIsSet = true;
89
-                            $constructedPolicy .= $value . ' ';
89
+                            $constructedPolicy .= $value.' ';
90 90
                             break;
91 91
                     }
92 92
                 }
@@ -103,7 +103,7 @@  discard block
 block discarded – undo
103 103
         }
104 104
 
105 105
         if ($this->configuration->getCspReportUri() !== null) {
106
-            $constructedPolicy .= 'report-uri ' . $this->configuration->getCspReportUri();
106
+            $constructedPolicy .= 'report-uri '.$this->configuration->getCspReportUri();
107 107
         }
108 108
 
109 109
         return $constructedPolicy;
Please login to merge, or discard this patch.
includes/Security/RoleConfiguration.php 1 patch
Indentation   +360 added lines, -360 removed lines patch added patch discarded remove patch
@@ -47,391 +47,391 @@
 block discarded – undo
47 47
 
48 48
 class RoleConfiguration
49 49
 {
50
-    const ACCESS_ALLOW = 1;
51
-    const ACCESS_DENY = -1;
52
-    const ACCESS_DEFAULT = 0;
53
-    const MAIN = 'main';
54
-    const ALL = '*';
55
-    /**
56
-     * A map of roles to rights
57
-     *
58
-     * For example:
59
-     *
60
-     * array(
61
-     *   'myrole' => array(
62
-     *       PageMyPage::class => array(
63
-     *           'edit' => self::ACCESS_ALLOW,
64
-     *           'create' => self::ACCESS_DENY,
65
-     *       )
66
-     *   )
67
-     * )
68
-     *
69
-     * Note that DENY takes precedence over everything else when roles are combined, followed by ALLOW, followed by
70
-     * DEFAULT. Thus, if you have the following ([A]llow, [D]eny, [-] (default)) grants in different roles, this should
71
-     * be the expected result:
72
-     *
73
-     * - (-,-,-) = - (default because nothing to explicitly say allowed or denied equates to a denial)
74
-     * - (A,-,-) = A
75
-     * - (D,-,-) = D
76
-     * - (A,D,-) = D (deny takes precedence over allow)
77
-     * - (A,A,A) = A (repetition has no effect)
78
-     *
79
-     * The public role is special, and is applied to all users automatically. Avoid using deny on this role.
80
-     *
81
-     * @var array
82
-     */
83
-    private $roleConfig = array(
84
-        'public'            => array(
85
-            /*
50
+	const ACCESS_ALLOW = 1;
51
+	const ACCESS_DENY = -1;
52
+	const ACCESS_DEFAULT = 0;
53
+	const MAIN = 'main';
54
+	const ALL = '*';
55
+	/**
56
+	 * A map of roles to rights
57
+	 *
58
+	 * For example:
59
+	 *
60
+	 * array(
61
+	 *   'myrole' => array(
62
+	 *       PageMyPage::class => array(
63
+	 *           'edit' => self::ACCESS_ALLOW,
64
+	 *           'create' => self::ACCESS_DENY,
65
+	 *       )
66
+	 *   )
67
+	 * )
68
+	 *
69
+	 * Note that DENY takes precedence over everything else when roles are combined, followed by ALLOW, followed by
70
+	 * DEFAULT. Thus, if you have the following ([A]llow, [D]eny, [-] (default)) grants in different roles, this should
71
+	 * be the expected result:
72
+	 *
73
+	 * - (-,-,-) = - (default because nothing to explicitly say allowed or denied equates to a denial)
74
+	 * - (A,-,-) = A
75
+	 * - (D,-,-) = D
76
+	 * - (A,D,-) = D (deny takes precedence over allow)
77
+	 * - (A,A,A) = A (repetition has no effect)
78
+	 *
79
+	 * The public role is special, and is applied to all users automatically. Avoid using deny on this role.
80
+	 *
81
+	 * @var array
82
+	 */
83
+	private $roleConfig = array(
84
+		'public'            => array(
85
+			/*
86 86
              * THIS ROLE IS GRANTED TO ALL LOGGED *OUT* USERS IMPLICITLY.
87 87
              *
88 88
              * USERS IN THIS ROLE DO NOT HAVE TO BE IDENTIFIED TO GET THE RIGHTS CONFERRED HERE.
89 89
              * DO NOT ADD ANY SECURITY-SENSITIVE RIGHTS HERE.
90 90
              */
91
-            '_childRoles'   => array(
92
-                'publicStats',
93
-            ),
94
-            PageTeam::class => array(
95
-                self::MAIN => self::ACCESS_ALLOW,
96
-            ),
97
-            PageXffDemo::class        => array(
98
-                self::MAIN  => self::ACCESS_ALLOW,
99
-            )
100
-        ),
101
-        'loggedIn'          => array(
102
-            /*
91
+			'_childRoles'   => array(
92
+				'publicStats',
93
+			),
94
+			PageTeam::class => array(
95
+				self::MAIN => self::ACCESS_ALLOW,
96
+			),
97
+			PageXffDemo::class        => array(
98
+				self::MAIN  => self::ACCESS_ALLOW,
99
+			)
100
+		),
101
+		'loggedIn'          => array(
102
+			/*
103 103
              * THIS ROLE IS GRANTED TO ALL LOGGED IN USERS IMPLICITLY.
104 104
              *
105 105
              * USERS IN THIS ROLE DO NOT HAVE TO BE IDENTIFIED TO GET THE RIGHTS CONFERRED HERE.
106 106
              * DO NOT ADD ANY SECURITY-SENSITIVE RIGHTS HERE.
107 107
              */
108
-            '_childRoles'             => array(
109
-                'public',
110
-            ),
111
-            PagePreferences::class    => array(
112
-                self::MAIN => self::ACCESS_ALLOW,
113
-                'refreshOAuth' => self::ACCESS_ALLOW,
114
-            ),
115
-            PageChangePassword::class => array(
116
-                self::MAIN => self::ACCESS_ALLOW,
117
-            ),
118
-            PageMultiFactor::class    => array(
119
-                self::MAIN          => self::ACCESS_ALLOW,
120
-                'scratch'           => self::ACCESS_ALLOW,
121
-                'enableYubikeyOtp'  => self::ACCESS_ALLOW,
122
-                'disableYubikeyOtp' => self::ACCESS_ALLOW,
123
-                'enableTotp'        => self::ACCESS_ALLOW,
124
-                'disableTotp'       => self::ACCESS_ALLOW,
125
-            ),
126
-            PageOAuth::class          => array(
127
-                'attach' => self::ACCESS_ALLOW,
128
-                'detach' => self::ACCESS_ALLOW,
129
-            ),
130
-        ),
131
-        'user'              => array(
132
-            '_description'                       => 'A standard tool user.',
133
-            '_editableBy'                        => array('admin', 'toolRoot'),
134
-            '_childRoles'                        => array(
135
-                'internalStats',
136
-            ),
137
-            PageMain::class                      => array(
138
-                self::MAIN => self::ACCESS_ALLOW,
139
-            ),
140
-            PageBan::class                       => array(
141
-                self::MAIN => self::ACCESS_ALLOW,
142
-            ),
143
-            PageEditComment::class               => array(
144
-                self::MAIN => self::ACCESS_ALLOW,
145
-            ),
146
-            PageEmailManagement::class           => array(
147
-                self::MAIN => self::ACCESS_ALLOW,
148
-                'view'     => self::ACCESS_ALLOW,
149
-            ),
150
-            PageExpandedRequestList::class       => array(
151
-                self::MAIN => self::ACCESS_ALLOW,
152
-            ),
153
-            PageLog::class                       => array(
154
-                self::MAIN => self::ACCESS_ALLOW,
155
-            ),
156
-            PageSearch::class                    => array(
157
-                self::MAIN => self::ACCESS_ALLOW,
158
-            ),
159
-            PageWelcomeTemplateManagement::class => array(
160
-                self::MAIN => self::ACCESS_ALLOW,
161
-                'select'   => self::ACCESS_ALLOW,
162
-                'view'     => self::ACCESS_ALLOW,
163
-            ),
164
-            PageViewRequest::class               => array(
165
-                self::MAIN       => self::ACCESS_ALLOW,
166
-                'seeAllRequests' => self::ACCESS_ALLOW,
167
-            ),
168
-            'RequestData'                        => array(
169
-                'seePrivateDataWhenReserved' => self::ACCESS_ALLOW,
170
-                'seePrivateDataWithHash'     => self::ACCESS_ALLOW,
171
-            ),
172
-            PageCustomClose::class               => array(
173
-                self::MAIN => self::ACCESS_ALLOW,
174
-            ),
175
-            PageComment::class                   => array(
176
-                self::MAIN => self::ACCESS_ALLOW,
177
-            ),
178
-            PageCloseRequest::class              => array(
179
-                self::MAIN => self::ACCESS_ALLOW,
180
-            ),
181
-            PageCreateRequest::class             => array(
182
-                self::MAIN => self::ACCESS_ALLOW,
183
-            ),
184
-            PageDeferRequest::class              => array(
185
-                self::MAIN => self::ACCESS_ALLOW,
186
-            ),
187
-            PageDropRequest::class               => array(
188
-                self::MAIN => self::ACCESS_ALLOW,
189
-            ),
190
-            PageReservation::class               => array(
191
-                self::MAIN => self::ACCESS_ALLOW,
192
-            ),
193
-            PageSendToUser::class                => array(
194
-                self::MAIN => self::ACCESS_ALLOW,
195
-            ),
196
-            PageBreakReservation::class          => array(
197
-                self::MAIN => self::ACCESS_ALLOW,
198
-            ),
199
-            PageJobQueue::class                  => array(
200
-                self::MAIN => self::ACCESS_ALLOW,
201
-                'view'     => self::ACCESS_ALLOW,
202
-                'all'      => self::ACCESS_ALLOW,
203
-            ),
204
-            'RequestCreation'                    => array(
205
-                User::CREATION_MANUAL => self::ACCESS_ALLOW,
206
-            ),
207
-            'GlobalInfo'                         => array(
208
-                'viewSiteNotice' => self::ACCESS_ALLOW,
209
-                'viewOnlineUsers' => self::ACCESS_ALLOW,
210
-            ),
211
-        ),
212
-        'admin'             => array(
213
-            '_description'                       => 'A tool administrator.',
214
-            '_editableBy'                        => array('admin', 'toolRoot'),
215
-            '_childRoles'                        => array(
216
-                'user',
217
-                'requestAdminTools',
218
-            ),
219
-            PageEmailManagement::class           => array(
220
-                'edit'   => self::ACCESS_ALLOW,
221
-                'create' => self::ACCESS_ALLOW,
222
-            ),
223
-            PageSiteNotice::class                => array(
224
-                self::MAIN => self::ACCESS_ALLOW,
225
-            ),
226
-            PageUserManagement::class            => array(
227
-                self::MAIN  => self::ACCESS_ALLOW,
228
-                'approve'   => self::ACCESS_ALLOW,
229
-                'decline'   => self::ACCESS_ALLOW,
230
-                'rename'    => self::ACCESS_ALLOW,
231
-                'editUser'  => self::ACCESS_ALLOW,
232
-                'suspend'   => self::ACCESS_ALLOW,
233
-                'editRoles' => self::ACCESS_ALLOW,
234
-            ),
235
-            PageWelcomeTemplateManagement::class => array(
236
-                'edit'   => self::ACCESS_ALLOW,
237
-                'delete' => self::ACCESS_ALLOW,
238
-                'add'    => self::ACCESS_ALLOW,
239
-            ),
240
-            PageJobQueue::class                  => array(
241
-                'acknowledge' => self::ACCESS_ALLOW,
242
-                'requeue'     => self::ACCESS_ALLOW,
243
-            ),
244
-        ),
245
-        'checkuser'         => array(
246
-            '_description'            => 'A user with CheckUser access',
247
-            '_editableBy'             => array('checkuser', 'toolRoot'),
248
-            '_childRoles'             => array(
249
-                'user',
250
-                'requestAdminTools',
251
-            ),
252
-            PageUserManagement::class => array(
253
-                self::MAIN  => self::ACCESS_ALLOW,
254
-                'suspend'   => self::ACCESS_ALLOW,
255
-                'editRoles' => self::ACCESS_ALLOW,
256
-            ),
257
-            'RequestData'             => array(
258
-                'seeUserAgentData' => self::ACCESS_ALLOW,
259
-            ),
260
-        ),
261
-        'toolRoot'          => array(
262
-            '_description' => 'A user with shell access to the servers running the tool',
263
-            '_editableBy'  => array('toolRoot'),
264
-            '_childRoles'  => array(
265
-                'admin',
266
-            ),
267
-            PageMultiFactor::class => array(
268
-                'enableU2F'         => self::ACCESS_ALLOW,
269
-                'disableU2F'        => self::ACCESS_ALLOW,
270
-            )
271
-        ),
272
-        'botCreation'       => array(
273
-            '_description'    => 'A user allowed to use the bot to perform account creations',
274
-            '_editableBy'     => array('admin', 'toolRoot'),
275
-            '_childRoles'     => array(),
276
-            'RequestCreation' => array(
277
-                User::CREATION_BOT => self::ACCESS_ALLOW,
278
-            ),
279
-        ),
280
-        'oauthCreation'       => array(
281
-            '_description'    => 'A user allowed to use the OAuth to perform account creations',
282
-            '_editableBy'     => array('admin', 'toolRoot'),
283
-            '_childRoles'     => array(),
284
-            'RequestCreation'                    => array(
285
-                User::CREATION_OAUTH  => self::ACCESS_ALLOW,
286
-            ),
287
-        ),
108
+			'_childRoles'             => array(
109
+				'public',
110
+			),
111
+			PagePreferences::class    => array(
112
+				self::MAIN => self::ACCESS_ALLOW,
113
+				'refreshOAuth' => self::ACCESS_ALLOW,
114
+			),
115
+			PageChangePassword::class => array(
116
+				self::MAIN => self::ACCESS_ALLOW,
117
+			),
118
+			PageMultiFactor::class    => array(
119
+				self::MAIN          => self::ACCESS_ALLOW,
120
+				'scratch'           => self::ACCESS_ALLOW,
121
+				'enableYubikeyOtp'  => self::ACCESS_ALLOW,
122
+				'disableYubikeyOtp' => self::ACCESS_ALLOW,
123
+				'enableTotp'        => self::ACCESS_ALLOW,
124
+				'disableTotp'       => self::ACCESS_ALLOW,
125
+			),
126
+			PageOAuth::class          => array(
127
+				'attach' => self::ACCESS_ALLOW,
128
+				'detach' => self::ACCESS_ALLOW,
129
+			),
130
+		),
131
+		'user'              => array(
132
+			'_description'                       => 'A standard tool user.',
133
+			'_editableBy'                        => array('admin', 'toolRoot'),
134
+			'_childRoles'                        => array(
135
+				'internalStats',
136
+			),
137
+			PageMain::class                      => array(
138
+				self::MAIN => self::ACCESS_ALLOW,
139
+			),
140
+			PageBan::class                       => array(
141
+				self::MAIN => self::ACCESS_ALLOW,
142
+			),
143
+			PageEditComment::class               => array(
144
+				self::MAIN => self::ACCESS_ALLOW,
145
+			),
146
+			PageEmailManagement::class           => array(
147
+				self::MAIN => self::ACCESS_ALLOW,
148
+				'view'     => self::ACCESS_ALLOW,
149
+			),
150
+			PageExpandedRequestList::class       => array(
151
+				self::MAIN => self::ACCESS_ALLOW,
152
+			),
153
+			PageLog::class                       => array(
154
+				self::MAIN => self::ACCESS_ALLOW,
155
+			),
156
+			PageSearch::class                    => array(
157
+				self::MAIN => self::ACCESS_ALLOW,
158
+			),
159
+			PageWelcomeTemplateManagement::class => array(
160
+				self::MAIN => self::ACCESS_ALLOW,
161
+				'select'   => self::ACCESS_ALLOW,
162
+				'view'     => self::ACCESS_ALLOW,
163
+			),
164
+			PageViewRequest::class               => array(
165
+				self::MAIN       => self::ACCESS_ALLOW,
166
+				'seeAllRequests' => self::ACCESS_ALLOW,
167
+			),
168
+			'RequestData'                        => array(
169
+				'seePrivateDataWhenReserved' => self::ACCESS_ALLOW,
170
+				'seePrivateDataWithHash'     => self::ACCESS_ALLOW,
171
+			),
172
+			PageCustomClose::class               => array(
173
+				self::MAIN => self::ACCESS_ALLOW,
174
+			),
175
+			PageComment::class                   => array(
176
+				self::MAIN => self::ACCESS_ALLOW,
177
+			),
178
+			PageCloseRequest::class              => array(
179
+				self::MAIN => self::ACCESS_ALLOW,
180
+			),
181
+			PageCreateRequest::class             => array(
182
+				self::MAIN => self::ACCESS_ALLOW,
183
+			),
184
+			PageDeferRequest::class              => array(
185
+				self::MAIN => self::ACCESS_ALLOW,
186
+			),
187
+			PageDropRequest::class               => array(
188
+				self::MAIN => self::ACCESS_ALLOW,
189
+			),
190
+			PageReservation::class               => array(
191
+				self::MAIN => self::ACCESS_ALLOW,
192
+			),
193
+			PageSendToUser::class                => array(
194
+				self::MAIN => self::ACCESS_ALLOW,
195
+			),
196
+			PageBreakReservation::class          => array(
197
+				self::MAIN => self::ACCESS_ALLOW,
198
+			),
199
+			PageJobQueue::class                  => array(
200
+				self::MAIN => self::ACCESS_ALLOW,
201
+				'view'     => self::ACCESS_ALLOW,
202
+				'all'      => self::ACCESS_ALLOW,
203
+			),
204
+			'RequestCreation'                    => array(
205
+				User::CREATION_MANUAL => self::ACCESS_ALLOW,
206
+			),
207
+			'GlobalInfo'                         => array(
208
+				'viewSiteNotice' => self::ACCESS_ALLOW,
209
+				'viewOnlineUsers' => self::ACCESS_ALLOW,
210
+			),
211
+		),
212
+		'admin'             => array(
213
+			'_description'                       => 'A tool administrator.',
214
+			'_editableBy'                        => array('admin', 'toolRoot'),
215
+			'_childRoles'                        => array(
216
+				'user',
217
+				'requestAdminTools',
218
+			),
219
+			PageEmailManagement::class           => array(
220
+				'edit'   => self::ACCESS_ALLOW,
221
+				'create' => self::ACCESS_ALLOW,
222
+			),
223
+			PageSiteNotice::class                => array(
224
+				self::MAIN => self::ACCESS_ALLOW,
225
+			),
226
+			PageUserManagement::class            => array(
227
+				self::MAIN  => self::ACCESS_ALLOW,
228
+				'approve'   => self::ACCESS_ALLOW,
229
+				'decline'   => self::ACCESS_ALLOW,
230
+				'rename'    => self::ACCESS_ALLOW,
231
+				'editUser'  => self::ACCESS_ALLOW,
232
+				'suspend'   => self::ACCESS_ALLOW,
233
+				'editRoles' => self::ACCESS_ALLOW,
234
+			),
235
+			PageWelcomeTemplateManagement::class => array(
236
+				'edit'   => self::ACCESS_ALLOW,
237
+				'delete' => self::ACCESS_ALLOW,
238
+				'add'    => self::ACCESS_ALLOW,
239
+			),
240
+			PageJobQueue::class                  => array(
241
+				'acknowledge' => self::ACCESS_ALLOW,
242
+				'requeue'     => self::ACCESS_ALLOW,
243
+			),
244
+		),
245
+		'checkuser'         => array(
246
+			'_description'            => 'A user with CheckUser access',
247
+			'_editableBy'             => array('checkuser', 'toolRoot'),
248
+			'_childRoles'             => array(
249
+				'user',
250
+				'requestAdminTools',
251
+			),
252
+			PageUserManagement::class => array(
253
+				self::MAIN  => self::ACCESS_ALLOW,
254
+				'suspend'   => self::ACCESS_ALLOW,
255
+				'editRoles' => self::ACCESS_ALLOW,
256
+			),
257
+			'RequestData'             => array(
258
+				'seeUserAgentData' => self::ACCESS_ALLOW,
259
+			),
260
+		),
261
+		'toolRoot'          => array(
262
+			'_description' => 'A user with shell access to the servers running the tool',
263
+			'_editableBy'  => array('toolRoot'),
264
+			'_childRoles'  => array(
265
+				'admin',
266
+			),
267
+			PageMultiFactor::class => array(
268
+				'enableU2F'         => self::ACCESS_ALLOW,
269
+				'disableU2F'        => self::ACCESS_ALLOW,
270
+			)
271
+		),
272
+		'botCreation'       => array(
273
+			'_description'    => 'A user allowed to use the bot to perform account creations',
274
+			'_editableBy'     => array('admin', 'toolRoot'),
275
+			'_childRoles'     => array(),
276
+			'RequestCreation' => array(
277
+				User::CREATION_BOT => self::ACCESS_ALLOW,
278
+			),
279
+		),
280
+		'oauthCreation'       => array(
281
+			'_description'    => 'A user allowed to use the OAuth to perform account creations',
282
+			'_editableBy'     => array('admin', 'toolRoot'),
283
+			'_childRoles'     => array(),
284
+			'RequestCreation'                    => array(
285
+				User::CREATION_OAUTH  => self::ACCESS_ALLOW,
286
+			),
287
+		),
288 288
 
289 289
 
290
-        // Child roles go below this point
291
-        'publicStats'       => array(
292
-            '_hidden'               => true,
293
-            StatsUsers::class       => array(
294
-                self::MAIN => self::ACCESS_ALLOW,
295
-                'detail'   => self::ACCESS_ALLOW,
296
-            ),
297
-            StatsTopCreators::class => array(
298
-                self::MAIN => self::ACCESS_ALLOW,
299
-            ),
300
-        ),
301
-        'internalStats'     => array(
302
-            '_hidden'                    => true,
303
-            StatsMain::class             => array(
304
-                self::MAIN => self::ACCESS_ALLOW,
305
-            ),
306
-            StatsFastCloses::class       => array(
307
-                self::MAIN => self::ACCESS_ALLOW,
308
-            ),
309
-            StatsInactiveUsers::class    => array(
310
-                self::MAIN => self::ACCESS_ALLOW,
311
-            ),
312
-            StatsMonthlyStats::class     => array(
313
-                self::MAIN => self::ACCESS_ALLOW,
314
-            ),
315
-            StatsReservedRequests::class => array(
316
-                self::MAIN => self::ACCESS_ALLOW,
317
-            ),
318
-            StatsTemplateStats::class    => array(
319
-                self::MAIN => self::ACCESS_ALLOW,
320
-            ),
321
-        ),
322
-        'requestAdminTools' => array(
323
-            '_hidden'                   => true,
324
-            PageBan::class              => array(
325
-                self::MAIN => self::ACCESS_ALLOW,
326
-                'set'      => self::ACCESS_ALLOW,
327
-                'remove'   => self::ACCESS_ALLOW,
328
-            ),
329
-            PageEditComment::class      => array(
330
-                'editOthers' => self::ACCESS_ALLOW,
331
-            ),
332
-            PageBreakReservation::class => array(
333
-                'force' => self::ACCESS_ALLOW,
334
-            ),
335
-            PageCustomClose::class      => array(
336
-                'skipCcMailingList' => self::ACCESS_ALLOW,
337
-            ),
338
-            'RequestData'               => array(
339
-                'reopenOldRequest'      => self::ACCESS_ALLOW,
340
-                'alwaysSeePrivateData'  => self::ACCESS_ALLOW,
341
-                'alwaysSeeHash'         => self::ACCESS_ALLOW,
342
-                'seeRestrictedComments' => self::ACCESS_ALLOW,
343
-            ),
344
-        ),
345
-    );
346
-    /** @var array
347
-     * List of roles which are *exempt* from the identification requirements
348
-     *
349
-     * Think twice about adding roles to this list.
350
-     *
351
-     * @category Security-Critical
352
-     */
353
-    private $identificationExempt = array('public', 'loggedIn');
290
+		// Child roles go below this point
291
+		'publicStats'       => array(
292
+			'_hidden'               => true,
293
+			StatsUsers::class       => array(
294
+				self::MAIN => self::ACCESS_ALLOW,
295
+				'detail'   => self::ACCESS_ALLOW,
296
+			),
297
+			StatsTopCreators::class => array(
298
+				self::MAIN => self::ACCESS_ALLOW,
299
+			),
300
+		),
301
+		'internalStats'     => array(
302
+			'_hidden'                    => true,
303
+			StatsMain::class             => array(
304
+				self::MAIN => self::ACCESS_ALLOW,
305
+			),
306
+			StatsFastCloses::class       => array(
307
+				self::MAIN => self::ACCESS_ALLOW,
308
+			),
309
+			StatsInactiveUsers::class    => array(
310
+				self::MAIN => self::ACCESS_ALLOW,
311
+			),
312
+			StatsMonthlyStats::class     => array(
313
+				self::MAIN => self::ACCESS_ALLOW,
314
+			),
315
+			StatsReservedRequests::class => array(
316
+				self::MAIN => self::ACCESS_ALLOW,
317
+			),
318
+			StatsTemplateStats::class    => array(
319
+				self::MAIN => self::ACCESS_ALLOW,
320
+			),
321
+		),
322
+		'requestAdminTools' => array(
323
+			'_hidden'                   => true,
324
+			PageBan::class              => array(
325
+				self::MAIN => self::ACCESS_ALLOW,
326
+				'set'      => self::ACCESS_ALLOW,
327
+				'remove'   => self::ACCESS_ALLOW,
328
+			),
329
+			PageEditComment::class      => array(
330
+				'editOthers' => self::ACCESS_ALLOW,
331
+			),
332
+			PageBreakReservation::class => array(
333
+				'force' => self::ACCESS_ALLOW,
334
+			),
335
+			PageCustomClose::class      => array(
336
+				'skipCcMailingList' => self::ACCESS_ALLOW,
337
+			),
338
+			'RequestData'               => array(
339
+				'reopenOldRequest'      => self::ACCESS_ALLOW,
340
+				'alwaysSeePrivateData'  => self::ACCESS_ALLOW,
341
+				'alwaysSeeHash'         => self::ACCESS_ALLOW,
342
+				'seeRestrictedComments' => self::ACCESS_ALLOW,
343
+			),
344
+		),
345
+	);
346
+	/** @var array
347
+	 * List of roles which are *exempt* from the identification requirements
348
+	 *
349
+	 * Think twice about adding roles to this list.
350
+	 *
351
+	 * @category Security-Critical
352
+	 */
353
+	private $identificationExempt = array('public', 'loggedIn');
354 354
 
355
-    /**
356
-     * RoleConfiguration constructor.
357
-     *
358
-     * @param array $roleConfig           Set to non-null to override the default configuration.
359
-     * @param array $identificationExempt Set to non-null to override the default configuration.
360
-     */
361
-    public function __construct(array $roleConfig = null, array $identificationExempt = null)
362
-    {
363
-        if ($roleConfig !== null) {
364
-            $this->roleConfig = $roleConfig;
365
-        }
355
+	/**
356
+	 * RoleConfiguration constructor.
357
+	 *
358
+	 * @param array $roleConfig           Set to non-null to override the default configuration.
359
+	 * @param array $identificationExempt Set to non-null to override the default configuration.
360
+	 */
361
+	public function __construct(array $roleConfig = null, array $identificationExempt = null)
362
+	{
363
+		if ($roleConfig !== null) {
364
+			$this->roleConfig = $roleConfig;
365
+		}
366 366
 
367
-        if ($identificationExempt !== null) {
368
-            $this->identificationExempt = $identificationExempt;
369
-        }
370
-    }
367
+		if ($identificationExempt !== null) {
368
+			$this->identificationExempt = $identificationExempt;
369
+		}
370
+	}
371 371
 
372
-    /**
373
-     * @param array $roles The roles to check
374
-     *
375
-     * @return array
376
-     */
377
-    public function getApplicableRoles(array $roles)
378
-    {
379
-        $available = array();
372
+	/**
373
+	 * @param array $roles The roles to check
374
+	 *
375
+	 * @return array
376
+	 */
377
+	public function getApplicableRoles(array $roles)
378
+	{
379
+		$available = array();
380 380
 
381
-        foreach ($roles as $role) {
382
-            if (!isset($this->roleConfig[$role])) {
383
-                // wat
384
-                continue;
385
-            }
381
+		foreach ($roles as $role) {
382
+			if (!isset($this->roleConfig[$role])) {
383
+				// wat
384
+				continue;
385
+			}
386 386
 
387
-            $available[$role] = $this->roleConfig[$role];
387
+			$available[$role] = $this->roleConfig[$role];
388 388
 
389
-            if (isset($available[$role]['_childRoles'])) {
390
-                $childRoles = self::getApplicableRoles($available[$role]['_childRoles']);
391
-                $available = array_merge($available, $childRoles);
389
+			if (isset($available[$role]['_childRoles'])) {
390
+				$childRoles = self::getApplicableRoles($available[$role]['_childRoles']);
391
+				$available = array_merge($available, $childRoles);
392 392
 
393
-                unset($available[$role]['_childRoles']);
394
-            }
393
+				unset($available[$role]['_childRoles']);
394
+			}
395 395
 
396
-            foreach (array('_hidden', '_editableBy', '_description') as $item) {
397
-                if (isset($available[$role][$item])) {
398
-                    unset($available[$role][$item]);
399
-                }
400
-            }
401
-        }
396
+			foreach (array('_hidden', '_editableBy', '_description') as $item) {
397
+				if (isset($available[$role][$item])) {
398
+					unset($available[$role][$item]);
399
+				}
400
+			}
401
+		}
402 402
 
403
-        return $available;
404
-    }
403
+		return $available;
404
+	}
405 405
 
406
-    public function getAvailableRoles()
407
-    {
408
-        $possible = array_diff(array_keys($this->roleConfig), array('public', 'loggedIn'));
406
+	public function getAvailableRoles()
407
+	{
408
+		$possible = array_diff(array_keys($this->roleConfig), array('public', 'loggedIn'));
409 409
 
410
-        $actual = array();
410
+		$actual = array();
411 411
 
412
-        foreach ($possible as $role) {
413
-            if (!isset($this->roleConfig[$role]['_hidden'])) {
414
-                $actual[$role] = array(
415
-                    'description' => $this->roleConfig[$role]['_description'],
416
-                    'editableBy'  => $this->roleConfig[$role]['_editableBy'],
417
-                );
418
-            }
419
-        }
412
+		foreach ($possible as $role) {
413
+			if (!isset($this->roleConfig[$role]['_hidden'])) {
414
+				$actual[$role] = array(
415
+					'description' => $this->roleConfig[$role]['_description'],
416
+					'editableBy'  => $this->roleConfig[$role]['_editableBy'],
417
+				);
418
+			}
419
+		}
420 420
 
421
-        return $actual;
422
-    }
421
+		return $actual;
422
+	}
423 423
 
424
-    /**
425
-     * @param string $role
426
-     *
427
-     * @return bool
428
-     */
429
-    public function roleNeedsIdentification($role)
430
-    {
431
-        if (in_array($role, $this->identificationExempt)) {
432
-            return false;
433
-        }
424
+	/**
425
+	 * @param string $role
426
+	 *
427
+	 * @return bool
428
+	 */
429
+	public function roleNeedsIdentification($role)
430
+	{
431
+		if (in_array($role, $this->identificationExempt)) {
432
+			return false;
433
+		}
434 434
 
435
-        return true;
436
-    }
435
+		return true;
436
+	}
437 437
 }
Please login to merge, or discard this patch.