假设我们有3个主要角色直接绑定到数据库表user
:ROLE_USER
,ROLE_MODERATOR
和ROLE_ADMIN
。
但是,我们还有一些其他角色,用于Crews
组件(参见下面的UML)。我对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 | | ... | | | | | | | +------------------+
这是基础结构,我希望这部分是清楚的。现在问题出现了......
问题
具有ROLE_MODERATOR
角色的每个用户都可以创建ForumTopic
个对象,但不能创建crew_id
设置的对象,因为该对象对于特定的工作人员来说是私有的。此外,只有具有ROLE_CREW_BOSS
,ROLE_CREW_LEFTHAND
或ROLE_CREW_RIGHTHAND
角色的机组成员(也是用户)才能编辑其工作人员的论坛主题。我如何检查这种复杂性?可能有Voter
?
更新1
我已经解决了50%的问题,但它并不牢固。我已经创建了一个特定于对象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;
}
这里唯一的问题是我没有使用相应的角色,因此我不能使用角色层次结构功能。任何人都能获得更好的解决方案或改进我目前的解决方案吗?
谢谢!
斯特芬
答案 0 :(得分:3)
Symfony的默认角色系统是角色绑定到用户。在manyToMany表crew_members中有一个角色字段从这个角度来看是没有意义的。
您想要的是基于授权的用户和 on the crew ,因此您应该使用ACL功能,并仅将角色用于全局许可。
$objectIdentity = ObjectIdentity::fromDomainObject($forumTopic);
$acl = $aclProvider->createAcl($objectIdentity);
$securityIdentity = UserSecurityIdentity::fromAccount($user);
// grant owner access
$acl->insertObjectAce($securityIdentity, MaskBuilder::MASK_EDIT);
$aclProvider->updateAcl($acl);
(您可以在http://symfony.com/doc/current/cookbook/security/acl.html上查看更多文档。您还可以使用优秀的https://github.com/Problematic/ProblematicAclManagerBundle)
您将其与选民合并:
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;
}
}
}
答案 1 :(得分:0)
我会使用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);
答案 2 :(得分:0)
您可以触摸解决方案! 如果要检查角色,您只需做一些事情。 首先,将您的选民注册为服务,以便使用安全上下文构建它:
在services.yml文件中添加:
services:
your_app.security.voter.forum_topic_owner:
class: Your\AppBundle\Security\Authorization\Voter\ForumTopicOwnerVoter
arguments: ["@security.context"]
tags:
- { name: security.vote
现在,您必须定义一个构造函数来获取securityContext并在投票方法中使用它:
<?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;
}
}
现在,你有一个选民,你必须在一个ForumTopic对象上调用它,显然你知道如何做到这一点并不是你的问题,我可以建议你去看看着名的Jms的SecureParam注释/ SecurityExtraBundle。以下是在控制器操作中使用它的方法:
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
}
我希望它对你有所帮助!
祝你好运!干杯。