2014-07-24 42 views
1

我有三个实体:用户商店类别用户商店类别也是一个双向关系的双向关系。 每个用户都可以创建多个商店,并且他可以为每个商店创建多个类别。 我设法使用选民保护商店,用户只能访问他的商店。安全实体使用选民

这是商店的路线

dashboard_store_view: 
path:  /{id}/view 
defaults: { _controller: ProjectStoreBundle:StoreDashboard:view } 

的URL是这样的

http://localhost/project/web/app_dev.php/dashboard/store/1/view 

这是控制器StoreDashboardController.php

<?php 
//.................. 
    public function viewAction(Store $store) 
{ 
    // keep in mind, this will call all registered security voters 
    if (false === $this->get('security.context')->isGranted('view', $store)) { 
     throw new AccessDeniedException('Unauthorised access!'); 
    }  
    $em = $this->getDoctrine()->getManager(); 

    $store = $em->getRepository('ProjectStoreBundle:Store')->findOneById($store); 

    return $this->render('ProjectDashboardBundle:Store:view.html.twig', 
    array(
     'store' => $store 
    )); 
} 

这是StoreVoter

<?php 

namespace Project\StoreBundle\Security\Authorization\Voter; 

use Symfony\Component\Security\Core\Exception\InvalidArgumentException; 
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; 
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; 
use Symfony\Component\Security\Core\User\UserInterface; 

class StoreVoter implements VoterInterface 
{ 
const VIEW = 'view'; 
const EDIT = 'edit'; 
const DELETE = 'delete'; 

public function supportsAttribute($attribute) 
{ 
    return in_array($attribute, array(
     self::VIEW, 
     self::EDIT, 
     self::DELETE, 
    )); 
} 

public function supportsClass($class) 
{ 
    $supportedClass = 'Project\StoreBundle\Entity\Store'; 

    return $supportedClass === $class || is_subclass_of($class, $supportedClass); 
} 

/** 
* @var \Project\StoreBundle\Entity\Store $store 
*/ 
public function vote(TokenInterface $token, $store, array $attributes) 
{ 
    // check if class of this object is supported by this voter 
    if (!$this->supportsClass(get_class($store))) { 
     return VoterInterface::ACCESS_ABSTAIN; 
    } 

    // check if the voter is used correct, only allow one attribute 
    // this isn't a requirement, it's just one easy way for you to 
    // design your voter 
    if(1 !== count($attributes)) { 
     throw new InvalidArgumentException(
      'Only one attribute is allowed for VIEW or EDIT' 
     ); 
    } 

    // set the attribute to check against 
    $attribute = $attributes[0]; 

    // get current logged in user 
    $user = $token->getUser(); 

    // check if the given attribute is covered by this voter 
    if (!$this->supportsAttribute($attribute)) { 
     return VoterInterface::ACCESS_ABSTAIN; 
    } 

    // make sure there is a user object (i.e. that the user is logged in) 
    if (!$user instanceof UserInterface) { 
     return VoterInterface::ACCESS_DENIED; 
    } 

    switch($attribute) { 
     case 'view': 
      // we assume that our data object has a method getUser() to 
      // get the current owner user entity for this data object 
      if ($user->getId() === $store->getUser()->getId()) { 
       return VoterInterface::ACCESS_GRANTED; 
      } 
      break; 
     case 'edit': 
      // we assume that our data object has a method getUser() to 
      // get the current owner user entity for this data object 
      if ($user->getId() === $store->getUser()->getId()) { 
       return VoterInterface::ACCESS_GRANTED; 
      } 
      break; 
     case 'delete': 
      // we assume that our data object has a method getUser() to 
      // get the current owner user entity for this data object 
      if ($user->getId() === $store->getUser()->getId()) { 
       return VoterInterface::ACCESS_GRANTED; 
      } 
      break; 

    } 
} 
} 

我试图做同样的事情类别,但我失败了每个类别保护自己的商店等埃夫里的用户可以编辑任何类别

这是路线

dashboard_category_edit: 
pattern: /{store_id}/edit/{id} 
defaults: { _controller: ProjectStoreBundle:CategoryDashboard:edit } 

网址是这样的

http://localhost/project/web/app_dev.php/dashboard/categories/store/1/edit/3 

CategoryDashboardCo ntroller.php

public function editAction(Category $category, Store $store) 
{  
    // keep in mind, this will call all registered security voters 
    if (false === $this->get('security.context')->isGranted('edit', $store)) { 
     throw new AccessDeniedException('Unauthorised access!'); 
    } 

    $form = $this->createForm(new CategoryEditType(), $category); 

    $request = $this->getRequest(); 

    if ($request->getMethod() == 'POST') 
    { 
     $form->bind($request); 

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

      $this->get('session')->getFlashBag()->add('info', 'Category bien modifié'); 

      return $this->redirect($this->generateUrl('dashboard_category_index', array('store_id' => $store->getId()))); 
     } 
    } 

    return $this->render('ProjectDashboardBundle:Category:edit.html.twig', 
    array(
     'form' => $form->createView() , 
     'store' => $store 
     )); 
} 

,这是CategoryVoter

<?php 

namespace Project\StoreBundle\Security\Authorization\Voter; 

use Symfony\Component\Security\Core\Exception\InvalidArgumentException; 
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; 
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; 
use Symfony\Component\Security\Core\User\UserInterface; 

