Skip to content

Commit

Permalink
[HttpFoundation] Add temporary URI signed
Browse files Browse the repository at this point in the history
  • Loading branch information
BaptisteContreras committed Mar 17, 2024
1 parent c71348a commit 59ca1eb
Show file tree
Hide file tree
Showing 9 changed files with 350 additions and 11 deletions.
3 changes: 3 additions & 0 deletions src/Symfony/Component/HttpFoundation/CHANGELOG.md
Expand Up @@ -4,6 +4,9 @@ CHANGELOG
7.1
---

* Add optional `$expirationParameter` argument to `UriSigner::__construct()`
* Add optional `$expiration` argument to `UriSigner::sign()`
* Rename `$parameter` argument of `UriSigner::__construct()` to `$hashParameter`
* Add `UploadedFile::getClientOriginalPath()`

7.0
Expand Down
@@ -0,0 +1,25 @@
<?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.
*/

/*
* 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\HttpFoundation\Exception;

interface ExceptionInterface extends \Throwable
{
}
@@ -0,0 +1,25 @@
<?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.
*/

/*
* 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\HttpFoundation\Exception;

interface ExceptionInterface extends \Throwable
{
}
@@ -0,0 +1,35 @@
--- src/Symfony/Component/HttpFoundation/Exception/ExceptionInterface.php 2024-03-17 17:28:21.134766504 +0000
+++ src/Symfony/Component/HttpFoundation/Exception/ExceptionInterface.php 2024-03-17 17:28:22.056798627 +0000
@@ -1,16 +1,16 @@
-<?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\HttpFoundation\Exception;
-
-interface ExceptionInterface extends \Throwable
-{
-}
+<?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\HttpFoundation\Exception;
+
+interface ExceptionInterface extends \Throwable
+{
+}
28 changes: 28 additions & 0 deletions src/Symfony/Component/HttpFoundation/Exception/LogicException.php
@@ -0,0 +1,28 @@
<?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.
*/

/*
* 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\HttpFoundation\Exception;

/**
* Base LogicException for Http Foundation component.
*/
class LogicException extends \LogicException implements ExceptionInterface
{
}
@@ -0,0 +1,28 @@
<?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.
*/

/*
* 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\HttpFoundation\Exception;

/**
* Base LogicException for Http Foundation component.
*/
class LogicException extends \LogicException implements ExceptionInterface
{
}
@@ -0,0 +1,41 @@
--- src/Symfony/Component/HttpFoundation/Exception/LogicException.php 2024-03-17 17:28:21.346773890 +0000
+++ src/Symfony/Component/HttpFoundation/Exception/LogicException.php 2024-03-17 17:28:22.090799811 +0000
@@ -1,19 +1,19 @@
-<?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\HttpFoundation\Exception;
-
-/**
- * Base LogicException for Http Foundation component.
- */
-class LogicException extends \LogicException implements ExceptionInterface
-{
-}
+<?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\HttpFoundation\Exception;
+
+/**
+ * Base LogicException for Http Foundation component.
+ */
+class LogicException extends \LogicException implements ExceptionInterface
+{
+}
109 changes: 108 additions & 1 deletion src/Symfony/Component/HttpFoundation/Tests/UriSignerTest.php
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\HttpFoundation\Tests;

use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Exception\LogicException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\UriSigner;

Expand All @@ -24,21 +25,38 @@ public function testSign()
$this->assertStringContainsString('?_hash=', $signer->sign('http://example.com/foo'));
$this->assertStringContainsString('?_hash=', $signer->sign('http://example.com/foo?foo=bar'));
$this->assertStringContainsString('&foo=', $signer->sign('http://example.com/foo?foo=bar'));

$this->assertStringContainsString('?_expiration=', $signer->sign('http://example.com/foo', 1));
$this->assertStringContainsString('&_hash=', $signer->sign('http://example.com/foo', 1));
$this->assertStringContainsString('?_expiration=', $signer->sign('http://example.com/foo?foo=bar', 1));
$this->assertStringContainsString('&_hash=', $signer->sign('http://example.com/foo?foo=bar', 1));
$this->assertStringContainsString('&foo=', $signer->sign('http://example.com/foo?foo=bar', 1));
}

