15

UserInterfaceSymfony2 で実装されている任意のオブジェクトに属性 (ロール) が付与されているかどうかを検証できるようにしたいと考えています。これは可能ですか?

UserInterface->getRoles()ロール階層が考慮されていないため、私のニーズには適していません。また、その部門で車輪を再発明したくないため、可能であれば Access Decision Manager を使用したいと考えています。

ありがとう。

以下のオリヴィエの解決策に応えて、ここに私の経験があります:

isGranted メソッドで security.context サービスを使用できます。オブジェクトである 2 番目の引数を渡すことができます。

$user = new Core\Model\User();
var_dump($user->getRoles(), $this->get('security.context')->isGranted('ROLE_ADMIN', $user));

出力:

array (size=1)
  0 => string 'ROLE_USER' (length=9)

boolean true

私の役割階層:

role_hierarchy:
    ROLE_USER:          ~
    ROLE_VERIFIED_USER: [ROLE_USER]
    ROLE_ADMIN:         [ROLE_VERIFIED_USER]
    ROLE_SUPERADMIN:    [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
    ROLE_ALLOWED_TO_SWITCH: ~

私のUserInterface->getRoles()方法:

public function getRoles()
{
    $roles = [$this->isVerified() ? 'ROLE_VERIFIED_USER' : 'ROLE_USER'];

    /**
     * @var UserSecurityRole $userSecurityRole
     */
    foreach ($this->getUserSecurityRoles() as $userSecurityRole) {
        $roles[] = $userSecurityRole->getRole();
    }

    return $roles;
}

ROLE_ADMIN明示的に割り当てる必要がありますが、ユーザーが作成されたばかりで、デフォルト以外のロールが割り当てられていない場合でも、現在ログインしているユーザーが許可されている限り、返さisGranted('ROLE_ADMIN', $user)れます。これにより、 の 2 番目の引数が単に無視され、によって提供されたが代わりに使用されると思われます。TRUEROLE_USERROLE_ADMINisGranted()TokenAccessDecisionManager->decide()SecurityContext

これがバグである場合はレポートを送信しますが、まだ何か間違っているのでしょうか?

4

8 に答える 8

18

認証が必要AccessDecisionManagerないため、セキュリティコンテキストは必要ありません。

$user = new Core\Model\User();

$token = new UsernamePasswordToken($user, 'none', 'none', $user->getRoles());
$isGranted = $this->get('security.access.decision_manager')
    ->decide($token, array('ROLE_ADMIN'));

RoleHierarchyVoterはデフォルトで登録されているため、ロール階層が正しく考慮されます。

アップデート

@redalaanait が指摘したように、security.access.decision_managerはプライベート サービスであるため、直接アクセスするのは適切ではありません。プライベート サービスにアクセスできるサービス エイリアシングを使用することをお勧めします。

于 2014-03-13T13:53:33.173 に答える
3

security.context2.6 以降は非推奨です。

使用AuthorizationChecker:

$token = new UsernamePasswordToken(
     $user,
     null,
     'secured_area',
     $user->getRoles()
);
$tokenStorage = $this->container->get('security.token_storage');
$tokenStorage->setToken($token);
$authorizationChecker = new AuthorizationChecker(
     $tokenStorage,
     $this->container->get('security.authentication.manager'),
     $this->container->get('security.access.decision_manager')
);
if (!$authorizationChecker->isGranted('ROLE_ADMIN')) {
    throw new AccessDeniedException();
}
于 2016-03-10T02:13:47.927 に答える
1

RoleVoterから渡された $object を無視しますSecurityContext->isGranted()。これにより、提供された$object (存在する場合)ではなくRoleHierarchyVoterからロールが抽出されるため、別のルートを見つける必要がありました。TokenUserInterface

たぶん、これを行うためのより良い方法があり、もしあれば知りたいのですが、これが私が思いついた解決策です:

最初にContainerAwareInterfaceUser クラスに実装して、その中からセキュリティ コンポーネントにアクセスできるようにしました。

final class User implements AdvancedUserInterface, ContainerAwareInterface
{
    // ...

    /**
     * @var ContainerInterface
     */
    private $container;

    // ...

    public function setContainer(ContainerInterface $container = null)
    {
        if (null === $container) {
            throw new \Exception('First argument to User->setContainer() must be an instance of ContainerInterface');
        }

        $this->container = $container;
    }

    // ...
}

hasRole()次に、メソッドを定義しました。

/**
 * @param string|\Symfony\Component\Security\Core\Role\RoleInterface $roleToCheck
 * @return bool
 * @throws \InvalidArgumentException
 */
public function hasRole($roleToCheck)
{
    if (!is_string($roleToCheck)) {
        if (!($roleToCheck instanceof \Symfony\Component\Security\Core\Role\RoleInterface)) {
            throw new \InvalidArgumentException('First argument expects a string or instance of RoleInterface');
        }
        $roleToCheck = $roleToCheck->getRole();
    }

    /**
     * @var \Symfony\Component\Security\Core\SecurityContext $thisSecurityContext
     */
    $thisSecurityContext = $this->container->get('security.context');
    $clientUser = $thisSecurityContext->getToken()->getUser();

    // determine if we're checking a role on the currently authenticated client user
    if ($this->equals($clientUser)) {
        // we are, so use the AccessDecisionManager and voter system instead
        return $thisSecurityContext->isGranted($roleToCheck);
    }

    /**
     * @var \Symfony\Component\Security\Core\Role\RoleHierarchy $thisRoleHierarchy
     */
    $thisRoleHierarchy = $this->container->get('security.role_hierarchy');
    $grantedRoles = $thisRoleHierarchy->getReachableRoles($this->getRoles());

    foreach ($grantedRoles as $grantedRole) {
        if ($roleToCheck === $grantedRole->getRole()) {
            return TRUE;
        }
    }

    return FALSE;
}

コントローラーから:

$user = new User();
$user->setContainer($this->container);

var_dump($user->hasRole('ROLE_ADMIN'));
var_dump($this->get('security.context')->isGranted('ROLE_ADMIN'));
var_dump($this->get('security.context')->isGranted('ROLE_ADMIN', $user));

$user->addUserSecurityRole('ROLE_ADMIN');
var_dump($user->hasRole('ROLE_ADMIN'));

出力:

boolean false
boolean true
boolean true

boolean true

有権者または登録済みの有権者は関与しませんがAccessDecisionManager(テスト対象のインスタンスが現在認証されているユーザーでない限り)、特定のユーザーが特定の役割を持っているかどうかを確認するだけでよいので、私のニーズには十分です。

于 2012-07-03T06:19:41.533 に答える
0

サービスを作成するAccessDecisionMaker (Shady のソリューションを使用)

<?php
namespace Bp\CommonBundle\Service;

use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\Security\Core\Role\RoleInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\SecurityContext;

class AccessDecisionMaker
{
    /** @var Container */
    private $container;

    /** @var  SecurityContext */
    private $securityContext;

    function __construct($container)
    {
        $this->container = $container;

        if (!$this->securityContext) {
            // Ensure security context is created only once
            $this->securityContext = new SecurityContext($this->container->get(
                'security.authentication.manager'
            ), $this->container->get('security.access.decision_manager'));
        }
    }

    public function isGranted($roleToCheck, UserInterface $user)
    {
        if (!is_string($roleToCheck)) {
            if (!($roleToCheck instanceof RoleInterface)) {
                throw new \InvalidArgumentException('First argument expects a string or instance of RoleInterface');
            }
            $roleToCheck = $roleToCheck->getRole();
        }

        $token = new UsernamePasswordToken($user, null, $this->container->getParameter(
            'fos_user.firewall_name'
        ), $user->getRoles());
        $this->securityContext->setToken($token);
        if ($this->securityContext->isGranted($roleToCheck)) {
            return true;
        }

        return false;
    }

}

これをサービスとして構成する

bp.access_decision_maker:
    class: Bp\CommonBundle\Service\AccessDecisionMaker
    arguments:  [@service_container ]

これを使って

$this->container->get('bp.access_decision_maker')->isGranted("ROLE_ADMIN",$user);
于 2013-11-30T20:18:28.027 に答える
0

これは次の問題のようです。

abstract class AbstractToken implements TokenInterface

コンストラクタを見てください。インスタンス化時にロールが作成され、実行時に照会されないように見えます。

public function __construct(array $roles = array())
{
    $this->authenticated = false;
    $this->attributes = array();

    $this->roles = array();
    foreach ($roles as $role) {
        if (is_string($role)) {
            $role = new Role($role);
        } elseif (!$role instanceof RoleInterface) {
            throw new \InvalidArgumentException(sprintf('$roles must be an array of strings, or RoleInterface instances, but got %s.', gettype($role)));
        }

        $this->roles[] = $role;
    }
}

したがって、トークンの作成後にロールを変更することはできません。オプションは、独自の有権者を作成することだと思います。私はまだ周りを見回しています。

于 2013-01-03T02:38:49.233 に答える
-3

メソッドでsecurity.contextサービスを利用できます。isGranted

オブジェクトである 2 番目の引数を渡すことができます (こちらを参照)。

コントローラーで:

$this->get('security.context')->isGranted('ROLE_FOOBAR', $myUser)
于 2012-07-02T07:01:04.623 に答える