From 9c97f150bd04ca3330400a541ea14a4c682cd43e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 8 Aug 2022 14:45:10 +0200 Subject: [PATCH] Move ACP authentication check into middleware This is similar to #4935 / 51154ba3f8f1d09b54560d5d1933f9053ef409cb. --- .../EnforceAcpAuthentication.class.php | 137 ++++++++++++++++++ .../install/files/lib/system/WCFACP.class.php | 86 +---------- .../system/request/RequestHandler.class.php | 2 + 3 files changed, 147 insertions(+), 78 deletions(-) create mode 100644 wcfsetup/install/files/lib/http/middleware/EnforceAcpAuthentication.class.php diff --git a/wcfsetup/install/files/lib/http/middleware/EnforceAcpAuthentication.class.php b/wcfsetup/install/files/lib/http/middleware/EnforceAcpAuthentication.class.php new file mode 100644 index 0000000000..30de5aac21 --- /dev/null +++ b/wcfsetup/install/files/lib/http/middleware/EnforceAcpAuthentication.class.php @@ -0,0 +1,137 @@ + + * @package WoltLabSuite\Core\Http\Middleware + * @since 6.0 + */ +final class EnforceAcpAuthentication implements MiddlewareInterface +{ + use TMultifactorRequirementEnforcer; + + private const ALLOWED_CONTROLLERS = [ + LoginForm::class, + ReauthenticationForm::class, + FullLogoutAction::class, + MultifactorAuthenticationForm::class, + ]; + + /** + * @inheritDoc + */ + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + if (!RequestHandler::getInstance()->isACPRequest()) { + return $handler->handle($request); + } + + if (WCFACP::inRescueMode()) { + return $handler->handle($request); + } + + $controller = RequestHandler::getInstance()->getActiveRequest()->getClassName(); + if (\in_array($controller, self::ALLOWED_CONTROLLERS)) { + return $handler->handle($request); + } + + if (!WCF::getUser()->userID) { + return $this->handleGuest($request); + } + + if (!WCF::getSession()->getPermission('admin.general.canUseAcp')) { + return $this->handleNoAcpPermission($request); + } + + if (WCF::getSession()->needsReauthentication()) { + return $this->handleReauthentication($request); + } + + $this->enforceMultifactorAuthentication(); + + // force debug mode if in ACP and authenticated + WCFACP::overrideDebugMode(); + + return $handler->handle($request); + } + + private function handleGuest(ServerRequestInterface $request): ResponseInterface + { + if (Helper::isAjaxRequest($request)) { + throw new AJAXException( + WCF::getLanguage()->getDynamicVariable('wcf.ajax.error.sessionExpired'), + AJAXException::SESSION_EXPIRED, + '' + ); + } + + return new RedirectResponse( + LinkHandler::getInstance()->getControllerLink( + LoginForm::class, + [ + 'url' => (string)$request->getUri(), + ] + ) + ); + } + + private function handleNoAcpPermission(ServerRequestInterface $request): ResponseInterface + { + WCF::getTPL()->assign([ + '__isLogin' => true, + ]); + + if (Helper::isAjaxRequest($request)) { + throw new AJAXException( + WCF::getLanguage()->getDynamicVariable('wcf.ajax.error.permissionDenied'), + AJAXException::INSUFFICIENT_PERMISSIONS + ); + } else { + throw new NamedUserException( + WCF::getLanguage()->getDynamicVariable('wcf.user.username.error.acpNotAuthorized') + ); + } + } + + private function handleReauthentication(ServerRequestInterface $request): ResponseInterface + { + if (Helper::isAjaxRequest($request)) { + throw new AJAXException( + WCF::getLanguage()->getDynamicVariable('wcf.user.reauthentication.explanation'), + AJAXException::SESSION_EXPIRED + ); + } + + return new RedirectResponse( + LinkHandler::getInstance()->getControllerLink( + ReauthenticationForm::class, + [ + 'url' => (string)$request->getUri() + ] + ) + ); + } +} diff --git a/wcfsetup/install/files/lib/system/WCFACP.class.php b/wcfsetup/install/files/lib/system/WCFACP.class.php index f5fc42760e..3fb27f0be6 100644 --- a/wcfsetup/install/files/lib/system/WCFACP.class.php +++ b/wcfsetup/install/files/lib/system/WCFACP.class.php @@ -138,87 +138,17 @@ class WCFACP extends WCF exit; } - } elseif ( - empty($pathInfo) - || !\preg_match('~^/?(login|(full-)?logout|multifactor-authentication|reauthentication)/~i', $pathInfo) - ) { - $isAjax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'; - - if (WCF::getUser()->userID == 0) { - // work-around for AJAX-requests within ACP - if ($isAjax) { - throw new AJAXException( - WCF::getLanguage()->getDynamicVariable('wcf.ajax.error.sessionExpired'), - AJAXException::SESSION_EXPIRED, - '' - ); - } - - // build redirect path - $application = ApplicationHandler::getInstance()->getActiveApplication(); - if ($application === null) { - throw new SystemException("You have aborted the installation, therefore this installation is unusable. You are required to reinstall the software."); - } - - HeaderUtil::redirect( - LinkHandler::getInstance()->getLink('Login', [ - 'url' => RouteHandler::getProtocol() . $_SERVER['HTTP_HOST'] . WCF::getSession()->requestURI, - ]) - ); - - exit; - } else { - try { - WCF::getSession()->checkPermissions(['admin.general.canUseAcp']); - } catch (PermissionDeniedException $e) { - self::getTPL()->assign([ - '__isLogin' => true, - ]); - - if ($isAjax) { - throw new AJAXException( - self::getLanguage()->getDynamicVariable('wcf.ajax.error.permissionDenied'), - AJAXException::INSUFFICIENT_PERMISSIONS, - $e->getTraceAsString() - ); - } else { - throw new NamedUserException( - self::getLanguage()->getDynamicVariable('wcf.user.username.error.acpNotAuthorized') - ); - } - } - - if (WCF::getSession()->needsReauthentication()) { - if ($isAjax) { - throw new AJAXException( - self::getLanguage()->getDynamicVariable('wcf.user.reauthentication.explanation'), - AJAXException::SESSION_EXPIRED - ); - } - - HeaderUtil::redirect(LinkHandler::getInstance()->getLink('Reauthentication', [ - 'url' => RouteHandler::getProtocol() . $_SERVER['HTTP_HOST'] . WCF::getSession()->requestURI, - ])); - - exit; - } - - // The autoloader is not available during the definition of `WCFACP`, - // thus we are unable to use the trait directly. - // - // Workaround this issue by using an anonymous class. - (new class { - use TMultifactorRequirementEnforcer { - enforceMultifactorAuthentication as public enforce; - } - })->enforce(); - - // force debug mode if in ACP and authenticated - self::$overrideDebugMode = true; - } } } + /** + * @since 6.0 + */ + public static function overrideDebugMode() + { + self::$overrideDebugMode = true; + } + /** * @inheritDoc */ diff --git a/wcfsetup/install/files/lib/system/request/RequestHandler.class.php b/wcfsetup/install/files/lib/system/request/RequestHandler.class.php index e29ade2f5a..17666388ea 100644 --- a/wcfsetup/install/files/lib/system/request/RequestHandler.class.php +++ b/wcfsetup/install/files/lib/system/request/RequestHandler.class.php @@ -15,6 +15,7 @@ use wcf\http\middleware\CheckForExpiredAppEvaluation; use wcf\http\middleware\CheckForOfflineMode; use wcf\http\middleware\CheckSystemEnvironment; use wcf\http\middleware\CheckUserBan; +use wcf\http\middleware\EnforceAcpAuthentication; use wcf\http\middleware\EnforceCacheControlPrivate; use wcf\http\middleware\EnforceFrameOptions; use wcf\http\Pipeline; @@ -97,6 +98,7 @@ final class RequestHandler extends SingletonFactory new EnforceFrameOptions(), new CheckSystemEnvironment(), new CheckUserBan(), + new EnforceAcpAuthentication(), new CheckForEnterpriseNonOwnerAccess(), new CheckForExpiredAppEvaluation(), new CheckForOfflineMode(), -- 2.20.1