Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
92.68% |
38 / 41 |
|
60.00% |
3 / 5 |
CRAP | |
0.00% |
0 / 1 |
BasicAuthentication | |
92.68% |
38 / 41 |
|
60.00% |
3 / 5 |
20.16 | |
0.00% |
0 / 1 |
__construct | |
93.10% |
27 / 29 |
|
0.00% |
0 / 1 |
12.05 | |||
logout | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
loginRequired | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getUserTable | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getUsername | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace Miniframe\Middleware; |
4 | |
5 | use Miniframe\Core\AbstractMiddleware; |
6 | use Miniframe\Core\Config; |
7 | use Miniframe\Core\Request; |
8 | use Miniframe\Core\Response; |
9 | use Miniframe\Response\RedirectResponse; |
10 | use Miniframe\Response\UnauthorizedResponse; |
11 | use RuntimeException; |
12 | |
13 | /** |
14 | * Basic HTTP Authentication class |
15 | * |
16 | * This middleware adds basic HTTP authentication to your application. |
17 | */ |
18 | class BasicAuthentication extends AbstractMiddleware |
19 | { |
20 | /** |
21 | * The username |
22 | * |
23 | * @var string|null |
24 | */ |
25 | protected $username = null; |
26 | |
27 | /** |
28 | * Forces the user to be logged in |
29 | * |
30 | * @param Request $request Reference to the Request object. |
31 | * @param Config $config Reference to the Config object. |
32 | */ |
33 | public function __construct(Request $request, Config $config) |
34 | { |
35 | parent::__construct($request, $config); |
36 | $continueWhenNoCredentials = false; |
37 | |
38 | // Checks if logging in is required in the current state |
39 | if (!$this->loginRequired()) { |
40 | $continueWhenNoCredentials = true; |
41 | } |
42 | |
43 | // Check if the current path is part of the exclusion list |
44 | $fullPath = '/' . implode('/', $request->getPath()); // $_SERVER['REQUEST_URI'] can't be used since |
45 | // that doesn't apply on Console requests |
46 | $excludeList = |
47 | $config->has('authentication', 'exclude') |
48 | ? (array)$config->get('authentication', 'exclude') |
49 | : []; |
50 | foreach ($excludeList as $excludePath) { |
51 | if (!is_string($excludePath)) { |
52 | throw new RuntimeException('Invalid exclude path: ' . var_export($excludePath, true)); |
53 | } |
54 | $regex = '/^' . str_replace(['?', '\\*'], ['.', '.*?'], preg_quote($excludePath, '/')) . '$/'; |
55 | if (preg_match($regex, $fullPath)) { |
56 | $continueWhenNoCredentials = true; |
57 | } |
58 | } |
59 | |
60 | // Fetches the realm |
61 | $realm = $this->config->get('authentication', 'realm'); |
62 | if (!is_string($realm)) { |
63 | $realm = 'Basic authentication'; |
64 | } |
65 | |
66 | // Check if we have credentials |
67 | if ( |
68 | !is_string($request->getServer('PHP_AUTH_USER')) || |
69 | !is_string($request->getServer('PHP_AUTH_PW')) |
70 | ) { |
71 | if ($continueWhenNoCredentials) { |
72 | return; |
73 | } else { |
74 | throw new UnauthorizedResponse($realm); |
75 | } |
76 | } |
77 | |
78 | // Fetch user table |
79 | $userTable = $this->getUserTable(); |
80 | |
81 | // Is the username valid? |
82 | if (!isset($userTable[$request->getServer('PHP_AUTH_USER')])) { |
83 | throw new UnauthorizedResponse($realm); |
84 | } |
85 | |
86 | // Is the password valid? |
87 | if (!password_verify($request->getServer('PHP_AUTH_PW'), $userTable[$request->getServer('PHP_AUTH_USER')])) { |
88 | throw new UnauthorizedResponse($realm); |
89 | } |
90 | |
91 | // We passed the BasicAuthentication middleware, continue. |
92 | $this->username = $request->getServer('PHP_AUTH_USER'); |
93 | } |
94 | |
95 | /** |
96 | * Returns a response that forces the user to log out |
97 | * |
98 | * @return Response |
99 | */ |
100 | public function logout(): Response |
101 | { |
102 | $host = $this->request->getServer('HTTP_HOST'); |
103 | $url = $this->request->isHttpsRequest() ? 'https' : 'http'; |
104 | $url .= '://logout@' . (is_string($host) ? $host : '') . $this->config->get('framework', 'base_href'); |
105 | return new RedirectResponse($url); |
106 | } |
107 | |
108 | /** |
109 | * Checks if logging in is required in the current state |
110 | * |
111 | * @return boolean |
112 | */ |
113 | protected function loginRequired(): bool |
114 | { |
115 | // Don't do authentication on shell requests |
116 | if ($this->request->isShellRequest()) { |
117 | return false; |
118 | } |
119 | |
120 | return true; |
121 | } |
122 | |
123 | /** |
124 | * Returns the user table |
125 | * |
126 | * @return array<string, string> |
127 | */ |
128 | protected function getUserTable(): array |
129 | { |
130 | // Fetch user table |
131 | $userTable = $this->config->get('authentication', 'user'); |
132 | if (!is_array($userTable)) { |
133 | throw new \RuntimeException('The usertable is invalid. Please check your configuration.'); |
134 | } |
135 | return $userTable; |
136 | } |
137 | |
138 | /** |
139 | * Returns the username |
140 | * |
141 | * @return string|null |
142 | */ |
143 | public function getUsername(): ?string |
144 | { |
145 | return $this->username; |
146 | } |
147 | } |