我想知道是否有人知道使用Symfony2 ACL系统实现这一目标的优雅方法。
我有一个Comment
实体(我的域名对象)需要ROLE_USER
可编辑,但这只能在发布评论的5分钟内允许 - 否则评论只能由ROLE_ADMIN
。
只有ROLE_USER
和ROLE_ADMIN
才能对其进行修改很简单,只需为每个RoleSecurityIdentity
制作一个ROLE_USER
。
现在,当我想要合并ObjectIdentity
的时间因素时,我的问题就出现了。我的第一个问题是它需要来自域对象的信息,而不仅仅是ACL表,但我认为这可以通过创建一个自定义Comment
类来解决,该类也可以保留PermissionGrantingStrategy
发布的时间。
现在是困难的部分
我想我需要创建一个自定义Comment
,它也知道创建时间。在检查PermissionGrantingStrategy
类型时必须加载它,但我不知道如何加载它。有谁知道是否有某种工厂可以配置这种东西?因此,如果某个实体有一个与之关联的特定{{1}},那么它会被使用,否则会使用默认值?
我知道这有点长,非常感谢有人知道如何实现这一点,因为ACL文档目前看起来有点稀疏。我的后备解决方案是简单地提供某种服务来检查是否可以编辑注释,而不是根本不打扰ACL。
答案 0 :(得分:36)
我发布这个解决方案,以便其他人可以看到我的最终代码,但这是我在实施有问题的Voter时发现的陷阱。
supportsAttribute :当您在isGranted
上调用SecurityContext
方法时,它似乎在委派vote
来电之前未实际检查此方法到VoterInterface
所以在vote
方法中你实际上必须自己检查属性。
supportsClass :在上面的问题答案中,似乎这个方法可能是基于工厂的选择的关键,VoterInterface
可以投票,但实际上symfony2文档读取:
supportsClass()方法用于检查选民是否支持当前用户令牌类。
因此,它实际上似乎与Voter
是否支持令牌类型有关。更糟糕的是,PHP Doc看起来很模糊:
检查选民是否支持给定的班级。
无论如何,主要问题是,在将调用委托给任何选民的SecurityContext
方法之前,vote
永远不会检查此方法 - 即使此方法是硬编码到return false
vote
仍然会被调用!
所以基本上这个故事的寓意似乎是:手动检查$attributes
和[{1}}进入$object
方法。
我的代码:
services.yml
vote
和选民班:
parameters:
comment_voter.class: Acme\Bundle\CommentBundle\Security\Authorization\Voter\CommentVoter
services:
comment_voter:
class: %comment_voter.class%
arguments: [@service_container]
public: false
tags:
- { name: security.voter }
最后是模板:
<?php
namespace Acme\Bundle\CommentBundle\Security\Authorization\Voter;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Acme\Bundle\CommentBundle\Entity\Comment;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* A class to check editing privileges for Comments.
*/
class CommentVoter implements VoterInterface {
const AUTHOR_EDIT_TIME_LIMIT = 300;
private $container;
public function __construct($container) {
$this->container = $container;
}
public function supportsAttribute($attribute) {
return $attribute === 'EDIT';
}
public function supportsClass($class) {
return true;
}
/**
* Checks whether or not the current user can edit a comment.
*
* Users with the role ROLE_COMMENT_MODERATOR may always edit.
* A comment's author can only edit within 5 minutes of it being posted.
*
* {@inheritdoc}
*/
public function vote(TokenInterface $token, $object, array $attributes) {
if ( !($object instanceof Comment) ) {
return VoterInterface::ACCESS_ABSTAIN;
}
// Only supports 'EDIT' for now.
if ( !$this->supportsAttribute($attributes[0]) ) {
return VoterInterface::ACCESS_ABSTAIN;
}
$user = $token->getUser();
if ( !($user instanceof UserInterface) ) {
return VoterInterface::ACCESS_DENIED;
}
// Is the token a comment moderator?
if ( $this->container->get('security.context')->isGranted('ROLE_COMMENT_MODERATOR') ) {
return VoterInterface::ACCESS_GRANTED;
}
// Is the token the author of the post and within the edit window.
$originalRevision = $object->getOriginalRevision();
if ( $originalRevision->getAuthor()->equals($user) ) {
if (
(time() - $originalRevision->getCreationDate()->getTimestamp())
<= self::AUTHOR_EDIT_TIME_LIMIT
) {
return VoterInterface::ACCESS_GRANTED;
}
}
return VoterInterface::ACCESS_DENIED;
}
}
我希望这对未来的其他人有所帮助,并且非常感谢有问题的我指向选民的方向。
答案 1 :(得分:24)
您是否考虑过使用选民?有一个cookbook recipe用于实现IP黑名单选民,但可以很容易地修改它以处理对Comment对象的编辑检查。
你可以在Symfony\Component\Security\Acl\Voter\AclVoter
(在线here)查看默认的AclVoter,虽然你的显然可以增加而不是替换它并且更简单。
作为概念的快速证明:
class CommentTimestampVoter implements VoterInterface
{
public function supportsAttribute($attribute)
{
return 'edit' === $attribute;
}
public function vote(TokenInterface $token, $object, array $attributes)
{
// 1. check if $token->getUser() has ROLE_ADMIN and return VoterInterface::ACCESS_GRANTED if so
// 2. check if $token->getUser() equals $object->getAuthor() and return VoterInterface::ACCESS_DENIED if not
// 3. check that $object->getCreatedAt() is within the window allowed for editing and return VoterInterface::ACCESS_GRANTED if so
// 4. return VoterInterface::ACCESS_DENIED
}
public function supportsClass($class)
{
return 'Acme\CommentBundle\Entity\Comment' === $class;
}
}