feature #8650 [Security][Acl] Add MutableAclProvider::updateUserSecurityIdentity (lemoinem)

This PR was merged into the 2.5-dev branch.

Discussion
----------

[Security][Acl] Add MutableAclProvider::updateUserSecurityIdentity

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #5787
| License       | MIT
| Doc PR        | symfony/symfony-docs#3319

This provides a very simple function to enable the update of a User's username while keeping its associated ACEs (by updating its corresponding UserSecurityIdentity)

Developers can add a listener on the preUpdate of a user and remove all the related ACLs:

```php
if ($args->hasChangedField('username')) {
    $aclProvider = $this->container->get('security.acl.provider');

    $oldUsername = $args->getOldValue ('username');
    $user        = $args->getEntity();

    $aclProvider->updateUserSecurityIdentity(UserSecurityIdentity::fromAccount($user) , $oldUsername);
}
```
Among the problems of not updating the UserSecurityIdentity:

- Inconsistent database, referring to a non-existent user.
- The user loses all its associated permissions
- If another user is created with the old username, it will inherit all the first user’s ACEs

This PR intends to fix Issue #5787 and is similar to and inspired from PR #8305.
This will also be heavily impacted by the outcome of #8848

Commits
-------

da53d92 [Security][Acl] Fix #5787 : Add MutableAclProvider::updateUserSecurityIdentity
This commit is contained in:
Fabien Potencier 2013-12-23 16:31:59 +01:00
commit 8d39213f4c
2 changed files with 66 additions and 0 deletions

View File

@ -351,6 +351,17 @@ class MutableAclProvider extends AclProvider implements MutableAclProviderInterf
}
}
/**
* Updates a user security identity when the user's username changes
*
* @param UserSecurityIdentity $usid
* @param string $oldUsername
*/
public function updateUserSecurityIdentity(UserSecurityIdentity $usid, $oldUsername)
{
$this->connection->executeQuery($this->getUpdateUserSecurityIdentitySql($usid, $oldUsername));
}
/**
* Constructs the SQL for deleting access control entries.
*
@ -633,6 +644,31 @@ QUERY;
);
}
/**
* Constructs the SQL for updating a user security identity.
*
* @param UserSecurityIdentity $usid
* @param string $oldUsername
* @return string
*/
protected function getUpdateUserSecurityIdentitySql(UserSecurityIdentity $usid, $oldUsername)
{
if ($usid->getUsername() == $oldUsername) {
throw new \InvalidArgumentException('There are no changes.');
}
$oldIdentifier = $usid->getClass().'-'.$oldUsername;
$newIdentifier = $usid->getClass().'-'.$usid->getUsername();
return sprintf(
'UPDATE %s SET identifier = %s WHERE identifier = %s AND username = %s',
$this->options['sid_table_name'],
$this->connection->quote($newIdentifier),
$this->connection->quote($oldIdentifier),
$this->connection->getDatabasePlatform()->convertBooleans(true)
);
}
/**
* Constructs the SQL for updating an ACE.
*

View File

@ -407,6 +407,36 @@ class MutableAclProviderTest extends \PHPUnit_Framework_TestCase
$provider->updateAcl($acl);
}
public function testUpdateUserSecurityIdentity()
{
$provider = $this->getProvider();
$acl = $provider->createAcl(new ObjectIdentity(1, 'Foo'));
$sid = new UserSecurityIdentity('johannes', 'FooClass');
$acl->setEntriesInheriting(!$acl->isEntriesInheriting());
$acl->insertObjectAce($sid, 1);
$acl->insertClassAce($sid, 5, 0, false);
$acl->insertObjectAce($sid, 2, 1, true);
$acl->insertClassFieldAce('field', $sid, 2, 0, true);
$provider->updateAcl($acl);
$newSid = new UserSecurityIdentity('mathieu', 'FooClass');
$provider->updateUserSecurityIdentity($newSid, 'johannes');
$reloadProvider = $this->getProvider();
$reloadedAcl = $reloadProvider->findAcl(new ObjectIdentity(1, 'Foo'));
$this->assertNotSame($acl, $reloadedAcl);
$this->assertSame($acl->isEntriesInheriting(), $reloadedAcl->isEntriesInheriting());
$aces = $acl->getObjectAces();
$reloadedAces = $reloadedAcl->getObjectAces();
$this->assertEquals(count($aces), count($reloadedAces));
foreach ($reloadedAces as $ace) {
$this->assertTrue($ace->getSecurityIdentity()->equals($newSid));
}
}
/**
* Data must have the following format:
* array(