Let's say we have 3 main roles that are directly bound to the database table user
: ROLE_USER
, ROLE_MODERATOR
and ROLE_ADMIN
.
BUT, we also got some other roles, which are used for the Crews
component (see UML below). I use the following roles for actions peformed in a Crew
: ROLE_CREW_BOSS
, ROLE_CREW_LEFTHAND
, ROLE_CREW_RIGHTHAND
, ROLE_CREW_MEMBER
.
+----------------+ +------------------+ | users | | crews | |----------------| |------------------| | id | | id | | username <---+ | name | | password | | +---> cash | | roles | | +-------------------+ | | ... | | ... | | | crew_members | | | | | | | |-------------------| | | | +----------------+ | | crew_id +--------------+ | | +----+ user_id | +--------^---------+ | roles | | | ... | +------------+ | | | | | | +------------------+ | | | | forum_topics | | | | |------------------| | | | | id | +-------------------+ +---+ crew_id | | title | | description | | ... | | | | | | | +------------------+
That is the base structure, I hope that part is clear. Now comes the problem...
The problem
Every user with the role ROLE_MODERATOR
can create ForumTopic
objects, but not the one where crew_id
is set, because that one is private for a specific crew. Also, only crew members (which are also users) that have the role ROLE_CREW_BOSS
, ROLE_CREW_LEFTHAND
or ROLE_CREW_RIGHTHAND
can edit the forum topics of their crew. How do I check those kind of complexity? With a Voter
maybe?
UPDATE 1
I have solved the problem for 50%, but it's not solid. I've created a voter specific for the object Entity\\ForumTopic
.
public function vote(TokenInterface $token, $object, array $attributes)
{
if ($object instanceof ObjectIdentityInterface) {
if ($object->getType() == 'Entity\\ForumTopic') {
/**
* @var Member $member
*/
$member = $token->getUser();
$userTable = new UserTable();
$user = $userTable->getByMember($member);
$userInCrewTable = new UserInCrewTable();
$crewMember = $userInCrewTable->getByUser($user);
if ($crewMember && in_array($crewMember->getRole(), array('boss', 'lefthand', 'righthand'))) {
return self::ACCESS_GRANTED;
}
}
}
return self::ACCESS_ABSTAIN;
}
The only problem here is that I don't use the respective roles, so I can't use the role hierarchy functionality for example. Anyone got a better solution or a improvement on my current solution?
Thanks!
Steffen
The default role system of Symfony is role bound to user. Having a role field in your manyToMany table crew_members does not make sense from this point of view.
What you want is authorization based on the user AND on the crew, so you should probably use ACL functionality, and use role only for global permission.
$objectIdentity = ObjectIdentity::fromDomainObject($forumTopic);
$acl = $aclProvider->createAcl($objectIdentity);
$securityIdentity = UserSecurityIdentity::fromAccount($user);
// grant owner access
$acl->insertObjectAce($securityIdentity, MaskBuilder::MASK_EDIT);
$aclProvider->updateAcl($acl);
(You can check further docs on http://symfony.com/doc/current/cookbook/security/acl.html. You can also use the excellent https://github.com/Problematic/ProblematicAclManagerBundle)
You combine it with a voter :
function vote(TokenInterface $token, $object, array $attributes)
{
if ($object instanceof ObjectIdentityInterface) {
if ($object->getType() == 'Entity\\ForumTopic') {
/**
* @var Member $member
*/
$member = $token->getUser();
if(in_array('ROLE_MODERATOR', $member->getRoles() && empty($object->getCrew()) {
return self::ACCESS_GRANTED;
}
// inject security component via dependecy injection
// delegate further check to ACL
if ($this->container['security']->isGranted('EDIT', $object)) {
return self::ACCESS_GRANTED;
}
}
}
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