diff --git a/src/Security/Authenticator.php b/src/Security/Authenticator.php index 424b4b2ba2..a6de495cd5 100644 --- a/src/Security/Authenticator.php +++ b/src/Security/Authenticator.php @@ -22,9 +22,11 @@ declare(strict_types = 1); namespace App\Security; use function App\Core\I18n\_m; +use App\Core\Log; use App\Core\Router\Router; use App\Entity\LocalUser; use App\Util\Common; +use App\Util\Exception\ClientException; use App\Util\Exception\NoSuchActorException; use App\Util\Exception\NotFoundException; use App\Util\Exception\ServerException; @@ -32,6 +34,7 @@ use App\Util\Nickname; use Stringable; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException; use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException; @@ -158,9 +161,23 @@ class Authenticator extends AbstractFormLoginAuthenticator implements Authentica if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) { return new RedirectResponse($targetPath); + } elseif (!\is_null($next = $request->request->get('_next') ?? $request->query->get('next'))) { + try { + if ($pos = mb_strrpos($next, '#')) { + $fragment = mb_substr($next, $pos); + $next = mb_substr($next, 0, $pos); + } + Router::match($next); + return new RedirectResponse(url: $next . ($fragment ?? '')); + } catch (ResourceNotFoundException $e) { + $user = Common::user(); + $user_id = !\is_null($user) ? $user->getId() : '(not logged in)'; + Log::warning("Suspicious activity: User with ID {$user_id} submitted a form where the `_next` parameter is not a valid local URL ({$next})"); + throw new ClientException(_m('Invalid form submission'), $e); + } + } else { + return new RedirectResponse(url: Router::url('root')); } - - return new RedirectResponse(Router::url('root')); } public function authenticate(Request $request): PassportInterface diff --git a/src/Util/Common.php b/src/Util/Common.php index 69db2abca6..1e185788cf 100644 --- a/src/Util/Common.php +++ b/src/Util/Common.php @@ -151,7 +151,7 @@ abstract class Common public static function ensureLoggedIn(): LocalUser { if (($user = self::user()) == null) { - throw new NoLoggedInUser(); + throw new NoLoggedInUser(self::getRequest()); // TODO Maybe redirect to login page and back } else { return $user; diff --git a/src/Util/Exception/NoLoggedInUser.php b/src/Util/Exception/NoLoggedInUser.php index 50d65046b5..95d0022c63 100644 --- a/src/Util/Exception/NoLoggedInUser.php +++ b/src/Util/Exception/NoLoggedInUser.php @@ -21,6 +21,8 @@ declare(strict_types = 1); namespace App\Util\Exception; +use Symfony\Component\HttpFoundation\Request; + /** * No user logged in * @@ -31,6 +33,10 @@ namespace App\Util\Exception; * @copyright 2020-2021 Free Software Foundation, Inc http://www.fsf.org * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later */ -class NoLoggedInUser extends ClientException +class NoLoggedInUser extends RedirectException { + public function __construct(Request $request) + { + parent::__construct('security_login', ['next' => $request->getRequestUri()]); + } } diff --git a/templates/security/login.html.twig b/templates/security/login.html.twig index d759840213..a41ac27414 100644 --- a/templates/security/login.html.twig +++ b/templates/security/login.html.twig @@ -41,6 +41,9 @@ + {% if app.request.query.has('next') %} + + {% endif %}