public function testCheck()
{
$signer = new UriSigner('foobar');

$this->assertFalse($signer->check('http://example.com/foo'));
$this->assertFalse($signer->check('http://example.com/foo?_hash=foo'));
$this->assertFalse($signer->check('http://example.com/foo?foo=bar&_hash=foo'));
$this->assertFalse($signer->check('http://example.com/foo?foo=bar&_hash=foo&bar=foo'));

$this->assertFalse($signer->check('http://example.com/foo?_expiration=4070908800'));
$this->assertFalse($signer->check('http://example.com/foo?_expiration=4070908800?_hash=foo'));
$this->assertFalse($signer->check('http://example.com/foo?_expiration=4070908800&foo=bar&_hash=foo'));
$this->assertFalse($signer->check('http://example.com/foo?_expiration=4070908800&foo=bar&_hash=foo&bar=foo'));

$this->assertTrue($signer->check($signer->sign('http://example.com/foo')));
$this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar')));
$this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar&0=integer')));

$this->assertTrue($signer->check($signer->sign('http://example.com/foo', new \DateTimeImmutable('2099-01-01 00:00:00'))));
$this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar', new \DateTimeImmutable('2099-01-01 00:00:00'))));
$this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar&0=integer', new \DateTimeImmutable('2099-01-01 00:00:00'))));

$this->assertSame($signer->sign('http://example.com/foo?foo=bar&bar=foo'), $signer->sign('http://example.com/foo?bar=foo&foo=bar'));
$this->assertSame($signer->sign('http://example.com/foo?foo=bar&bar=foo', 1), $signer->sign('http://example.com/foo?bar=foo&foo=bar', 1));
}

public function testCheckWithDifferentArgSeparator()
Expand All @@ -51,6 +69,12 @@ public function testCheckWithDifferentArgSeparator()
$signer->sign('http://example.com/foo?foo=bar&baz=bay')
);
$this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar&baz=bay')));

$this->assertSame(
'http://example.com/foo?_expiration=4070908800&_hash=xfui5FoP0vbD9Cp7pI0tHnqR1Fmj2UARqkIUw7SZVfQ%3D&baz=bay&foo=bar',
$signer->sign('http://example.com/foo?foo=bar&baz=bay', new \DateTimeImmutable('2099-01-01 00:00:00'))
);
$this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar&baz=bay', new \DateTimeImmutable('2099-01-01 00:00:00'))));
}

public function testCheckWithRequest()
Expand All @@ -60,17 +84,27 @@ public function testCheckWithRequest()
$this->assertTrue($signer->checkRequest(Request::create($signer->sign('http://example.com/foo'))));
$this->assertTrue($signer->checkRequest(Request::create($signer->sign('http://example.com/foo?foo=bar'))));
$this->assertTrue($signer->checkRequest(Request::create($signer->sign('http://example.com/foo?foo=bar&0=integer'))));

$this->assertTrue($signer->checkRequest(Request::create($signer->sign('http://example.com/foo', new \DateTimeImmutable('2099-01-01 00:00:00')))));
$this->assertTrue($signer->checkRequest(Request::create($signer->sign('http://example.com/foo?foo=bar', new \DateTimeImmutable('2099-01-01 00:00:00')))));
$this->assertTrue($signer->checkRequest(Request::create($signer->sign('http://example.com/foo?foo=bar&0=integer', new \DateTimeImmutable('2099-01-01 00:00:00')))));
}

public function testCheckWithDifferentParameter()
{
$signer = new UriSigner('foobar', 'qux');
$signer = new UriSigner('foobar', 'qux', 'abc');

$this->assertSame(
'http://example.com/foo?baz=bay&foo=bar&qux=rIOcC%2FF3DoEGo%2FvnESjSp7uU9zA9S%2F%2BOLhxgMexoPUM%3D',
$signer->sign('http://example.com/foo?foo=bar&baz=bay')
);
$this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar&baz=bay')));

$this->assertSame(
'http://example.com/foo?abc=4070908800&baz=bay&foo=bar&qux=hdhUhBVPpzKJdz5ZjC%2FkLvtOYdGKOvKVOczmmMIZK0A%3D',
$signer->sign('http://example.com/foo?foo=bar&baz=bay', new \DateTimeImmutable('2099-01-01 00:00:00'))
);
$this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar&baz=bay', new \DateTimeImmutable('2099-01-01 00:00:00'))));
}

