diff --git a/htdocs/index.php b/htdocs/index.php index f3f9d7efcc..f5705ac9dd 100644 --- a/htdocs/index.php +++ b/htdocs/index.php @@ -74,12 +74,14 @@ function array_find(array $array, callable $callback) // Middleware that happens on every request. This doesn't include // any authentication middleware, because that's done dynamically // based on the module router, depending on if the module is public. -$middlewarechain = (new \LORIS\Middleware\Language()) +$middlewarechain = (new \LORIS\Middleware\RequestAttributeBuilder()) + ->withMiddleware(new \LORIS\Middleware\Language()) ->withMiddleware(new \LORIS\Middleware\ContentLength()) ->withMiddleware(new \LORIS\Middleware\LorisMenu()) ->withMiddleware(new \LORIS\Middleware\ContentLength()) ->withMiddleware(new \LORIS\Middleware\AWS()) ->withMiddleware(new \LORIS\Middleware\ContentSecurityPolicy()) + ->withMiddleware(new \LORIS\Middleware\RedirectControl()) ->withMiddleware(new \LORIS\Middleware\MFA()) ->withMiddleware(new \LORIS\Middleware\ResponseGenerator()); diff --git a/src/Middleware/RedirectControl.php b/src/Middleware/RedirectControl.php new file mode 100644 index 0000000000..5f40c27e3f --- /dev/null +++ b/src/Middleware/RedirectControl.php @@ -0,0 +1,64 @@ +getServerParams()['QUERY_STRING'] ?? null; + if ($queryString === null || !str_contains($queryString, "redirect")) { + return $this->next->process($request, $handler); + } + + // if redirect in query string, check if a query parameter has a + // redirection set for a third-party + $query = $request->getQueryParams(); + if (array_key_exists("redirect", $query)) { + // redirect uri + $redirect = rtrim($query["redirect"], "/"); + + // if the redirection does not starts with current host, it is + // trying to redirect outside, then force redirect to main/login page. + $uri = $request->getUri(); + $baseURL = $request->getAttribute("baseurl"); + if (!str_starts_with($redirect, $baseURL)) { + error_log("[error][redirect] Tentative of redirection outside of LORIS: {$redirect}"); + + // nullify uri query parameters + $uri = $uri->withQuery(""); + + // force reload the current host without redirect query param + return new \LORIS\Http\Response\JSON\SeeOther($uri); + } + } + + // + return $this->next->process($request, $handler); + } +} diff --git a/src/Middleware/RequestAttributeBuilder.php b/src/Middleware/RequestAttributeBuilder.php new file mode 100644 index 0000000000..f35495419d --- /dev/null +++ b/src/Middleware/RequestAttributeBuilder.php @@ -0,0 +1,62 @@ +user(); + $request = $request->withAttribute("user", $user); + + // add loris instance to request attributes + $lorisInstance = new \LORIS\LorisInstance( + $factory->database(), + $factory->config(), + [ + __DIR__ . "/../../project/modules", + __DIR__ . "/../../modules/", + ] + ); + $request = $request->withAttribute("loris", $lorisInstance); + + // add baseurl to request attributes + $uri = $request->getURI(); + $baseurl = $uri->withPath("")->withQuery(""); + $request = $request->withAttribute( + "baseurl", + $baseurl->__toString() + ); + + // + return $this->next->process($request, $handler); + } +} diff --git a/src/Router/BaseRouter.php b/src/Router/BaseRouter.php index df93da367d..09e4371e99 100644 --- a/src/Router/BaseRouter.php +++ b/src/Router/BaseRouter.php @@ -33,15 +33,9 @@ class BaseRouter extends PrefixRouter implements RequestHandlerInterface { /** * Construct a BaseRouter - * - * @param \LORIS\LorisInstance $loris The LORIS instance being routed - * @param \User $user The user accessing LORIS. (May be an - * AnonymousUser instance). */ - public function __construct( - protected \LORIS\LorisInstance $loris, - protected \User $user - ) { + public function __construct() + { } /** @@ -58,16 +52,19 @@ public function handle(ServerRequestInterface $request) : ResponseInterface $uri = $request->getUri(); $path = $uri->getPath(); + // from request attributes + $loris = $request->getAttribute("loris"); + $user = $request->getAttribute("user"); + // Replace multiple slashes in the URL with a single slash $path = preg_replace("/\/+/", "/", $path); // Remove any trailing slash remaining, so that foo/ and foo are the same // route - $path = preg_replace("/\/$/", "", $path); - $request = $request->withAttribute("user", $this->user) - ->withAttribute("loris", $this->loris); + $path = preg_replace("/\/$/", "", $path); + $modulename = null; if ($path == "") { - if ($this->user instanceof \LORIS\AnonymousUser) { + if ($user instanceof \LORIS\AnonymousUser) { $modulename = "login"; } else { $modulename = "dashboard"; @@ -79,14 +76,14 @@ public function handle(ServerRequestInterface $request) : ResponseInterface } $components = []; - if (empty($modulename)) { + if ($modulename === null) { $components = preg_split("/\/+?/", $path); $modulename = $components[0]; } $factory = \NDB_Factory::singleton(); $ehandler = new \LORIS\Middleware\ExceptionHandlingMiddleware(); - $logSettings = $this->loris->getConfiguration()->getLogSettings(); + $logSettings = $loris->getConfiguration()->getLogSettings(); $exceptionloglevel = $logSettings->getExceptionLogLevel(); if ($exceptionloglevel != "none") { @@ -97,7 +94,7 @@ public function handle(ServerRequestInterface $request) : ResponseInterface $ehandler->setLogger(new \PSR\Log\NullLogger); } - if ($this->loris->hasModule($modulename)) { + if ($loris->hasModule($modulename)) { $uri = $request->getURI(); $suburi = $this->stripPrefix($modulename, $uri); @@ -110,14 +107,12 @@ public function handle(ServerRequestInterface $request) : ResponseInterface $baseurl = ''; } $baseurl = $uri->withPath($baseurl)->withQuery(""); - $request = $request->withAttribute("baseurl", $baseurl->__toString()); + $factory->setBaseURL((string) $baseurl); - $factory->setBaseURL((string )$baseurl); - - $module = $this->loris->getModule($modulename); + $module = $loris->getModule($modulename); $module->registerAutoloader(); - $lang = \LORIS\Middleware\Language::detectLocale($this->loris, $request); + $lang = \LORIS\Middleware\Language::detectLocale($loris, $request); $request = $request->withAttribute("lang", $lang); if (file_exists(__DIR__ . "/../../project/locale/")) { @@ -168,15 +163,12 @@ public function handle(ServerRequestInterface $request) : ResponseInterface // FIXME: This should all be one candidates module, not a bunch // of hacks in the base router. if (preg_match("/^([0-9]{6,10})$/", $components[0])) { - $baseurl = $uri->withPath("")->withQuery(""); - - $factory->setBaseURL((string )$baseurl); + $baseurl = $request->getAttribute("baseurl"); + $factory->setBaseURL((string) $baseurl); if (count($components) == 1) { - $request = $request - ->withAttribute("baseurl", $baseurl->__toString()) - ->withAttribute("CandID", $components[0]); + $request = $request->withAttribute("CandID", $components[0]); - $module = $this->loris->getModule("timepoint_list"); + $module = $loris->getModule("timepoint_list"); $module->registerAutoloader(); $requestloglevel = $logSettings->getRequestLogLevel(); @@ -194,7 +186,7 @@ public function handle(ServerRequestInterface $request) : ResponseInterface // Fall through to 404. We don't have any routes that go farther // than 1 level.. - return (new \LORIS\Middleware\PageDecorationMiddleware($this->user)) + return (new \LORIS\Middleware\PageDecorationMiddleware($user)) ->process( $request, new NoopResponder(new \LORIS\Http\Error($request, 404))