Symfony 2 - Permission to verify ACLs based on "separate" roles

Let's say we have three main roles that are directly related to the user database table: ROLE_USER , ROLE_MODERATOR and ROLE_ADMIN .

BUT, we also got some other roles that are used for the Crews component (see UML below). I use the following roles for actions generated in 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 |
                                                              |  ... |
                                                              |  |
                                                              |  |
                                                              |  |
                                                              + ------------------ + 

This is the basic structure, I hope this part is clear. Now the problem is ...

Problem

Each user with the ROLE_MODERATOR role can create ForumTopic objects, but not the one where crew_id set, because that one is private for a specific crew. In addition, only crew members (who are also users) who have the roles ROLE_CREW_BOSS , ROLE_CREW_LEFTHAND or ROLE_CREW_RIGHTHAND can edit their team's forum topics. How to check this complexity? With Voter maybe?

UPDATE 1

I solved the problem by 50%, but not durable. I created a selector specific to the 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 am not using the appropriate roles, so I cannot use the functionality of the role hierarchy, for example. Has anyone got a better solution or an improvement to my current solution?

Thanks!

Steffen

+8
symfony acl
source share
3 answers

The default role system for Symfony is a user-related role. Having a role field in your manyToMany crew_members table does not make sense from this point of view.

What you want is user-based authorization AND in the team, so you should probably use the ACL functionality and use the role only for global permissions.

  $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 documents at http://symfony.com/doc/current/cookbook/security/acl.html . You can also use the excellent https://github.com/Problematic/ProblematicAclManagerBundle )

You combine it with the 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; } } } 
+3
source share

I would use Symfony acl:

 // creating the ACL $aclProvider = $this->get('security.acl.provider'); $objectIdentity = ObjectIdentity::fromDomainObject($comment); $acl = $aclProvider->createAcl($objectIdentity); $roleSecurityIdentity = new RoleSecurityIdentity('ROLE_CREW'); $securityIdentity = $roleSecurityIdentity; // grant owner access $acl->insertObjectAce($securityIdentity, MaskBuilder::MASK_OWNER); $aclProvider->updateAcl($acl); 
0
source share

You can touch the solution! You just need to do a few things if you want to test the roles. First, register your voter as a service to create it in a security context:

Add this to your services.yml file:

 services: your_app.security.voter.forum_topic_owner: class: Your\AppBundle\Security\Authorization\Voter\ForumTopicOwnerVoter arguments: ["@security.context"] tags: - { name: security.vote 

Now you will need to define a constructor to get the securityContext and use it in the voting method:

 <?php namespace Your\AppBundle\Security\Authorization\Voter; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\SecurityContext; class ForumTopicOwnerVoter implements VoterInterface { /** @var SecurityContext */ protected $securityContext; /** * @param SecurityContext $securityContext SecurityContext is the main entry point of the Security component. */ public function __construct(SecurityContext $securityContext) { $this->securityContext = $securityContext; } /** * {@inheritDoc} */ public function supportsAttribute($attribute) { return 'FORUM_TOPIC_OWNER' === $attribute; } /** * {@inheritDoc} */ public function supportsClass($class) { return $class->getType() == 'Entity\\ForumTopic'; } /** * {@inheritDoc} */ public function vote(TokenInterface $token, $forumTopic, array $attributes) { foreach ($attributes as $attribute) { if ($this->supportsAttribute($attribute) && $this->supportsClass($forumTopic)) { $user = $token->getUser(); if ($user->hasRole('ROLE_CREW_BOSS') or $this->securityContext->isGranted('ROLE_LEFTHAND') ) { return VoterInterface::ACCESS_GRANTED; } } } return VoterInterface::ACCESS_DENIED; } } 

Now you have Voter, you should call it a ForumTopic object, apparently, you know how to do this, and this is not your problem. May I suggest that you look at the SecureParam annotation of the well-known JMS / SecurityExtraBundle anyway. Here is a way to use it in your controller action:

 namespace Your\AppBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use JMS\SecurityExtraBundle\Annotation\Secure; use JMS\SecurityExtraBundle\Annotation\SecureParam; /** * ForumTopic controller. * */ class ForumTopicController extends Controller /** * Edit an existing forum topic entity. * * @param Request $request An HTTP request. * @param ForumTopic $forumTopic A forumTopic entity. * * @Secure(roles="ROLE_CREW") * @SecureParam(name="forumTopic", permissions="FORUM_TOPIC_OWNER") * @ParamConverter("forumTopic", class="YourAppBundle:ForumTopic") */ public function editAction(Request $request, ForumTopic $forumTopic) { //Add here your logic } 

I hope this was helpful to you!

Good luck

Greetings.

0
source share

All Articles