Symfony Security FOSOAuthServerBundle public and private routes - symfony

I am using the FOSOAuthBundle for my REST application
I would like most of my routes to require authorization however there are a few that should have public access
I have the following in my security.yml:
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:
oauth_token:
pattern: ^/login
security: false
api:
pattern: ^/
fos_oauth: true
stateless: true
anonymous: false
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: [ IS_AUTHENTICATED_FULLY ] }
For example:
I have a Products Entity and Controller
I would like the CRUD operations to be private except for Read
So: POST, PUT, DELETE on /products(/:id) should be private while GET should be public.
I have tried adding the following to the access_control:
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/products$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: [ IS_AUTHENTICATED_FULLY ] }
I thought this would open up all methods on /products but I get the error:
{
"error": "access_denied",
"error_description": "OAuth2 authentication required"
}
I have many entities and controllers I am trying to apply this to.
How would I go about opening up specific routes (including the method requirements)?

You can make new firewall with regex and set it like this. You have to put it in front of your api firewall in order to match this regex first.
api_anonym_area:
pattern: (^/api/products/.*)
methods: [GET]
security: false
Or you can make it
api_anonym_area:
pattern: (^/api/products/.*)
methods: [GET]
anonymous: true
with
access_control:
- { path: ^/api/products/.*, role: IS_AUTHENTICATED_ANONYMOUSLY}
In first case you wont have token, in in second case you will have token (its good when you expect authenticated or anonymous users to come).

To achieve this, best way would be to code the permissions in the controllers, I don't think this is possible via the security.yml configuration.
You should remove:
- { path: ^/, roles: [ IS_AUTHENTICATED_FULLY ] }
And manage permissions inside the controller actions, for example (taken from symfony documentation at http://symfony.com/doc/current/security.html)
public function updateProduct($id)
{
// The second parameter is used to specify on what object the role is tested.
$this->denyAccessUnlessGranted('ROLE_ADMIN', null, 'Unable to access this page!');
// Old way :
// if (false === $this->get('security.authorization_checker')->isGranted('ROLE_ADMIN')) {
// throw $this->createAccessDeniedException('Unable to access this page!');
// }
// ...
}

Related

FOS User Bundle every role except ADMIN is denied

I have a Symfony 3 CRM with FOS User Bundle installed for the login. The issue is, most users of this CRM will be engineers belonging to a company who are only allowed to see certain parts, so I have created their own dashboard specifically. Admin users can see everything and simply redirect to the main dashboard. However, it seems that only users with ROLE_ADMIN are allowed to access the CRM and everyone else is denied regardless of where they go.
Here is my security file:
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
role_hierarchy:
ROLE_STAFF: ROLE_USER
ROLE_ADMIN: ROLE_STAFF
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_token_generator: security.csrf.token_manager
# if you are using Symfony < 2.8, use the following config instead:
# csrf_provider: form.csrf_provider
use_referer: false
success_handler: login_success_handler
logout: true
anonymous: true
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, role: ROLE_ADMIN }
- { path: ^/staff-dashboard, role: ROLE_STAFF }
As you can see I have created a custom role called ROLE_STAFF which is assigned to engineers (or staff members) and they are allowed to view the staff-dashboard link. I have a test user with the ROLE_STAFF role but I still am unable to view staff-dashboard.
I also have the following if statement in my controller, so it redirects all those users who are NOT admin to the staff-dashboard:
if($userRole === "ROLE_ADMIN") {
return $this->render('AppBundle:pages:dashboard.html.twig', array(
'latest' => $latest,
'cashflow_chart' => $ob,
'job_chart' => $ob2
));
} else {
return $this->redirectToRoute('app_staff_dashboard');
}
But again, this does not work.
Any help with this is appreciated - I haven't yet found a solid solution.
Access control rules are processed in order specified in config. First matched rule (matched path) is checked and processing stops.
For your case 2 rules match path /staff-dashboard: 1 - { path: ^/, role: ROLE_ADMIN } and 2 - { path: ^/staff-dashboard, role: ROLE_STAFF }. The 1-st one is checked. Staff users don't pass this rule.
Change the order of these 2 rules.

Symfony Security: Auth is not needed even if user doesn't match role

I encountered a strange issue. I have the following security.yml:
security:
encoders:
Symfony\Component\Security\Core\User\User: plaintext
role_hierarchy:
ROLE_USER:
ROLE_EDITOR: [ROLE_USER]
ROLE_ADMIN: [ROLE_USER, ROLE_EDITOR]
providers:
in_memory:
memory:
users:
admin: { password: 123456, roles: [ 'ROLE_ADMIN' ] }
editor: { password: 123456, roles: [ 'ROLE_EDITOR' ] }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
backend:
pattern: ^/backend
anonymous: ~
provider: in_memory
form_login:
login_path: backend_login
check_path: backend_login_check
access_control:
- { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY, host: example\.com$ }
- { path: ^/backend_login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/backend, roles: ROLE_ADMIN }
- { path: ^/user/fetch, roles: ROLE_USER }
- { path: ^/level, roles: ROLE_USER }
- { path: ^/gallery, roles: ROLE_USER }
I have an window development machine with XAMPP running and everything works out properly. I can log in to the backend and if I'm not logged in and try to open a backend route, I'm redirected to the login page.
This is my routing portion:
backend_login:
pattern: /backend_login
defaults: { _controller: FooBackendBundle:Security:login }
backend_login_check:
pattern: /backend/login_check
But when I'm uploading it to my integration linux server, I can open the backend without having to log in. It seems like Symfony does not care about the role the current user has.
The code and the symfony version are both the exact same (Symfony 2.3).
If I remove the anonymous: ~ part from the backend firewall, it will redirect to the login page, but also creates an inifite redirection loop.
Does anybody have an idea how to solve this?
From the Symfony 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.
When you set access_control in your security config, you want to put your least-restrictive matches last. In your case you will always match on the first pattern since all routes match on ^/ and therefore do not require any authentication. Change your access_control to this:
access_control:
- { path: ^/backend_login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/backend, roles: ROLE_ADMIN }
- { path: ^/user/fetch, roles: ROLE_USER }
- { path: ^/level, roles: ROLE_USER }
- { path: ^/gallery, roles: ROLE_USER }
- { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }
I removed the host parameter as it didn't seem relevant.

