在没有SonataUserBundle的Sonata中删除每个用户角色的特定路由

时间:2016-10-25 20:26:33

标签: php symfony sonata-admin sonata-user-bundle

我在Symfony 3中使用了SonataAdminBundle。因为我使用Symfony 3,我仍然无法使用SonataUserBundle。所以我只使用SonataAdminBundle和FOSUserBundle。

现在我试图实现的是隐藏每个角色的特定路线。例如,我只有三个角色;

  • 超级管理员
  • 管理
  • 另一个角色

Super Admin拥有admin所拥有的所有角色,admin拥有所有第三个角色,第三个拥有ROLE_USER。超级管理员应该能够创建新用户并为他分配角色。超级管理员还应该能够更改用户的密码。用户应该能够更改自己帐户的密码。最后,Super Admin应该的其他角色能够更改自己的角色并创建新用户。

如何在不使用SonataUserBundle的情况下实现此目的。为了删除路线,我试过这样的事情:

protected function configureRoutes(RouteCollection $collection)
{
    $securityContext = $this->getConfigurationPool()->getContainer()->get('security.authorization_checker');

    if (!$securityContext->isGranted('ROLE_SUPER_ADMIN')) {
        $collection->remove('create');
        $collection->remove('edit');
    }
}

但我想有更好的解决方案。我完全了解official documentation about security,但我对此感到困惑,这是否意味着我必须对我security.yml文件中所有不同管理员的每个角色进行硬编码?没有SonataUserBundle,这甚至可以工作吗?我不想为ACL添加额外的数据库表。

有人可以协助和/或提供一个好例子吗?我会非常感激的。

2 个答案:

答案 0 :(得分:2)

如何在没有SonataUserBundle

的情况下管理Sonata中的用户和角色

答案:我们需要和SonataUserBundle做同样的事情。(但让我们简化一下)

在Symfony flat中基于ROLE_的安全性类比:

  • 房子:有门和钥匙的建筑物(系统)。
  • 门:放置在限制访问的房屋内 - isGranted()

    // the door is here, we need the key to open it.
    if ($this->isGranted('ROLE_FOO')) {
        // restricted access to do something
    } 
    
  • 密钥:授予访问受限门的权限 - ROLE_*

    class User extends FOSUser
    { 
        public function getRoles()
        {
            // the keys comes from DB or manually.
            // e.g: 
            return ['ROLE_FOO'];
        }
    }
    
  • 主密钥:可以打开多个门的密钥:

    # app/config/security.yml
    
    security:
        role_hierarchy:
            # other than opening the door "isGranted('ROLE_BAR')"
            # we can also opening the door "isGranted('ROLE_FOO')" with this single key.
            ROLE_BAR: ROLE_FOO
    

按照这种类比,SonataAdminBundle已经创建了限制访问所管理实体的每个默认操作(e.g. list action)的大门。

所以我们的工作就是“仅”为用户分配密钥(除非您需要创建自己的门)。有很多方法可以存档(这取决于需要做什么) )。

注意:如果您没有角色层次结构,则只有单个密钥(即您没有主密钥),这使得角色(密钥)的分配灵活性降低。

现在,SonataAdminBundle使用特定方式检查管理类上下文中的键,只需执行以下操作:$admin->isGranted('list'),这是因为他有自己的isGranted()函数(其中'list'是动作名称),但它真正做的是在检查之前构建角色名称(通过使用当前的管理代码),所以他最终验证了这一点:isGranted('ROLE_APP_BUNDLE_ADMIN_FOO_ADMIN_LIST') - 这个关键是什么我们需要“给予”用户 - 。

如何从Sonata管理系统获取角色列表?

在控制器上下文中:

