-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRouter.php
More file actions
executable file
·102 lines (89 loc) · 4.49 KB
/
Router.php
File metadata and controls
executable file
·102 lines (89 loc) · 4.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
<?php
namespace App\Routing;
use App\Controllers\ErrorController;
use App\Core\Csrf;
use App\Services\Exceptions\AuthException;
use App\Services\Interfaces\AuthServiceInterface;
use FastRoute;
final class Router
{
// Dependency Injection of FastRoute dispatcher routes, created in Routes.php
private FastRoute\Dispatcher $dispatcher;
private AuthServiceInterface $authService;
public function __construct(FastRoute\Dispatcher $dispatcher, AuthServiceInterface $authService)
{
$this->dispatcher = $dispatcher;
$this->authService = $authService;
}
/** nikic/fast-route | https://packagist.org/packages/nikic/fast-route.
* Modification on the basic usage implementation from docs, contains additional header data regarding route access rights. */
public function dispatch(): void
{
// Fetch method and URI from somewhere
$httpMethod = $_SERVER['REQUEST_METHOD'];
$uri = $_SERVER['REQUEST_URI'];
// Strip query string (?foo=bar) and decode URI
if (false !== $pos = strpos($uri, '?')) {
$uri = substr($uri, 0, $pos);
}
$uri = rawurldecode($uri);
// Use dispatcher retrieved from Routes.php
$routeInfo = $this->dispatcher->dispatch($httpMethod, $uri);
$errorController = new ErrorController();
switch ($routeInfo[0]) {
case FastRoute\Dispatcher::NOT_FOUND:
$errorController->notFound();
exit;
case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
$errorController->methodNotAllowed();
exit;
case FastRoute\Dispatcher::FOUND:
try {
$handler = $routeInfo[1];
$pathParams = $routeInfo[2];
$routeReqAccess = $handler['accessRole'];
// Checks if user is logged in for pages that require authentication
$this->authService->requireAuthentication($routeReqAccess);
// Checks if the already logged-in user is visiting the loginPage OR signupPage, redirect to / (home)
$this->authService->denyAuthenticatedOnAuthRoutes($handler['action'][1]);
// Checks if accessing a project route, validate if user has access with required role or higher
if ($pathParams['projectId'] ?? false) {
$userProjectRole = $this->authService->requireProjectAccess((int)$pathParams['projectId'], $routeReqAccess);
$_SESSION['auth']['projectRole'] = $userProjectRole->value;
} else {
unset($_SESSION['auth']['projectRole']);
}
// Upon POST -> verify CSRF token. If not valid, exit 403 (handled in Csrf::Verify)
if ($_SERVER['REQUEST_METHOD'] === 'POST')
Csrf::requireVerification($_POST['csrf'] ?? null);
// If no auth was required OR user passed project auth guards
// AND CSRF token on POST is validated -> call handler
call_user_func_array($handler['action'], $pathParams);
break;
} catch (AuthException $e) {
if ($e->getMessage() === AuthException::ALREADY_LOGGED_IN) {
$_SESSION['flash_info'][] = $e->getMessage();
$redirect = '/';
} else {
$_SESSION['flash_errors'][] = $e->getMessage();
switch ($e->getMessage()) {
case AuthException::PROJECT_ACCESS_DENIED:
case AuthException::PROJECT_INSUFFICIENT_PERMISSIONS:
$redirect = '/';
break;
// Named for clarity
case AuthException::REQUIRES_LOGIN:
case AuthException::CSRF_TOKEN_MISMATCH:
default:
$redirect = '/login';
break;
}
}
} catch (\Exception) {
$_SESSION['flash_errors'][] = "An unexpected error occurred.";
$redirect = '/';
}
header("Location: $redirect", true, 302);
}
}
}