How can I add the _locale parameter to security paths? - security

I setup my security settings to protect everything which is under the root path /, exept for a public page to view the privacy policy, /privacy. Everything works fine.
# security.yml
access_control:
- { path: ^/privacy$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, role: ROLE_USER }
Now I added some different translations to the privacy policy page, so that the route changes from /privacy to /{_locale}/privacy. Unfortunately I cannot add the _locale parameter to the security path like this:
access_control:
...
- { path: ^/{_locale}/privacy$, role: IS_AUTHENTICATED_ANONYMOUSLY }
So how do I have to modify my security.yml so that I can add the _locale to the path and to restrict it to predefined languages, e.g. (en|fr|es|de)?

Nowadays (since Symfony 4.1 or later) you can define the locales in one place and use it everywhere in your application
in config/services.yaml add
parameters:
myAppName.locales: en|fr|es|de
in config/routes.yaml
cms:
prefix: /{_locale}/
controller: App\Controller\DefaultController::index
requirements:
_locale: '%myAppName.locales%'
in config/packages/security.yaml
security:
## .... no changes here
access_control:
- { path: ^/(%myAppName.locales%)/cms, roles: ROLE_ADMIN }

If all your locales are 2-character ones (en|fr|es|de|...) you can use a more generic regex like this:
- { path: '^/[a-z]{2}/privacy$', role: 'IS_AUTHENTICATED_ANONYMOUSLY' }
This way you won't have to touch your security.access_control every time you add a new locale.
For locales in the form EN_en you could use something like this btw:
- { path: '^/[a-zA-Z]{2}_[a-zA-Z]{2}/privacy$', role: 'IS_AUTHENTICATED_ANONYMOUSLY' }

I managed to solve the problem like this
access_control:
...
- { path: ^/(en|fr|es|de)/privacy$, role: IS_AUTHENTICATED_ANONYMOUSLY }
A recommendation for other answerers: I now have to enter this string (en|fr|de) into all routes manually. When I add a language I have to modifiy this string in many occurences. It would be much better if one could define a string
parameters:
languages: "(en|fr|es|de)"
And use this in the route
- { path: ^/%languages%/privacy$, role: IS_AUTHENTICATED_ANONYMOUSLY }
But I don't think this will work.

