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.
Related
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.
In my app I have 2 parts, a frontend generated manually and a backend dashboard generated with Sonata admin. And I have an issue with the login of both parts:
app/config/security.yml
security:
encoders:
FOS\UserBundle\Model\UserInterface: sha512
m_and_m\UsuariosBundle\Entity\Usuarios: { algorithm: sha512, iterations: 10 }
m_and_m\ClientesBundle\Entity\Clientes: { algorithm: sha512, iterations: 10 }
access_control:
# URL of FOSUserBundle which need to be available to anonymous users
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
# Admin login page needs to be access without credential
- { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/login_check$, role: IS_AUTHENTICATED_ANONYMOUSLY }
# Secured part of the site
# This config requires being logged for the whole site and having the admin role for the admin part.
# Change these rules to adapt them to your needs
- { path: ^/admin/login ,role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/logout ,role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/* ,role: [ROLE_ADMIN, ROLE_SONATA_ADMIN,TOTAL] }
- { path: ^/usuarios/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/usuarios/registro, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/usuarios/*, roles: ROLE_USUARIO }
- { path: ^/clientes/*, roles: ROLE_CLIENTES }
role_hierarchy:
ROLE_ADMIN: [ROLE_USER, ROLE_SONATA_ADMIN,ROLE_USUARIO,ROLE_CLIENTE]
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
#SONATA:
#- ROLE_SONATA_PAGE_ADMIN_PAGE_EDIT # If you are using acl then this line must be commented
providers:
fos_userbundle:
id: fos_user.user_manager
chain_provider:
chain:
providers: [usuarios, clientes]
usuarios:
entity: { class: m_and_m\UsuariosBundle\Entity\Usuarios, property: email }
clientes:
entity: { class: m_and_m\ClientesBundle\Entity\Clientes, property: email }
firewalls:
# -> custom firewall for the admin area of the URL
admin:
pattern: /admin(.*)
context: user
form_login:
provider: fos_userbundle
login_path: /admin/login
use_forward: false
check_path: /admin/login_check
failure_path: /admin/login
logout:
path: /admin/logout
target: /admin
anonymous: true
# -> end custom configuration
# default login area for standard users
frontend:
pattern: ^/*
context: frontend
provider: chain_provider
anonymous: ~
form_login:
login_path: usuarios_login
check_path: usuarios_login_check
logout:
path: usuarios_logout
UsuariosBundle/Resources/config/services.yml
services:
usuarios.admin.usuarios:
class: m_and_m\UsuariosBundle\Admin\UsuariosAdmin
arguments: [~, m_and_m\UsuariosBundle\Entity\Usuarios, SonataAdminBundle:CRUD]
tags:
- {name: sonata.admin, manager_type: orm, group: Usuarios(Admin), label: Usuarios}
login_listener:
class: m_and_m\UsuariosBundle\Listener\LoginListener
arguments: [#security.context, #router]
tags:
- { name: kernel.event_listener, event: security.interactive_login }
- { name: kernel.event_listener, event: kernel.response }
Following the doc I have created a LoginListener in my usuariosbundle.
When I log in from the frontend all goes perfect. But the Sonata admin dashboard login goes to the LoginListener.php file and returns an error:
FatalErrorException: Error: Call to undefined method Application\Sonata\UserBundle\Entity\User::getNombre() in
C:\wamp\www\m_and_m\src\m_and_m\UsuariosBundle\Listener\LoginListener.php
line 24
LoginListener.php
<?php
namespace m_and_m\UsuariosBundle\Listener;
use Symfony\Componene\EventDispatcher\Event;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Routing\Router;
use Symfony\Component\HttpFoundation\RedirectResponse;
class LoginListener
{
private $contexto, $router, $usuario=null,$role=null;
public function __construct(SecurityContext $context, Router $router)
{
$this->contexto=$context;
$this->router = $router;
}
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
$token = $event->getAuthenticationToken();
$this->usuario = $token->getUser()->getNombre();
$this->role = $token->getRoles();
$this->role=$this->role[0]->getrole();
if($this->role=='ROLE_CLIENTE' && $token->getUser()->getActivo()==false){
$this->usuario = null;
}
}
public function onKernelResponse(FilterResponseEvent $event)
{
if(null != $this->usuario)
{
if($this->role=='ROLE_USUARIO'){
$portada=$this->router->generate('portada_usuario');
}
else{
$portada=$this->router->generate('portada_cliente');
}
$event->setResponse(new RedirectResponse($portada));
$event->stopPropagation();
}
}
}
?>
And I donĀ“t know why is this happening, cause both providers are in different context. Do I need to do something more to separate both logins?
You are calling a method that does not exist in that entity. If you pay attention in the error you will see that the user class that comes from the token is not the one you think it is.
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!');
// }
// ...
}
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.
After installing Symfony2 cmf, when I tried to view the admin / dashboard I have the following error:
The function "is_granted" does not exist in SonataAdminBundle :: standard_layout.html.twig at line 95
I struggled with that quite a lot of time, too. Here's how to fix it:
Add the SecurityBundle to app/AppKernel.php
public function registerBundles()
{
$bundles = array(
// ...
// support for the admin
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
);
// ...
}
Create a security.yml in your app/config folder, e.g. with this demo content:
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:
memory:
users:
user: { password: userpass, roles: [ 'ROLE_USER' ] }
admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
anonymous: ~
http_basic:
realm: "Secured Demo Area"
access_control:
#- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
#- { path: ^/_internal/secure, roles: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 }
and load it it your app/config/config.yml:
imports:
- { resource: security.yml }
That worked for me.
Just as a feedback to whoever else might face this issue :
acme's solution works. Apparently, users MUST be provided as described in
http://symfony.com/doc/master/cmf/tutorials/creating-cms-using-cmf-and-sonata.html