FOSUserBundle allow access to whole site

guys!
I'am trying to configure user system (FOSUserBundle) for my Symfony app. All was going Ok until I tried to allow access to whole site and restrict access to some routes using access control in security configuration file.
firewalls:
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_provider: form.csrf_provider
default_target_path: /
logout: true
anonymous: true
access_control:
- { path: ^/$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/profile$, roles: ROLE_USER }
- { path: ^/admin$, roles: ROLE_ADMIN }
I've tried also
#
- { path: ^/.*, roles: IS_AUTHENTICATED_ANONYMOUSLY }
#
and when i go to index page (/), it redirects me to login page. I want to have index page visible for all visitors, not only for those who logged in.
Just can't figure out this moment. Thank you for answers.
try this:
access_control:
- { path: ^/profile$, roles: ROLE_USER }
- { path: ^/admin$, roles: ROLE_ADMIN }
- { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }
Your problem may be related to trying to specify the role as an array of role but you give only one value:
- { path: ^/*, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/profile$, role: ROLE_USER }
- { path: ^/admin$, role: ROLE_ADMIN }
for more roles you need the []
I resolved the issue. Sorry for my dumbness, i've been wandering for 2 days why this happen.
Redirect was from controller. My mistake, sorry. Thank you for answers anyway.
public function startAction()
{
$user = $this->getUser();
if ($user) {
return $this->render('HellfireCommonBundle:Index:index.html.twig', array('user' => $user));
} else {
return $this->redirect($this->generateUrl('fos_user_security_login'));
}
}

FOS UserBundle Access Denied

I'm trying to use FOS UserBundle to manage users on my project.
Here is my security.yml file :
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:
in_memory:
memory:
users:
user: { password: userpass, roles: [ 'ROLE_USER' ] }
admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_provider: form.csrf_provider
logout: true
anonymous: true
access_control:
#- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
I have manually set my user's rights in my controller like below :
public function testGroupsAction( UserInterface $user )
{
$user->addRole("ROLE_ADMIN");
$this->getDoctrine()->getManager()->persist($user);
$this->getDoctrine()->getManager()->flush();
echo "<pre>";
\Doctrine\Common\Util\Debug::dump($user->getRoles());
echo "</pre>";die;
}
the $user->getRoles() function returns me an array with all my user's roles :
array (size=3)
0 => string 'ROLE_SUPER_ADMIN' (length=16)
1 => string 'ROLE_ADMIN' (length=10)
2 => string 'ROLE_USER' (length=9)
(ROLE_SUPER_ADMIN has been added during my tests)
However when i try to reach a route like "/admin/my/route"n i've got a 403 access forbidden.
Any idea why Symfony doesn't want my user to access admin pages?
Edit :
When i look in the profiler, the user only has [ROLE_USER]...
Thank you.
I finally got it working.
Thanks to Zizoujab, I tried FOSUserBundle's commands to promote a user :
> php app/console fos:user:promote myUser
It worked perfectly well. However, as I have no ssh access nor any other command line tool on my server, i needed to do it via PHP code.
So i went to the Command code FOS\UserBundle\Command\PromoteUserCommand which uses the FOS\UserBundle\Util\UserManipulator to do actions on the user.
So if you want to modify your User directly in your controller, you can use it, but I don't know if it is the best way to do it. Just call it via your container like this :
/**
* #Route("/user/{id}", name="test_user")
* #ParamConverter("user" , class="MyBundle:User")
*/
public function testUserAction( UserInterface $user )
{
$userManipulator = $this->container->get("fos_user.util.user_manipulator");
$userManipulator->addRole($user,'ROLE_ADMIN');
return new Response();
}
Hope it helps.

Register and Reset form always available

today I have noticed I can always access the register and reset form regardless if I am authenticated or not.
Here is my security.yml:
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:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_provider: form.csrf_provider
logout:
delete_cookies:
activeGame: {}
anonymous: true
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
Regarding http://symfony.com/doc/current/cookbook/security/remember_me.html#forcing-the-user-to-re-authenticate-before-accessing-certain-resources it seems "normal" to be able to access this pages.
But how can I "easily" disable it for authenticated user or did I miss anything?
Thanks in advance!
You may be able to accomplish what you are looking for by using the newly introduced allow_if expression for access controls.
- { path: ^/register, allow_if: "not is_authenticated()" }
Another way may be:
- { path: ^/register, allow_if: "user == 'anon'" }
I havent fully tested this but it should only allow users who are not authenticated fully or authenticated remembered to access that path
Here is a little bit about the security
Here are some of the variable and functions available in expressions
Then here is some info on the Expressions you can use in allow_if
IF however, you do not want to throw a 403 Access Denied Exception when logged in users try and access those pages. Instead you would like to redirect them elsewhere then you can add a check to their respective controller actions. Something like:
public function registerAction()
{
if (true === $this->get('security.context')->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
return $this->redirect($this->generateUrl('some_route_to_send_them_to'));
}
// ...
}

Resources