我有一个Symfony 3应用程序,它使用Doctrine ORM进行实体管理。目前,我正致力于启用CRUD支持。我已经发现我可以使用security voters来限制对实体或控制器的访问。例如,我将其配置为只有管理员可以创建,更新或删除A类实体的方式。
对于我的实体类型B的实例,我还想让各自所有者更新(不创建或删除),我设法轻松完成。但是,不应允许所有者修改所有实体的属性 - 只是其中一些属性。我如何用Symfony实现这一点?另外,我使用Form Bundle来创建和验证表单。
编辑:我添加了一些相关代码。控制器调用denyAccessUnlessGranted
,触发选民。只是为了澄清,该代码已经正常工作。我的问题与我还没有的代码有关。
控制器:
public function editAction(Request $request, int $id) {
$em = $this->getDoctrine()->getManager();
$project = $em->getRepository(Project::class)->findOneBy(['id'=>$id]);
$this->denyAccessUnlessGranted(ProjectVoter::EDIT, $project);
$users = $em->getRepository(EntityUser::class)->findAll();
$groups = $em->getRepository(Group::class)->findAll();
$tags = $em->getRepository(Tag::class)->findAll();
$form = $this->createForm(ProjectType::class, $project, [
'possibleAdmins' => $users,
'possibleRequiredGroups' => $groups,
'possibleTags' => $tags,
]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$project = $form->getData();
$em->flush();
return $this->redirectToRoute('projects_show', ['id'=>$project->getId()]);
}
return $this->render('project/editor.html.twig',
['project'=>$project, 'form'=>$form->createView()]);
}
选民:
protected function voteOnAttribute($attribute, $subject, TokenInterface $token) {
/** @var UserInterface $user */
$user = $token->getUser();
if (!$user instanceof UserInterface) {
// the user must be logged in; if not, deny access
return false;
}
else if ($this->decisionManager->decide($token, ['ROLE_ADMIN'])) {
return true; // system-wide admins shall always have access
}
switch($attribute) {
case self::SHOW:
return ($subject->isVisible() || $subject->getAdmins()->contains($user);
case self::EDIT:
return $subject->getAdmins()->contains($user);
case self::REMOVE:
return false;
}
return false;
}
答案 0 :(得分:1)
据我所知,没有与个别属性特别相关的访问功能。当然,只要我发布此内容,其他人就会恰到好处。
您可能会考虑做的是定义两个编辑角色,EDIT_BY_ADMIN和EDIT_BY_OWNER。然后,您可以测试条件并选择要使用的表单类型。
$projectTypeClass = null;
if ($this->isGranted(ProjectVoter::EDIT_BY_ADMIN,$project)) {
$projectTypeClass = ProjectAdminType::class);
}
elseif ($this->isGranted(ProjectVoter::EDIT_BY_OWNER,$project)) {
$projectTypeClass = ProjectOwnerType::class);
}
if (!$projectTypeClass) {
// throw access denied exception
}
$form = $this->createForm($projectTypeClass, $project, [
这应该可以解决问题。当然有很多变化。您可以坚持使用一种项目类型并在类型类中进行访问测试,但这需要表单侦听器。
如果您需要更多粒度,那么您可以改为添加一些EDIT_PROP1,EDIT_PROP2类型的角色。
当然,如果你真的进入它,那么你可以将一些访问代码移动到某种类型的数据库中。或者可以看一下那里的一些访问控制列表组件。
答案 1 :(得分:0)
我最终提出了这个解决方案:
我没有使用多个FormType
,而是只使用了一个,最终根据选民的结果启用或禁用了属性的表单字段。为此,我定义了一个新的交换机案例,如Cerad建议的那样(在本答案中将其命名为ProjectVoter::MODIFY_PROTECTED_PROPERTY
用于演示目的)并根据自己的喜好添加了业务逻辑。
我刚启用或禁用了表单字段,因为我实际上希望用户看到他/她无法编辑该属性。但是很可能很容易也不可能首先add
这个领域,所以它不可见。
表单类型:
信息:$this->tokenStorage
和$this->accessDecisionManager
分别是注入服务("security.token_storage"
和"security.access.decision_manager"
)。
public function buildForm(FormBuilderInterface $builder, array $options) {
$token = $options['token'] ?? $this->tokenStorage->getToken();
$project = $builder->getData();
$builder
->add('name')
// ...
->add('protectedProperty', null, [
'disabled' => !$this->accessDecisionManager->decide($token, [ProjectVoter::MODIFY_PROTECTED_PROPERTY], $project),
])
;
}
我还在其token
函数中添加了一个名为configureOptions
的表单类型的选项,默认为null
,因此可以为任意用户而不是一个用户构建表单如果需要,目前已登录。