Merge branch '2.8' into 3.4
* 2.8: [Security] guardAuthenticationProvider::authenticate cannot return null according to interface specification [VarDumper] Remove decoration from actual output in tests [PropertyInfo] Minor cleanup and perf improvement [Bridge/Doctrine] fix count() notice on PHP 7.2 [Security] Skip user checks if not implementing UserInterface [HttpFoundation] Add HTTP_EARLY_HINTS const [DoctrineBridge] Improve exception message at `IdReader::getIdValue()` fixed CS Use new PHP7.2 functions in hasColorSupport [VarDumper] Fix dumping of SplObjectStorage Fixed being logged out on failed attempt in guard
This commit is contained in:
commit
2abb25e881
@ -95,10 +95,7 @@ class IdReader
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->om->contains($object)) {
|
if (!$this->om->contains($object)) {
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(sprintf('Entity of type "%s" passed to the choice field must be managed. Maybe you forget to persist it in the entity manager?', get_class($object)));
|
||||||
'Entities passed to the choice field must be managed. Maybe '.
|
|
||||||
'persist them in the entity manager?'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->om->initializeObject($object);
|
$this->om->initializeObject($object);
|
||||||
|
@ -630,6 +630,34 @@ class UniqueEntityValidatorTest extends ConstraintValidatorTestCase
|
|||||||
$this->validator->validate($entity, $constraint);
|
$this->validator->validate($entity, $constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testValidateUniquenessOnNullResult()
|
||||||
|
{
|
||||||
|
$repository = $this->createRepositoryMock();
|
||||||
|
$repository
|
||||||
|
->method('find')
|
||||||
|
->will($this->returnValue(null))
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->em = $this->createEntityManagerMock($repository);
|
||||||
|
$this->registry = $this->createRegistryMock($this->em);
|
||||||
|
$this->validator = $this->createValidator();
|
||||||
|
$this->validator->initialize($this->context);
|
||||||
|
|
||||||
|
$constraint = new UniqueEntity(array(
|
||||||
|
'message' => 'myMessage',
|
||||||
|
'fields' => array('name'),
|
||||||
|
'em' => self::EM_NAME,
|
||||||
|
));
|
||||||
|
|
||||||
|
$entity = new SingleIntIdEntity(1, null);
|
||||||
|
|
||||||
|
$this->em->persist($entity);
|
||||||
|
$this->em->flush();
|
||||||
|
|
||||||
|
$this->validator->validate($entity, $constraint);
|
||||||
|
$this->assertNoViolation();
|
||||||
|
}
|
||||||
|
|
||||||
public function testValidateInheritanceUniqueness()
|
public function testValidateInheritanceUniqueness()
|
||||||
{
|
{
|
||||||
$constraint = new UniqueEntity(array(
|
$constraint = new UniqueEntity(array(
|
||||||
|
@ -148,15 +148,23 @@ class UniqueEntityValidator extends ConstraintValidator
|
|||||||
*/
|
*/
|
||||||
if ($result instanceof \Iterator) {
|
if ($result instanceof \Iterator) {
|
||||||
$result->rewind();
|
$result->rewind();
|
||||||
} elseif (is_array($result)) {
|
if ($result instanceof \Countable && 1 < \count($result)) {
|
||||||
|
$result = array($result->current(), $result->current());
|
||||||
|
} else {
|
||||||
|
$result = $result->current();
|
||||||
|
$result = null === $result ? array() : array($result);
|
||||||
|
}
|
||||||
|
} elseif (\is_array($result)) {
|
||||||
reset($result);
|
reset($result);
|
||||||
|
} else {
|
||||||
|
$result = null === $result ? array() : array($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If no entity matched the query criteria or a single entity matched,
|
/* If no entity matched the query criteria or a single entity matched,
|
||||||
* which is the same as the entity being validated, the criteria is
|
* which is the same as the entity being validated, the criteria is
|
||||||
* unique.
|
* unique.
|
||||||
*/
|
*/
|
||||||
if (0 === count($result) || (1 === count($result) && $entity === ($result instanceof \Iterator ? $result->current() : current($result)))) {
|
if (!$result || (1 === \count($result) && current($result) === $entity)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,17 +297,38 @@ class DeprecationErrorHandler
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if STDOUT is defined and supports colorization.
|
||||||
|
*
|
||||||
|
* Reference: Composer\XdebugHandler\Process::supportsColor
|
||||||
|
* https://github.com/composer/xdebug-handler
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
private static function hasColorSupport()
|
private static function hasColorSupport()
|
||||||
{
|
{
|
||||||
if ('\\' === DIRECTORY_SEPARATOR) {
|
if (!defined('STDOUT')) {
|
||||||
return
|
return false;
|
||||||
defined('STDOUT') && function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support(STDOUT)
|
}
|
||||||
|| '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD
|
|
||||||
|
if (DIRECTORY_SEPARATOR === '\\') {
|
||||||
|
return (function_exists('sapi_windows_vt100_support')
|
||||||
|
&& sapi_windows_vt100_support(STDOUT))
|
||||||
|| false !== getenv('ANSICON')
|
|| false !== getenv('ANSICON')
|
||||||
|| 'ON' === getenv('ConEmuANSI')
|
|| 'ON' === getenv('ConEmuANSI')
|
||||||
|| 'xterm' === getenv('TERM');
|
|| 'xterm' === getenv('TERM');
|
||||||
}
|
}
|
||||||
|
|
||||||
return defined('STDOUT') && function_exists('posix_isatty') && @posix_isatty(STDOUT);
|
if (function_exists('stream_isatty')) {
|
||||||
|
return stream_isatty(STDOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (function_exists('posix_isatty')) {
|
||||||
|
return posix_isatty(STDOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stat = fstat(STDOUT);
|
||||||
|
// Check if formatted mode is S_IFCHR
|
||||||
|
return $stat ? 0020000 === ($stat['mode'] & 0170000) : false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,31 +83,34 @@ class StreamOutput extends Output
|
|||||||
*
|
*
|
||||||
* Colorization is disabled if not supported by the stream:
|
* Colorization is disabled if not supported by the stream:
|
||||||
*
|
*
|
||||||
* - the stream is redirected (eg php file.php >log)
|
* This is tricky on Windows, because Cygwin, Msys2 etc emulate pseudo
|
||||||
* - Windows without VT100 support, Ansicon, ConEmu, Mintty
|
* terminals via named pipes, so we can only check the environment.
|
||||||
* - non tty consoles
|
*
|
||||||
|
* Reference: Composer\XdebugHandler\Process::supportsColor
|
||||||
|
* https://github.com/composer/xdebug-handler
|
||||||
*
|
*
|
||||||
* @return bool true if the stream supports colorization, false otherwise
|
* @return bool true if the stream supports colorization, false otherwise
|
||||||
*/
|
*/
|
||||||
protected function hasColorSupport()
|
protected function hasColorSupport()
|
||||||
{
|
{
|
||||||
if (function_exists('stream_isatty') && !@stream_isatty($this->stream)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (DIRECTORY_SEPARATOR === '\\') {
|
if (DIRECTORY_SEPARATOR === '\\') {
|
||||||
if (function_exists('sapi_windows_vt100_support')) {
|
return (function_exists('sapi_windows_vt100_support')
|
||||||
$vt100Enabled = @sapi_windows_vt100_support($this->stream);
|
&& @sapi_windows_vt100_support($this->stream))
|
||||||
} else {
|
|
||||||
$vt100Enabled = '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD;
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
$vt100Enabled
|
|
||||||
|| false !== getenv('ANSICON')
|
|| false !== getenv('ANSICON')
|
||||||
|| 'ON' === getenv('ConEmuANSI')
|
|| 'ON' === getenv('ConEmuANSI')
|
||||||
|| 'xterm' === getenv('TERM');
|
|| 'xterm' === getenv('TERM');
|
||||||
}
|
}
|
||||||
|
|
||||||
return function_exists('posix_isatty') && @posix_isatty($this->stream);
|
if (function_exists('stream_isatty')) {
|
||||||
|
return @stream_isatty($this->stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (function_exists('posix_isatty')) {
|
||||||
|
return @posix_isatty($this->stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stat = @fstat($this->stream);
|
||||||
|
// Check if formatted mode is S_IFCHR
|
||||||
|
return $stat ? 0020000 === ($stat['mode'] & 0170000) : false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ class Response
|
|||||||
const HTTP_CONTINUE = 100;
|
const HTTP_CONTINUE = 100;
|
||||||
const HTTP_SWITCHING_PROTOCOLS = 101;
|
const HTTP_SWITCHING_PROTOCOLS = 101;
|
||||||
const HTTP_PROCESSING = 102; // RFC2518
|
const HTTP_PROCESSING = 102; // RFC2518
|
||||||
|
const HTTP_EARLY_HINTS = 103; // RFC8297
|
||||||
const HTTP_OK = 200;
|
const HTTP_OK = 200;
|
||||||
const HTTP_CREATED = 201;
|
const HTTP_CREATED = 201;
|
||||||
const HTTP_ACCEPTED = 202;
|
const HTTP_ACCEPTED = 202;
|
||||||
|
@ -67,7 +67,7 @@ class DumpDataCollectorTest extends TestCase
|
|||||||
|
|
||||||
ob_start();
|
ob_start();
|
||||||
$collector->collect(new Request(), new Response());
|
$collector->collect(new Request(), new Response());
|
||||||
$output = ob_get_clean();
|
$output = preg_replace("/\033\[[^m]*m/", '', ob_get_clean());
|
||||||
|
|
||||||
$this->assertSame("DumpDataCollectorTest.php on line {$line}:\n123\n", $output);
|
$this->assertSame("DumpDataCollectorTest.php on line {$line}:\n123\n", $output);
|
||||||
$this->assertSame(1, $collector->getDumpsCount());
|
$this->assertSame(1, $collector->getDumpsCount());
|
||||||
@ -111,7 +111,8 @@ EOTXT;
|
|||||||
|
|
||||||
ob_start();
|
ob_start();
|
||||||
$collector->__destruct();
|
$collector->__destruct();
|
||||||
$this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", ob_get_clean());
|
$output = preg_replace("/\033\[[^m]*m/", '', ob_get_clean());
|
||||||
|
$this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFlushNothingWhenDataDumperIsProvided()
|
public function testFlushNothingWhenDataDumperIsProvided()
|
||||||
@ -123,10 +124,11 @@ EOTXT;
|
|||||||
ob_start();
|
ob_start();
|
||||||
$collector->dump($data);
|
$collector->dump($data);
|
||||||
$line = __LINE__ - 1;
|
$line = __LINE__ - 1;
|
||||||
|
$output = preg_replace("/\033\[[^m]*m/", '', ob_get_clean());
|
||||||
if (\PHP_VERSION_ID >= 50400) {
|
if (\PHP_VERSION_ID >= 50400) {
|
||||||
$this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", ob_get_clean());
|
$this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", $output);
|
||||||
} else {
|
} else {
|
||||||
$this->assertSame("\"DumpDataCollectorTest.php on line {$line}:\"\n456\n", ob_get_clean());
|
$this->assertSame("\"DumpDataCollectorTest.php on line {$line}:\"\n456\n", $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
ob_start();
|
ob_start();
|
||||||
|
@ -144,7 +144,7 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!in_array($prefix, $this->arrayMutatorPrefixes)) {
|
if (!\in_array($prefix, $this->arrayMutatorPrefixes)) {
|
||||||
return $types;
|
return $types;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +194,7 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array($prefix, $this->arrayMutatorPrefixes)) {
|
if (\in_array($prefix, $this->arrayMutatorPrefixes)) {
|
||||||
$type = new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $type);
|
$type = new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +220,7 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
|
|||||||
return array($this->extractFromReflectionType($reflectionType));
|
return array($this->extractFromReflectionType($reflectionType));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array($prefix, array('is', 'can'))) {
|
if (\in_array($prefix, array('is', 'can'))) {
|
||||||
return array(new Type(Type::BUILTIN_TYPE_BOOL));
|
return array(new Type(Type::BUILTIN_TYPE_BOOL));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
namespace Symfony\Component\PropertyInfo;
|
namespace Symfony\Component\PropertyInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description extractor Interface.
|
* Guesses the property's human readable description.
|
||||||
*
|
*
|
||||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||||
*/
|
*/
|
||||||
|
@ -99,7 +99,7 @@ class PropertyInfoExtractor implements PropertyInfoExtractorInterface
|
|||||||
private function extract($extractors, $method, array $arguments)
|
private function extract($extractors, $method, array $arguments)
|
||||||
{
|
{
|
||||||
foreach ($extractors as $extractor) {
|
foreach ($extractors as $extractor) {
|
||||||
$value = call_user_func_array(array($extractor, $method), $arguments);
|
$value = \call_user_func_array(array($extractor, $method), $arguments);
|
||||||
if (null !== $value) {
|
if (null !== $value) {
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ class SimpleAuthenticationProvider implements AuthenticationProviderInterface
|
|||||||
$user = $this->userProvider->loadUserByUsername($user);
|
$user = $this->userProvider->loadUserByUsername($user);
|
||||||
|
|
||||||
if (!$user instanceof UserInterface) {
|
if (!$user instanceof UserInterface) {
|
||||||
throw new AuthenticationServiceException('The user provider must return a UserInterface object.');
|
return $authToken;
|
||||||
}
|
}
|
||||||
} catch (UsernameNotFoundException $e) {
|
} catch (UsernameNotFoundException $e) {
|
||||||
$e->setUsername($user);
|
$e->setUsername($user);
|
||||||
|
@ -12,10 +12,11 @@
|
|||||||
namespace Symfony\Component\Security\Core\Tests\Authentication\Provider;
|
namespace Symfony\Component\Security\Core\Tests\Authentication\Provider;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\Security\Core\Exception\DisabledException;
|
|
||||||
use Symfony\Component\Security\Core\Authentication\Provider\SimpleAuthenticationProvider;
|
use Symfony\Component\Security\Core\Authentication\Provider\SimpleAuthenticationProvider;
|
||||||
|
use Symfony\Component\Security\Core\Exception\DisabledException;
|
||||||
use Symfony\Component\Security\Core\Exception\LockedException;
|
use Symfony\Component\Security\Core\Exception\LockedException;
|
||||||
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
||||||
|
use Symfony\Component\Security\Core\User\UserChecker;
|
||||||
|
|
||||||
class SimpleAuthenticationProviderTest extends TestCase
|
class SimpleAuthenticationProviderTest extends TestCase
|
||||||
{
|
{
|
||||||
@ -121,6 +122,20 @@ class SimpleAuthenticationProviderTest extends TestCase
|
|||||||
$this->getProvider($authenticator, $userProvider)->authenticate($token);
|
$this->getProvider($authenticator, $userProvider)->authenticate($token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testAuthenticateSkipsUserChecksForNonUserInterfaceObjects()
|
||||||
|
{
|
||||||
|
$token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock();
|
||||||
|
$token->expects($this->any())
|
||||||
|
->method('getUser')
|
||||||
|
->will($this->returnValue('string-user'));
|
||||||
|
$authenticator = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\SimpleAuthenticatorInterface')->getMock();
|
||||||
|
$authenticator->expects($this->once())
|
||||||
|
->method('authenticateToken')
|
||||||
|
->will($this->returnValue($token));
|
||||||
|
|
||||||
|
$this->assertSame($token, $this->getProvider($authenticator, null, new UserChecker())->authenticate($token));
|
||||||
|
}
|
||||||
|
|
||||||
protected function getProvider($simpleAuthenticator = null, $userProvider = null, $userChecker = null, $key = 'test')
|
protected function getProvider($simpleAuthenticator = null, $userProvider = null, $userChecker = null, $key = 'test')
|
||||||
{
|
{
|
||||||
if (null === $userChecker) {
|
if (null === $userChecker) {
|
||||||
|
@ -18,7 +18,6 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInt
|
|||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
|
|
||||||
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
|
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
|
||||||
use Symfony\Component\Security\Http\SecurityEvents;
|
use Symfony\Component\Security\Http\SecurityEvents;
|
||||||
|
|
||||||
@ -118,11 +117,6 @@ class GuardAuthenticatorHandler
|
|||||||
*/
|
*/
|
||||||
public function handleAuthenticationFailure(AuthenticationException $authenticationException, Request $request, GuardAuthenticatorInterface $guardAuthenticator, $providerKey)
|
public function handleAuthenticationFailure(AuthenticationException $authenticationException, Request $request, GuardAuthenticatorInterface $guardAuthenticator, $providerKey)
|
||||||
{
|
{
|
||||||
$token = $this->tokenStorage->getToken();
|
|
||||||
if ($token instanceof PostAuthenticationGuardToken && $providerKey === $token->getProviderKey()) {
|
|
||||||
$this->tokenStorage->setToken(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = $guardAuthenticator->onAuthenticationFailure($request, $authenticationException);
|
$response = $guardAuthenticator->onAuthenticationFailure($request, $authenticationException);
|
||||||
if ($response instanceof Response || null === $response) {
|
if ($response instanceof Response || null === $response) {
|
||||||
// returning null is ok, it means they want the request to continue
|
// returning null is ok, it means they want the request to continue
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace Symfony\Component\Security\Guard\Provider;
|
namespace Symfony\Component\Security\Guard\Provider;
|
||||||
|
|
||||||
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
|
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
|
||||||
|
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||||
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
||||||
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
||||||
use Symfony\Component\Security\Guard\AuthenticatorInterface;
|
use Symfony\Component\Security\Guard\AuthenticatorInterface;
|
||||||
@ -62,7 +63,7 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface
|
|||||||
*/
|
*/
|
||||||
public function authenticate(TokenInterface $token)
|
public function authenticate(TokenInterface $token)
|
||||||
{
|
{
|
||||||
if (!$this->supports($token)) {
|
if (!$token instanceof GuardTokenInterface) {
|
||||||
throw new \InvalidArgumentException('GuardAuthenticationProvider only supports GuardTokenInterface.');
|
throw new \InvalidArgumentException('GuardAuthenticationProvider only supports GuardTokenInterface.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,19 +87,13 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface
|
|||||||
throw new AuthenticationExpiredException();
|
throw new AuthenticationExpiredException();
|
||||||
}
|
}
|
||||||
|
|
||||||
// find the *one* GuardAuthenticator that this token originated from
|
$guardAuthenticator = $this->findOriginatingAuthenticator($token);
|
||||||
foreach ($this->guardAuthenticators as $key => $guardAuthenticator) {
|
|
||||||
// get a key that's unique to *this* guard authenticator
|
|
||||||
// this MUST be the same as GuardAuthenticationListener
|
|
||||||
$uniqueGuardKey = $this->providerKey.'_'.$key;
|
|
||||||
|
|
||||||
if ($uniqueGuardKey == $token->getGuardProviderKey()) {
|
if (null === $guardAuthenticator) {
|
||||||
return $this->authenticateViaGuard($guardAuthenticator, $token);
|
throw new AuthenticationException(sprintf('Token with provider key "%s" did not originate from any of the guard authenticators of provider "%s".', $token->getGuardProviderKey(), $this->providerKey));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// no matching authenticator found - but there will be multiple GuardAuthenticationProvider
|
return $this->authenticateViaGuard($guardAuthenticator, $token);
|
||||||
// instances that will be checked if you have multiple firewalls.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function authenticateViaGuard($guardAuthenticator, PreAuthenticationGuardToken $token)
|
private function authenticateViaGuard($guardAuthenticator, PreAuthenticationGuardToken $token)
|
||||||
@ -107,18 +102,11 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface
|
|||||||
$user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider);
|
$user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider);
|
||||||
|
|
||||||
if (null === $user) {
|
if (null === $user) {
|
||||||
throw new UsernameNotFoundException(sprintf(
|
throw new UsernameNotFoundException(sprintf('Null returned from %s::getUser()', get_class($guardAuthenticator)));
|
||||||
'Null returned from %s::getUser()',
|
|
||||||
get_class($guardAuthenticator)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$user instanceof UserInterface) {
|
if (!$user instanceof UserInterface) {
|
||||||
throw new \UnexpectedValueException(sprintf(
|
throw new \UnexpectedValueException(sprintf('The %s::getUser() method must return a UserInterface. You returned %s.', get_class($guardAuthenticator), is_object($user) ? get_class($user) : gettype($user)));
|
||||||
'The %s::getUser() method must return a UserInterface. You returned %s.',
|
|
||||||
get_class($guardAuthenticator),
|
|
||||||
is_object($user) ? get_class($user) : gettype($user)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->userChecker->checkPreAuth($user);
|
$this->userChecker->checkPreAuth($user);
|
||||||
@ -130,18 +118,37 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface
|
|||||||
// turn the UserInterface into a TokenInterface
|
// turn the UserInterface into a TokenInterface
|
||||||
$authenticatedToken = $guardAuthenticator->createAuthenticatedToken($user, $this->providerKey);
|
$authenticatedToken = $guardAuthenticator->createAuthenticatedToken($user, $this->providerKey);
|
||||||
if (!$authenticatedToken instanceof TokenInterface) {
|
if (!$authenticatedToken instanceof TokenInterface) {
|
||||||
throw new \UnexpectedValueException(sprintf(
|
throw new \UnexpectedValueException(sprintf('The %s::createAuthenticatedToken() method must return a TokenInterface. You returned %s.', get_class($guardAuthenticator), is_object($authenticatedToken) ? get_class($authenticatedToken) : gettype($authenticatedToken)));
|
||||||
'The %s::createAuthenticatedToken() method must return a TokenInterface. You returned %s.',
|
|
||||||
get_class($guardAuthenticator),
|
|
||||||
is_object($authenticatedToken) ? get_class($authenticatedToken) : gettype($authenticatedToken)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $authenticatedToken;
|
return $authenticatedToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function findOriginatingAuthenticator(PreAuthenticationGuardToken $token)
|
||||||
|
{
|
||||||
|
// find the *one* GuardAuthenticator that this token originated from
|
||||||
|
foreach ($this->guardAuthenticators as $key => $guardAuthenticator) {
|
||||||
|
// get a key that's unique to *this* guard authenticator
|
||||||
|
// this MUST be the same as GuardAuthenticationListener
|
||||||
|
$uniqueGuardKey = $this->providerKey.'_'.$key;
|
||||||
|
|
||||||
|
if ($uniqueGuardKey === $token->getGuardProviderKey()) {
|
||||||
|
return $guardAuthenticator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no matching authenticator found - but there will be multiple GuardAuthenticationProvider
|
||||||
|
// instances that will be checked if you have multiple firewalls.
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public function supports(TokenInterface $token)
|
public function supports(TokenInterface $token)
|
||||||
{
|
{
|
||||||
|
if ($token instanceof PreAuthenticationGuardToken) {
|
||||||
|
return null !== $this->findOriginatingAuthenticator($token);
|
||||||
|
}
|
||||||
|
|
||||||
return $token instanceof GuardTokenInterface;
|
return $token instanceof GuardTokenInterface;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ class GuardAuthenticatorHandlerTest extends TestCase
|
|||||||
/**
|
/**
|
||||||
* @dataProvider getTokenClearingTests
|
* @dataProvider getTokenClearingTests
|
||||||
*/
|
*/
|
||||||
public function testHandleAuthenticationClearsToken($tokenClass, $tokenProviderKey, $actualProviderKey, $shouldTokenBeCleared)
|
public function testHandleAuthenticationClearsToken($tokenClass, $tokenProviderKey, $actualProviderKey)
|
||||||
{
|
{
|
||||||
$token = $this->getMockBuilder($tokenClass)
|
$token = $this->getMockBuilder($tokenClass)
|
||||||
->disableOriginalConstructor()
|
->disableOriginalConstructor()
|
||||||
@ -91,12 +91,7 @@ class GuardAuthenticatorHandlerTest extends TestCase
|
|||||||
->method('getProviderKey')
|
->method('getProviderKey')
|
||||||
->will($this->returnValue($tokenProviderKey));
|
->will($this->returnValue($tokenProviderKey));
|
||||||
|
|
||||||
// make the $token be the current token
|
$this->tokenStorage->expects($this->never())
|
||||||
$this->tokenStorage->expects($this->once())
|
|
||||||
->method('getToken')
|
|
||||||
->will($this->returnValue($token));
|
|
||||||
|
|
||||||
$this->tokenStorage->expects($shouldTokenBeCleared ? $this->once() : $this->never())
|
|
||||||
->method('setToken')
|
->method('setToken')
|
||||||
->with(null);
|
->with(null);
|
||||||
$authException = new AuthenticationException('Bad password!');
|
$authException = new AuthenticationException('Bad password!');
|
||||||
@ -116,9 +111,9 @@ class GuardAuthenticatorHandlerTest extends TestCase
|
|||||||
{
|
{
|
||||||
$tests = array();
|
$tests = array();
|
||||||
// correct token class and matching firewall => clear the token
|
// correct token class and matching firewall => clear the token
|
||||||
$tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'the_firewall_key', true);
|
$tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'the_firewall_key');
|
||||||
$tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'different_key', false);
|
$tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'different_key');
|
||||||
$tests[] = array('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', 'the_firewall_key', 'the_firewall_key', false);
|
$tests[] = array('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', 'the_firewall_key', 'the_firewall_key');
|
||||||
|
|
||||||
return $tests;
|
return $tests;
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ use Symfony\Component\Security\Core\User\UserInterface;
|
|||||||
use Symfony\Component\Security\Guard\AuthenticatorInterface;
|
use Symfony\Component\Security\Guard\AuthenticatorInterface;
|
||||||
use Symfony\Component\Security\Guard\Provider\GuardAuthenticationProvider;
|
use Symfony\Component\Security\Guard\Provider\GuardAuthenticationProvider;
|
||||||
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
|
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
|
||||||
|
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Ryan Weaver <weaverryan@gmail.com>
|
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||||
@ -198,6 +199,40 @@ class GuardAuthenticationProviderTest extends TestCase
|
|||||||
$actualToken = $provider->authenticate($token);
|
$actualToken = $provider->authenticate($token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSupportsChecksGuardAuthenticatorsTokenOrigin()
|
||||||
|
{
|
||||||
|
$authenticatorA = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock();
|
||||||
|
$authenticatorB = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock();
|
||||||
|
$authenticators = array($authenticatorA, $authenticatorB);
|
||||||
|
|
||||||
|
$mockedUser = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock();
|
||||||
|
$provider = new GuardAuthenticationProvider($authenticators, $this->userProvider, 'first_firewall', $this->userChecker);
|
||||||
|
|
||||||
|
$token = new PreAuthenticationGuardToken($mockedUser, 'first_firewall_1');
|
||||||
|
$supports = $provider->supports($token);
|
||||||
|
$this->assertTrue($supports);
|
||||||
|
|
||||||
|
$token = new PreAuthenticationGuardToken($mockedUser, 'second_firewall_0');
|
||||||
|
$supports = $provider->supports($token);
|
||||||
|
$this->assertFalse($supports);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationException
|
||||||
|
* @expectedExceptionMessageRegExp /second_firewall_0/
|
||||||
|
*/
|
||||||
|
public function testAuthenticateFailsOnNonOriginatingToken()
|
||||||
|
{
|
||||||
|
$authenticatorA = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock();
|
||||||
|
$authenticators = array($authenticatorA);
|
||||||
|
|
||||||
|
$mockedUser = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock();
|
||||||
|
$provider = new GuardAuthenticationProvider($authenticators, $this->userProvider, 'first_firewall', $this->userChecker);
|
||||||
|
|
||||||
|
$token = new PreAuthenticationGuardToken($mockedUser, 'second_firewall_0');
|
||||||
|
$provider->authenticate($token);
|
||||||
|
}
|
||||||
|
|
||||||
protected function setUp()
|
protected function setUp()
|
||||||
{
|
{
|
||||||
$this->userProvider = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserProviderInterface')->getMock();
|
$this->userProvider = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserProviderInterface')->getMock();
|
||||||
|
@ -184,10 +184,11 @@ class SplCaster
|
|||||||
$storage = array();
|
$storage = array();
|
||||||
unset($a[Caster::PREFIX_DYNAMIC."\0gcdata"]); // Don't hit https://bugs.php.net/65967
|
unset($a[Caster::PREFIX_DYNAMIC."\0gcdata"]); // Don't hit https://bugs.php.net/65967
|
||||||
|
|
||||||
foreach (clone $c as $obj) {
|
$clone = clone $c;
|
||||||
|
foreach ($clone as $obj) {
|
||||||
$storage[spl_object_hash($obj)] = array(
|
$storage[spl_object_hash($obj)] = array(
|
||||||
'object' => $obj,
|
'object' => $obj,
|
||||||
'info' => $c->getInfo(),
|
'info' => $clone->getInfo(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ class CliDumper extends AbstractDumper
|
|||||||
{
|
{
|
||||||
parent::__construct($output, $charset, $flags);
|
parent::__construct($output, $charset, $flags);
|
||||||
|
|
||||||
if ('\\' === DIRECTORY_SEPARATOR && 'ON' !== @getenv('ConEmuANSI') && 'xterm' !== @getenv('TERM')) {
|
if ('\\' === DIRECTORY_SEPARATOR && !$this->isWindowsTrueColor()) {
|
||||||
// Use only the base 16 xterm colors when using ANSICON or standard Windows 10 CLI
|
// Use only the base 16 xterm colors when using ANSICON or standard Windows 10 CLI
|
||||||
$this->setStyles(array(
|
$this->setStyles(array(
|
||||||
'default' => '31',
|
'default' => '31',
|
||||||
@ -467,7 +467,7 @@ class CliDumper extends AbstractDumper
|
|||||||
protected function supportsColors()
|
protected function supportsColors()
|
||||||
{
|
{
|
||||||
if ($this->outputStream !== static::$defaultOutput) {
|
if ($this->outputStream !== static::$defaultOutput) {
|
||||||
return @(is_resource($this->outputStream) && function_exists('posix_isatty') && posix_isatty($this->outputStream));
|
return $this->hasColorSupport($this->outputStream);
|
||||||
}
|
}
|
||||||
if (null !== static::$defaultColors) {
|
if (null !== static::$defaultColors) {
|
||||||
return static::$defaultColors;
|
return static::$defaultColors;
|
||||||
@ -495,23 +495,10 @@ class CliDumper extends AbstractDumper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('\\' === DIRECTORY_SEPARATOR) {
|
$h = stream_get_meta_data($this->outputStream) + array('wrapper_type' => null);
|
||||||
static::$defaultColors = @(
|
$h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'wb') : $this->outputStream;
|
||||||
function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support($this->outputStream)
|
|
||||||
|| '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD
|
|
||||||
|| false !== getenv('ANSICON')
|
|
||||||
|| 'ON' === getenv('ConEmuANSI')
|
|
||||||
|| 'xterm' === getenv('TERM')
|
|
||||||
);
|
|
||||||
} elseif (function_exists('posix_isatty')) {
|
|
||||||
$h = stream_get_meta_data($this->outputStream) + array('wrapper_type' => null);
|
|
||||||
$h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'wb') : $this->outputStream;
|
|
||||||
static::$defaultColors = @posix_isatty($h);
|
|
||||||
} else {
|
|
||||||
static::$defaultColors = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return static::$defaultColors;
|
return static::$defaultColors = $this->hasColorSupport($h);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -537,4 +524,69 @@ class CliDumper extends AbstractDumper
|
|||||||
|
|
||||||
$this->dumpLine($cursor->depth, true);
|
$this->dumpLine($cursor->depth, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the stream supports colorization.
|
||||||
|
*
|
||||||
|
* Reference: Composer\XdebugHandler\Process::supportsColor
|
||||||
|
* https://github.com/composer/xdebug-handler
|
||||||
|
*
|
||||||
|
* @param mixed $stream A CLI output stream
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function hasColorSupport($stream)
|
||||||
|
{
|
||||||
|
if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DIRECTORY_SEPARATOR === '\\') {
|
||||||
|
return (function_exists('sapi_windows_vt100_support')
|
||||||
|
&& @sapi_windows_vt100_support($stream))
|
||||||
|
|| false !== getenv('ANSICON')
|
||||||
|
|| 'ON' === getenv('ConEmuANSI')
|
||||||
|
|| 'xterm' === getenv('TERM');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (function_exists('stream_isatty')) {
|
||||||
|
return @stream_isatty($stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (function_exists('posix_isatty')) {
|
||||||
|
return @posix_isatty($stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stat = @fstat($stream);
|
||||||
|
// Check if formatted mode is S_IFCHR
|
||||||
|
return $stat ? 0020000 === ($stat['mode'] & 0170000) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the Windows terminal supports true color.
|
||||||
|
*
|
||||||
|
* Note that this does not check an output stream, but relies on environment
|
||||||
|
* variables from known implementations, or a PHP and Windows version that
|
||||||
|
* supports true color.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function isWindowsTrueColor()
|
||||||
|
{
|
||||||
|
$result = 183 <= getenv('ANSICON_VER')
|
||||||
|
|| 'ON' === getenv('ConEmuANSI')
|
||||||
|
|| 'xterm' === getenv('TERM');
|
||||||
|
|
||||||
|
if (!$result && PHP_VERSION_ID >= 70200) {
|
||||||
|
$version = sprintf(
|
||||||
|
'%s.%s.%s',
|
||||||
|
PHP_WINDOWS_VERSION_MAJOR,
|
||||||
|
PHP_WINDOWS_VERSION_MINOR,
|
||||||
|
PHP_WINDOWS_VERSION_BUILD
|
||||||
|
);
|
||||||
|
$result = $version >= '10.0.15063';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,4 +144,23 @@ EOTXT;
|
|||||||
array(\SplDoublyLinkedList::IT_MODE_LIFO | \SplDoublyLinkedList::IT_MODE_DELETE, 'IT_MODE_LIFO | IT_MODE_DELETE'),
|
array(\SplDoublyLinkedList::IT_MODE_LIFO | \SplDoublyLinkedList::IT_MODE_DELETE, 'IT_MODE_LIFO | IT_MODE_DELETE'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCastObjectStorageIsntModified()
|
||||||
|
{
|
||||||
|
$var = new \SplObjectStorage();
|
||||||
|
$var->attach(new \stdClass());
|
||||||
|
$var->rewind();
|
||||||
|
$current = $var->current();
|
||||||
|
|
||||||
|
$this->assertDumpMatchesFormat('%A', $var);
|
||||||
|
$this->assertSame($current, $var->current());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCastObjectStorageDumpsInfo()
|
||||||
|
{
|
||||||
|
$var = new \SplObjectStorage();
|
||||||
|
$var->attach(new \stdClass(), new \DateTime());
|
||||||
|
|
||||||
|
$this->assertDumpMatchesFormat('%ADateTime%A', $var);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user