phpsymfonyinternationalizationsymfony5symfony-routing

Internationalized routing don't work fine with security access controls


I'm trying to use Internationalized routing.

So for one route many URLs.

But one route still being uniquely identified by its name.

This is a great feature but the doc don't says how to deal with the security component.

In fact, I'm using url to make a basic secure of the app.

But since every URL can be different regarding on the locale, the only way I found to make thing still working is to duplicate as many path as route name got.

This my security.yaml right now :

# ./config/packages/security.yaml @ line 27 -- 41
access_control:
        # Allow every user to visit index_no_locale page (Default page when no locale is selected).
        # This will redirect to login page with default locale.
        - { path: ^/$, roles: IS_AUTHENTICATED_ANONYMOUSLY}
        # Allow anonymous to visit login page.
        - { path: ^(/en/login|/fr/connexion|/de/verbindung), roles: IS_AUTHENTICATED_ANONYMOUSLY}
        # Allow anonymous to visit password_reset page.
        - { path: ^(/en/password/reset|/fr/mot-de-passe/reinitialisation|/de/passwort/zurücksetzen), roles: IS_AUTHENTICATED_ANONYMOUSLY}
        # Allow anonymous to visit email_send_password_forgot
        - { path: ^(/en/email/send/password/forgot|/fr/courriel/envoyer/mot-de-passe|/de/email/schreiben/passwort/vergessen), roles: IS_AUTHENTICATED_ANONYMOUSLY}
        # Allow only admin to visit admin url
        - { path: ^/admin, roles: IS_ADMIN}
        # Allow authenticated user ONLY to visit every other urls
        # Specific user right controls are managed inside controller
        - { path: ^/, roles: IS_AUTHENTICATED_FULLY}

As I say this is working, but I've got to duplicate path for every existing route.

I let you see the route for better understand :

> php bin/console debug:router
  Name                             Method   Scheme   Host   Path      
-------------------------------- -------- -------- ------ --------------------
  password_reset.en                ANY      ANY      ANY    /{_locale}/password/reset/{user_id}/{token_value}           
  password_reset.fr                ANY      ANY      ANY    /{_locale}/mot-de-passe/reinitialisation/{user_id}/{token_value}
  password_reset.de                ANY      ANY      ANY    /{_locale}/passwort/zurücksetzen/{user_id}/{token_value}    
  login.en                         ANY      ANY      ANY    /{_locale}/login                                            
  login.fr                         ANY      ANY      ANY    /{_locale}/connexion                                        
  login.de                         ANY      ANY      ANY    /{_locale}/verbindung                                       
  logout                           ANY      ANY      ANY    /{_locale}/logout                                           
  index_no_locale                  ANY      ANY      ANY    /                 
...

I really don't like this for evident reason of redundant modifications to opeer each time a URL have to be added or modified.

The main problem is when user is not logged in and visit a URL where IS_AUTHENTICATED_FULLY role is required.

So now the 'Voter' say that access is denied and redirect response to login_path define into security.yaml

Here is the problem. I can't define a proper path to login_path because I can't guess what was the locale used by the user. So I hardcoded this :

# ./config/packages/security.yaml @ line 1 -- 21
security:
    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    providers:
        ...
    firewalls:
        dev:
            ...
        main:
            anonymous: true

            form_login:
                login_path: /en/login
                check_path: /en/login
                provider:   my_provider
            logout:
                path: /en/logout
            guard:
                ...

Same for logout actually...


Solution

  • According to this https://symfony.com/doc/current/security.html#logging-out

    You can have security.firewalls.main.logout.path referring to a route name. As for login, you can also refer to a route name, but the recommended way to do it is with guard authenticators see https://symfony.com/doc/current/security/form_login.html.

    EDIT:

    As for the access_control, when I look at the code in Symfony\Component\HttpFoundation\RequestMatcher, it does not look for a route name. So I see two options.

    1. You find a way to create your own RequestMatcher and make Symfony using it, which in my opinion seems overcomplicated for what you need.
    2. Make an entry for every locale in access_control which you are kind of doing right now. For better readability, I would normally put every entry on its own line instead of using regex or like you do, but that's just me.