Symfony2 ACL 与另一个标准相结合

发布于 2024-12-11 09:10:27 字数 818 浏览 0 评论 0原文

我想知道是否有人知道使用 Symfony2 ACL 系统实现此目的的优雅方法。

我有一个 Comment 实体(我的域对象),需要由 ROLE_USER 进行编辑,但这仅在评论发布后 5 分钟内允许 - 否则评论只能由ROLE_ADMIN编辑。

使其只能由 ROLE_USERROLE_ADMIN 编辑很简单,只需为每个角色创建一个 RoleSecurityIdentity 即可。

现在,当我想合并 ROLE_USER 的时间因素时,我的问题就出现了。我的第一个问题是它需要来自域对象的信息,而不仅仅是 ACL 表,但我认为这可以通过创建一个自定义 ObjectIdentity 类来解决,该类也可以保存 Comment< /code> 已发布。

现在是最困难的部分

我想我需要创建一个自定义的PermissionGrantingStrategy,它知道还要查看创建时间。当检查 Comment 类型时必须加载它,但我不知道如何加载它。有谁知道是否有某种工厂可以配置此类事情?因此,如果一个实体具有与其关联的特定 PermissionGrantingStrategy ,那么它就会被使用,否则将使用默认值?

我知道这有点长,如果有人知道如何实现这一点,非常感谢,因为 ACL 文档目前似乎有点稀疏。我的后备解决方案是简单地提供某种服务来检查评论是否可以编辑,而根本不用担心 ACL。

I'm wondering if anyone knows of an elegant way to achieve this using the Symfony2 ACL system.

I have a Comment entity (my domain object) which needs to be editable by ROLE_USER but this is only allowed within 5 minutes of the comment being posted - otherwise the comment can only be edited by ROLE_ADMIN.

Making it so that it can only be edited by ROLE_USER and ROLE_ADMIN is simple, just make a RoleSecurityIdentity for each.

Now my problem occurs when I want to incorporate the time factor for ROLE_USER. My first problem is that it needs information from the domain object, not just the ACL table but I think this is solvable by making a custom ObjectIdentity class which can also hold the time that the Comment was posted.

Now for the hard part

I think I need to create a custom PermissionGrantingStrategy that knows to also look at the creation time. This has to be loaded when a Comment type is being checked, but I don't know how to get it to load. Does anyone know if there's some kind of factory through which this sort of thing can be configured? So that if an entity has a specific PermissionGrantingStrategy associated with it then it gets used otherwise the default is used?

I know this is a bit of a long one, many thanks if anyone knows how to achieve this as the ACL documentation seems a tad sparse at the moment. My fallback solution is to simply make some kind of service to check if a Comment can be edited and not bother with ACL at all.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

好多鱼好多余 2024-12-18 09:10:27

我发布这个解决方案,以便其他人可以看到我的最终代码,但以下是我在按照有问题的建议实现投票器时发现的陷阱。

supportsAttribute:当您在 SecurityContext 上调用 isGranted 方法时,它似乎在委托 之前实际上并没有检查此方法vote 调用 VoterInterface,因此在您的 vote 方法中,您实际上必须自己检查属性。

supportsClass:在上面有问题的答案中,似乎这个方法可能是基于工厂的选择的关键,其中 VoterInterface 可以投票,但实际上 symfony2 文档是这样写的:

supportsClass()方法用于检查投票者是否支持当前用户令牌类别。

因此,它实际上似乎与Voter是否支持令牌类型有关。更糟糕的是 PHP 文档似乎很模糊:

检查选民是否支持给定类别。

无论如何,主要问题是在将调用委托给任何投票者的 vote 方法之前,SecurityContext 永远不会检查此方法 - 即使此方法被硬编码为返回 false 投票仍将被调用!

所以基本上这个故事的寓意似乎是:检查$attributes$object 手动进入 vote 方法。

我的代码:

services.yml

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;
    }

}

最后是模板:

{% if is_granted('EDIT', comment) %}<a href="#">Edit</a>{% endif %}

我希望这对将来的其他人有帮助,并非常感谢 Problematic 为我指明了方向的选民。

I'm posting this solution so that others can see my final code but here are the pitfalls I found when implementing a Voter as Problematic suggested.

supportsAttribute: It appears that when you call the isGranted method on the SecurityContext that it doesn't actually check this method before delegating a vote call to a VoterInterface so inside your vote method you actually have to check the attributes yourself.

supportsClass: In problematic's answer above it seemed like this method could be a key for a Factory based selection of which VoterInterfaces can vote but actually the symfony2 documentation reads:

The supportsClass() method is used to check if the voter supports the current user token class.

Therefore it actually seems to pertain to whether or not the Voter supports the token type. To make matters worse the PHP Doc seems vague:

Checks if the voter supports the given class.

At any rate the main problem is that this method is never checked by the SecurityContext before delegating the call to the vote method of any voter - even if this method is hardcoded to return false vote will still be called!

So basically the moral of the story appeared to be: check the $attributes and $object coming in on the vote method manually.

My Code:

services.yml

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 }

and the voter class:

<?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;
    }

}

and finally template:

{% if is_granted('EDIT', comment) %}<a href="#">Edit</a>{% endif %}

I hope this helps someone else in the future and a big thanks to Problematic for pointing me in the direction of Voters.

浮生未歇 2024-12-18 09:10:27

您是否考虑过使用选民?有一个用于实现 IP 黑名单投票者的 食谱,但可以轻松修改它以处理检查用于对 Comment 对象进行编辑。

您可以在 Symfony\Component\Security\Acl\Voter\AclVoter 查看默认的 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;
    }
}

Have you considered using a voter? There's a cookbook recipe for implementing an IP blacklist voter, but it could be easily modified to handle checking for edits on Comment objects.

You can look at the default AclVoter at Symfony\Component\Security\Acl\Voter\AclVoter (online here), though yours can obviously augment instead of replace it and be much simpler.

As a quick proof of concept:

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;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文