public function getSonataRoles()
{
    $roles = [];

    // the sonata admin container
    $pool = $this->get('sonata.admin.pool');

    foreach ($pool->getAdminServiceIds() as $id) {
        // gets the registered admin instance from id service name
        $admin = $pool->getInstance($id);

        // the role security handler instance (must be configured)
        $securityHandler = $admin->getSecurityHandler();

        // gets the base role name from admin code 
        // e.g. 'ROLE_APP_BUNDLE_ADMIN_FOO_ADMIN_%s'
        $baseRole = $securityHandler->getBaseRole($admin);

        // gets the access actions (e.g. LIST, CREATE, EDIT, etc.)
        foreach (array_keys($admin->getSecurityInformation()) as $action) {
            // add the final role name
            // e.g. 'ROLE_APP_BUNDLE_ADMIN_FOO_ADMIN_LIST'
            $roles[] = sprintf($baseRole, $action);
        }
    }

    return $roles;
}

接下来,您可以执行任何操作(例如,创建自定义表单类型以管理用户角色属性)。您可以对这些角色进行排序,分组,以便以最简单的方式向用户显示此列表。

在这里,我们可以在不使用role_hierarchy的情况下分配角色和工作。

更多详情http://symfony.com/doc/current/bundles/SonataAdminBundle/reference/security.html

答案 1 :(得分:1)

您可以为用户实体定义自定义用户权限选民,请参阅here

namespace AppBundle\Security;

use AppBundle\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;

class UserVoter extends Voter
{

    private $decisionManager;

    public function __construct(AccessDecisionManagerInterface $decisionManager)
    {
        $this->decisionManager = $decisionManager;
    }

    protected function supports($attribute, $subject)
    {

        // only vote on User objects inside this voter
        if (!$subject instanceof User) {
            return false;
        }

        return true;
    }

    protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
    {
        // ROLE_SUPER_ADMIN can do anything! The power!
        if ($this->decisionManager->decide($token, array('ROLE_SUPER_ADMIN'))) {
            return true;
        }

        $user = $token->getUser();

        if (!$user instanceof User) {
            // the user must be logged in; if not, deny access
            return false;
        }

        /** @var User $targetUser */
        $targetUser = $subject;

        // Put your custom logic here
        switch ($attribute) {
            case "ROLE_SONATA_ADMIN_USER_VIEW":
                return true;
            case "ROLE_SONATA_ADMIN_USER_EDIT":
                return ($user === $targetUser);
        }
        return false;

    }
}

然后您创建服务

sonata_admin.user_voter:
    class: AppBundle\Security\UserVoter
    arguments: ['@security.access.decision_manager']
    public: false
    tags:
        - { name: security.voter }

小心access decision strategy,如果将其定义为unanimousconsensus

,我可能无法运行,具体取决于您的配置

如果您不想让每个用户都能访问用户列表,您还可以向用户自己的编辑页面添加直接链接/路由。

修改

要限制用户角色编辑,因为您不希望用户编辑自己的角色,您只需编辑configureFormFields功能:

protected function configureFormFields(FormMapper $formMapper)
{
    $formMapper
        ->add('username')
        ->add('plainPassword', 'text', array(
                 'required' => false,
            )
        )
        /* your other fields */
    ;



    if ($this->isGranted('ROLE_SUPER_ADMIN')) {
        $formMapper->add('roles', \Symfony\Component\Form\Extension\Core\Type\CollectionType::class, array(
            'entry_type'   => \Symfony\Component\Form\Extension\Core\Type\ChoiceType::class,
            'entry_options'  => array(
                'choices'  => array(
                    "ROLE_OPTICKSB2B" => "ROLE_OPTICKSB2B",
                    "ROLE_ADMIN" => "ROLE_ADMIN",
                    "ROLE_SUPER_ADMIN" => "ROLE_SUPER_ADMIN"
                ),
            )
        ));
    }

    $formMapper
        ->add('isActive')
        ->add('title')
        ->add('firstname')
        ->add('lastname')
    ;
}

显然,Symfony表单组件会检查你,而不是添加其他字段。