我有三个实体:用户,商店和类别。 用户与商店和店与类别也是一个双向关系的双向关系。 每个用户都可以创建多个商店,并且他可以为每个商店创建多个类别。 我设法使用选民保护商店,用户只能访问他的商店。安全实体使用选民
这是商店的路线
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
));
}
它是一个很好的解决方案?
每个'Category'只能有一个'Store'? – pazulx
是的,每个类别只能有一个商店,而商店可以有多个类别 – hous
如果用户与_Store_的所有者不匹配,您的_StoreVoter_会发生什么?你不会返回一个ACCESS_DENID? – rgazelot