summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabien Potencier <fabien.potencier@gmail.com>2015-10-05 17:24:55 +0200
committerFabien Potencier <fabien.potencier@gmail.com>2015-10-05 17:24:55 +0200
commit4e3ea9f244ad465865c2384f3d9ba2f89361d364 (patch)
tree1ecfb1dd66e5203b64b8d6341fc76a15499a4350
parente7529149fc3d42bab75d5d84f33dfa08bf649079 (diff)
parent99d73ecb12dedf5c772aab7f00e7d39b60c5f4ed (diff)
downloadsymfony-security-4e3ea9f244ad465865c2384f3d9ba2f89361d364.zip
symfony-security-4e3ea9f244ad465865c2384f3d9ba2f89361d364.tar.gz
symfony-security-4e3ea9f244ad465865c2384f3d9ba2f89361d364.tar.bz2
Merge branch '2.8'
* 2.8: (21 commits) [Security][bugfix] "Remember me" cookie cleared on logout with custom "secure"/"httponly" config options [1] [ci] Use current PHP_BINARY when running ./phpunit Fixed typos [UPGRADE-3.0] fix bullet indentation Throw exception if tempnam returns false in ProcessPipes [DomCrawler] Deprecated using /_root/ in XPath expressions Pass missing request template variables Simplify AbstractVoter [Form] add missing deprecation triggers Throw exception if tempnam returns false Fix PropertyAccessor modifying array in object when array key does not exist [DependencyInjection] Add autowiring capabilities Fixing typo in variable name Add a few additional tests for the Crawler [Form] remove obsolete deprecation comments Updated the style of the event commands [Debug] Deprecate providing $fileLinkFormat as second argument [Form] minor CS fix Updated PHPDoc of the AbstractVoter class [Security] InMemoryUserProvider now concerns whether user's password is changed when refreshing ...
-rw-r--r--Core/Authorization/Voter/AbstractVoter.php26
-rw-r--r--Core/Tests/Authorization/Voter/AbstractVoterTest.php5
-rw-r--r--Core/Tests/User/InMemoryUserProviderTest.php33
-rw-r--r--Core/User/InMemoryUserProvider.php37
-rw-r--r--Http/RememberMe/AbstractRememberMeServices.php2
-rw-r--r--Http/Tests/RememberMe/AbstractRememberMeServicesTest.php32
-rw-r--r--Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php11
-rw-r--r--Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php11
8 files changed, 109 insertions, 48 deletions
diff --git a/Core/Authorization/Voter/AbstractVoter.php b/Core/Authorization/Voter/AbstractVoter.php
index 71f570f..665d5f1 100644
--- a/Core/Authorization/Voter/AbstractVoter.php
+++ b/Core/Authorization/Voter/AbstractVoter.php
@@ -41,10 +41,9 @@ abstract class AbstractVoter implements VoterInterface
// abstain vote by default in case none of the attributes are supported
$vote = self::ACCESS_ABSTAIN;
- $class = get_class($object);
foreach ($attributes as $attribute) {
- if (!$this->supports($attribute, $class)) {
+ if (!$this->supports($attribute, $object)) {
continue;
}
@@ -61,33 +60,16 @@ abstract class AbstractVoter implements VoterInterface
}
/**
- * Determines if the attribute and class are supported by this voter.
- *
- * To determine if the passed class is instance of the supported class, the
- * isClassInstanceOf() method can be used.
+ * Determines if the attribute and object are supported by this voter.
*
* @param string $attribute An attribute
- * @param string $class The fully qualified class name of the passed object
+ * @param string $object The object to secure
*
- * @return bool True if the attribute and class is supported, false otherwise
+ * @return bool True if the attribute and object is supported, false otherwise
*/
abstract protected function supports($attribute, $class);
/**
- * A helper method to test if the actual class is instanceof or equal
- * to the expected class.
- *
- * @param string $actualClass The actual class name
- * @param string $expectedClass The expected class name
- *
- * @return bool
- */
- protected function isClassInstanceOf($actualClass, $expectedClass)
- {
- return $expectedClass === $actualClass || is_subclass_of($actualClass, $expectedClass);
- }
-
- /**
* Perform a single access check operation on a given attribute, object and token.
* It is safe to assume that $attribute and $object's class pass supports method call.
*
diff --git a/Core/Tests/Authorization/Voter/AbstractVoterTest.php b/Core/Tests/Authorization/Voter/AbstractVoterTest.php
index aafef5a..537dc4c 100644
--- a/Core/Tests/Authorization/Voter/AbstractVoterTest.php
+++ b/Core/Tests/Authorization/Voter/AbstractVoterTest.php
@@ -63,9 +63,8 @@ class AbstractVoterTest_Voter extends AbstractVoter
return 'EDIT' === $attribute;
}
- protected function supports($attribute, $class)
+ protected function supports($attribute, $object)
{
- return $this->isClassInstanceOf($class, 'stdClass')
- && in_array($attribute, array('EDIT', 'CREATE'));
+ return $object instanceof \stdClass && in_array($attribute, array('EDIT', 'CREATE'));
}
}
diff --git a/Core/Tests/User/InMemoryUserProviderTest.php b/Core/Tests/User/InMemoryUserProviderTest.php
index dfc4237..0a1815f 100644
--- a/Core/Tests/User/InMemoryUserProviderTest.php
+++ b/Core/Tests/User/InMemoryUserProviderTest.php
@@ -18,18 +18,39 @@ class InMemoryUserProviderTest extends \PHPUnit_Framework_TestCase
{
public function testConstructor()
{
- $provider = new InMemoryUserProvider(array(
+ $provider = $this->createProvider();
+
+ $user = $provider->loadUserByUsername('fabien');
+ $this->assertEquals('foo', $user->getPassword());
+ $this->assertEquals(array('ROLE_USER'), $user->getRoles());
+ $this->assertFalse($user->isEnabled());
+ }
+
+ public function testRefresh()
+ {
+ $user = new User('fabien', 'bar');
+
+ $provider = $this->createProvider();
+
+ $refreshedUser = $provider->refreshUser($user);
+ $this->assertEquals('foo', $refreshedUser->getPassword());
+ $this->assertEquals(array('ROLE_USER'), $refreshedUser->getRoles());
+ $this->assertFalse($refreshedUser->isEnabled());
+ $this->assertFalse($refreshedUser->isCredentialsNonExpired());
+ }
+
+ /**
+ * @return InMemoryUserProvider
+ */
+ protected function createProvider()
+ {
+ return new InMemoryUserProvider(array(
'fabien' => array(
'password' => 'foo',
'enabled' => false,
'roles' => array('ROLE_USER'),
),
));
-
- $user = $provider->loadUserByUsername('fabien');
- $this->assertEquals('foo', $user->getPassword());
- $this->assertEquals(array('ROLE_USER'), $user->getRoles());
- $this->assertFalse($user->isEnabled());
}
public function testCreateUser()
diff --git a/Core/User/InMemoryUserProvider.php b/Core/User/InMemoryUserProvider.php
index 624eb3d..9aa39ca 100644
--- a/Core/User/InMemoryUserProvider.php
+++ b/Core/User/InMemoryUserProvider.php
@@ -67,17 +67,9 @@ class InMemoryUserProvider implements UserProviderInterface
*/
public function loadUserByUsername($username)
{
- if (!isset($this->users[strtolower($username)])) {
- $ex = new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username));
- $ex->setUsername($username);
-
- throw $ex;
- }
+ $user = $this->getUser($username);
- $user = $this->users[strtolower($username)];
-
- return new User($user->getUsername(), $user->getPassword(), $user->getRoles(), $user->isEnabled(), $user->isAccountNonExpired(),
- $user->isCredentialsNonExpired(), $user->isAccountNonLocked());
+ return new User($user->getUsername(), $user->getPassword(), $user->getRoles(), $user->isEnabled(), $user->isAccountNonExpired(), $user->isCredentialsNonExpired(), $user->isAccountNonLocked());
}
/**
@@ -89,7 +81,9 @@ class InMemoryUserProvider implements UserProviderInterface
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
}
- return $this->loadUserByUsername($user->getUsername());
+ $storedUser = $this->getUser($user->getUsername());
+
+ return new User($storedUser->getUsername(), $storedUser->getPassword(), $storedUser->getRoles(), $storedUser->isEnabled(), $storedUser->isAccountNonExpired(), $storedUser->isCredentialsNonExpired() && $storedUser->getPassword() === $user->getPassword(), $storedUser->isAccountNonLocked());
}
/**
@@ -99,4 +93,25 @@ class InMemoryUserProvider implements UserProviderInterface
{
return $class === 'Symfony\Component\Security\Core\User\User';
}
+
+ /**
+ * Returns the user by given username.
+ *
+ * @param string $username The username.
+ *
+ * @return User
+ *
+ * @throws UsernameNotFoundException If user whose given username does not exist.
+ */
+ private function getUser($username)
+ {
+ if (!isset($this->users[strtolower($username)])) {
+ $ex = new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username));
+ $ex->setUsername($username);
+
+ throw $ex;
+ }
+
+ return $this->users[strtolower($username)];
+ }
}
diff --git a/Http/RememberMe/AbstractRememberMeServices.php b/Http/RememberMe/AbstractRememberMeServices.php
index aa2a50d..0352eb4 100644
--- a/Http/RememberMe/AbstractRememberMeServices.php
+++ b/Http/RememberMe/AbstractRememberMeServices.php
@@ -294,7 +294,7 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface
$this->logger->debug('Clearing remember-me cookie.', array('name' => $this->options['name']));
}
- $request->attributes->set(self::COOKIE_ATTR_NAME, new Cookie($this->options['name'], null, 1, $this->options['path'], $this->options['domain']));
+ $request->attributes->set(self::COOKIE_ATTR_NAME, new Cookie($this->options['name'], null, 1, $this->options['path'], $this->options['domain'], $this->options['secure'], $this->options['httponly']));
}
/**
diff --git a/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php b/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php
index d3daa35..4ea4f5d 100644
--- a/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php
+++ b/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php
@@ -82,16 +82,35 @@ class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase
$this->assertSame('fookey', $returnedToken->getProviderKey());
}
- public function testLogout()
+ /**
+ * @dataProvider provideOptionsForLogout
+ */
+ public function testLogout(array $options)
{
- $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null));
+ $service = $this->getService(null, $options);
$request = new Request();
$response = new Response();
$token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
$service->logout($request, $response, $token);
- $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
+ $cookie = $request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME);
+
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Cookie', $cookie);
+ $this->assertTrue($cookie->isCleared());
+ $this->assertSame($options['name'], $cookie->getName());
+ $this->assertSame($options['path'], $cookie->getPath());
+ $this->assertSame($options['domain'], $cookie->getDomain());
+ $this->assertSame($options['secure'], $cookie->isSecure());
+ $this->assertSame($options['httponly'], $cookie->isHttpOnly());
+ }
+
+ public function provideOptionsForLogout()
+ {
+ return array(
+ array(array('name' => 'foo', 'path' => '/', 'domain' => null, 'secure' => false, 'httponly' => true)),
+ array(array('name' => 'foo', 'path' => '/bar', 'domain' => 'baz.com', 'secure' => true, 'httponly' => false)),
+ );
}
public function testLoginFail()
@@ -267,6 +286,13 @@ class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase
$userProvider = $this->getProvider();
}
+ if (!isset($options['secure'])) {
+ $options['secure'] = false;
+ }
+ if (!isset($options['httponly'])) {
+ $options['httponly'] = true;
+ }
+
return $this->getMockForAbstractClass('Symfony\Component\Security\Http\RememberMe\AbstractRememberMeServices', array(
array($userProvider), 'foosecret', 'fookey', $options, $logger,
));
diff --git a/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php b/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php
index 889211c..43aaf92 100644
--- a/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php
+++ b/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php
@@ -180,7 +180,7 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test
public function testLogout()
{
- $service = $this->getService(null, array('name' => 'foo', 'path' => '/foo', 'domain' => 'foodomain.foo'));
+ $service = $this->getService(null, array('name' => 'foo', 'path' => '/foo', 'domain' => 'foodomain.foo', 'secure' => true, 'httponly' => false));
$request = new Request();
$request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue')));
$response = new Response();
@@ -201,6 +201,8 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test
$this->assertTrue($cookie->isCleared());
$this->assertEquals('/foo', $cookie->getPath());
$this->assertEquals('foodomain.foo', $cookie->getDomain());
+ $this->assertTrue($cookie->isSecure());
+ $this->assertFalse($cookie->isHttpOnly());
}
public function testLogoutSimplyIgnoresNonSetRequestCookie()
@@ -311,6 +313,13 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test
$userProvider = $this->getProvider();
}
+ if (!isset($options['secure'])) {
+ $options['secure'] = false;
+ }
+ if (!isset($options['httponly'])) {
+ $options['httponly'] = true;
+ }
+
return new PersistentTokenBasedRememberMeServices(array($userProvider), 'foosecret', 'fookey', $options, $logger, new SecureRandom(sys_get_temp_dir().'/_sf2.seed'));
}
diff --git a/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php b/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php
index 2a892c3..dab811b 100644
--- a/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php
+++ b/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php
@@ -153,7 +153,7 @@ class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
public function testLogout()
{
- $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null));
+ $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null, 'secure' => true, 'httponly' => false));
$request = new Request();
$response = new Response();
$token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
@@ -164,6 +164,8 @@ class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($cookie->isCleared());
$this->assertEquals('/', $cookie->getPath());
$this->assertNull($cookie->getDomain());
+ $this->assertTrue($cookie->isSecure());
+ $this->assertFalse($cookie->isHttpOnly());
}
public function testLoginFail()
@@ -264,6 +266,13 @@ class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
$userProvider = $this->getProvider();
}
+ if (!isset($options['secure'])) {
+ $options['secure'] = false;
+ }
+ if (!isset($options['httponly'])) {
+ $options['httponly'] = true;
+ }
+
$service = new TokenBasedRememberMeServices(array($userProvider), 'foosecret', 'fookey', $options, $logger);
return $service;