public function testSignerWorksWithFragments()
Expand All @@ -81,6 +115,79 @@ public function testSignerWorksWithFragments()
'http://example.com/foo?_hash=EhpAUyEobiM3QTrKxoLOtQq5IsWyWedoXDPqIjzNj5o%3D&bar=foo&foo=bar#foobar',
$signer->sign('http://example.com/foo?bar=foo&foo=bar#foobar')
);

$this->assertTrue($signer->check($signer->sign('http://example.com/foo?bar=foo&foo=bar#foobar')));

$this->assertSame(
'http://example.com/foo?_expiration=4070908800&_hash=qHl626U5d7LMsVtBxPt9GNzysdSxyOQ1fHA59Y1ib0Y%3D&bar=foo&foo=bar#foobar',
$signer->sign('http://example.com/foo?bar=foo&foo=bar#foobar', new \DateTimeImmutable('2099-01-01 00:00:00'))
);

$this->assertTrue($signer->check($signer->sign('http://example.com/foo?bar=foo&foo=bar#foobar', new \DateTimeImmutable('2099-01-01 00:00:00'))));
}

public function testSignWithUriExpiration()
{
$signer = new UriSigner('foobar');

$this->assertSame($signer->sign('http://example.com/foo?foo=bar&bar=foo', new \DateTimeImmutable('2099-01-01 00:00:00')), $signer->sign('http://example.com/foo?bar=foo&foo=bar', 4070908800));
}

public function testSignWithoutExpirationAndWithReservedHashParameter()
{
$signer = new UriSigner('foobar');

$this->expectException(LogicException::class);

$signer->sign('http://example.com/foo?_hash=bar');
}

public function testSignWithoutExpirationAndWithReservedParameter()
{
$signer = new UriSigner('foobar');

$this->expectException(LogicException::class);

$signer->sign('http://example.com/foo?_expiration=4070908800');
}

public function testSignWithExpirationAndWithReservedHashParameter()
{
$signer = new UriSigner('foobar');

$this->expectException(LogicException::class);

$signer->sign('http://example.com/foo?_hash=bar', new \DateTimeImmutable('2099-01-01 00:00:00'));
}

public function testSignWithExpirationAndWithReservedParameter()
{
$signer = new UriSigner('foobar');

$this->expectException(LogicException::class);

$signer->sign('http://example.com/foo?_expiration=4070908800', new \DateTimeImmutable('2099-01-01 00:00:00'));
}

public function testCheckWithUriExpiration()
{
$signer = new UriSigner('foobar');

$this->assertFalse($signer->check($signer->sign('http://example.com/foo', new \DateTimeImmutable('2000-01-01 00:00:00'))));
$this->assertFalse($signer->check($signer->sign('http://example.com/foo?foo=bar', new \DateTimeImmutable('2000-01-01 00:00:00'))));
$this->assertFalse($signer->check($signer->sign('http://example.com/foo?foo=bar&0=integer', new \DateTimeImmutable('2000-01-01 00:00:00'))));

$this->assertFalse($signer->check($signer->sign('http://example.com/foo', 1577836800))); // 2000-01-01
$this->assertFalse($signer->check($signer->sign('http://example.com/foo?foo=bar', 1577836800))); // 2000-01-01
$this->assertFalse($signer->check($signer->sign('http://example.com/foo?foo=bar&0=integer', 1577836800))); // 2000-01-01

$relativeUriFromNow1 = $signer->sign('http://example.com/foo', new \DateInterval('PT3S'));
$relativeUriFromNow2 = $signer->sign('http://example.com/foo?foo=bar', new \DateInterval('PT3S'));
$relativeUriFromNow3 = $signer->sign('http://example.com/foo?foo=bar&0=integer', new \DateInterval('PT3S'));
sleep(10);

$this->assertFalse($signer->check($relativeUriFromNow1));
$this->assertFalse($signer->check($relativeUriFromNow2));
$this->assertFalse($signer->check($relativeUriFromNow3));
}
}

0 comments on commit 59ca1eb

Please sign in to comment.