How to prevent access to specific routes in Symfony 2 - security

I'm new to symfony2 and I don't understand the way the security.yml works
I have 2 roles in my application: ROLE_USER, ROLE_ADMIN.
ROLE_USER: can only view (cannot CRUD)
ROLE_ADMIN: can do everything (can CRUD)
I have 2 basic questions:
Is there only 1 security.yml in app/config? Can I create a security.yml for my bundle ThePartner\EZFBundle\Resource\config where I can specify which route a role can access?
I want to prevent ROLE_USER to access to route blue_form_new, blue_form_create, blue_form_edit, blue_form_update, blue_form_delete? This ROLE_USER could only access blue_form, blue_form_show
Here is my ThePartner\EZFBundle\Resources\routing.yml
ThePartnerEZFBundle_blue_form:
resource: "#ThePartnerEZFBundle/Resources/config/routing/blueform.yml"
prefix: /blue_form
Here is the ThePartnerEZFBundle/Resources/config/routing/blueform.yml
blue_form:
pattern: /
defaults: { _controller: "ThePartnerEZFBundle:BlueForm:index" }
blue_form_show:
pattern: /{id}/show
defaults: { _controller: "ThePartnerEZFBundle:BlueForm:show" }
blue_form_new:
pattern: /new
defaults: { _controller: "ThePartnerEZFBundle:BlueForm:new" }
blue_form_create:
pattern: /create
defaults: { _controller: "ThePartnerEZFBundle:BlueForm:create" }
requirements: { _method: post }
blue_form_edit:
pattern: /{id}/edit
defaults: { _controller: "ThePartnerEZFBundle:BlueForm:edit" }
blue_form_update:
pattern: /{id}/update
defaults: { _controller: "ThePartnerEZFBundle:BlueForm:update" }
requirements: { _method: post|put }
blue_form_delete:
pattern: /{id}/delete
defaults: { _controller: "ThePartnerEZFBundle:BlueForm:delete" }
requirements: { _method: post|delete }
Thanks guys

All you need to configure is your security.yml. You can define multiple firewalls which will apply to different routes:
security:
firewalls:
your_first_firewall:
pattern: /public/ #this is regexp, so all urls starting with /public/ will match
security: false #this will be public, no firewall
your_second_firewall:
pattern: /nonPublic/
security: true
Remember that order of your firewall entries is important - first pattern matched will "win".
You can also import security settings from your bundle. To do that you need to import your bundle's security.yml file in main config.yml - described here)
# app/config/config.yml
imports:
- { resource: '#AcmeDemoBundle/Resources/config/security.yml' }

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 }

User roles in route settings

To check if a user have access to a controller depending on his role we usually do something like this in the top of the controller:
$this->denyAccessUnlessGranted('ROLE_ADMIN');
or use annotation:
/**
* #Security("has_role('ROLE_ADMIN')")
*/
And all protected controllers have such a check.
Is it possible to put this check in a route settings, e. g. this way or somehow similar:
protected_route:
path: /top_secret
defaults: { _controller: '...', roles: [ROLE_ADMIN] }
?
Yes you should look at this page:
http://symfony.com/doc/current/book/security.html#securing-url-patterns-access-control
# app/config/security.yml
security:
# ...
firewalls:
# ...
access_control:
# require ROLE_ADMIN for /admin*
- { path: ^/admin, roles: ROLE_ADMIN }

How can I add the _locale parameter to security paths?

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 }

Symfony2 lock full site with firewall

I would like to close my full site and only access to authenticated users, but I would like to keep some routes to public. Public routes would be:
/
/news
/registration
All otheres are locked.
I've made a firewall which looks like this:
firewalls:
user_login:
pattern: ^/
anonymous: ~
user_area:
pattern: ^/
form_login:
login_path: _main_index #this is a route to /
logout:
path: _main_logout #this is a route to /logout
target: _main_index #this is a route to /
invalidate_session: false
access_control:
- { path: ^/news, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/registration, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user-panel, roles: ROLE_ACTIVE_USER } #is this neccessary?
Then when I'm logging in to a restricted area (/user-panel) it sais I need to fully authenticate myself.
I store my roles in security.yml, not in database.
I hope you can help me! Thank you very much!
EDIT:
My loginCheckAction looks like this::
$encodedPassword = $this->get('user.user_service')->generatePasswordHash($user, $request->request->get('_password'));
if ($user->getPassword() == $encodedPassword) {
$user->setLastLoginOn(new \DateTime());
$this->em->user($rocker);
$this->em->flush();
$token = new UsernamePasswordToken($user, $user->getPassword(), 'user_area', array($user->getRoles()));
$request->getSession()->set('_security_user_area', serialize($token));
return $this->redirect($this->generateUrl('_user_panel'));
}
You need to add an catch-all firewall that requires authentication, like this:
- { path: ^/, roles: ROLE_ACTIVE_USER }
That will make all pages that aren't explicitely listed to be accessible without authentication (i.e. with IS_AUTHENTICATED_ANONYMOUSLY) inaccessible. However, since you didn't list your homepage yet, you'll need to add that too.
- { path: ^/$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
Make sure to put it above the catch-all entry, as Symfony processes entries from top to bottom, and stops when it found a matching entry.
You need to allow anonymus token
user_area:
pattern: ^/
form_login:
login_path: _main_index #this is a route to /
logout:
path: _main_logout #this is a route to /logout
target: _main_index #this is a route to /
invalidate_session: false
anonymous: ~
Try this.
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/registration, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/news, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/(css|js), roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/(_wdt|_profiler), roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_ACTIVE_USER }
I think login_check is used internally by the symfony security system just like logout. This empty function should work.
/**
* #Route("/login_check", name="login_check")
*/
public function loginCheckAction()
{
}
If you want to redirect the user to the panel route after successful login you need to implement login event listener. Symfony documentation How to create an Event Listener.
Some resources for symfony loginlistener.
http://www.ens.ro/2012/03/14/symfony2-login-event-listener/
https://gist.github.com/smottt/1075753
First of all, one of the major problem was the multiple firewall on the same route.
Symfony uses /login route to login, /login_check to check credentials and /logout to log out by default. For me, all routes was good, except the login route, because I wanted to log in users from / route.
Because of this, I had to implement UserProviderInterface into my UserRepository like this: Authenticating Someone with a Custom Entity Provider - Symfony2 documentation
I just modified this on my way (I user email as username).
Logincheck and logout is working automatically, because I use the default routes.
And for all of this, you have to implement AdvancedUserInterface to Users entity!!

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