Symfony firewall access_control doesn't work if no firewall match - symfony

I have a problem with symfony firewalls...
Here an example:
app_customer_provider:
security: true
lazy: true
stateless person: true
custom_authenticators:
- XXX\Authenticator
- XXX\Authenticator
request_matcher: - XXX\Mathcer
In the access control access_control I have a protected route, ex /ping.
When I try to login with a JWT that matches the request_matcher, the authenticator works fine; blocking or allow access to the various routes.
The problem arises when no request_matcher intervenes. All routes are open despite being role protected, ex /ping is accessible.
Can you help me about it? Is there something I’m doing wrong or I’m missing?

It seems it is a symfony behavior...
If no firewall is hitted, the security_access is not considered.
I use this workaround, use a fallback firewall in this way :
secured_area:
pattern: ^/

Related

Symfony 4 - Security - Share context from multiple firewalls to one another

I think I reached some limit with the Symfony security component. Here's my problem: I have two firewall to manage two users type (with two distinct entities) authentication and access to two different part of the website. I have a third part to manage files, uploads, ... that have to be private and both users types need to access it.
So I made multiple providers in security.yml:
providers:
# used to reload user from session & other features (e.g. switch_user)
core_user_provider:
entity:
class: Akyos\CoreBundle\Entity\User
property: email
platform_user_provider:
entity:
class: App\Entity\Platform\UserPlatform
property: email
file_manager_provider:
chain:
providers: [core_user_provider, platform_user_provider]
And multiple firewalls
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
core:
pattern: ^/(app|admin)/
context: shared
provider: core_user_provider
anonymous: lazy
guard:
authenticators:
- Akyos\CoreBundle\Security\CoreBundleAuthenticator
logout:
path: app_logout
target: 'home'
remember_me:
secret: '%kernel.secret%'
lifetime: 604800 # 1 week in seconds
path: /
file_manager:
pattern: ^/(file-manager)
context: shared
provider: file_manager_provider
anonymous: lazy
guard:
authenticators:
- App\Security\FileManagerAuthenticator
logout:
path: file_manager_logout
target: 'home'
remember_me:
secret: '%kernel.secret%'
lifetime: 604800 # 1 week in seconds
path: /
platform:
pattern: ^/(platorm_login|plateforme)
context: shared
provider: platform_user_provider
anonymous: lazy
guard:
authenticators:
- App\Security\PlatformAuthenticator
logout:
path: platform_logout
target: 'home'
remember_me:
secret: '%kernel.secret%'
lifetime: 604800 # 1 week in seconds
path: /
main:
anonymous: lazy
So a Platform user can't access Core, and a Core user can't access Platform. But both users needs to access File-manager, without re-log in. I can't place /file-manager urls under Core or Platform firewall because the other wouldn't grant access to it. So I need a third firewall to manage File-manager access. It use a chain provider that groups both Core and Platform users. It doesn't work either because if a Core user authenticate through the Core firewall it is not authenticated for the File-manager one, so it redirect to File-manager login page.. if the user logs in File-manager part it can access it, but when it turn back to Core part it has to re-connect again.
I tried several things but the closest solution is to use the context option on firewalls, so when a user is logged in through Core part it can access File-manager part without re-log because both firewalls shared the same context. That's what I want. But I also need it for the Platform firewall! So I also add same context option to it, and it works, both users types can access File-manager without log in again :D But as the three firewalls share the same context, Core users can access to Platform and vice-versa, and that breaks all the separation logic.. :'(
I need a way to tell security component "File-manager firewall has same context as Core firewall, and File-manager firewall has same context as Platform firewall, but Core and Platform firewalls doesn't share the same context". Something like this:
firewalls:
core:
context: core
file_manager:
context: [core,platform]
platform:
context: platform
main:
anonymous: lazy
I found nothing about it. Maybe it can't be done, maybe I have to create custom provider or authenticator to hack it. Maybe I can make it without Symfony, it's only php after all, so could I make the file-manager part accessible to every one (so under the main firewall) and add a Listener that would check if the request is for file-manager, find in session if there is a previous logged-in user, check if the user is a Core or a Platform user and then redirect if not... ? How can I find the previous Core or Platform user in session, when on a "main firewall" page (= authenticated as anonymous), without Symfony functions ? I'm not good enough to know how I could achieve that. Help ?
Thanks
I've finally let 3 providers and firewalls with context shared between it. To prevent Core users to access Platform, and vice-versa, I added access_control:
- { path: ^/file-manager, roles: [ROLE_PLATFORM, ROLE_CORE] }
- { path: ^/core, roles: ROLE_CORE }
- { path: ^/plateforme, roles: ROLE_PLATFORM }
so it ends with a 403 access denied error. That's not the behavior I want so I also added 'access_denied_url' option on both core and platform firewall to redirect user on the good login page. As contexts are shared, users are already logged, so on login template I check instance of user object to advice him to disconnect first before trying to access this part.
{% if instanceOf(app.user, 'App\\Entity\\PlatformUser') %}
You're already logged in Platform space, please log out before access Core space.
{% else %}
You're already logged in as {{ app.user.username }}, log out or access core panel.
{% endif %}
It's a bit confusing to have context shared between parts that shouldn't share anything but access to file-manager, but no user can access other part so.. that works.

PUT method for Symfony firewall form_login paths

I'm configuring the Symfony firewall to handle a login form. The trick is that the check route must use the PUT method, not POST.
This is the part of my firewall configuration that is related to my problem:
firewalls:
main:
pattern: ^/
anonymous: true
provider: customer
form_login:
login_path: api_user_login
check_path: api_user_login_connect
As you can see, I don't use paths but routes instead. Mainly because I need to specify the method for the check_path (PUT in my case)
Here is my api_user_login_connect route:
api_user_login_connect:
path: /users
defaults: { _controller: ApiBundle:Login:connect }
methods: [PUT]
Symfony issues an error when I try to access /users, saying that there is no route for /users (in GET I assume). So I understand that the firewall won't let me use the PUT method for the check_path route, or that it doesn't read the whole route from the configuration, but just the path that is defined in the route.
How can I tell Symfony firewall to check credentials using a PUT request?
We found the answer before I posted this question, here it is...
For the firewall to accept login check on methods other than GET, you must this option: post_only: false. It's true by default.
firewalls:
main:
pattern: ^/
anonymous: true
provider: customer
post_only: false

Symfony 2 "Your session has timed out or you have disabled cookies"

One of three things caused this and I am not sure which of the 3 it was. So I will mention all 3 in the hope it will help others save time.
Initially I changed database user credentials within parameters.yml
This wasn't working as the user in question couldn't log in from localhost. That said, I used the site to test the connection, which might have upset the cookie.
I had some cache folder permissions issues due to a missing image. So I had to clear the cache and adjust some permissions as you do every time.
Finally, I changed the paths for security.yml
form_login:
login_path: /login
check_path: /login_check
logout:
path: /logout
to:
form_login:
login_path: /account/login
check_path: /account/login_check
logout:
path: /account/logout
Along with the appropriate changes in routing.yml
The result was that my already logged in user not longer passed security credentials and if I tried to login in via a different user/browser, I was always faced with:
"Your session has timed out or you have disabled cookies"
Many many hours were spent following red herrings, checking security, login handling, redis etc.
Answer below.
I ultimately found the answer here:
Symfony authentication - can't get past login page in production (The answer by pleerock)
But wanted to link the error message in my subject line with this solution below:
security:
firewalls:
main:
form_login:
require_previous_session: false
This fixed the issue for browsers which hadn't been logged in prior to the problem.
For my browser which had already been logged in, I had to manually delete the session cookie to get things working again.
I think Adi's answer is not a solution, just work around.
i did realise
in config.yml there is cookie_domain parameter;
session:
save_path: ~
cookie_domain: %cookie_domain%
if you use a custom domain like test.myapp you should set here the same. When these both do not match this problem occurs.
It should appear as below;
cookie_domain: 'test.myapp'
your actual domain: test.myapp
i hope this helps you.

Authentication : app_dev.php/_wdt/511509b611682 instead of homepage

I'm looking at security in Symfony 2.0 and I have a problem I can't explain.
My security bundle is very simple for now.
I try to put everything working before putting real providers.
So, now, when I go on the site, It sends me on the login form as expected. I put the user and the password and then, instead of home page, i am send to "/.../app_dev.php/_wdt/511509b611682" (different number each time).
My user isn't marked as authenticated in the debug toolbar.
If i take off the end of the url, i arrive on homepage. My user seems to be identified and authenticated in the debug toolbar.
This arrives only in dev environment. In prod environment, it seems to work as expected.
Thanks for your help
Further to #artworkad's answer, you have to add the dev firewall before your main firewall, otherwise it will never match:
security:
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
#...
When you log in using development environment you will be redirected to index_dev.php/_wdt/4e95412bc6871.
WDT aka web debug toolbar can be removed from the scope of the firewall via
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
Actually it is not related to SecurityBundle, anyways it is documented here https://github.com/FriendsOfSymfony/FOSUserBundle/issues/368
Also you can put this lines in access_control:
{ path: ^/_wdt, role: IS_AUTHENTICATED_ANONYMOUSLY }
{ path: ^/_profiler, role: IS_AUTHENTICATED_ANONYMOUSLY }
but I prefer keep my access_control for only my app routes.

Authenticate multiple symfony2 firewalls with one login form

I have two firewalls:
api (for API calls)
main (for everything else)
My client app login happens via the main firewall. However, it does interact with endpoints under the api firewall to fetch data. The problem here is that I don't want to force the user to log in a second time for authenticating against the second firewall.
How can I authenticate against both firewalls with just a single login form?
Perhaps you could try the 'context' firewall property.
Say you have a configuration something like this (which presumably you do):
security:
// providers etc ...
firewall:
main:
pattern: # ...
provider: my_users
http_basic: ~
api:
pattern: # ...
provider: my_users
http_basic: ~
In this case the user's session will contain a '_security_main' property after authenticating against the 'main' firewall, and then when they attempt to access an 'api' location they will be prompted to re-auth and will then gain a '_security_api' session property.
To prevent this re-prompt, you can add the 'context' property to each firewall definition you wish to share the same authentication - so:
security:
# providers etc ...
firewall:
main:
pattern: # ...
provider: my_users
http_basic: ~
context: primary_auth # new
api:
pattern: # ...
provider: my_users
http_basic: ~
context: primary_auth # new
In this case, upon authentication with the 'main' firewall, a '_security_primary_auth' property will be set in the user's session. Any subsequent requests inside the 'api' firewill will then use the value of '_security_primary_auth' to establish authentication status (and so the user will appear authenticated).
Of course this authentication context sharing will work both ways around (whether they auth first with the 'main' or the 'api' firewall) - if you only wanted transience in one direction, things would be more complex.
Hope this helps.

Resources