通过SecurityIdentity而不是ObjectIdentity查找ACE

时间:2013-11-22 21:31:42

标签: symfony acl

Symfony2 ACL可以相对轻松地按ObjectIdentity搜索ACE记录,可选择按SecurityIdentity进行过滤。

但是,我需要反过来:我需要获取给定SecurityIdentity的所有ACE,可选择按ObjectIdentity过滤。

我将如何做到这一点?

1 个答案:

答案 0 :(得分:3)

这是我最终提出的解决方案。

它有一些重大限制:

  • 它天真地假设它找到的任何ACE都授予访问权限。
  • 它只与单个用户匹配,并忽略授予该用户角色的任何访问权限。
  • 它不支持按掩码过滤(不是绝对必要,但会非常有用)。

另请注意,此方法返回 ObjectIdentities ,而不是 ACE 。从技术上讲,这不是我在我的OP中所要求的,但它足够接近,我认为无论如何我都会发布它。

namespace Acme\DemoBundle\Security\Authorization\Acl;

use Doctrine\DBAL\Connection;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
use Symfony\Component\Security\Acl\Dbal\MutableAclProvider;

class AclProvider
    extends MutableAclProvider
{
    /** @var Connection */
    protected $connection;

    /** Locates all objects that the specified User has access to.
     *
     * Note that this method has a few limitations:
     *  - No support for filtering by mask.
     *  - No support for ACEs that match one of the User's roles (only ACEs that
     *      reference the User's security identity will be matched).
     *  - Every ACE that matches is assumed to grant access.
     *
     * @param UserInterface $user
     * @param string        $type   If set, filter by object type (classname).
     *
     * @return ObjectIdentity[]
     */
    public function findObjectIdentitiesForUser(UserInterface $user, $type=null)
    {
        $securityIdentity = UserSecurityIdentity::fromAccount($user);
        $identifier = sprintf(
            '%s-%s'
                , $securityIdentity->getClass()
                , $securityIdentity->getUsername()
        );

        $sql = <<<END
SELECT
          o.object_identifier
        , c.class_type
    FROM
        {$this->options['sid_table_name']} s
    LEFT JOIN
        {$this->options['entry_table_name']} e
            ON (
                    (e.security_identity_id = s.id)
                or  {$this->connection->getDatabasePlatform()->getIsNullExpression('e.security_identity_id')}
            )
    LEFT JOIN
        {$this->options['oid_table_name']} o
            ON (o.id = e.object_identity_id)
    LEFT JOIN
        {$this->options['class_table_name']} c
            ON (c.id = o.class_id)
    WHERE
            s.identifier = {$this->connection->quote($identifier)}
END;

        if($type)
        {
            $sql .= <<<END
        AND c.class_type = {$this->connection->quote($type)}
END;
        }

        $objectIdentities = array();

        /* @kludge It would be awesome if we could use hydrateObjectIdentities()
         *  here.  Then we could do super fancy stuff like filter by mask and
         *  check whether ACEs grant or deny access.
         *
         * Unfortunately, that method is not accessible to subclasses.
         */
        foreach($this->connection->executeQuery($sql)->fetchAll() as $row)
        {
            $objectIdentities[] = new ObjectIdentity(
                  $row['object_identifier']
                , $row['class_type']
            );
        }

        return $objectIdentities;
    }

您还需要通过在捆绑包的services.yml文件中添加以下内容来覆盖服务容器中的ACL提供程序:

parameters:
  security.acl.dbal.provider.class:
    Acme\DemoBundle\Security\Authorization\Acl\AclProvider

然后你可以调用这个方法,例如,在这样的控制器中:

use Acme\DemoBundle\Security\Authorization\Acl\AclProvider;

/** @var AclProvider $aclProvider */
$aclProvider = $this->get('security.acl.provider');
$objectIdentities = $aclProvider->findObjectIdentitiesForUser($user, $type);