From 39952c2d95cf4806063abbc2c7508cf9ab4f93e5 Mon Sep 17 00:00:00 2001 From: Dag Date: Fri, 30 Aug 2024 00:07:58 +0200 Subject: [PATCH] refactor: implement middleware chain (#4240) * refactor: implement middleware chain * refactor --- lib/RssBridge.php | 75 ++++--------------- lib/bootstrap.php | 1 + middlewares/BasicAuthMiddleware.php | 38 ++++++++++ middlewares/MaintenanceMiddleware.php | 17 +++++ middlewares/Middleware.php | 8 ++ middlewares/SecurityMiddleware.php | 21 ++++++ middlewares/TokenAuthenticationMiddleware.php | 29 +++++++ 7 files changed, 128 insertions(+), 61 deletions(-) create mode 100644 middlewares/BasicAuthMiddleware.php create mode 100644 middlewares/MaintenanceMiddleware.php create mode 100644 middlewares/Middleware.php create mode 100644 middlewares/SecurityMiddleware.php create mode 100644 middlewares/TokenAuthenticationMiddleware.php diff --git a/lib/RssBridge.php b/lib/RssBridge.php index 9c8f5767..230488bf 100644 --- a/lib/RssBridge.php +++ b/lib/RssBridge.php @@ -12,63 +12,6 @@ final class RssBridge public function main(Request $request): Response { - foreach ($request->toArray() as $key => $value) { - if (!is_string($value)) { - return new Response(render(__DIR__ . '/../templates/error.html.php', [ - 'message' => "Query parameter \"$key\" is not a string.", - ]), 400); - } - } - - if (Configuration::getConfig('system', 'enable_maintenance_mode')) { - return new Response(render(__DIR__ . '/../templates/error.html.php', [ - 'title' => '503 Service Unavailable', - 'message' => 'RSS-Bridge is down for maintenance.', - ]), 503); - } - - // HTTP Basic auth check - if (Configuration::getConfig('authentication', 'enable')) { - if (Configuration::getConfig('authentication', 'password') === '') { - return new Response('The authentication password cannot be the empty string', 500); - } - $user = $request->server('PHP_AUTH_USER'); - $password = $request->server('PHP_AUTH_PW'); - if ($user === null || $password === null) { - $html = render(__DIR__ . '/../templates/error.html.php', [ - 'message' => 'Please authenticate in order to access this instance!', - ]); - return new Response($html, 401, ['WWW-Authenticate' => 'Basic realm="RSS-Bridge"']); - } - if ( - (Configuration::getConfig('authentication', 'username') !== $user) - || (! hash_equals(Configuration::getConfig('authentication', 'password'), $password)) - ) { - $html = render(__DIR__ . '/../templates/error.html.php', [ - 'message' => 'Please authenticate in order to access this instance!', - ]); - return new Response($html, 401, ['WWW-Authenticate' => 'Basic realm="RSS-Bridge"']); - } - // At this point the username and password was correct - } - - // Add token as attribute to request - $request = $request->withAttribute('token', $request->get('token')); - - // Token authentication check - if (Configuration::getConfig('authentication', 'token')) { - if (! $request->attribute('token')) { - return new Response(render(__DIR__ . '/../templates/token.html.php', [ - 'message' => '', - ]), 401); - } - if (! hash_equals(Configuration::getConfig('authentication', 'token'), $request->attribute('token'))) { - return new Response(render(__DIR__ . '/../templates/token.html.php', [ - 'message' => 'Invalid token', - ]), 401); - } - } - $action = $request->get('action', 'Frontpage'); $actionName = strtolower($action) . 'Action'; $actionName = implode(array_map('ucfirst', explode('-', $actionName))); @@ -77,11 +20,21 @@ final class RssBridge return new Response(render(__DIR__ . '/../templates/error.html.php', ['message' => 'Invalid action']), 400); } - $controller = self::$container[$actionName]; + $handler = self::$container[$actionName]; - $response = $controller($request); - - return $response; + $middlewares = [ + new SecurityMiddleware(), + new MaintenanceMiddleware(), + new BasicAuthMiddleware(), + new TokenAuthenticationMiddleware(), + ]; + $action = function ($req) use ($handler) { + return $handler($req); + }; + foreach (array_reverse($middlewares) as $middleware) { + $action = fn ($req) => $middleware($req, $action); + } + return $action($request); } public static function getLogger(): Logger diff --git a/lib/bootstrap.php b/lib/bootstrap.php index 1d866067..36b13e19 100644 --- a/lib/bootstrap.php +++ b/lib/bootstrap.php @@ -37,6 +37,7 @@ spl_autoload_register(function ($className) { __DIR__ . '/../caches/', __DIR__ . '/../formats/', __DIR__ . '/../lib/', + __DIR__ . '/../middlewares/', ]; foreach ($folders as $folder) { $file = $folder . $className . '.php'; diff --git a/middlewares/BasicAuthMiddleware.php b/middlewares/BasicAuthMiddleware.php new file mode 100644 index 00000000..6b0803e2 --- /dev/null +++ b/middlewares/BasicAuthMiddleware.php @@ -0,0 +1,38 @@ +server('PHP_AUTH_USER'); + $password = $request->server('PHP_AUTH_PW'); + if ($user === null || $password === null) { + $html = render(__DIR__ . '/../templates/error.html.php', [ + 'message' => 'Please authenticate in order to access this instance!', + ]); + return new Response($html, 401, ['WWW-Authenticate' => 'Basic realm="RSS-Bridge"']); + } + if ( + (Configuration::getConfig('authentication', 'username') !== $user) + || (!hash_equals(Configuration::getConfig('authentication', 'password'), $password)) + ) { + $html = render(__DIR__ . '/../templates/error.html.php', [ + 'message' => 'Please authenticate in order to access this instance!', + ]); + return new Response($html, 401, ['WWW-Authenticate' => 'Basic realm="RSS-Bridge"']); + } + return $next($request); + } +} diff --git a/middlewares/MaintenanceMiddleware.php b/middlewares/MaintenanceMiddleware.php new file mode 100644 index 00000000..de8a1baf --- /dev/null +++ b/middlewares/MaintenanceMiddleware.php @@ -0,0 +1,17 @@ + '503 Service Unavailable', + 'message' => 'RSS-Bridge is down for maintenance.', + ]), 503); + } +} diff --git a/middlewares/Middleware.php b/middlewares/Middleware.php new file mode 100644 index 00000000..83d93a3b --- /dev/null +++ b/middlewares/Middleware.php @@ -0,0 +1,8 @@ +toArray() as $key => $value) { + if (!is_string($value)) { + return new Response(render(__DIR__ . '/../templates/error.html.php', [ + 'message' => "Query parameter \"$key\" is not a string.", + ]), 400); + } + } + return $next($request); + } +} diff --git a/middlewares/TokenAuthenticationMiddleware.php b/middlewares/TokenAuthenticationMiddleware.php new file mode 100644 index 00000000..f8234629 --- /dev/null +++ b/middlewares/TokenAuthenticationMiddleware.php @@ -0,0 +1,29 @@ +withAttribute('token', $request->get('token')); + + if (! $request->attribute('token')) { + return new Response(render(__DIR__ . '/../templates/token.html.php', [ + 'message' => 'Missing token', + ]), 401); + } + if (! hash_equals(Configuration::getConfig('authentication', 'token'), $request->attribute('token'))) { + return new Response(render(__DIR__ . '/../templates/token.html.php', [ + 'message' => 'Invalid token', + ]), 401); + } + + return $next($request); + } +}