class CategoryVoter implements VoterInterface 
{ 
const VIEW = 'view'; 
const EDIT = 'edit'; 
const DELETE = 'delete'; 

public function supportsAttribute($attribute) 
{ 
    return in_array($attribute, array(
     self::VIEW, 
     self::EDIT, 
     self::DELETE, 
    )); 
} 

public function supportsClass($class) 
{ 
    $supportedClass = 'Project\StoreBundle\Entity\Category'; 

    return $supportedClass === $class || is_subclass_of($class, $supportedClass); 
} 

/** 
* @var \Project\StoreBundle\Entity\Category $category 
*/ 
public function vote(TokenInterface $token, $category, array $attributes) 
{ 
    // check if class of this object is supported by this voter 
    if (!$this->supportsClass(get_class($category))) { 
     return VoterInterface::ACCESS_ABSTAIN; 
    } 

    // check if the voter is used correct, only allow one attribute 
    // this isn't a requirement, it's just one easy way for you to 
    // design your voter 
    if(1 !== count($attributes)) { 
     throw new InvalidArgumentException(
      'Only one attribute is allowed for VIEW or EDIT' 
     ); 
    } 

    // set the attribute to check against 
    $attribute = $attributes[0]; 

    // get current logged in user 
    $user = $token->getUser(); 

    // check if the given attribute is covered by this voter 
    if (!$this->supportsAttribute($attribute)) { 
     return VoterInterface::ACCESS_ABSTAIN; 
    } 

    // make sure there is a user object (i.e. that the user is logged in) 
    if (!$user instanceof UserInterface) { 
     return VoterInterface::ACCESS_DENIED; 
    } 

    switch($attribute) { 
     case 'view': 
      // we assume that our data object has a method getUser() to 
      // get the current owner user entity for this data object 
      if ($user->getId() === $category->getStore()->getUser()->getId()) { 
       return VoterInterface::ACCESS_GRANTED; 
      } 
      break; 
     case 'edit': 
      // we assume that our data object has a method getUser() to 
      // get the current owner user entity for this data object 
      if ($user->getId() === $category->getStore()->getUser()->getId()) { 
       return VoterInterface::ACCESS_GRANTED; 
      } 
      break; 
     case 'delete': 
      // we assume that our data object has a method getUser() to 
      // get the current owner user entity for this data object 
      if ($user->getId() === $category->getStore()->getUser()->getId()) { 
       return VoterInterface::ACCESS_GRANTED; 
      } 
      break; 

    } 
} 
} 

的问题是,一类是不realted到用户,但它涉及到存储,所以我怎么能保证呢?


我觉得这个解决方案做验证,如果$分类 - > getStore <> $存储,以便扔AccessDeniedException异常不使用的选民和它现在的工作很好。

if ($category->getStore() <> $store) { 
     throw new AccessDeniedException('Unauthorised access!'); 
    } 

所以控制器将是这样

/** 
* @ParamConverter("store", options={"mapping": {"store_id":"id"}}) 
*/ 
public function editAction(Category $category, Store $store) 
{  

    if ($category->getStore() <> $store) { 
     throw new AccessDeniedException('Unauthorised access!'); 
    } 

    $form = $this->createForm(new CategoryEditType(), $category); 

    $request = $this->getRequest(); 

    if ($request->getMethod() == 'POST') 
    { 
     $form->bind($request); 

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

      $this->get('session')->getFlashBag()->add('info', 'Category bien modifié'); 

      return $this->redirect($this->generateUrl('dashboard_category_index', array('store_id' => $store->getId()))); 
     } 
    } 

    return $this->render('ProjectDashboardBundle:Category:edit.html.twig', 
    array(
     'form' => $form->createView() , 
     'store' => $store 
     )); 
} 

它是一个很好的解决方案?

+0

每个'Category'只能有一个'Store'? – pazulx

+0

是的,每个类别只能有一个商店,而商店可以有多个类别 – hous

+0

如果用户与_Store_的所有者不匹配,您的_StoreVoter_会发生什么?你不会返回一个ACCESS_DENID? – rgazelot

回答

0

我觉得这个解决方案,让表类店铺的ID,然后做两个验证, 如果id_store表类别不相符商店的老板,如果id_store表类别不匹配现有商店

/** 
* @ParamConverter("store", options={"mapping": {"store_id":"id"}}) 
*/ 
public function editAction(Category $category, Store $store) 
{  

    // get id_store in table category 
    $idStore = $category->getStore(); 

    // if id_store in table category doesn't match user 
    if (false === $this->get('security.context')->isGranted('edit', $idStore)) { 
     throw new AccessDeniedException('Unauthorised access!'); 
    }  

    // if id_store in table category doesn't match current store 
    if (false === ($idStore === $store)) { 
     throw new AccessDeniedException('Unauthorised access!'); 
    }  

    $form = $this->createForm(new CategoryEditType(), $category); 

    $request = $this->getRequest(); 

    if ($request->getMethod() == 'POST') 
    { 
     $form->bind($request); 

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

      $this->get('session')->getFlashBag()->add('info', 'Category bien modifié'); 

      return $this->redirect($this->generateUrl('dashboard_category_index', array('store_id' => $store->getId()))); 
     } 
    } 

    return $this->render('ProjectDashboardBundle:Category:edit.html.twig', 
    array(
     'form' => $form->createView() , 
     'store' => $store 
     )); 
} 
0

如果每个Category只有一个Store当你想编辑Category时,在路由中使用store_id没有意义。只需使用category_id并通过致电$store = $category->getStore();$category得到$store。更改editAction

/** 
* @ParamConverter("category", options={"mapping": {"category_id":"id"}}) 
*/ 
public function editAction(Category $category) 
{  
    // keep in mind, this will call all registered security voters 
    if (false === $this->get('security.context')->isGranted('edit', $category)) { 
     throw new AccessDeniedException('Unauthorised access!'); 
    } 
    $store = $category->getStore(); 
    (...) 
+0

我不知道如果检查授予的访问类别是最好的方法。由于用户与Store有关系,该检查必须位于Store对象上? – rgazelot

+0

所以,只需在'$ store = $ category-> getStore();''store'得到'$ store' – pazulx