[Security][ACL] Fixed ObjectIdentity::fromDomainObject and UserSecurityIdentity::from(Account|Token) when working with proxies
Backported ClassUtils class from Doctrine Common 2.2 Fixes #2611, #2056, #2048, #2035
This commit is contained in:
parent
b47cb35e7b
commit
6483d88f69
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Security\Acl\Domain;
|
namespace Symfony\Component\Security\Acl\Domain;
|
||||||
|
|
||||||
|
use Symfony\Component\Security\Core\Util\ClassUtils;
|
||||||
use Symfony\Component\Security\Acl\Exception\InvalidDomainObjectException;
|
use Symfony\Component\Security\Acl\Exception\InvalidDomainObjectException;
|
||||||
use Symfony\Component\Security\Acl\Model\DomainObjectInterface;
|
use Symfony\Component\Security\Acl\Model\DomainObjectInterface;
|
||||||
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
|
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
|
||||||
@ -59,9 +60,9 @@ final class ObjectIdentity implements ObjectIdentityInterface
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if ($domainObject instanceof DomainObjectInterface) {
|
if ($domainObject instanceof DomainObjectInterface) {
|
||||||
return new self($domainObject->getObjectIdentifier(), get_class($domainObject));
|
return new self($domainObject->getObjectIdentifier(), ClassUtils::getRealClass($domainObject));
|
||||||
} elseif (method_exists($domainObject, 'getId')) {
|
} elseif (method_exists($domainObject, 'getId')) {
|
||||||
return new self($domainObject->getId(), get_class($domainObject));
|
return new self($domainObject->getId(), ClassUtils::getRealClass($domainObject));
|
||||||
}
|
}
|
||||||
} catch (\InvalidArgumentException $invalid) {
|
} catch (\InvalidArgumentException $invalid) {
|
||||||
throw new InvalidDomainObjectException($invalid->getMessage(), 0, $invalid);
|
throw new InvalidDomainObjectException($invalid->getMessage(), 0, $invalid);
|
||||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Security\Acl\Domain;
|
|||||||
|
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use Symfony\Component\Security\Core\Util\ClassUtils;
|
||||||
use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
|
use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,7 +53,7 @@ final class UserSecurityIdentity implements SecurityIdentityInterface
|
|||||||
*/
|
*/
|
||||||
static public function fromAccount(UserInterface $user)
|
static public function fromAccount(UserInterface $user)
|
||||||
{
|
{
|
||||||
return new self($user->getUsername(), get_class($user));
|
return new self($user->getUsername(), ClassUtils::getRealClass($user));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,7 +70,7 @@ final class UserSecurityIdentity implements SecurityIdentityInterface
|
|||||||
return self::fromAccount($user);
|
return self::fromAccount($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new self((string) $user, is_object($user)? get_class($user) : get_class($token));
|
return new self((string) $user, is_object($user) ? ClassUtils::getRealClass($user) : ClassUtils::getRealClass($token));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
55
src/Symfony/Component/Security/Core/Util/ClassUtils.php
Normal file
55
src/Symfony/Component/Security/Core/Util/ClassUtils.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Security\Core\Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class related functionality for objects that
|
||||||
|
* might or might not be proxy objects at the moment.
|
||||||
|
*
|
||||||
|
* @see Doctrine\Common\Util\ClassUtils
|
||||||
|
*
|
||||||
|
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||||
|
* @author Johannes Schmitt <schmittjoh@gmail.com>
|
||||||
|
*/
|
||||||
|
class ClassUtils
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Marker for Proxy class names.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
const MARKER = '__CG__';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length of the proxy marker
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
const MARKER_LENGTH = 6;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the real class name of a class name that could be a proxy.
|
||||||
|
*
|
||||||
|
* @param string|object
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function getRealClass($object)
|
||||||
|
{
|
||||||
|
$class = is_object($object) ? get_class($object) : $object;
|
||||||
|
|
||||||
|
if (false === $pos = strrpos($class, '\\'.self::MARKER.'\\')) {
|
||||||
|
return $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
return substr($class, $pos + self::MARKER_LENGTH + 2);
|
||||||
|
}
|
||||||
|
}
|
@ -9,83 +9,108 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Symfony\Component\Security\Tests\Acl\Domain;
|
namespace Symfony\Component\Security\Tests\Acl\Domain
|
||||||
|
|
||||||
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
|
|
||||||
|
|
||||||
class ObjectIdentityTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
{
|
||||||
public function testConstructor()
|
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
|
||||||
|
|
||||||
|
class ObjectIdentityTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
$id = new ObjectIdentity('fooid', 'footype');
|
public function testConstructor()
|
||||||
|
{
|
||||||
|
$id = new ObjectIdentity('fooid', 'footype');
|
||||||
|
|
||||||
|
$this->assertEquals('fooid', $id->getIdentifier());
|
||||||
|
$this->assertEquals('footype', $id->getType());
|
||||||
|
}
|
||||||
|
|
||||||
$this->assertEquals('fooid', $id->getIdentifier());
|
// Test that constructor never changes passed type, even with proxies
|
||||||
$this->assertEquals('footype', $id->getType());
|
public function testConstructorWithProxy()
|
||||||
}
|
{
|
||||||
|
$id = new ObjectIdentity('fooid', 'Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Tests\Acl\Domain\TestDomainObject');
|
||||||
|
|
||||||
public function testFromDomainObjectPrefersInterfaceOverGetId()
|
$this->assertEquals('fooid', $id->getIdentifier());
|
||||||
{
|
$this->assertEquals('Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Tests\Acl\Domain\TestDomainObject', $id->getType());
|
||||||
$domainObject = $this->getMock('Symfony\Component\Security\Acl\Model\DomainObjectInterface');
|
}
|
||||||
$domainObject
|
|
||||||
->expects($this->once())
|
public function testFromDomainObjectPrefersInterfaceOverGetId()
|
||||||
->method('getObjectIdentifier')
|
{
|
||||||
->will($this->returnValue('getObjectIdentifier()'))
|
$domainObject = $this->getMock('Symfony\Component\Security\Acl\Model\DomainObjectInterface');
|
||||||
;
|
$domainObject
|
||||||
$domainObject
|
->expects($this->once())
|
||||||
->expects($this->never())
|
->method('getObjectIdentifier')
|
||||||
->method('getId')
|
->will($this->returnValue('getObjectIdentifier()'))
|
||||||
->will($this->returnValue('getId()'))
|
;
|
||||||
;
|
$domainObject
|
||||||
|
->expects($this->never())
|
||||||
|
->method('getId')
|
||||||
|
->will($this->returnValue('getId()'))
|
||||||
|
;
|
||||||
|
|
||||||
|
$id = ObjectIdentity::fromDomainObject($domainObject);
|
||||||
|
$this->assertEquals('getObjectIdentifier()', $id->getIdentifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFromDomainObjectWithoutInterface()
|
||||||
|
{
|
||||||
|
$id = ObjectIdentity::fromDomainObject(new TestDomainObject());
|
||||||
|
$this->assertEquals('getId()', $id->getIdentifier());
|
||||||
|
$this->assertEquals('Symfony\Component\Security\Tests\Acl\Domain\TestDomainObject', $id->getType());
|
||||||
|
}
|
||||||
|
|
||||||
$id = ObjectIdentity::fromDomainObject($domainObject);
|
public function testFromDomainObjectWithProxy()
|
||||||
$this->assertEquals('getObjectIdentifier()', $id->getIdentifier());
|
{
|
||||||
}
|
$id = ObjectIdentity::fromDomainObject(new \Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Tests\Acl\Domain\TestDomainObject());
|
||||||
|
$this->assertEquals('getId()', $id->getIdentifier());
|
||||||
public function testFromDomainObjectWithoutInterface()
|
$this->assertEquals('Symfony\Component\Security\Tests\Acl\Domain\TestDomainObject', $id->getType());
|
||||||
{
|
}
|
||||||
$id = ObjectIdentity::fromDomainObject(new TestDomainObject());
|
|
||||||
$this->assertEquals('getId()', $id->getIdentifier());
|
/**
|
||||||
}
|
* @dataProvider getCompareData
|
||||||
|
*/
|
||||||
/**
|
public function testEquals($oid1, $oid2, $equal)
|
||||||
* @dataProvider getCompareData
|
{
|
||||||
*/
|
if ($equal) {
|
||||||
public function testEquals($oid1, $oid2, $equal)
|
$this->assertTrue($oid1->equals($oid2));
|
||||||
{
|
} else {
|
||||||
if ($equal) {
|
$this->assertFalse($oid1->equals($oid2));
|
||||||
$this->assertTrue($oid1->equals($oid2));
|
}
|
||||||
} else {
|
}
|
||||||
$this->assertFalse($oid1->equals($oid2));
|
|
||||||
|
public function getCompareData()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(new ObjectIdentity('123', 'foo'), new ObjectIdentity('123', 'foo'), true),
|
||||||
|
array(new ObjectIdentity('123', 'foo'), new ObjectIdentity(123, 'foo'), true),
|
||||||
|
array(new ObjectIdentity('1', 'foo'), new ObjectIdentity('2', 'foo'), false),
|
||||||
|
array(new ObjectIdentity('1', 'bla'), new ObjectIdentity('1', 'blub'), false),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
if (!class_exists('Doctrine\DBAL\DriverManager')) {
|
||||||
|
$this->markTestSkipped('The Doctrine2 DBAL is required for this test');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCompareData()
|
class TestDomainObject
|
||||||
{
|
{
|
||||||
return array(
|
public function getObjectIdentifier()
|
||||||
array(new ObjectIdentity('123', 'foo'), new ObjectIdentity('123', 'foo'), true),
|
{
|
||||||
array(new ObjectIdentity('123', 'foo'), new ObjectIdentity(123, 'foo'), true),
|
return 'getObjectIdentifier()';
|
||||||
array(new ObjectIdentity('1', 'foo'), new ObjectIdentity('2', 'foo'), false),
|
}
|
||||||
array(new ObjectIdentity('1', 'bla'), new ObjectIdentity('1', 'blub'), false),
|
|
||||||
);
|
public function getId()
|
||||||
}
|
{
|
||||||
|
return 'getId()';
|
||||||
protected function setUp()
|
|
||||||
{
|
|
||||||
if (!class_exists('Doctrine\DBAL\DriverManager')) {
|
|
||||||
$this->markTestSkipped('The Doctrine2 DBAL is required for this test');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestDomainObject
|
namespace Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Tests\Acl\Domain
|
||||||
{
|
{
|
||||||
public function getObjectIdentifier()
|
class TestDomainObject extends \Symfony\Component\Security\Tests\Acl\Domain\TestDomainObject
|
||||||
{
|
{
|
||||||
return 'getObjectIdentifier()';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getId()
|
|
||||||
{
|
|
||||||
return 'getId()';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,15 @@ class UserSecurityIdentityTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals('Foo', $id->getClass());
|
$this->assertEquals('Foo', $id->getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that constructor never changes the type, even for proxies
|
||||||
|
public function testContructorWithProxy()
|
||||||
|
{
|
||||||
|
$id = new UserSecurityIdentity('foo', 'Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Tests\Acl\Domain\Foo');
|
||||||
|
|
||||||
|
$this->assertEquals('foo', $id->getUsername());
|
||||||
|
$this->assertEquals('Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Tests\Acl\Domain\Foo', $id->getClass());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider getCompareData
|
* @dataProvider getCompareData
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Symfony\Component\Security\Tests\Core\Util
|
||||||
|
{
|
||||||
|
use Symfony\Component\Security\Core\Util\ClassUtils;
|
||||||
|
|
||||||
|
class ClassUtilsTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
static public function dataGetClass()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('stdClass', 'stdClass'),
|
||||||
|
array('Symfony\Component\Security\Core\Util\ClassUtils', 'Symfony\Component\Security\Core\Util\ClassUtils'),
|
||||||
|
array('MyProject\Proxies\__CG__\stdClass', 'stdClass'),
|
||||||
|
array('MyProject\Proxies\__CG__\OtherProject\Proxies\__CG__\stdClass', 'stdClass'),
|
||||||
|
array('MyProject\Proxies\__CG__\Symfony\Component\Security\Tests\Core\Util\ChildObject', 'Symfony\Component\Security\Tests\Core\Util\ChildObject'),
|
||||||
|
array(new TestObject(), 'Symfony\Component\Security\Tests\Core\Util\TestObject'),
|
||||||
|
array(new \Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Tests\Core\Util\TestObject(), 'Symfony\Component\Security\Tests\Core\Util\TestObject'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider dataGetClass
|
||||||
|
*/
|
||||||
|
public function testGetRealClass($object, $expectedClassName)
|
||||||
|
{
|
||||||
|
$this->assertEquals($expectedClassName, ClassUtils::getRealClass($object));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestObject
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Tests\Core\Util
|
||||||
|
{
|
||||||
|
class TestObject extends \Symfony\Component\Security\Tests\Core\Util\TestObject
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user