我在Symfony 2.1中使用ACL,我需要创建一个新的SecurityIdentity,以便我的ACL可以设置为某种组的功能。
请注意以下情况:存在具有用户(具有不同角色)的组,每个组都具有用户信息。在组1中,具有ROLE_ADMIN的用户无法从同一组的信息中编辑其他用户,但在组2中,具有ROLE_ADMIN的用户可以编辑其他用户信息。
所以基本上我的ACL会根据用户所在的组别而有所不同。
我以为我会通过创建新的“GroupSecurityIdentity”来解决这个问题。然而,这个类本身是不够的,因为当我使用它时会遇到这个异常:
$sid must either be an instance of UserSecurityIdentity, or RoleSecurityIdentity.
我的问题是:如何“注册”我的新SecurityIdentity,以便将其用作RoleSecurityIdentity和UserSecurityIdentity?
有哪些更好的方法可以实现类似于我想要的系统?
答案 0 :(得分:2)
我正在使用FOSUserBundle中的GroupInterface
,但我想你也可以使用自己的AclProvider
。需要添加以下文件:
要更改的方法是私有的 - 必须复制整个文件,但唯一的更改必须是hydrateObjectIdentities
我们必须复制整个文件,因为它必须扩展MutableAclProvider
,但我们正在使用自定义文件,因此无法扩展库存<parameter key="security.acl.dbal.provider.class">
Acme\Bundle\DemoBundle\Security\Acl\Dbal\MutableAclProvider
</parameter>
<parameter key="security.acl.security_identity_retrieval_strategy.class">
Acme\Bundle\DemoBundle\Security\Acl\Domain\SecurityIdentityRetrievalStrategy
</parameter>
。更改的方法是getInsertSecurityIdentitySql
和getSelectSecurityIdentityIdSql
。
接下来:通过提供以下参数重新连接依赖注入容器:
User
交叉手指的时间,看它是否有效。由于这是旧代码,我可能忘记了一些东西。
这个想法是让组名与角色相对应。
一种简单的方法是让您的UserInterface::getRoles
实体重新实施public function getRoles()
{
$roles = parent::getRoles();
// This can be cached should there be any performance issues
// which I highly doubt there would be.
foreach ($this->getGroups() as $group) {
// GroupInterface::getRole() would probably have to use its
// canonical name to get something like `ROLE_GROUP_NAME_OF_GROUP`
$roles[] = $group->getRole();
}
return $roles;
}
:
GroupInterface::getRole()
public function getRole()
{
$name = $this->getNameCanonical();
return 'ROLE_GROUP_'.mb_convert_case($name, MB_CASE_UPPER, 'UTF-8');
}
的可能实现:
<?php
namespace Acme\Bundle\DemoBundle\Authorization\Voter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
class MySecureObjectVoter implements VoterInterface
{
/**
* {@inheritDoc}
*/
public function supportsAttribute($attribute)
{
$supported = array('VIEW');
return in_array($attribute, $supported);
}
/**
* {@inheritDoc}
*/
public function supportsClass($class)
{
return $class instanceof GroupableInterface;
}
/**
* {@inheritDoc}
*/
public function vote(TokenInterface $token, $object, array $attributes)
{
$result = VoterInterface::ACCESS_ABSTAIN;
if (!$object instanceof MySecureObject) {
return VoterInterface::ACCESS_ABSTAIN;
}
foreach ($attributes as $attribute) {
if (!$this->supportsAttribute($attribute)) {
continue;
}
// Access is granted, if the user and object have at least 1
// group in common.
if ('VIEW' === $attribute) {
$objGroups = $object->getGroups();
$userGroups = $token->getUser()->getGroups();
foreach ($userGroups as $userGroup) {
foreach ($objGroups as $objGroup) {
if ($userGroup->equals($objGroup)) {
return VoterInterface::ACCESS_GRANTED;
}
}
}
return voterInterface::ACCESS_DENIED;
}
}
}
}
现在只需要创建cookbook article中所写的所需ACE-s。
最后,您可以使用自定义选民来检查特定组的存在以及用户是否有权访问所述对象。可能的实施:
{{1}}
有关选民的更多详情,请参阅cookbook example。
我会避免创建自定义安全标识。使用提供的其他两种方法。如果您将拥有大量记录并且每个记录必须具有不同的访问设置,则第二种解决方案效果最佳。选民可以用来设置简单的访问授权逻辑(大多数小型系统似乎都属于这种逻辑)或者需要灵活性。
答案 1 :(得分:0)
我在这里写下我的答案,以便跟踪此错误消息。
我使用ACL实现了组支持,我不得不破解symfony核心&#34; MutableAclProvider.php&#34;
protected function getSelectSecurityIdentityIdSql(SecurityIdentityInterface $sid)
{
if ($sid instanceof UserSecurityIdentity) {
$identifier = $sid->getClass().'-'.$sid->getUsername();
$username = true;
} elseif ($sid instanceof RoleSecurityIdentity) {
$identifier = $sid->getRole();
$username = false;
}else {
//throw new \InvalidArgumentException('$sid must either be an instance of UserSecurityIdentity, or RoleSecurityIdentity.');
$identifier = $sid->getClass().'-'.$sid->getGroupname();
$username = true;
}
return sprintf(
'SELECT id FROM %s WHERE identifier = %s AND username = %s',
$this->options['sid_table_name'],
$this->connection->quote($identifier),
$this->connection->getDatabasePlatform()->convertBooleans($username)
);
}
即使提供的对象不是UserSecurityIdentity或RoleSecurityIdentity的实例,它也会返回一个值。所以现在我可以使用自定义&#34; GroupSecurityIdentity&#34;
它不容易实施,但很适合我的系统。