From da2e2bda5743b32cb54ae4d598c1bb2209aee719 Mon Sep 17 00:00:00 2001 From: Freek Van der Herten Date: Sat, 12 Nov 2022 17:49:33 +0100 Subject: [PATCH 01/11] wip --- .github/FUNDING.yml | 1 + .github/ISSUE_TEMPLATE/config.yml | 14 ++ .github/dependabot.yml | 12 ++ .github/workflows/dependabot-auto-merge.yml | 32 +++ ...ixer.yml => fix-php-code-style-issues.yml} | 17 +- .github/workflows/run-tests.yml | 21 +- .php-cs-fixer.php | 35 --- README.md | 4 +- composer.json | 30 ++- phpspec.yml | 5 - src/BaseUrlSigner.php | 180 +++++----------- src/MD5UrlSigner.php | 21 -- src/Md5UrlSigner.php | 15 ++ src/UrlSigner.php | 25 +-- tests/MD5UrlSignerTest.php | 201 +++++++----------- 15 files changed, 256 insertions(+), 357 deletions(-) create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/dependabot-auto-merge.yml rename .github/workflows/{php-cs-fixer.yml => fix-php-code-style-issues.yml} (52%) delete mode 100644 .php-cs-fixer.php delete mode 100644 phpspec.yml delete mode 100644 src/MD5UrlSigner.php create mode 100644 src/Md5UrlSigner.php diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..5ccc87c --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: spatie diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..b26e2ec --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,14 @@ +blank_issues_enabled: false +contact_links: + - name: Ask a question + url: https://github.com/spatie/lighthouse-php/discussions/new?category=q-a + about: Ask the community for help + - name: Request a feature + url: https://github.com/spatie/lighthouse-php/discussions/new?category=ideas + about: Share ideas for new features + - name: Report a security issue + url: https://github.com/spatie/lighthouse-php/security/policy + about: Learn how to notify us for sensitive bugs + - name: Report a bug + url: https://github.com/spatie/lighthouse-php/issues/new + about: Report a reproducable bug diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..30c8a49 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + labels: + - "dependencies" \ No newline at end of file diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml new file mode 100644 index 0000000..e7e28b9 --- /dev/null +++ b/.github/workflows/dependabot-auto-merge.yml @@ -0,0 +1,32 @@ +name: dependabot-auto-merge +on: pull_request_target + +permissions: + pull-requests: write + contents: write + +jobs: + dependabot: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' }} + steps: + + - name: Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@v1.3.5 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + + - name: Auto-merge Dependabot PRs for semver-minor updates + if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}} + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + + - name: Auto-merge Dependabot PRs for semver-patch updates + if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}} + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/php-cs-fixer.yml b/.github/workflows/fix-php-code-style-issues.yml similarity index 52% rename from .github/workflows/php-cs-fixer.yml rename to .github/workflows/fix-php-code-style-issues.yml index 5811f0c..150750c 100644 --- a/.github/workflows/php-cs-fixer.yml +++ b/.github/workflows/fix-php-code-style-issues.yml @@ -1,21 +1,22 @@ -name: Check & fix styling +name: Fix PHP code style issues -on: [push] +on: + push: + paths: + - '**.php' jobs: - php-cs-fixer: + php-code-styling: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: ref: ${{ github.head_ref }} - - name: Run PHP CS Fixer - uses: docker://oskarstark/php-cs-fixer-ga - with: - args: --config=.php-cs-fixer.php --allow-risky=yes + - name: Fix PHP code style issues + uses: aglipanci/laravel-pint-action@1.0.0 - name: Commit changes uses: stefanzweifel/git-auto-commit-action@v4 diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index ffe3e86..e2a20cf 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -6,31 +6,38 @@ jobs: test: runs-on: ${{ matrix.os }} strategy: - fail-fast: true + fail-fast: false matrix: - os: [ubuntu-latest, windows-latest] - php: [8.1, 8.0, 7.4] - stability: [prefer-lowest, prefer-stable] + os: [ubuntu-latest] + php: [8.2, 8.1] + stability: [prefer-stable] name: P${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }} steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo coverage: none - name: Setup problem matchers run: | echo "::add-matcher::${{ runner.tool_cache }}/php.json" echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + - name: Install dependencies run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction + - name: Install Chrome Launcher + run: npm install chrome-launcher + + - name: Install Lighthouse + run: npm install lighthouse + - name: Execute tests - run: vendor/bin/phpunit + run: composer test diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php deleted file mode 100644 index ea229df..0000000 --- a/.php-cs-fixer.php +++ /dev/null @@ -1,35 +0,0 @@ -in([ - __DIR__ . '/src', - __DIR__ . '/tests', - ]) - ->name('*.php') - ->notName('*.blade.php') - ->ignoreDotFiles(true) - ->ignoreVCS(true); - -return (new PhpCsFixer\Config()) - ->setRules([ - '@PSR12' => true, - 'array_syntax' => ['syntax' => 'short'], - 'ordered_imports' => ['sort_algorithm' => 'alpha'], - 'no_unused_imports' => true, - 'not_operator_with_successor_space' => true, - 'trailing_comma_in_multiline' => true, - 'phpdoc_scalar' => true, - 'unary_operator_spaces' => true, - 'binary_operator_spaces' => true, - 'blank_line_before_statement' => [ - 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], - ], - 'phpdoc_single_line_var_spacing' => true, - 'phpdoc_var_without_name' => true, - 'method_argument_space' => [ - 'on_multiline' => 'ensure_fully_multiline', - 'keep_multiple_spaces_after_comma' => true, - ], - 'single_trait_insert_per_statement' => true, - ]) - ->setFinder($finder); diff --git a/README.md b/README.md index 1691802..b9eb422 100644 --- a/README.md +++ b/README.md @@ -58,9 +58,9 @@ composer require spatie/url-signer A signer-object can sign URLs and validate signed URLs. A secret key is used to generate signatures. ```php -use Spatie\UrlSigner\MD5UrlSigner; +use Spatie\UrlSigner\Md5UrlSigner; -$urlSigner = new MD5UrlSigner('mysecretkey'); +$urlSigner = new Md5UrlSigner('mysecretkey'); ``` ### Generating URLs diff --git a/composer.json b/composer.json index 53260a0..a593db5 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,12 @@ "homepage": "https://github.com/spatie/url-signer", "license": "MIT", "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://github.com/freekmurze", + "role": "Developer" + }, { "name": "Sebastian De Deyne", "email": "sebastian@spatie.be", @@ -19,11 +25,12 @@ } ], "require": { - "php": "^7.4|^8.0", + "php": "^8.1", "league/uri": "^6.0", "league/uri-components": "^2.2" }, "require-dev": { + "pestphp/pest": "^1.22", "phpunit/phpunit": "^9.5" }, "autoload": { @@ -31,12 +38,23 @@ "Spatie\\UrlSigner\\": "src" } }, + "autoload-dev": { + "psr-4": { + "Spatie\\UrlSigner\\Tests\\": "tests" + } + }, "scripts": { - "test": "phpunit" + "test": "vendor/bin/pest", + "test-coverage": "vendor/bin/pest --coverage", + "format": "vendor/bin/pint" }, - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" + "config": { + "sort-packages": true, + "allow-plugins": { + "phpstan/extension-installer": true, + "pestphp/pest-plugin": true } - } + }, + "minimum-stability": "dev", + "prefer-stable": true } diff --git a/phpspec.yml b/phpspec.yml deleted file mode 100644 index 7fcfac7..0000000 --- a/phpspec.yml +++ /dev/null @@ -1,5 +0,0 @@ -suites: - urlsigner_suite: - namespace: Spatie\UrlSigner - psr4_prefix: Spatie\UrlSigner - src_path: src diff --git a/src/BaseUrlSigner.php b/src/BaseUrlSigner.php index 740ebad..4c8eba3 100644 --- a/src/BaseUrlSigner.php +++ b/src/BaseUrlSigner.php @@ -11,61 +11,34 @@ abstract class BaseUrlSigner implements UrlSigner { - /** - * The key that is used to generate secure signatures. - * - * @var string - */ - protected $signatureKey; - - /** - * The URL's query parameter name for the expiration. - * - * @var string - */ - protected $expiresParameter; - - /** - * The URL's query parameter name for the signature. - * - * @var string - */ - protected $signatureParameter; - - /** - * @param string $signatureKey - * @param string $expiresParameter - * @param string $signatureParameter - * - * @throws InvalidSignatureKey - */ - public function __construct($signatureKey, $expiresParameter = 'expires', $signatureParameter = 'signature') - { - if ($signatureKey == '') { + public function __construct( + protected string $defaultSignatureKey, + protected string $expiresParameterName = 'expires', + protected string $signatureParameterName = 'signature' + ) { + if ($this->defaultSignatureKey == '') { throw new InvalidSignatureKey('The signature key is empty'); } - - $this->signatureKey = $signatureKey; - $this->expiresParameter = $expiresParameter; - $this->signatureParameter = $signatureParameter; } - /** - * Get a secure URL to a controller action. - * - * @param string $url - * @param \DateTime|int $expiration - * - * @throws InvalidExpiration - * - * @return string - */ - public function sign($url, $expiration) - { + abstract protected function createSignature( + string $url, + string $expiration, + string $signatureKey, + ): string; + + public function sign( + string $url, + int|DateTime $expiration, + string $signatureKey = null, + ): string { + $signatureKey ??= $this->defaultSignatureKey; + $url = Http::createFromString($url); $expiration = $this->getExpirationTimestamp($expiration); - $signature = $this->createSignature((string) $url, $expiration); + + $signature = $this->createSignature((string) $url, $expiration, $signatureKey); return (string) $this->signUrl($url, $expiration, $signature); } @@ -79,25 +52,20 @@ public function sign($url, $expiration) * * @return \League\Url\UrlImmutable */ - protected function signUrl(UriInterface $url, $expiration, $signature) + protected function signUrl(UriInterface $url, string $expiration, $signature) { $query = QueryString::extract($url->getQuery()); - $query[$this->expiresParameter] = $expiration; - $query[$this->signatureParameter] = $signature; + $query[$this->expiresParameterName] = $expiration; + $query[$this->signatureParameterName] = $signature; return $url->withQuery($this->buildQueryStringFromArray($query)); } - /** - * Validate a signed url. - * - * @param string $url - * - * @return bool - */ - public function validate($url) + public function validate(string $url, string $signatureKey = null): bool { + $signatureKey ??= $this->defaultSignatureKey; + $url = Http::createFromString($url); $query = QueryString::extract($url->getQuery()); @@ -106,93 +74,53 @@ public function validate($url) return false; } - $expiration = $query[$this->expiresParameter]; + $expiration = $query[$this->expiresParameterName]; if (! $this->isFuture($expiration)) { return false; } - if (! $this->hasValidSignature($url)) { + if (! $this->hasValidSignature($url, $signatureKey)) { return false; } return true; } - /** - * Generate a token to identify the secure action. - * - * @param UriInterface|string $url - * @param string $expiration - * - * @return string - */ - abstract protected function createSignature($url, string $expiration); - /** - * Check if a query is missing a necessary parameter. - * - * @param array $query - * - * @return bool - */ - protected function isMissingAQueryParameter(array $query) + + protected function isMissingAQueryParameter(array $query): bool { - if (! isset($query[$this->expiresParameter])) { + if (! isset($query[$this->expiresParameterName])) { return true; } - if (! isset($query[$this->signatureParameter])) { + if (! isset($query[$this->signatureParameterName])) { return true; } return false; } - /** - * Check if a timestamp is in the future. - * - * @param int $timestamp - * - * @return bool - */ - protected function isFuture($timestamp) + protected function isFuture(int $timestamp): bool { - return ((int) $timestamp) >= (new DateTime())->getTimestamp(); + return $timestamp >= (new DateTime())->getTimestamp(); } - /** - * Retrieve the intended URL by stripping off the UrlSigner specific parameters. - * - * @param UriInterface $url - * - * @return UriInterface - */ - protected function getIntendedUrl(UriInterface $url) + protected function getIntendedUrl(UriInterface $url): UriInterface { $intendedQuery = QueryString::extract($url->getQuery()); - unset($intendedQuery[$this->expiresParameter]); - unset($intendedQuery[$this->signatureParameter]); + unset($intendedQuery[$this->expiresParameterName]); + unset($intendedQuery[$this->signatureParameterName]); return $url->withQuery($this->buildQueryStringFromArray($intendedQuery) ?? ''); } - /** - * Retrieve the expiration timestamp for a link based on an absolute DateTime or a relative number of days. - * - * @param \DateTime|int $expiration The expiration date of this link. - * - DateTime: The value will be used as expiration date - * - int: The expiration time will be set to X days from now - * - * @throws \Spatie\UrlSigner\Exceptions\InvalidExpiration - * - * @return string - */ - protected function getExpirationTimestamp($expiration) + protected function getExpirationTimestamp(DateTime|int $expiration): string { if (is_int($expiration)) { - $expiration = (new DateTime())->modify((int) $expiration.' days'); + $expiration = (new DateTime())->modify($expiration.' days'); } if (! $expiration instanceof DateTime) { @@ -206,37 +134,27 @@ protected function getExpirationTimestamp($expiration) return (string) $expiration->getTimestamp(); } - /** - * Determine if the url has a forged signature. - * - * @param UriInterface $url - * - * @return bool - */ - protected function hasValidSignature(UriInterface $url) + protected function hasValidSignature( + UriInterface|string $url, + string $signatureKey + ): bool { $query = QueryString::extract($url->getQuery()); - $expiration = $query[$this->expiresParameter]; - $providedSignature = $query[$this->signatureParameter]; + $expiration = $query[$this->expiresParameterName]; + $providedSignature = $query[$this->signatureParameterName]; $intendedUrl = $this->getIntendedUrl($url); - $validSignature = $this->createSignature($intendedUrl, $expiration); + $validSignature = $this->createSignature($intendedUrl, $expiration, $signatureKey); return hash_equals($validSignature, $providedSignature); } - /** - * Turn a key => value associate array into a query string. - * - * @param array $query - * - * @return string|null - */ - protected function buildQueryStringFromArray(array $query) + protected function buildQueryStringFromArray(array $query): ?string { $buildQuery = []; + foreach ($query as $key => $value) { $buildQuery[] = [$key, $value]; } diff --git a/src/MD5UrlSigner.php b/src/MD5UrlSigner.php deleted file mode 100644 index ac5e54a..0000000 --- a/src/MD5UrlSigner.php +++ /dev/null @@ -1,21 +0,0 @@ -signatureKey}"); - } -} diff --git a/src/Md5UrlSigner.php b/src/Md5UrlSigner.php new file mode 100644 index 0000000..3b275b9 --- /dev/null +++ b/src/Md5UrlSigner.php @@ -0,0 +1,15 @@ +assertInstanceOf(MD5UrlSigner::class, $urlSigner); - } - - /** @test */ - public function it_will_throw_an_exception_for_an_empty_signatureKey() - { - $this->expectException(InvalidSignatureKey::class); - - $urlSigner = new MD5UrlSigner(''); - } - - /** @test */ - public function it_returns_false_when_validating_a_forged_url() - { - $signedUrl = 'http://myapp.com/somewhereelse/?expires=4594900544&signature=41d5c3a92c6ef94e73cb70c7dcda0859'; - $urlSigner = new MD5UrlSigner('random_monkey'); - - $this->assertFalse($urlSigner->validate($signedUrl)); - } - - /** @test */ - public function it_returns_false_when_validating_an_expired_url() - { - $signedUrl = 'http://myapp.com/?expires=1123690544&signature=93e02326d7572632dd6edfa2665f2743'; - $urlSigner = new MD5UrlSigner('random_monkey'); - - $this->assertFalse($urlSigner->validate($signedUrl)); - } - - /** @test */ - public function it_returns_true_when_validating_an_non_expired_url() - { - $url = 'http://myapp.com'; - $expiration = 10000; - $urlSigner = new MD5UrlSigner('random_monkey'); - $signedUrl = $urlSigner->sign($url, $expiration); - - $this->assertTrue($urlSigner->validate($signedUrl)); - } - - public function unsignedUrlProvider() - { - return [ - ['http://myapp.com/?expires=4594900544'], - ['http://myapp.com/?signature=41d5c3a92c6ef94e73cb70c7dcda0859'], - ]; - } - - /** - * @test - * @dataProvider unsignedUrlProvider - */ - public function it_returns_false_when_validating_an_unsigned_url($unsignedUrl) - { - $urlSigner = new MD5UrlSigner('random_monkey'); - - $this->assertFalse($urlSigner->validate($unsignedUrl)); - } - - /** @test */ - public function it_does_a_strict_check_on_expirations() - { - $url = 'http://myapp.com'; - $expiration = '30'; - $urlSigner = new MD5UrlSigner('random_monkey'); - - $this->expectException(InvalidExpiration::class); - - $urlSigner->sign($url, $expiration); - } - - public function pastExpirationProvider() - { - return [ - [DateTime::createFromFormat('d/m/Y H:i:s', '10/08/2005 18:15:44')], - [-10], - ]; - } - - /** - * @test - * @dataProvider pastExpirationProvider - */ - public function it_doesnt_allow_expirations_in_the_past($pastExpiration) - { - $url = 'http://myapp.com'; - $urlSigner = new MD5UrlSigner('random_monkey'); - - $this->expectException(InvalidExpiration::class); - - $urlSigner->sign($url, $pastExpiration); - } - - /** @test */ - public function it_keeps_the_urls_query_parameters_intact() - { - $url = 'https://myapp.com/?foo=bar&baz=qux'; - $expiration = DateTime::createFromFormat( - 'd/m/Y H:i:s', - '10/08/2115 18:15:44', - new DateTimeZone('Europe/Brussels') - ); - $expected = 'https://myapp.com/?foo=bar&baz=qux&expires=4594900544&signature=728971d9fd0682793d2a1e96b734d949'; - - $urlSigner = new MD5UrlSigner('random_monkey'); - $signedUrl = $urlSigner->sign($url, $expiration); - - $this->assertStringContainsString('?foo=bar&baz=qux', $signedUrl); - $this->assertTrue($urlSigner->validate($signedUrl)); - } -} +use Spatie\UrlSigner\Md5UrlSigner; +use Spatie\UrlSigner\UrlSigner; + +it('can be initialized', function () { + $urlSigner = new Md5UrlSigner('random_monkey'); + + expect($urlSigner)->toBeInstanceOf(UrlSigner::class); +}); + +it('will throw an exception fro an empty signature key', function () { + new Md5UrlSigner(''); +})->throws(InvalidSignatureKey::class); + +it('returns false when validating a forged url', function () { + $signedUrl = 'http://myapp.com/somewhereelse/?expires=4594900544&signature=41d5c3a92c6ef94e73cb70c7dcda0859'; + $urlSigner = new Md5UrlSigner('random_monkey'); + + expect($urlSigner->validate($signedUrl))->toBeFalse(); +}); + +it('returns false when validating an expired url', function () { + $signedUrl = 'http://myapp.com/?expires=1123690544&signature=93e02326d7572632dd6edfa2665f2743'; + $urlSigner = new Md5UrlSigner('random_monkey'); + + expect($urlSigner->validate($signedUrl))->toBeFalse(); +}); + +it('returns true when validating a non-expired url', function () { + $url = 'http://myapp.com'; + + $expiration = 10000; + $urlSigner = new Md5UrlSigner('random_monkey'); + $signedUrl = $urlSigner->sign($url, $expiration); + + expect($urlSigner->validate($signedUrl))->toBeTrue(); +}); + +it('returns false when validating an unsigned url', function (string $unsignedUrl) { + $urlSigner = new Md5UrlSigner('random_monkey'); + + expect($urlSigner->validate($unsignedUrl))->toBeFalse(); +})->with('unsignedUrls'); + +it('does not allow expirations in the past', function ($pastExpiration) { + $url = 'http://myapp.com'; + $urlSigner = new Md5UrlSigner('random_monkey'); + + $this->expectException(InvalidExpiration::class); + + $urlSigner->sign($url, $pastExpiration); +})->with([ + [DateTime::createFromFormat('d/m/Y H:i:s', '10/08/2005 18:15:44')], + [-10], +])->throws(InvalidExpiration::class); + + +it('keep url query parameters intact', function () { + $url = 'https://myapp.com/?foo=bar&baz=qux'; + $expiration = DateTime::createFromFormat( + 'd/m/Y H:i:s', + '10/08/2115 18:15:44', + new DateTimeZone('Europe/Brussels') + ); + $expected = 'https://myapp.com/?foo=bar&baz=qux&expires=4594900544&signature=728971d9fd0682793d2a1e96b734d949'; + + $urlSigner = new Md5UrlSigner('random_monkey'); + $signedUrl = $urlSigner->sign($url, $expiration); + + expect($signedUrl)->toContain('?foo=bar&baz=qux'); + expect($urlSigner->validate($signedUrl))->toBeTrue(); +}); + +dataset('unsignedUrls', [ + ['http://myapp.com/?expires=4594900544'], + ['http://myapp.com/?signature=41d5c3a92c6ef94e73cb70c7dcda0859'], +]); From e52977a7e64fc2d32d55d69fdf6cb0aed0fc5c6a Mon Sep 17 00:00:00 2001 From: freekmurze Date: Sat, 12 Nov 2022 16:50:14 +0000 Subject: [PATCH 02/11] Fix styling --- src/BaseUrlSigner.php | 12 ++++-------- src/Md5UrlSigner.php | 3 +-- tests/MD5UrlSignerTest.php | 1 - 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/BaseUrlSigner.php b/src/BaseUrlSigner.php index 4c8eba3..0d0bfaa 100644 --- a/src/BaseUrlSigner.php +++ b/src/BaseUrlSigner.php @@ -46,10 +46,9 @@ public function sign( /** * Add expiration and signature query parameters to an url. * - * @param UriInterface $url - * @param string $expiration - * @param string $signature - * + * @param UriInterface $url + * @param string $expiration + * @param string $signature * @return \League\Url\UrlImmutable */ protected function signUrl(UriInterface $url, string $expiration, $signature) @@ -87,8 +86,6 @@ public function validate(string $url, string $signatureKey = null): bool return true; } - - protected function isMissingAQueryParameter(array $query): bool { if (! isset($query[$this->expiresParameterName])) { @@ -137,8 +134,7 @@ protected function getExpirationTimestamp(DateTime|int $expiration): string protected function hasValidSignature( UriInterface|string $url, string $signatureKey - ): bool - { + ): bool { $query = QueryString::extract($url->getQuery()); $expiration = $query[$this->expiresParameterName]; diff --git a/src/Md5UrlSigner.php b/src/Md5UrlSigner.php index 3b275b9..72bb144 100644 --- a/src/Md5UrlSigner.php +++ b/src/Md5UrlSigner.php @@ -8,8 +8,7 @@ protected function createSignature( string $url, string $expiration, string $signatureKey - ): string - { + ): string { return md5("{$url}::{$expiration}::{$signatureKey}"); } } diff --git a/tests/MD5UrlSignerTest.php b/tests/MD5UrlSignerTest.php index 72651dc..b4aeea3 100644 --- a/tests/MD5UrlSignerTest.php +++ b/tests/MD5UrlSignerTest.php @@ -57,7 +57,6 @@ [-10], ])->throws(InvalidExpiration::class); - it('keep url query parameters intact', function () { $url = 'https://myapp.com/?foo=bar&baz=qux'; $expiration = DateTime::createFromFormat( From 7affdc27dffca98f98dd8dd61237e58532aac955 Mon Sep 17 00:00:00 2001 From: Freek Van der Herten Date: Sat, 12 Nov 2022 17:54:08 +0100 Subject: [PATCH 03/11] wip --- src/BaseUrlSigner.php | 6 +++--- src/Exceptions/InvalidExpiration.php | 13 ++++++++++++- src/Exceptions/InvalidSignatureKey.php | 8 +++++++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/BaseUrlSigner.php b/src/BaseUrlSigner.php index 4c8eba3..b768472 100644 --- a/src/BaseUrlSigner.php +++ b/src/BaseUrlSigner.php @@ -17,7 +17,7 @@ public function __construct( protected string $signatureParameterName = 'signature' ) { if ($this->defaultSignatureKey == '') { - throw new InvalidSignatureKey('The signature key is empty'); + throw InvalidSignatureKey::signatureEmpty(); } } @@ -124,11 +124,11 @@ protected function getExpirationTimestamp(DateTime|int $expiration): string } if (! $expiration instanceof DateTime) { - throw new InvalidExpiration('Expiration date must be an instance of DateTime or an integer'); + throw InvalidExpiration::wrongType(); } if (! $this->isFuture($expiration->getTimestamp())) { - throw new InvalidExpiration('Expiration date must be in the future'); + throw InvalidExpiration::isInPast(); } return (string) $expiration->getTimestamp(); diff --git a/src/Exceptions/InvalidExpiration.php b/src/Exceptions/InvalidExpiration.php index 1d49edc..b41ae4a 100644 --- a/src/Exceptions/InvalidExpiration.php +++ b/src/Exceptions/InvalidExpiration.php @@ -2,6 +2,17 @@ namespace Spatie\UrlSigner\Exceptions; -class InvalidExpiration extends \Exception +use Exception; + +class InvalidExpiration extends Exception { + public static function isInPast(): self + { + return new self('Expiration date must be in the future'); + } + + public static function wrongType(): self + { + return new self('Expiration date must be an instance of DateTime or an integer'); + } } diff --git a/src/Exceptions/InvalidSignatureKey.php b/src/Exceptions/InvalidSignatureKey.php index dd7f8bb..3bf0b10 100644 --- a/src/Exceptions/InvalidSignatureKey.php +++ b/src/Exceptions/InvalidSignatureKey.php @@ -2,6 +2,12 @@ namespace Spatie\UrlSigner\Exceptions; -class InvalidSignatureKey extends \Exception +use Exception; + +class InvalidSignatureKey extends Exception { + public static function signatureEmpty(): self + { + return new self('The signature key is empty'); + } } From 4f20022b4588a9ba83d195504b22de4b528b7b8f Mon Sep 17 00:00:00 2001 From: Freek Van der Herten Date: Sat, 12 Nov 2022 20:15:53 +0100 Subject: [PATCH 04/11] wip --- composer.json | 7 +- src/BaseUrlSigner.php | 85 ++++++------------- src/Support/Str.php | 24 ++++++ src/Support/Url.php | 41 +++++++++ ...UrlSignerTest.php => Md5UrlSignerTest.php} | 5 +- tests/Support/StrTest.php | 28 ++++++ tests/Support/UrlTest.php | 20 +++++ 7 files changed, 147 insertions(+), 63 deletions(-) create mode 100644 src/Support/Str.php create mode 100644 src/Support/Url.php rename tests/{MD5UrlSignerTest.php => Md5UrlSignerTest.php} (95%) create mode 100644 tests/Support/StrTest.php create mode 100644 tests/Support/UrlTest.php diff --git a/composer.json b/composer.json index a593db5..3c7deb2 100644 --- a/composer.json +++ b/composer.json @@ -25,13 +25,10 @@ } ], "require": { - "php": "^8.1", - "league/uri": "^6.0", - "league/uri-components": "^2.2" + "php": "^8.1" }, "require-dev": { - "pestphp/pest": "^1.22", - "phpunit/phpunit": "^9.5" + "pestphp/pest": "^1.22" }, "autoload": { "psr-4": { diff --git a/src/BaseUrlSigner.php b/src/BaseUrlSigner.php index 0a4ddcc..fcbeb9f 100644 --- a/src/BaseUrlSigner.php +++ b/src/BaseUrlSigner.php @@ -3,11 +3,10 @@ namespace Spatie\UrlSigner; use DateTime; -use League\Uri\Http; -use League\Uri\QueryString; -use Psr\Http\Message\UriInterface; use Spatie\UrlSigner\Exceptions\InvalidExpiration; use Spatie\UrlSigner\Exceptions\InvalidSignatureKey; +use Spatie\UrlSigner\Support\Str; +use Spatie\UrlSigner\Support\Url; abstract class BaseUrlSigner implements UrlSigner { @@ -34,46 +33,31 @@ public function sign( ): string { $signatureKey ??= $this->defaultSignatureKey; - $url = Http::createFromString($url); - $expiration = $this->getExpirationTimestamp($expiration); - $signature = $this->createSignature((string) $url, $expiration, $signatureKey); + $signature = $this->createSignature($url, $expiration, $signatureKey); - return (string) $this->signUrl($url, $expiration, $signature); + return $this->signUrl($url, $expiration, $signature); } - /** - * Add expiration and signature query parameters to an url. - * - * @param UriInterface $url - * @param string $expiration - * @param string $signature - * @return \League\Url\UrlImmutable - */ - protected function signUrl(UriInterface $url, string $expiration, $signature) + protected function signUrl(string $url, string $expiration, $signature): string { - $query = QueryString::extract($url->getQuery()); - - $query[$this->expiresParameterName] = $expiration; - $query[$this->signatureParameterName] = $signature; - - return $url->withQuery($this->buildQueryStringFromArray($query)); + return Url::addQueryParameters($url, [ + $this->expiresParameterName => $expiration, + $this->signatureParameterName => $signature, + ]); } public function validate(string $url, string $signatureKey = null): bool { $signatureKey ??= $this->defaultSignatureKey; - $url = Http::createFromString($url); - - $query = QueryString::extract($url->getQuery()); - - if ($this->isMissingAQueryParameter($query)) { + $queryParameters = Url::queryParameters($url); + if ($this->isMissingAQueryParameter($queryParameters)) { return false; } - $expiration = $query[$this->expiresParameterName]; + $expiration = $queryParameters[$this->expiresParameterName]; if (! $this->isFuture($expiration)) { return false; @@ -104,41 +88,39 @@ protected function isFuture(int $timestamp): bool return $timestamp >= (new DateTime())->getTimestamp(); } - protected function getIntendedUrl(UriInterface $url): UriInterface + protected function getIntendedUrl(string $url): string { - $intendedQuery = QueryString::extract($url->getQuery()); - - unset($intendedQuery[$this->expiresParameterName]); - unset($intendedQuery[$this->signatureParameterName]); - - return $url->withQuery($this->buildQueryStringFromArray($intendedQuery) ?? ''); + return Url::withoutParameters($url, [ + $this->expiresParameterName, + $this->signatureParameterName, + ]); } - protected function getExpirationTimestamp(DateTime|int $expiration): string + protected function getExpirationTimestamp(DateTime|int $expirationInSeconds): string { - if (is_int($expiration)) { - $expiration = (new DateTime())->modify($expiration.' days'); + if (is_int($expirationInSeconds)) { + $expirationInSeconds = (new DateTime())->modify($expirationInSeconds.' seconds'); } - if (! $expiration instanceof DateTime) { + if (! $expirationInSeconds instanceof DateTime) { throw InvalidExpiration::wrongType(); } - if (! $this->isFuture($expiration->getTimestamp())) { + if (! $this->isFuture($expirationInSeconds->getTimestamp())) { throw InvalidExpiration::isInPast(); } - return (string) $expiration->getTimestamp(); + return (string) $expirationInSeconds->getTimestamp(); } protected function hasValidSignature( - UriInterface|string $url, - string $signatureKey + string $url, + string $signatureKey, ): bool { - $query = QueryString::extract($url->getQuery()); + $queryParameters = Url::queryParameters($url); - $expiration = $query[$this->expiresParameterName]; - $providedSignature = $query[$this->signatureParameterName]; + $expiration = $queryParameters[$this->expiresParameterName]; + $providedSignature = $queryParameters[$this->signatureParameterName]; $intendedUrl = $this->getIntendedUrl($url); @@ -146,15 +128,4 @@ protected function hasValidSignature( return hash_equals($validSignature, $providedSignature); } - - protected function buildQueryStringFromArray(array $query): ?string - { - $buildQuery = []; - - foreach ($query as $key => $value) { - $buildQuery[] = [$key, $value]; - } - - return QueryString::build($buildQuery); - } } diff --git a/src/Support/Str.php b/src/Support/Str.php new file mode 100644 index 0000000..73060ae --- /dev/null +++ b/src/Support/Str.php @@ -0,0 +1,24 @@ +sign($url, $expiration); @@ -77,3 +76,7 @@ ['http://myapp.com/?expires=4594900544'], ['http://myapp.com/?signature=41d5c3a92c6ef94e73cb70c7dcda0859'], ]); + +it('the expiration is measured in seconds', function() { + +}); diff --git a/tests/Support/StrTest.php b/tests/Support/StrTest.php new file mode 100644 index 0000000..9c9197a --- /dev/null +++ b/tests/Support/StrTest.php @@ -0,0 +1,28 @@ +toBe($expected); +})->with([ + ['https://spatie.be?hey', '?', 'hey'], + ['https://spatie.be', '?', ''], + ['https://spatie.be?', '?', ''], + ['https://?spatie.be?', '?', 'spatie.be?'], + ['https://?spatie.be?', '!', ''], +]); + +it('can get the string before a string', function(string $string, string $after, string $expected) { + $actual = Str::before($string, $after); + + expect($actual)->toBe($expected); +})->with([ + ['https://spatie.be?hey', '?', 'https://spatie.be'], + ['https://spatie.be', '?', 'https://spatie.be'], + ['https://?spatie.be?', '?', 'https://'], + ['https://?spatie.be?', '!', 'https://?spatie.be?'], + ['?https://spatie.be?hey', '?', ''], + +]); \ No newline at end of file diff --git a/tests/Support/UrlTest.php b/tests/Support/UrlTest.php new file mode 100644 index 0000000..3c22cba --- /dev/null +++ b/tests/Support/UrlTest.php @@ -0,0 +1,20 @@ +toBe($actualParameters); +})->with([ + ['spatie.be?a=1&b=2', ['a' => '1', 'b' => '2']], + //['spatie.be', []], +]); + +it('can add query parameters to a URL', function(string $url, array $add, string $expectedUrl) { + $actualUrl = Url::addQueryParameters($url, $add); + + expect($expectedUrl)->toBe($actualUrl); +})->with([ + ['spatie.be', ['a' => 1, 'b' => 2], 'spatie.be?a=1&b=2'] +]); \ No newline at end of file From 72feedd5df882fa47d9260c10c8688fb69874df2 Mon Sep 17 00:00:00 2001 From: freekmurze Date: Sat, 12 Nov 2022 19:16:26 +0000 Subject: [PATCH 05/11] Fix styling --- src/BaseUrlSigner.php | 1 - src/Support/Str.php | 4 ++-- src/Support/Url.php | 8 ++++---- tests/Md5UrlSignerTest.php | 3 +-- tests/Support/StrTest.php | 10 +++++----- tests/Support/UrlTest.php | 10 +++++----- 6 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/BaseUrlSigner.php b/src/BaseUrlSigner.php index fcbeb9f..58d342f 100644 --- a/src/BaseUrlSigner.php +++ b/src/BaseUrlSigner.php @@ -5,7 +5,6 @@ use DateTime; use Spatie\UrlSigner\Exceptions\InvalidExpiration; use Spatie\UrlSigner\Exceptions\InvalidSignatureKey; -use Spatie\UrlSigner\Support\Str; use Spatie\UrlSigner\Support\Url; abstract class BaseUrlSigner implements UrlSigner diff --git a/src/Support/Str.php b/src/Support/Str.php index 73060ae..c279d19 100644 --- a/src/Support/Str.php +++ b/src/Support/Str.php @@ -10,7 +10,7 @@ public static function before(string $fullString, string $character): string return $fullString; } - return substr($fullString, 0, strpos($fullString, $character)); + return substr($fullString, 0, strpos($fullString, $character)); } public static function after(string $fullString, string $character): string @@ -21,4 +21,4 @@ public static function after(string $fullString, string $character): string return substr($fullString, strpos($fullString, $character) + 1); } -} \ No newline at end of file +} diff --git a/src/Support/Url.php b/src/Support/Url.php index e5cdca1..e6ca1ca 100644 --- a/src/Support/Url.php +++ b/src/Support/Url.php @@ -21,14 +21,14 @@ public static function addQueryParameters(string $url, array $newQueryParameters $baseUrl = Str::before($url, '?'); - return $baseUrl . '?' . http_build_query($allQueryParameters); + return $baseUrl.'?'.http_build_query($allQueryParameters); } public static function withoutParameters(string $url, array $unwantedParameterNames = []): string { $urlQueryParameters = self::queryParameters($url); - foreach($unwantedParameterNames as $name) { + foreach ($unwantedParameterNames as $name) { if (array_key_exists($name, $urlQueryParameters)) { unset($urlQueryParameters[$name]); } @@ -36,6 +36,6 @@ public static function withoutParameters(string $url, array $unwantedParameterNa $baseUrl = Str::before($url, '?'); - return $baseUrl . '?' . http_build_query($urlQueryParameters); + return $baseUrl.'?'.http_build_query($urlQueryParameters); } -} \ No newline at end of file +} diff --git a/tests/Md5UrlSignerTest.php b/tests/Md5UrlSignerTest.php index e778736..f55456e 100644 --- a/tests/Md5UrlSignerTest.php +++ b/tests/Md5UrlSignerTest.php @@ -77,6 +77,5 @@ ['http://myapp.com/?signature=41d5c3a92c6ef94e73cb70c7dcda0859'], ]); -it('the expiration is measured in seconds', function() { - +it('the expiration is measured in seconds', function () { }); diff --git a/tests/Support/StrTest.php b/tests/Support/StrTest.php index 9c9197a..570c5dd 100644 --- a/tests/Support/StrTest.php +++ b/tests/Support/StrTest.php @@ -2,10 +2,10 @@ use Spatie\UrlSigner\Support\Str; -it('can get the string after a string', function(string $string, string $after, string $expected) { - $actual = Str::after($string, $after); +it('can get the string after a string', function (string $string, string $after, string $expected) { + $actual = Str::after($string, $after); - expect($actual)->toBe($expected); + expect($actual)->toBe($expected); })->with([ ['https://spatie.be?hey', '?', 'hey'], ['https://spatie.be', '?', ''], @@ -14,7 +14,7 @@ ['https://?spatie.be?', '!', ''], ]); -it('can get the string before a string', function(string $string, string $after, string $expected) { +it('can get the string before a string', function (string $string, string $after, string $expected) { $actual = Str::before($string, $after); expect($actual)->toBe($expected); @@ -25,4 +25,4 @@ ['https://?spatie.be?', '!', 'https://?spatie.be?'], ['?https://spatie.be?hey', '?', ''], -]); \ No newline at end of file +]); diff --git a/tests/Support/UrlTest.php b/tests/Support/UrlTest.php index 3c22cba..eeebd24 100644 --- a/tests/Support/UrlTest.php +++ b/tests/Support/UrlTest.php @@ -2,8 +2,8 @@ use Spatie\UrlSigner\Support\Url; -it('can get the query parameters of a URL', function(string $url, array $expectedParameters) { - $actualParameters= Url::queryParameters($url); +it('can get the query parameters of a URL', function (string $url, array $expectedParameters) { + $actualParameters = Url::queryParameters($url); expect($expectedParameters)->toBe($actualParameters); })->with([ @@ -11,10 +11,10 @@ //['spatie.be', []], ]); -it('can add query parameters to a URL', function(string $url, array $add, string $expectedUrl) { +it('can add query parameters to a URL', function (string $url, array $add, string $expectedUrl) { $actualUrl = Url::addQueryParameters($url, $add); expect($expectedUrl)->toBe($actualUrl); })->with([ - ['spatie.be', ['a' => 1, 'b' => 2], 'spatie.be?a=1&b=2'] -]); \ No newline at end of file + ['spatie.be', ['a' => 1, 'b' => 2], 'spatie.be?a=1&b=2'], +]); From 1ee9545408c9359f9c5220380a09669b7bfd28a4 Mon Sep 17 00:00:00 2001 From: Freek Van der Herten Date: Sat, 12 Nov 2022 20:19:14 +0100 Subject: [PATCH 06/11] wip --- src/Support/Url.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Support/Url.php b/src/Support/Url.php index e5cdca1..df710ec 100644 --- a/src/Support/Url.php +++ b/src/Support/Url.php @@ -36,6 +36,10 @@ public static function withoutParameters(string $url, array $unwantedParameterNa $baseUrl = Str::before($url, '?'); + if (count($urlQueryParameters) === 0) { + return $baseUrl; + } + return $baseUrl . '?' . http_build_query($urlQueryParameters); } } \ No newline at end of file From d3f84474d346f18102640887199b45926b4085a0 Mon Sep 17 00:00:00 2001 From: freekmurze Date: Sat, 12 Nov 2022 19:20:17 +0000 Subject: [PATCH 07/11] Fix styling --- src/Support/Url.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Support/Url.php b/src/Support/Url.php index df710ec..27074d5 100644 --- a/src/Support/Url.php +++ b/src/Support/Url.php @@ -21,14 +21,14 @@ public static function addQueryParameters(string $url, array $newQueryParameters $baseUrl = Str::before($url, '?'); - return $baseUrl . '?' . http_build_query($allQueryParameters); + return $baseUrl.'?'.http_build_query($allQueryParameters); } public static function withoutParameters(string $url, array $unwantedParameterNames = []): string { $urlQueryParameters = self::queryParameters($url); - foreach($unwantedParameterNames as $name) { + foreach ($unwantedParameterNames as $name) { if (array_key_exists($name, $urlQueryParameters)) { unset($urlQueryParameters[$name]); } @@ -40,6 +40,6 @@ public static function withoutParameters(string $url, array $unwantedParameterNa return $baseUrl; } - return $baseUrl . '?' . http_build_query($urlQueryParameters); + return $baseUrl.'?'.http_build_query($urlQueryParameters); } -} \ No newline at end of file +} From 5b603600c28d8513e86595fe6d5be7843eef3dc8 Mon Sep 17 00:00:00 2001 From: Freek Van der Herten Date: Sat, 12 Nov 2022 20:28:44 +0100 Subject: [PATCH 08/11] wip --- tests/Md5UrlSignerTest.php | 50 +++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/tests/Md5UrlSignerTest.php b/tests/Md5UrlSignerTest.php index f55456e..e038716 100644 --- a/tests/Md5UrlSignerTest.php +++ b/tests/Md5UrlSignerTest.php @@ -5,10 +5,13 @@ use Spatie\UrlSigner\Md5UrlSigner; use Spatie\UrlSigner\UrlSigner; -it('can be initialized', function () { - $urlSigner = new Md5UrlSigner('random_monkey'); +beforeEach(function() { + $this->urlSigner = new Md5UrlSigner('random_monkey'); + +}); - expect($urlSigner)->toBeInstanceOf(UrlSigner::class); +it('can be initialized', function () { + expect($this->urlSigner)->toBeInstanceOf(UrlSigner::class); }); it('will throw an exception fro an empty signature key', function () { @@ -17,47 +20,39 @@ it('returns false when validating a forged url', function () { $signedUrl = 'http://myapp.com/somewhereelse/?expires=4594900544&signature=41d5c3a92c6ef94e73cb70c7dcda0859'; - $urlSigner = new Md5UrlSigner('random_monkey'); - expect($urlSigner->validate($signedUrl))->toBeFalse(); + expect($this->urlSigner->validate($signedUrl))->toBeFalse(); }); it('returns false when validating an expired url', function () { $signedUrl = 'http://myapp.com/?expires=1123690544&signature=93e02326d7572632dd6edfa2665f2743'; - $urlSigner = new Md5UrlSigner('random_monkey'); - expect($urlSigner->validate($signedUrl))->toBeFalse(); + expect($this->urlSigner->validate($signedUrl))->toBeFalse(); }); it('returns true when validating a non-expired url', function () { $url = 'http://myapp.com'; $expiration = 10000; - $urlSigner = new Md5UrlSigner('random_monkey'); - $signedUrl = $urlSigner->sign($url, $expiration); + $signedUrl = $this->urlSigner->sign($url, $expiration); - expect($urlSigner->validate($signedUrl))->toBeTrue(); + expect($this->urlSigner->validate($signedUrl))->toBeTrue(); }); it('returns false when validating an unsigned url', function (string $unsignedUrl) { - $urlSigner = new Md5UrlSigner('random_monkey'); - - expect($urlSigner->validate($unsignedUrl))->toBeFalse(); + expect($this->urlSigner->validate($unsignedUrl))->toBeFalse(); })->with('unsignedUrls'); it('does not allow expirations in the past', function ($pastExpiration) { $url = 'http://myapp.com'; - $urlSigner = new Md5UrlSigner('random_monkey'); - - $this->expectException(InvalidExpiration::class); - $urlSigner->sign($url, $pastExpiration); + $this->urlSigner->sign($url, $pastExpiration); })->with([ [DateTime::createFromFormat('d/m/Y H:i:s', '10/08/2005 18:15:44')], [-10], ])->throws(InvalidExpiration::class); -it('keep url query parameters intact', function () { +it('will keep url query parameters intact', function () { $url = 'https://myapp.com/?foo=bar&baz=qux'; $expiration = DateTime::createFromFormat( 'd/m/Y H:i:s', @@ -65,11 +60,10 @@ new DateTimeZone('Europe/Brussels') ); - $urlSigner = new Md5UrlSigner('random_monkey'); - $signedUrl = $urlSigner->sign($url, $expiration); + $signedUrl = $this->urlSigner->sign($url, $expiration); expect($signedUrl)->toContain('?foo=bar&baz=qux'); - expect($urlSigner->validate($signedUrl))->toBeTrue(); + expect($this->urlSigner->validate($signedUrl))->toBeTrue(); }); dataset('unsignedUrls', [ @@ -77,5 +71,17 @@ ['http://myapp.com/?signature=41d5c3a92c6ef94e73cb70c7dcda0859'], ]); -it('the expiration is measured in seconds', function () { +it('using a custom key results in a different signed url', function () { + $signedUsingRegularKey = $this->urlSigner->sign('https://spatie.be', 5); + $signedUsingCustomKey = $this->urlSigner->sign('https://spatie.be', 5, 'custom-key'); + + expect($signedUsingRegularKey)->not()->toBe($signedUsingCustomKey); +}); + +it('can sign and validate urls with a custom key', function() { + $signedUsingCustomKey = $this->urlSigner->sign('https://spatie.be', 5, 'custom-key'); + + expect($this->urlSigner->validate($signedUsingCustomKey, 'custom-key'))->toBeTrue(); + expect($this->urlSigner->validate($signedUsingCustomKey, 'wrong-custom-key'))->toBeFalse(); + }); From 83771d3c078cf9f658afcf43192c30007c4d16c1 Mon Sep 17 00:00:00 2001 From: freekmurze Date: Sat, 12 Nov 2022 19:29:27 +0000 Subject: [PATCH 09/11] Fix styling --- tests/Md5UrlSignerTest.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/Md5UrlSignerTest.php b/tests/Md5UrlSignerTest.php index e038716..1cb27f9 100644 --- a/tests/Md5UrlSignerTest.php +++ b/tests/Md5UrlSignerTest.php @@ -5,9 +5,8 @@ use Spatie\UrlSigner\Md5UrlSigner; use Spatie\UrlSigner\UrlSigner; -beforeEach(function() { +beforeEach(function () { $this->urlSigner = new Md5UrlSigner('random_monkey'); - }); it('can be initialized', function () { @@ -78,10 +77,9 @@ expect($signedUsingRegularKey)->not()->toBe($signedUsingCustomKey); }); -it('can sign and validate urls with a custom key', function() { +it('can sign and validate urls with a custom key', function () { $signedUsingCustomKey = $this->urlSigner->sign('https://spatie.be', 5, 'custom-key'); expect($this->urlSigner->validate($signedUsingCustomKey, 'custom-key'))->toBeTrue(); expect($this->urlSigner->validate($signedUsingCustomKey, 'wrong-custom-key'))->toBeFalse(); - }); From 7da9f97ec736c18382f565c72e4e53eb52139e58 Mon Sep 17 00:00:00 2001 From: Freek Van der Herten Date: Sat, 12 Nov 2022 20:31:31 +0100 Subject: [PATCH 10/11] wip --- .github/workflows/run-tests.yml | 6 ------ README.md | 16 +++++++--------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index e2a20cf..9cbb4ff 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -33,11 +33,5 @@ jobs: - name: Install dependencies run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction - - name: Install Chrome Launcher - run: npm install chrome-launcher - - - name: Install Lighthouse - run: npm install lighthouse - - name: Execute tests run: composer test diff --git a/README.md b/README.md index b9eb422..307c2b4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,3 @@ - -[](https://supportukrainenow.org) - # Create secured URLs with a limited lifetime [![Latest Version on Packagist](https://img.shields.io/packagist/v/spatie/url-signer.svg?style=flat-square)](https://packagist.org/packages/spatie/url-signer) @@ -16,7 +13,7 @@ $urlSigner = new MD5UrlSigner('randomkey'); $urlSigner->sign('https://myapp.com', 30); -// => The generated url will be valid for 30 days +// => The generated url will be valid for 30 seconds ``` This will output an URL that looks like `https://myapp.com/?expires=xxxx&signature=xxxx`. @@ -28,8 +25,6 @@ your application can validate it with: $urlSigner->validate('https://myapp.com/?expires=xxxx&signature=xxxx'); ``` -Spatie is a webdesign agency in Antwerp, Belgium. You'll find an overview of all our open source projects [on our website](https://spatie.be/opensource). - ## Support us [](https://spatie.be/github-ad-click/url-signer) @@ -75,12 +70,12 @@ $urlSigner->sign('https://myapp.com', $expirationDate); // => The generated url will be valid for 10 days ``` -If an integer is provided as expiration date, the url will be valid for that amount of days. +If an integer is provided as expiration date, the url will be valid for that amount of seconds. ```php $urlSigner->sign('https://myapp.com', 30); -// => The generated url will be valid for 30 days +// => The generated url will be valid for 30 seconds ``` ### Validating URLs @@ -98,6 +93,7 @@ $urlSigner->validate('https://myapp.com/?expires=1439223344&signature=2d42f65bd0 ``` ## Writing custom signers + This packages provides a signer that uses md5 to generate signature. You can create your own signer by implementing the `Spatie\UrlSigner\UrlSigner`-interface. If you let your signer extend `Spatie\UrlSigner\BaseUrlSigner` you'll only need to provide the `createSignature`-method. @@ -107,10 +103,11 @@ signer by implementing the `Spatie\UrlSigner\UrlSigner`-interface. If you let yo The tests can be run with: ``` -$ vendor/bin/phpspec run +composer test ``` ## Integrations + To get started quickly in Laravel you can use the [spatie/laravel-url-signer](https://github.com/spatie/laravel-url-signer) package. ## Changelog @@ -127,6 +124,7 @@ If you've found a bug regarding security please mail [security@spatie.be](mailto ## Credits +- [Freek Van der Herten](https://github.com/freekmurze) - [Sebastian De Deyne](https://github.com/sebastiandedeyne) - [All Contributors](../../contributors) From 0d45f5c8708cf0b4629b99ab3bbd597bc576ce93 Mon Sep 17 00:00:00 2001 From: Freek Van der Herten Date: Sat, 12 Nov 2022 20:35:20 +0100 Subject: [PATCH 11/11] wip --- UPGRADING.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 UPGRADING.md diff --git a/UPGRADING.md b/UPGRADING.md new file mode 100644 index 0000000..3450892 --- /dev/null +++ b/UPGRADING.md @@ -0,0 +1,4 @@ +## From v1 to v2 + +- the expiration passed to `sign` and `validate` is now in seconds instead of days +- all the rest of the API has stayed the same 👍 \ No newline at end of file