I'm a bit disconcerted by the way access control lists are implemented in Symfony2.
In Zend Framework (versions 1 & 2), a list of resources and a list of roles are defined and each role is assigned a subset of resources it's allowed to access. Resources and roles are therefore the main vocabulary of ACL implementation, which is not the case in Symfony2, where only roles rule.
In a legacy app database, I have tables defining a list of roles, a list of resources and a list of allowed resources for each role (many-to-many relationship). Each user is assigned a role (admin, super admin, editor, and such).
I need to make use of this database in a Symfony2 application. My resources look like this : ARTICLE_EDIT, ARTICLE_WRITE, COMMENT_EDIT, etc.
My User
entity in Symfony implements the Symfony\Component\Security\Core\User\UserInterface
interface and therefore has a getRoles)
method.
I intend to use this method to define the allowed resources, which means I use roles as resources (I mean that what's called resources in Zend Framework is called roles here).
Do you confirm that I should use this method ?
This means I don't care anymore about the role (admin, editor, ...) of each user, but only about its resources.
I would then use $this->get('security.context')->isGranted('ROLE_ARTICLE_WRITE')
in my controllers.
Is this the right way to do it and wouldn't it be a circumvented way to use roles in Symfony?
To answer this question years later, it was pretty easy to solve.
The solution is to mix the notions of roles and resources.
Let's assume a role
table, a resource
table and and role_resource
many to many relation are defined.
Users are stored in a user
table.
Here are the corresponding Doctrine entities:
User:
use Symfony\Component\Security\Core\User\UserInterface;
class User implements UserInterface
{
/**
* @Id @Column(type="integer")
* @GeneratedValue
*/
private $id;
/**
* @ManyToOne(targetEntity="Role")
* @JoinColumn(name="role_id", referencedColumnName="id")
**/
private $role;
// ...
}
Role:
class Role
{
/**
* @Id @Column(type="integer")
* @GeneratedValue
*/
private $id;
/** @Column(type="string") */
private $name;
/**
* @ManyToMany(targetEntity="Resource")
* @JoinTable(name="role_resource",
* joinColumns={@JoinColumn(name="role_id", referencedColumnName="id")},
* inverseJoinColumns={@JoinColumn(name="resource_id", referencedColumnName="id")}
* )
**/
private $resources;
// ...
}
Resource:
class Resource
{
/**
* @Id @Column(type="integer")
* @GeneratedValue
*/
private $id;
/** @Column(type="string") */
private $name;
// ...
}
So now the solution is to implement the getRoles
of UserInterface
this way:
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Role\Role;
class User implements UserInterface
{
// ...
/**
* @var Role[]
**/
private $roles;
/**
* {@inheritDoc}
*/
public function getRoles()
{
if (isset($this->roles)) {
return $this->roles;
}
$this->roles = array();
$userRole = $this->getRole();
$resources = $userRole->getResources();
foreach ($resources as $resource) {
$this->roles[] = new Role('ROLE_' . $resource);
}
return $this->roles;
}
}
This way, resources attributed to the current user can be checked this way (considering there is a resource whose name is ARTICLE_WRITE
):
$this->get('security.context')->isGranted('ROLE_ARTICLE_WRITE')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With