I have the same issue and I resolve it by making some changes to the pattern in my secure area in firewalls setting. And then updating the access controls. The code is below
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
admin_area:
context: admin_area
anonymous: ~
pattern: ^/(en|ar)/admin/*
form_login:
login_path: admin_login
check_path: admin_login
default_target_path: admin_home
provider: admins
logout:
path: admin_logout
target: admin_login
below is the access_controll
access_control:
- { path: ^/(en|ar)/admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/(en|ar)/admin, roles: ROLE_ADMIN }
- { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }

Related

symfony security prod doesn't work

I'm using symfony 3.2.3, and I want restrict access on same pages.
Ok, no problem! Configuring security.yml, all can be done, and actually it works in dev environment.
But, when I switch my symfony site in production mode, nothing works any more!
Switching to prod commands:
php bin\console cache:clear --env=prod --no-debug
I have 4 pages:
/homepage (can access everyone)
/lucky (can access everyone)
/auth_area (can access only logged users)
/backend (can access only ROLE_ADMIN users)
Here my security.yml
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
role_hierarchy:
ROLE_USER_LOGGED: IS_AUTHENTICATED_FULLY
ROLE_ADMIN: ROLE_USER_LOGGED
providers:
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
default:
anonymous: ~
http_basic: ~
form_login:
provider: fos_userbundle
csrf_token_generator: security.csrf.token_manager
logout:
path: /logout
target: /
access_control:
- { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/backend, roles: ROLE_ADMIN }
- { path: ^/auth_area, roles: IS_AUTHENTICATED_FULLY }
Finally, all works fine on dev, nothing works in prod: anonymous user can access everywhere! ;(
Thanks in advance!
As written by gp_sflover in a comment:
PS: I think the first directive is always matched and cause your problem.
and in the documentation:
For each incoming request, Symfony checks each access_control entry to find one that matches the current request. As soon as it finds a matching access_control entry, it stops - only the first matching access_control is used to enforce access.
So you have to change the order of your rules and put the rule that match every request (path: ^/) at the end:
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/backend, roles: ROLE_ADMIN }
- { path: ^/auth_area, roles: IS_AUTHENTICATED_FULLY }
- { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }

Symfony access control forbids user with correct role

security.yml:
role_hierarchy:
admin: [test, simple]
providers:
database:
entity: { class: UserBundle:User, property: username }
firewalls:
dev:
pattern: ^/(_(profiler|wdt|error)|css|images|js)/
security: false
prod:
pattern: ^/
provider: database
anonymous: true
form_login:
login_path: public_login
check_path: public_login_check
default_target_path: dashboard
always_use_default_target_path: true
csrf_provider: form.csrf_provider
logout:
path: logout
target: public_login
access_control:
- { path: ^/(.+), roles: admin }
- { path: ^/$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
When i login, I get 403 forbidden exception. Then i check profiler/security, and roles looks like that:
Roles [ROLE_USER, admin]
When i switch access control to:
- { path: ^/(.+), roles: ROLE_USER }
It works fine.
Why the hell my access control doesn't allow me to access pages with "admin" role, but does with "ROLE_USER" ?
My goal is to drop built-in roles (like ROLE_USER, ROLE_ADMIN etc), because I'm writing application for existing database, which contains already defined roles for users, so i want to use them.
I have confirmed that 'ROLE_' prefix is required - its because symfony by default use its own RoleVoter implementation.
To get rid of this prefix, custom RoleVoter is needed. To do that you need to create custom class implementing RoleVoterInterface, make it service and tag it with 'security.voter', to enforce security layer to use it instead of default one.
Read more about implementing own RoleVoters on this example.
You are not using the right syntax for roles in the Security configuration
you should change
- { path: ^/(.+), roles: admin }
To:
- { path: ^/(.+), roles: ROLE_ADMIN }

Redirect loop on any url with https in Symfony2

Wherever I try to implement the https channel, there will be an infinite redirect loop. This is what the security.yml file looks like:
security:
encoders:
FOS\UserBundle\Model\UserInterface: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
main:
pattern: .*
form_login:
provider: fos_userbundle
csrf_provider: form.csrf_provider
check_path: /login_check
login_path: /login
default_target_path: /home
logout:
path: /logout
target: /index
security: true
anonymous: true
remember_me:
key: mySecret
lifetime: 604800 #seven days
path: /
domain: ~
access_control:
- { path: ^/js, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/css, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/_wdt, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/_profiler, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/index*, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/.*, role: ROLE_ADMIN }
- { path: ^/.*, roles: ROLE_USER }
For example if I change the admin path to:
- { path: ^/admin/.*, role: ROLE_ADMIN, requires_channel: https }
the loop will occur.
Also in the routing/entity.yml I tried something like this:
entity_index:
pattern: /
defaults: { _controller: MyBundle:Entity:index }
schemes: [https]
Anybody has an idea how to solve this?
I had the same problem because the server was sitting behind a reverse proxy, which commmunicated over ssl with the client, but http with the server the application was running on. I only used this, because the connection between RP and App Server is secure since it doesn't leave the internal network. if you're really positive you really have SSL running and this problem occurs, there's one ugly fix for that... But it's really last resort and to use on your own risk:
you can overwrite the server vars in your app.php like this:
$_SERVER['HTTPS'] = 'on';
$_SERVER['SERVER_PORT'] = 443;
again, this is an ugly fix, not a solution, only use at your only risks and responsibilty, I wouldn't do this if I didn't have the problem with my reverse proxy not forwarding the port and wasn't 100% sure I had SSL between client and RP. Don't forget, this makes you vulnerable if you're not really running valid ssl
For me the problem was not on the side of Symfony but Nginx config was wrong. In case you're using Nginx as well make sure, that you don't have this set:
fastcgi_param HTTPS off; Otherwise set it to on and restart the server. Hope it helps
While it looks like the original poster's issue was a case of HTTPS not being enabled on their development server, I recently encountered a similar issue, and thought I would share how I resolved it.
In my case, my security.yml looked something like this:
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_provider: form.csrf_provider
default_target_path: /dm
logout: true
anonymous: true
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
- { path: ^/dm, role: IS_AUTHENTICATED_REMEMBERED, requires_channel: https }
- { path: ^/dm/*, role: IS_AUTHENTICATED_REMEMBERED, requires_channel: https }
- { path: ^/*, role: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
And in my case, https worked on every page, except any that required any sort of login. Additionally, my javascript and css files I was pulling from google cdn were failing to load. In the end there were two issues with the code that I had:
Issue 1:
Identifying the channel multiple times. In my case, I had a directive in my routing.yml file that was specifying to use http, as well as the one in my security.yml file above, specifying https. This meant that the paths managed by my app (the ones that required me to login) were being routed to https by the security system, which sent it back to the routing system that tried to change it to http, then sent it back to the security system which changed it back to https, infinite redirect loop. By removing the directive in routing.yml, this issue was fixed.
Issue 2:
I was missing one line from my cdn configuration for assetic:
ssl: ['https://ajax.googleapis.com/ajax/libs/']
Placed in config.yml like so:
framework:
#esi: ~
translator: { fallbacks: ["en"] }
secret: "%secret%"
router:
resource: "%kernel.root_dir%/config/routing.yml"
strict_requirements: ~
form: ~
csrf_protection: ~
validation: { enable_annotations: true }
templating:
engines: ['twig']
#assets_version: SomeVersionScheme
packages:
cdn:
base_urls:
http: ['https://ajax.googleapis.com/ajax/libs/']
ssl: ['https://ajax.googleapis.com/ajax/libs/']
Missing this line meant that assetic ignored my package definition when using ssl, and tried to load the files from my own server, where they didn't exist.

Symfony2 - 2 firewalls, 1 login

Question: I want to create an admin part in my Symfony2 website that would be available only to users with a ROLE_ADMIN
I don't know if I should create a new firewall or use acces controls. I tried to do both together but the admin part is still accessible to all users.
Currently all the website is under secured area firewall and pages i want available to anonymous are freed with access control.
Here is my security.yml
security:
encoders:
Symfony\Component\Security\Core\User\User: plaintext
FOS\UserBundle\Model\UserInterface: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
my_facebook_provider:
id: my_user.facebook_provider
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/login$
security: false
context: login
admin:
pattern: /admin/
form_login:
provider: fos_userbundle
check_path: /login_check
login_path: /login
anonymous: ~
secured_area:
pattern: ^/
anonymous: ~
form_login:
login_path: /login
check_path: /login_check
default_target_path: tk_group_homepage
provider: fos_userbundle
remember_me: true
csrf_provider: form.csrf_provider
remember_me:
key: %secret%
lifetime: 31536000 # 365 days in seconds
fos_facebook:
app_url: "%api_facebook_name%"
server_url: "%api_facebook_server%"
check_path: /login_facebook_check
default_target_path: tk_user_homepage
provider: my_facebook_provider
logout:
path: fos_user_security_logout
target: fos_user_security_login
invalidate_session: false
context: login
access_control:
- { path: ^/$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/new, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/invitation, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/(subscribe|about|blog|press|contact), role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, role: IS_AUTHENTICATED_REMEMBERED }
- { path: ^/admin/, role: ROLE_ADMIN }
I am also thinking about checking in the controller is the user has an admin role and throwing an exception if not, as my admin part is only one page currently. But I do not know if it is best practice and it could be a problem if i want to extend my admin part.
And I do not want to create a new user provider as we would be only 2 admins.
Thank you very much,
Jules
You should remove the admin firewall and rely on access_control; If you have admin login form under the /admin/ URL, you of course will not be able to see it before logging in, so you should either use the /login form to sign in as admin, or modify your access_control:
- { path: ^/admin/login/, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
here is what official doc says about your situation:
Multiple firewalls don't share security context If you're using multiple firewalls and you authenticate against one firewall, you will
not be authenticated against any other firewalls automatically.
Different firewalls are like different security systems. To do this
you have to explicitly specify the same Firewall Context for different
firewalls. But usually for most applications, having one main firewall
is enough.
http://symfony.com/doc/current/book/security.html#book-security-common-pitfalls
You should read the whole Common pitfalls section
If you would really really like to use different firewalls, just do as the documentation states, and share the same firewall context beetween them. This is also described in the documentation:
http://symfony.com/doc/current/reference/configuration/security.html#reference-security-firewall-context
and here is a simple example:
admin:
(... other options ...)
context: my_security_context
secured_area:
context: my_security_context
(... other options ...)
The Access Control looks for the first match.
Because of that you need to put this line:
- { path: ^/admin/, role: ROLE_ADMIN }
Before this line:
- { path: ^/$, role: IS_AUTHENTICATED_ANONYMOUSLY }
If you do not, /admin/whatever matches the path ^/$ and needs no ROLE_ADMIN.

Check_path isn't behind symfony's firewall , how to correct this?

I am trying to authenticate against symfony2 firewall , here is my security config
security:
encoders:
Symfony\Component\Security\Core\User\User: plaintext
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
in_memory:
users:
user: { password: user, roles: [ 'ROLE_USER' ] }
admin: { password: admin, roles: [ 'ROLE_ADMIN' ]}
#main:
#entity: { class: Surgeworks\CoreBundle\Entity\User, property: username}
firewalls:
public:
pattern: .*
security: false
anonymous: true
form_login:
check_path: /{_locale}/admin/logincheck
login:
pattern: ^/{_locale}/admin/login$
security: false
anonymous: ~
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
anonymous: ~
secured_area :
provider: in_memory
pattern: ^/{_locale}/admin/.*
form_login:
check_path: /{_locale}/admin/logincheck
login_path: /{_locale}/admin/login
logout:
path : /{_locale}/admin/logout
target : /{_locale}/admin/
remember_me:
key: aSecretKey
lifetime: 3600
path: /admin/
domain: ~ # Defaults to the current domain from $_SERVER
access_control:
- { path: ^/{_locale}/admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/{_locale}/, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/ar/admin/logincheck, roles: ROLE_ADMIN }
- { path: ^/(ar|en|fr)/admin/, roles: ROLE_ADMIN }
and here is my routing in DaghoSiteBundle/Resources/config/routing.yml:
_admin:
pattern: /admin/
defaults: { _controller: DaghoSiteBundle:Login:login , _locale : ar }
requirements:
_locale: (ar|en|fr)
login:
pattern: /admin/login
defaults : { _controller: DaghoSiteBundle:Login:login , _locale : ar }
requirements:
_locale: (ar|en|fr)
logincheck:
pattern: /admin/logincheck
#defaults: { _controller: DaghoSiteBundle:Login:logincheck , _locale: ar }
#requirements:
#_locale: (ar|en|fr)
logout:
pattern: /admin/logout
I can't login through these setting ,
it always throw an exception
Unable to find the controller for path "/ar/admin/logincheck". Maybe
you forgot to add the matching route in your routing configuration
and even if had setup the route check_path page >> i would be able to view the check_path without being redirected to login page ..
/en/admin >> login page
/en/logincheck >> display the logincheck template (i.e /en/logincheck isn't behind firewall )
how to fix , or debug this issue , please advise
UPDATE:
sorry I might forget to write that i had prefixed my bundle with the {_locale} like this
in my routing.yml
DaghoSiteBundle:
resource: "#DaghoSiteBundle/Resources/config/routing.yml"
prefix: /{_locale}
requirements:
_locale: ar|en|fr
defaults: { _locale: ar }
I think you must change your route patterns to include your _locale parameter. Instead of, for example, pattern: /admin/logincheck you should use pattern: {_locale}/admin/logincheck
You can also debug your routes by using a console command app/console router:debug.
I am not 100% sure this fixes your problem, but I hope it helps you gather more info about your issue.
Why dont you try
logincheck:
pattern: /admin/login_check
#defaults: { _controller: DaghoSiteBundle:Login:logincheck , _locale: ar }
#requirements:
#_locale: (ar|en|fr)
This solved my problem, but my situation is simple and doesn't have the {_locale} functionality, but maybe it will help you anyway.
From the Security chapter of the Symf2 Book:
Common Pitfalls #3: Be sure /login_check is behind a firewall.
In your security.yml file it looks like your check_path route is /{_locale}/admin/logincheck, and your secured path is anything that's behind ^/{_locale}/admin/.*
so that seems to be good.
You might want to try removing the .* (do you really need it?)
and further down the file in the access control section you provide a specific entry to make sure the check_path requires authentication:
- { path: ^/ar/admin/logincheck, roles: ROLE_ADMIN }
Maybe try specifying this instead:
- { path: ^/{_locale}/admin/logincheck, roles: ROLE_ADMIN }
But actually, I was wondering, can you even use {placeholders} in the security.yml pattern values? I know you can in the routing file, but I'm not sure if the security yml works the same way? I don't see it being used in any examples in the main symf2 book in the security or routing chapters?

Resources