--- /dev/null
+<?php
+
+namespace wcf\http\middleware;
+
+use Laminas\Diactoros\Response\RedirectResponse;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\MiddlewareInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+use wcf\acp\action\FullLogoutAction;
+use wcf\acp\form\LoginForm;
+use wcf\acp\form\MultifactorAuthenticationForm;
+use wcf\acp\form\ReauthenticationForm;
+use wcf\http\Helper;
+use wcf\system\exception\AJAXException;
+use wcf\system\exception\NamedUserException;
+use wcf\system\request\LinkHandler;
+use wcf\system\request\RequestHandler;
+use wcf\system\user\multifactor\TMultifactorRequirementEnforcer;
+use wcf\system\WCF;
+use wcf\system\WCFACP;
+
+/**
+ * Checks all ACP requests for proper authentication.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2022 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @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()
+ ]
+ )
+ );
+ }
+}
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
*/