I created customs Twig templates for http error display to keep the site design unified by extending my base layout. (I want to keep my navigation menu and display the error, unlike the regular error messages)
It's working as expected but for the 404.
In the navigation menu of my base layout, I have a lot of is_granted('SOME_ROLES')
to display the availables sections of the site depending of user's rights. When a 404 is thrown, the navigation menu is displayed as if the user is disconnected : {% if is_granted("IS_AUTHENTICATED_REMEMBERED") %}
being false.
After some searches, I found that the router is executed before the firewall. Since no route is found when a 404 is thrown, the firewall isn't executed and the rights aren't send to the template.
The only workaround I found (source from 2014) is to add at the very bottom of the routes.yaml file this route definition :
pageNotFound:
path: /{path}
defaults:
_controller: App\Exception\PageNotFound::pageNotFound
Since every other routes hasn't match, this one should be the not found.
The controller :
<?php
namespace App\Exception;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class PageNotFound
{
public function pageNotFound()
{
return (new NotFoundHttpException());
}
}
Because a controller is executed, the firewall is executed and the 404 error page is shown as I expected (hooray !).
My question is : Is there any proper way to fix that issue instead of that workaround?
We had a similar issue.
example.com/supersecretarea/
, we wanted than unauthorized users get a 403 error code when accessing any url behindexample.com/supersecretarea/
, even in the event that the page doesn't exist. Symfony's behavior does not allow that and checks for a 404 (either because there is no route or because the route has parameter which didn't resolve, likeexample.com/supersecretarea/user/198
when the is no user198
).What we ended up doing was to override the default router in Symfony (
Symfony\Bundle\FrameworkBundle\Routing\Router
) to modify its behavior:CatchAllController
simply renders the 404 error page:What happens is that during the regular process of Symfony's router, if something should trigger a 404 error, we catch that exception within the
matchRequest
function. This function is supposed to return information about which controller action to run to render the page, so that's what we do: we tell the router that we want to render a 404 page (with a 404 code). All the security is handled in betweenmatchRequest
returning andcatchAll
being called, so firewalls get to trigger 403 errors, we have an authentication token, etc.There is at least one functional issue to this approach (that we managed to fix for now). Symfony has an optional system that remembers the last page you tried to load, so that if you get redirected to the login page and successfully log in, you'll be redirected to that page you were trying to load initially. When the firewall throws an exception, this occurs:
But now that we allow non-existing pages to trigger firewall redirections to the login page (say,
example.com/registered_users_only/*
redirects to the loading page, and an unauthenticated user clicks onexample.com/registered_users_only/page_that_does_not_exist
), we absolutely don't want to save that non-existing page as the new "TargetPath" to redirect to after a successful login, otherwise the user will see a seemingly random 404 error. We decided to extend the exception listener'ssetTargetPath
, and defined a service that toggles whether a target path should be saved by the exception listener or not.That's the purpose of the commented
$this->targetPathSavingStatus->disableSaveTargetPath();
line from above: to turn the default-on status of whether to save target path on firewall exceptions to off when there's a 404 (thetargetPathSavingStatus
variables here point to a very simple service used only to store that piece of information).This part of the solution is not very satisfactory. I'd like to find something better. It does seem to do the job for now though.
Of course if you have
always_use_default_target_path
totrue
, then there is no need for this particular fix.EDIT:
To make Symfony use my versions of the Router and Exception listener, I added the following code in the
process()
method ofKernel.php
: