我在Symfony 3中使用SonataAdminBundle。因为我使用Symfony 3,我仍然不能使用SonataUserBundle。所以我只使用SonataAdminBundle和FOSUserBundle。
现在我试图实现的是隐藏每个角色的特定路由。例如,我只有三个角色;
- 超级管理员管理
- 另一个角色
Super Admin拥有Admin拥有的所有角色,Admin拥有第三个角色的所有角色,第三个角色显然拥有ROLE_USER。超级管理员应该能够创建新的用户,并分配一个角色给他。超级管理员还应该能够更改用户的密码。用户应该能够更改自己帐户的密码。最后,超级管理员应该而不是能够更改自己的角色并创建新用户。
如何在不使用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');
}
}
但我想有一个更好的解决方案。我完全了解关于安全的官方文档,但我对此感到困惑,这是否意味着我必须在我的security.yml
文件中为所有不同的管理员硬编码每个角色?即使没有SonataUserBundle,它也能工作吗?我不想为ACL添加额外的数据库表。
谁能帮助和/或提供一个好的例子?我真的很感激。
如何管理Sonata中没有SonataUserBundle
的用户和角色?
答案:我们需要做同样的SonataUserBundle
(但让我们简化一点)
Symfony flat中基于ROLE_
的安全性类比:
- The house:有门和钥匙的建筑(系统)。
-
门:在房子里限制进入的地方-
isGranted()
:// the door is here, we need the key to open it. if ($this->isGranted('ROLE_FOO')) { // restricted access to do something }
-
key:授予权限进入受限门-
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
已经创建了门来限制对被管理实体的每个默认操作(例如list
操作)的访问。
所以我们的工作是"仅"将钥匙分配给用户(除非您需要创建自己的门)。有很多方法可以实现这一点(这取决于你需要什么)。
注意:如果你没有角色层次结构,你只有单个键(即你没有主键),这使得它不灵活的角色(键)分配。
现在,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
您可以为您的user实体定义自定义用户权限Voter,见这里。
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 }
小心访问决策策略,如果它被定义为unanimous
或consensus
,则可能无法根据您的配置工作
如果你不想让每个用户都能访问用户列表,你也可以添加一个直接链接/路由到用户自己的编辑页面。
编辑
要限制用户角色编辑,因为您不希望用户编辑自己的角色,您可以简单地编辑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 forms组件将为您检查是否添加了其他字段