Merge branch '5.1'

* 5.1:
  Handle fetch mode deprecation of DBAL 2.11.
  Fixed security-* package dependencies
  Fixed handling of CSRF logout error
  [WebProfilerBundle] changed label of memory usage in time panel (Mb into MiB)
  [DotEnv][WebLink][Templating][ErrorHandler] Updated README with minimal example
This commit is contained in:
Nicolas Grekas 2020-05-28 10:21:24 +02:00
commit 71d1d70945
12 changed files with 215 additions and 13 deletions

View File

@ -56,7 +56,7 @@ class DoctrineTokenProvider implements TokenProviderInterface
$paramValues = ['series' => $series];
$paramTypes = ['series' => \PDO::PARAM_STR];
$stmt = $this->conn->executeQuery($sql, $paramValues, $paramTypes);
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
$row = method_exists($stmt, 'fetchAssociative') ? $stmt->fetchAssociative() : $stmt->fetch(\PDO::FETCH_ASSOC);
if ($row) {
return new PersistentToken($row['class'], $row['username'], $series, $row['value'], new \DateTime($row['last_used']));

View File

@ -0,0 +1,88 @@
<?php
namespace Security\RememberMe;
use Doctrine\DBAL\DriverManager;
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\Doctrine\Security\RememberMe\DoctrineTokenProvider;
use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken;
use Symfony\Component\Security\Core\Exception\TokenNotFoundException;
/**
* @requires extension pdo_sqlite
*/
class DoctrineTokenProviderTest extends TestCase
{
public static function setUpBeforeClass()
{
if (\PHP_VERSION_ID >= 80000) {
self::markTestSkipped('Doctrine DBAL 2.x is incompatible with PHP 8.');
}
}
public function testCreateNewToken()
{
$provider = $this->bootstrapProvider();
$token = new PersistentToken('someClass', 'someUser', 'someSeries', 'tokenValue', new \DateTime('2013-01-26T18:23:51'));
$provider->createNewToken($token);
$this->assertEquals($provider->loadTokenBySeries('someSeries'), $token);
}
public function testLoadTokenBySeriesThrowsNotFoundException()
{
$provider = $this->bootstrapProvider();
$this->expectException(TokenNotFoundException::class);
$provider->loadTokenBySeries('someSeries');
}
public function testUpdateToken()
{
$provider = $this->bootstrapProvider();
$token = new PersistentToken('someClass', 'someUser', 'someSeries', 'tokenValue', new \DateTime('2013-01-26T18:23:51'));
$provider->createNewToken($token);
$provider->updateToken('someSeries', 'newValue', $lastUsed = new \DateTime('2014-06-26T22:03:46'));
$token = $provider->loadTokenBySeries('someSeries');
$this->assertEquals('newValue', $token->getTokenValue());
$this->assertEquals($token->getLastUsed(), $lastUsed);
}
public function testDeleteToken()
{
$provider = $this->bootstrapProvider();
$token = new PersistentToken('someClass', 'someUser', 'someSeries', 'tokenValue', new \DateTime('2013-01-26T18:23:51'));
$provider->createNewToken($token);
$provider->deleteTokenBySeries('someSeries');
$this->expectException(TokenNotFoundException::class);
$provider->loadTokenBySeries('someSeries');
}
/**
* @return DoctrineTokenProvider
*/
private function bootstrapProvider()
{
$connection = DriverManager::getConnection([
'driver' => 'pdo_sqlite',
'url' => 'sqlite:///:memory:',
]);
$connection->executeUpdate(<<< 'SQL'
CREATE TABLE rememberme_token (
series char(88) UNIQUE PRIMARY KEY NOT NULL,
value char(88) NOT NULL,
lastUsed datetime NOT NULL,
class varchar(100) NOT NULL,
username varchar(200) NOT NULL
);
SQL
);
return new DoctrineTokenProvider($connection);
}
}

View File

@ -23,9 +23,9 @@
"symfony/event-dispatcher": "^5.1",
"symfony/http-kernel": "^5.0",
"symfony/polyfill-php80": "^1.15",
"symfony/security-core": "^4.4|^5.0",
"symfony/security-core": "^5.1",
"symfony/security-csrf": "^4.4|^5.0",
"symfony/security-guard": "^4.4|^5.0",
"symfony/security-guard": "^5.1",
"symfony/security-http": "^5.1"
},
"require-dev": {

View File

@ -94,7 +94,7 @@ class TimelineEngine {
createLabel(name, duration, memory, period) {
const label = this.renderer.createText(name, period.start * this.scale, this.labelY, 'timeline-label');
const sublabel = this.renderer.createTspan(` ${duration} ms / ${memory} Mb`, 'timeline-sublabel');
const sublabel = this.renderer.createTspan(` ${duration} ms / ${memory} MiB`, 'timeline-sublabel');
label.appendChild(sublabel);

View File

@ -219,7 +219,13 @@ class PdoAdapter extends AbstractAdapter implements PruneableInterface
}
$stmt->execute();
while ($row = $stmt->fetch(\PDO::FETCH_NUM)) {
if (method_exists($stmt, 'iterateNumeric')) {
$stmt = $stmt->iterateNumeric();
} else {
$stmt->setFetchMode(\PDO::FETCH_NUM);
}
foreach ($stmt as $row) {
if (null === $row[1]) {
$expired[] = $row[0];
} else {

View File

@ -24,11 +24,11 @@ trait PdoPruneableTrait
$getPdoConn = $o->getMethod('getConnection');
$getPdoConn->setAccessible(true);
/** @var \Doctrine\DBAL\Statement $select */
/** @var \Doctrine\DBAL\Statement|\PDOStatement $select */
$select = $getPdoConn->invoke($cache)->prepare('SELECT 1 FROM cache_items WHERE item_id LIKE :id');
$select->bindValue(':id', sprintf('%%%s', $name));
$select->execute();
return 0 === \count($select->fetchAll(\PDO::FETCH_COLUMN));
return 1 !== (int) (method_exists($select, 'fetchOne') ? $select->fetchOne() : $select->fetch(\PDO::FETCH_COLUMN));
}
}

View File

@ -4,10 +4,32 @@ Dotenv Component
Symfony Dotenv parses `.env` files to make environment variables stored in them
accessible via `$_SERVER` or `$_ENV`.
Getting Started
---------------
```
$ composer require symfony/dotenv
```
```php
use Symfony\Component\Dotenv\Dotenv;
$dotenv = new Dotenv();
$dotenv->load(__DIR__.'/.env');
// you can also load several files
$dotenv->load(__DIR__.'/.env', __DIR__.'/.env.dev');
// overwrites existing env variables
$dotenv->overload(__DIR__.'/.env');
// loads .env, .env.local, and .env.$APP_ENV.local or .env.$APP_ENV
$dotenv->loadEnv(__DIR__.'/.env');
```
Resources
---------
* [Documentation](https://symfony.com/doc/current/components/dotenv.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)

View File

@ -3,6 +3,35 @@ ErrorHandler Component
The ErrorHandler component provides tools to manage errors and ease debugging PHP code.
Getting Started
---------------
```
$ composer require symfony/error-handler
```
```php
use Symfony\Component\ErrorHandler\Debug;
use Symfony\Component\ErrorHandler\ErrorHandler;
use Symfony\Component\ErrorHandler\DebugClassLoader;
Debug::enable();
// or enable only one feature
//ErrorHandler::register();
//DebugClassLoader::enable();
$data = ErrorHandler::call(static function () use ($filename, $datetimeFormat) {
// if any code executed inside this anonymous function fails, a PHP exception
// will be thrown, even if the code uses the '@' PHP silence operator
$data = json_decode(file_get_contents($filename), true);
$data['read_at'] = date($datetimeFormat);
file_put_contents($filename, json_encode($data));
return $data;
});
```
Resources
---------

View File

@ -111,7 +111,7 @@ class ExceptionListener
}
if ($exception instanceof LogoutException) {
$this->handleLogoutException($exception);
$this->handleLogoutException($event, $exception);
return;
}
@ -183,10 +183,12 @@ class ExceptionListener
}
}
private function handleLogoutException(LogoutException $exception): void
private function handleLogoutException(ExceptionEvent $event, LogoutException $exception): void
{
$event->setException(new AccessDeniedHttpException($exception->getMessage(), $exception));
if (null !== $this->logger) {
$this->logger->info('A LogoutException was thrown.', ['exception' => $exception]);
$this->logger->info('A LogoutException was thrown; wrapping with AccessDeniedHttpException', ['exception' => $exception]);
}
}

View File

@ -21,6 +21,7 @@ use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverIn
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\LogoutException;
use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Symfony\Component\Security\Http\Firewall\ExceptionListener;
@ -157,6 +158,17 @@ class ExceptionListenerTest extends TestCase
$this->assertSame(null === $eventException ? $exception : $eventException, $event->getThrowable()->getPrevious());
}
public function testLogoutException()
{
$event = $this->createEvent(new LogoutException('Invalid CSRF.'));
$listener = $this->createExceptionListener();
$listener->onKernelException($event);
$this->assertEquals('Invalid CSRF.', $event->getException()->getMessage());
$this->assertEquals(403, $event->getException()->getStatusCode());
}
public function getAccessDeniedExceptionProvider()
{
return [

View File

@ -9,10 +9,33 @@ for changes. It also provides a concrete template engine implementation using
PHP with additional tools for escaping and separating templates into blocks and
layouts.
Getting Started
---------------
```
$ composer require symfony/templating
```
```php
use Symfony\Component\Templating\Loader\FilesystemLoader;
use Symfony\Component\Templating\PhpEngine;
use Symfony\Component\Templating\Helper\SlotsHelper;
use Symfony\Component\Templating\TemplateNameParser;
$filesystemLoader = new FilesystemLoader(__DIR__.'/views/%name%');
$templating = new PhpEngine(new TemplateNameParser(), $filesystemLoader);
$templating->set(new SlotsHelper());
echo $templating->render('hello.php', ['firstname' => 'Fabien']);
// hello.php
Hello, <?= $view->escape($firstname) ?>!
```
Resources
---------
* [Documentation](https://symfony.com/doc/current/components/templating.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)

View File

@ -8,10 +8,30 @@ This component implements the [HTML5's Links](https://www.w3.org/TR/html5/links.
and [Resource Hints](https://www.w3.org/TR/resource-hints/) W3C's specifications.
It can also be used with extensions defined in the [HTML5 link type extensions wiki](http://microformats.org/wiki/existing-rel-values#HTML5_link_type_extensions).
Getting Started
---------------
```
$ composer require symfony/web-link
```
```php
use Symfony\Component\WebLink\GenericLinkProvider;
use Symfony\Component\WebLink\HttpHeaderSerializer;
use Symfony\Component\WebLink\Link;
$linkProvider = (new GenericLinkProvider())
->withLink(new Link('preload', '/bootstrap.min.css'));
header('Link: '.(new HttpHeaderSerializer())->serialize($linkProvider->getLinks()));
echo 'Hello';
```
Resources
---------
* [Documentation](https://symfony.com/doc/current/components/web_link.html)
* [Documentation](https://symfony.com/doc/current/web_link.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)