首页 文章

Symfony 2:表单权限授权策略

提问于
浏览
2

我有一个包含五个不同实体的Symfony应用程序(它们并不重要) .

对于每个实体,注册用户必须具有NONE,READ,EDIT,DELETE权限 . 我要 grab 的关键部分是每个用户可以为每个实体拥有不同的权限;用户A可以编辑实体A,但只能查看实体B等 .

现在,在每个用户的选项页面上,管理员应该能够看到他对每个表单的权限 . 单选按钮应显示每个表单的四个选项 . 就像是:

Entity A:  O NONE    O READ    X EDIT    O DELETE
Entity B:  O NONE    X READ    O EDIT    O DELETE
...

我知道我的选择基本上是在创建某种类型的选民系统或访问控制列表之间 .

首先,我刚开始列出我的UserType中系统中当前的所有角色:

$builder
        ...
        ->add('roles', 'choice', array(
            'choices' => $this->roles,
            'choices_as_values' => true,
            'label' => 'Roles',
            'expanded' => true,
            'multiple' => true,
            'mapped' => true,
        ))
    ;

但我觉得从长远来看这不会很有效 . 无论哪种方式,这还会显示与特定实体的访问控制无关的其他系统角色(例如ROLE_USER,ROLE_ADMIN等)

我不是在寻找一个完整的解决方案或类似的东西,我只是很难开始并看到如何实现这一目标的大局 . (是的,我知道Symfony文档......有时候这些东西起初并没有多大意义) .

PROGRESS UPDATE

我决定 Access Control List .

首先,在创建新实体时,我使用Symfony文档中提到的标准ACL创建策略:

public function postAvrequestAction(Request $request){
        $entity = new AvRequest();

        $form = $this->get('form.factory')->createNamed('', new AvRequestType(), $entity);
        $form->handleRequest($request);

        if ($form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $em->persist($entity);
            $em->flush();

            $serializer = $this->get('serializer');
            $serialized = $serializer->serialize($entity, 'json');


            // creating the ACL
            $aclProvider = $this->get('security.acl.provider');
            $objectIdentity = ObjectIdentity::fromDomainObject($entity);
            $acl = $aclProvider->createAcl($objectIdentity);

            // retrieving the security identity of the currently logged-in user
            $tokenStorage = $this->get('security.token_storage');
            $users = $em->getRepository('AppBundle:User')->findAll();

            //$tokenStorage->getToken()->getUser();
            foreach($users as $user){
              $securityIdentity = UserSecurityIdentity::fromAccount($user);

              // grant owner access based on owner's overall permissions for this type of entity
              $acl->insertObjectAce($securityIdentity, 0);
              $aclProvider->updateAcl($acl);
            }

            return new Response($serialized, 201);
        }

        return new JsonResponse(array(
            'errors' => $this->getFormErrors($form)
        ));
    }

接下来,我创建了一个包含所有必需依赖项的服务,以更新每个实体的用户权限:

#services.yml
services:
    user_service:
      class: AppBundle\Resources\Services\UserService
      arguments: [ @doctrine.orm.entity_manager, @service_container, @security.authorization_checker, @security.acl.provider ]

该服务具有以下功能:

/**
 * ACLs grant user permission on every instance of each entity.
 * In order to edit permissions across all of these entites for each user,
 * first iterate over all entities. 
 * For each entity, update the permission for the specified user.
 *
 * @param  \AppBundle\Entity\User $user  The user object whose permissions should be updated
 * @param String $entity  The entity whose permissions should be updated (e.g. 'AppBundle:AvRequest')
 * @param int $permission  The bitmask value of the permission level (e.g. MaskBuilder::MASK_VIEW (=4))
 * 
 * @return null
 */
  public function editPermission(User $user, $entity, $permission){
    $allEntities = $this->em->getRepository($entity)->findAll();

    foreach($allEntities as $oneEntity){
      // locate the ACL
      $objectIdentity = ObjectIdentity::fromDomainObject($oneEntity);
      $acl = $this->aclProvider->findAcl($objectIdentity);

      // update user access
      $objectAces = $acl->getObjectAces();
      foreach($objectAces as $i => $ace) {
          $acl->updateObjectAce($i, $permission); 
      }
    }
  }

此函数遍历实体的每个实例,并为指定用户提供相同的权限级别 .

The next step that I haven't quite figured out yet is 为实体上的用户设置主权限级别,如我的单选按钮所述 . 我需要能够为每个实体类型转到用户's profile page, see a radio list of the user'的权限,提交单选按钮值,然后在保存时运行editPermission()函数 .

1 回答

  • 0

    您正在寻找Access Control Lists . 用户或用户组很容易设置权限 .

    按用户添加访问级别:

    $builder = new MaskBuilder();
    $builder
        ->add('view')
        ->add('edit')
        ->add('delete')
        ->add('undelete')
    ;
    $mask = $builder->get(); // int(29)
    
    $identity = new UserSecurityIdentity('johannes', 'AppBundle\Entity\User');
    $acl->insertObjectAce($identity, $mask);
    

    按实体指定最小访问级别:

    public function addCommentAction(Post $post)
    {
        $comment = new Comment();
    
        // ... setup $form, and submit data
    
        if ($form->isValid()) {
            $entityManager = $this->getDoctrine()->getManager();
            $entityManager->persist($comment);
            $entityManager->flush();
    
            // creating the ACL
            $aclProvider = $this->get('security.acl.provider');
            $objectIdentity = ObjectIdentity::fromDomainObject($comment);
            $acl = $aclProvider->createAcl($objectIdentity);
    
            // retrieving the security identity of the currently logged-in user
            $tokenStorage = $this->get('security.token_storage');
            $user = $tokenStorage->getToken()->getUser();
            $securityIdentity = UserSecurityIdentity::fromAccount($user);
    
            // grant owner access
            $acl->insertObjectAce($securityIdentity, MaskBuilder::MASK_OWNER);
            $aclProvider->updateAcl($acl);
        }
    }
    public function editCommentAction(Comment $comment)
    {
        $authorizationChecker = $this->get('security.authorization_checker');
    
        // check for edit access
        if (false === $authorizationChecker->isGranted('EDIT', $comment)) {
            throw new AccessDeniedException();
        }
    
        // ... retrieve actual comment object, and do your editing here
    }
    

相关问题