diff --git a/.gitattributes b/.gitattributes index 94213456..cb903a56 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,12 +1,13 @@ -/docs export-ignore -/test export-ignore -/.gitattributes export-ignore -/.github export-ignore -/.gitignore export-ignore -/*.yml export-ignore -/CONTRIBUTING.md export-ignore -/*.dist export-ignore -/phpbench.json export-ignore -/composer.lock export-ignore -/README.md export-ignore -/Makefile export-ignore +/docs export-ignore +/test export-ignore +/.gitattributes export-ignore +/.github export-ignore +/.gitignore export-ignore +/*.yml export-ignore +/CONTRIBUTING.md export-ignore +/*.dist export-ignore +/phpbench.json export-ignore +/composer.lock export-ignore +/README.md export-ignore +/Makefile export-ignore +/composer-require-checker.json export-ignore diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 609f60bb..a3075cdf 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -16,6 +16,7 @@ jobs: - "locked" php-version: - "7.4" + - "8.0" operating-system: - "ubuntu-latest" @@ -31,14 +32,16 @@ jobs: ini-values: memory_limit=-1 tools: composer:v2, cs2pr + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + - name: "Cache dependencies" uses: "actions/cache@v2" with: - path: | - ~/.composer/cache - vendor - key: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" - restore-keys: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" + path: ${{ steps.composer-cache.outputs.dir }} + key: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.lock') }}" + restore-keys: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-" - name: "Install lowest dependencies" if: ${{ matrix.dependencies == 'lowest' }} diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml index f25fbe95..11a72075 100644 --- a/.github/workflows/coding-standards.yml +++ b/.github/workflows/coding-standards.yml @@ -31,14 +31,16 @@ jobs: ini-values: memory_limit=-1 tools: composer:v2, cs2pr + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + - name: "Cache dependencies" uses: "actions/cache@v2" with: - path: | - ~/.composer/cache - vendor - key: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" - restore-keys: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" + path: ${{ steps.composer-cache.outputs.dir }} + key: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.lock') }}" + restore-keys: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-" - name: "Install lowest dependencies" if: ${{ matrix.dependencies == 'lowest' }} diff --git a/.github/workflows/composer-json-lint.yml b/.github/workflows/composer-json-lint.yml new file mode 100644 index 00000000..bbcbcf9d --- /dev/null +++ b/.github/workflows/composer-json-lint.yml @@ -0,0 +1,67 @@ +name: "Lint composer.json" + +on: + pull_request: + push: + +jobs: + coding-standards: + name: "Lint composer.json" + + runs-on: ${{ matrix.operating-system }} + + strategy: + matrix: + dependencies: + - "highest" + php-version: + - "7.4" + operating-system: + - "ubuntu-latest" + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + php-version: "${{ matrix.php-version }}" + ini-values: memory_limit=-1 + tools: composer:v2, composer-normalize, composer-require-checker, composer-unused + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: "Cache dependencies" + uses: "actions/cache@v2" + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.lock') }}" + restore-keys: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-" + + - name: "Install lowest dependencies" + if: ${{ matrix.dependencies == 'lowest' }} + run: "composer update --prefer-lowest --no-interaction --no-progress" + + - name: "Install highest dependencies" + if: ${{ matrix.dependencies == 'highest' }} + run: "composer update --no-interaction --no-progress" + + - name: "Install locked dependencies" + if: ${{ matrix.dependencies == 'locked' }} + run: "composer install --no-interaction --no-progress" + + - name: "Validate composer.json" + run: "composer validate --strict" + + - name: "Normalize composer.json" + run: "composer-normalize --dry-run" + + - name: "Check composer.json explicit dependencies" + run: "composer-require-checker check --config-file=$(realpath composer-require-checker.json)" + + - name: "Check composer.json unused dependencies" + run: "composer-unused" diff --git a/.github/workflows/mutation-tests.yml b/.github/workflows/mutation-tests.yml index 0707bb2b..d683fb7f 100644 --- a/.github/workflows/mutation-tests.yml +++ b/.github/workflows/mutation-tests.yml @@ -31,14 +31,16 @@ jobs: ini-values: memory_limit=-1 tools: composer:v2, cs2pr + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + - name: "Cache dependencies" uses: "actions/cache@v2" with: - path: | - ~/.composer/cache - vendor - key: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" - restore-keys: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" + path: ${{ steps.composer-cache.outputs.dir }} + key: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.lock') }}" + restore-keys: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-" - name: "Install lowest dependencies" if: ${{ matrix.dependencies == 'lowest' }} diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index fe8989c7..908e6da1 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -19,6 +19,7 @@ jobs: - "development" php-version: - "7.4" + - "8.0" operating-system: - "ubuntu-latest" @@ -29,18 +30,21 @@ jobs: - name: "Install PHP" uses: "shivammathur/setup-php@v2" with: + coverage: "none" php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1 tools: composer:v2, cs2pr + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + - name: "Cache dependencies" uses: "actions/cache@v2" with: - path: | - ~/.composer/cache - vendor - key: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" - restore-keys: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" + path: ${{ steps.composer-cache.outputs.dir }} + key: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.lock') }}" + restore-keys: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-" - name: "Install lowest dependencies" if: ${{ matrix.dependencies == 'lowest' }} @@ -62,7 +66,7 @@ jobs: run: "make phpunit" phpunit-rc: - name: "PHPUnit tests on PHP 8" + name: "PHPUnit tests (nightly)" runs-on: ${{ matrix.operating-system }} @@ -71,7 +75,7 @@ jobs: dependencies: - "locked" php-version: - - "8.0" + - "8.1" operating-system: - "ubuntu-latest" @@ -87,14 +91,16 @@ jobs: ini-values: memory_limit=-1 tools: composer:v2, cs2pr + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + - name: "Cache dependencies" uses: "actions/cache@v2" with: - path: | - ~/.composer/cache - vendor - key: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" - restore-keys: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" + path: ${{ steps.composer-cache.outputs.dir }} + key: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.lock') }}" + restore-keys: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-" - name: "Install locked dependencies" if: ${{ matrix.dependencies == 'locked' }} diff --git a/.github/workflows/release-on-milestone-closed.yml b/.github/workflows/release-on-milestone-closed.yml new file mode 100644 index 00000000..3b69e7b3 --- /dev/null +++ b/.github/workflows/release-on-milestone-closed.yml @@ -0,0 +1,72 @@ +# https://help.github.com/en/categories/automating-your-workflow-with-github-actions + +name: "Automatic Releases" + +on: + milestone: + types: + - "closed" + +jobs: + release: + name: "GIT tag, release & create merge-up PR" + runs-on: ubuntu-latest + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Release" + uses: "laminas/automatic-releases@v1" + with: + command-name: "laminas:automatic-releases:release" + env: + "SHELL_VERBOSITY": "3" + "GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }} + "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} + "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} + "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} + + - name: "Create Merge-Up Pull Request" + uses: "laminas/automatic-releases@v1" + with: + command-name: "laminas:automatic-releases:create-merge-up-pull-request" + env: + "SHELL_VERBOSITY": "3" + "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }} + "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} + "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} + "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} + + - name: "Create and/or Switch to new Release Branch" + uses: "laminas/automatic-releases@v1" + with: + command-name: "laminas:automatic-releases:switch-default-branch-to-next-minor" + env: + "SHELL_VERBOSITY": "3" + "GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }} + "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} + "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} + "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} + + - name: "Bump Changelog Version On Originating Release Branch" + uses: "laminas/automatic-releases@v1" + with: + command-name: "laminas:automatic-releases:bump-changelog" + env: + "SHELL_VERBOSITY": "3" + "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }} + "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} + "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} + "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} + + - name: "Create new milestones" + uses: "laminas/automatic-releases@v1" + with: + command-name: "laminas:automatic-releases:create-milestones" + env: + "SHELL_VERBOSITY": "3" + "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }} + "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} + "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} + "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index db6068ad..7e4c9d52 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -31,14 +31,16 @@ jobs: ini-values: memory_limit=-1 tools: composer:v2, cs2pr + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + - name: "Cache dependencies" uses: "actions/cache@v2" with: - path: | - ~/.composer/cache - vendor - key: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" - restore-keys: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" + path: ${{ steps.composer-cache.outputs.dir }} + key: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.lock') }}" + restore-keys: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-" - name: "Install lowest dependencies" if: ${{ matrix.dependencies == 'lowest' }} diff --git a/Makefile b/Makefile index 5f8692ed..734e3d74 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ phpcs: .PHONY: phpstan phpstan: - @vendor/bin/phpstan analyse + @vendor/bin/phpstan analyse --memory-limit=-1 .PHONY: phpbench phpbench: diff --git a/README.md b/README.md index 7c87b1a2..1e30dd8e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Latest Stable Version]](https://packagist.org/packages/lcobucci/jwt) [![Unstable Version]](https://packagist.org/packages/lcobucci/jwt) -[![Build Status]](https://github.com/lcobucci/jwt/actions?query=workflow%3A%22PHPUnit%20Tests%22+branch%3Amaster) +[![Build Status]](https://github.com/lcobucci/jwt/actions?query=workflow%3A%22PHPUnit%20Tests%22+branch%3A4.1.x) [![Code Coverage]](https://codecov.io/gh/lcobucci/jwt) A simple library to work with JSON Web Token and JSON Web Signature based on the [RFC 7519](https://tools.ietf.org/html/rfc7519). @@ -32,5 +32,5 @@ free to check out Auth0's PHP SDK and free plan at [Total Downloads]: https://img.shields.io/packagist/dt/lcobucci/jwt.svg?style=flat-square [Latest Stable Version]: https://img.shields.io/packagist/v/lcobucci/jwt.svg?style=flat-square [Unstable Version]: https://img.shields.io/packagist/vpre/lcobucci/jwt.svg?style=flat-square -[Build Status]: https://img.shields.io/github/workflow/status/lcobucci/jwt/PHPUnit%20tests/master?style=flat-square -[Code Coverage]: https://codecov.io/gh/lcobucci/jwt/branch/master/graph/badge.svg +[Build Status]: https://img.shields.io/github/workflow/status/lcobucci/jwt/PHPUnit%20tests/4.1.x?style=flat-square +[Code Coverage]: https://codecov.io/gh/lcobucci/jwt/branch/4.1.x/graph/badge.svg diff --git a/composer-require-checker.json b/composer-require-checker.json new file mode 100644 index 00000000..3d3e56d1 --- /dev/null +++ b/composer-require-checker.json @@ -0,0 +1,8 @@ +{ + "symbol-whitelist" : [ + "null", "true", "false", + "static", "self", "parent", + "array", "string", "int", "float", "bool", "iterable", "callable", "void", "object", + "OpenSSLAsymmetricKey" + ] +} diff --git a/composer.json b/composer.json index b6788da6..a3dea13d 100644 --- a/composer.json +++ b/composer.json @@ -1,14 +1,7 @@ { "name": "lcobucci/jwt", - "description": "A simple library to work with JSON Web Token and JSON Web Signature", "type": "library", - "authors": [ - { - "name": "Luís Cobucci", - "email": "lcobucci@gmail.com", - "role": "Developer" - } - ], + "description": "A simple library to work with JSON Web Token and JSON Web Signature", "keywords": [ "JWT", "JWS" @@ -16,24 +9,38 @@ "license": [ "BSD-3-Clause" ], + "authors": [ + { + "name": "Luís Cobucci", + "email": "lcobucci@gmail.com", + "role": "Developer" + } + ], "require": { "php": "^7.4 || ^8.0", + "ext-hash": "*", + "ext-json": "*", "ext-mbstring": "*", "ext-openssl": "*", + "ext-sodium": "*", "lcobucci/clock": "^2.0" }, "require-dev": { - "infection/infection": "^0.20", + "infection/infection": "^0.21", "lcobucci/coding-standard": "^6.0", - "mikey179/vfsstream": "^1.6", - "phpbench/phpbench": "^0.17", + "mikey179/vfsstream": "^1.6.7", + "phpbench/phpbench": "^1.0@alpha", "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^0.12", "phpstan/phpstan-deprecation-rules": "^0.12", "phpstan/phpstan-phpunit": "^0.12", "phpstan/phpstan-strict-rules": "^0.12", "phpunit/php-invoker": "^3.1", - "phpunit/phpunit": "^9.4" + "phpunit/phpunit": "^9.5" + }, + "config": { + "preferred-install": "dist", + "sort-packages": true }, "autoload": { "psr-4": { @@ -49,14 +56,5 @@ ], "Lcobucci\\JWT\\FunctionalTests\\": "test/functional" } - }, - "config": { - "preferred-install": "dist", - "sort-packages": true - }, - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } } } diff --git a/composer.lock b/composer.lock index 346bfda7..685c5045 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "36f5b286daebc58d9834b6e695dba174", + "content-hash": "606dc98202ec466dc147e1b3f58dfafc", "packages": [ { "name": "lcobucci/clock", @@ -51,6 +51,10 @@ } ], "description": "Yet another clock abstraction", + "support": { + "issues": "https://github.com/lcobucci/clock/issues", + "source": "https://github.com/lcobucci/clock/tree/2.0.x" + }, "funding": [ { "url": "https://github.com/lcobucci", @@ -124,8 +128,85 @@ "assertion", "validation" ], + "support": { + "issues": "https://github.com/beberlei/assert/issues", + "source": "https://github.com/beberlei/assert/tree/v3.3.0" + }, "time": "2020-11-13T20:02:54+00:00" }, + { + "name": "composer/package-versions-deprecated", + "version": "1.11.99.1", + "source": { + "type": "git", + "url": "https://github.com/composer/package-versions-deprecated.git", + "reference": "7413f0b55a051e89485c5cb9f765fe24bb02a7b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/7413f0b55a051e89485c5cb9f765fe24bb02a7b6", + "reference": "7413f0b55a051e89485c5cb9f765fe24bb02a7b6", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1.0 || ^2.0", + "php": "^7 || ^8" + }, + "replace": { + "ocramius/package-versions": "1.11.99" + }, + "require-dev": { + "composer/composer": "^1.9.3 || ^2.0@dev", + "ext-zip": "^1.13", + "phpunit/phpunit": "^6.5 || ^7" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "support": { + "issues": "https://github.com/composer/package-versions-deprecated/issues", + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.1" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2020-11-11T10:22:58+00:00" + }, { "name": "composer/xdebug-handler", "version": "1.4.5", @@ -168,6 +249,11 @@ "Xdebug", "performance" ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/1.4.5" + }, "funding": [ { "url": "https://packagist.com", @@ -186,22 +272,22 @@ }, { "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v0.7.0", + "version": "v0.7.1", "source": { "type": "git", "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", - "reference": "e8d808670b8f882188368faaf1144448c169c0b7" + "reference": "fe390591e0241955f22eb9ba327d137e501c771c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/e8d808670b8f882188368faaf1144448c169c0b7", - "reference": "e8d808670b8f882188368faaf1144448c169c0b7", + "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/fe390591e0241955f22eb9ba327d137e501c771c", + "reference": "fe390591e0241955f22eb9ba327d137e501c771c", "shasum": "" }, "require": { "composer-plugin-api": "^1.0 || ^2.0", "php": ">=5.3", - "squizlabs/php_codesniffer": "^2 || ^3 || 4.0.x-dev" + "squizlabs/php_codesniffer": "^2.0 || ^3.0 || ^4.0" }, "require-dev": { "composer/composer": "*", @@ -248,7 +334,11 @@ "stylecheck", "tests" ], - "time": "2020-06-25T14:57:39+00:00" + "support": { + "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", + "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" + }, + "time": "2020-12-07T18:04:37+00:00" }, { "name": "doctrine/annotations", @@ -319,6 +409,10 @@ "docblock", "parser" ], + "support": { + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/1.11.1" + }, "time": "2020-10-26T10:28:16+00:00" }, { @@ -370,6 +464,10 @@ "standard", "style" ], + "support": { + "issues": "https://github.com/doctrine/coding-standard/issues", + "source": "https://github.com/doctrine/coding-standard/tree/8.2.0" + }, "time": "2020-10-25T14:56:19+00:00" }, { @@ -421,6 +519,10 @@ "constructor", "instantiate" ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.4.0" + }, "funding": [ { "url": "https://www.doctrine-project.org/sponsorship.html", @@ -497,6 +599,10 @@ "parser", "php" ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/1.2.1" + }, "funding": [ { "url": "https://www.doctrine-project.org/sponsorship.html", @@ -552,6 +658,10 @@ } ], "description": "Abstract Test Framework Adapter for Infection", + "support": { + "issues": "https://github.com/infection/abstract-testframework-adapter/issues", + "source": "https://github.com/infection/abstract-testframework-adapter/tree/0.3" + }, "time": "2020-08-30T13:50:12+00:00" }, { @@ -604,6 +714,10 @@ } ], "description": "Infection Extension Installer", + "support": { + "issues": "https://github.com/infection/extension-installer/issues", + "source": "https://github.com/infection/extension-installer/tree/0.1.1" + }, "time": "2020-04-25T22:40:05+00:00" }, { @@ -646,20 +760,24 @@ } ], "description": "Stream Wrapper: Include Interceptor. Allows to replace included (autoloaded) file with another one.", + "support": { + "issues": "https://github.com/infection/include-interceptor/issues", + "source": "https://github.com/infection/include-interceptor/tree/0.2.4" + }, "time": "2020-08-07T22:40:37+00:00" }, { "name": "infection/infection", - "version": "0.20.2", + "version": "0.21.0", "source": { "type": "git", "url": "https://github.com/infection/infection.git", - "reference": "6035c1566af6a5a8d833a276351e5e18ed412305" + "reference": "dfacb1e3d9def7fd34c6fb8df4d4bd098815dc82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/infection/infection/zipball/6035c1566af6a5a8d833a276351e5e18ed412305", - "reference": "6035c1566af6a5a8d833a276351e5e18ed412305", + "url": "https://api.github.com/repos/infection/infection/zipball/dfacb1e3d9def7fd34c6fb8df4d4bd098815dc82", + "reference": "dfacb1e3d9def7fd34c6fb8df4d4bd098815dc82", "shasum": "" }, "require": { @@ -671,11 +789,12 @@ "infection/extension-installer": "^0.1.0", "infection/include-interceptor": "^0.2.4", "justinrainbow/json-schema": "^5.2", - "nikic/php-parser": "^4.10.2", + "nikic/php-parser": "^4.10.3", "ocramius/package-versions": "^1.2 || ^2.0", "ondram/ci-detector": "^3.3.0", "php": "^7.4 || ^8.0", - "sanmai/pipeline": "^3.1 || ^5.0", + "sanmai/later": "^0.1.1", + "sanmai/pipeline": "^5.1", "sebastian/diff": "^3.0.2 || ^4.0", "seld/jsonlint": "^1.7", "symfony/console": "^3.4.29 || ^4.1.19 || ^5.0", @@ -700,10 +819,9 @@ "phpstan/phpstan-strict-rules": "^0.12.5", "phpstan/phpstan-webmozart-assert": "^0.12.2", "phpunit/phpunit": "^9.3.11", - "symfony/phpunit-bridge": "^4.4.14 || ^5.1.6", + "symfony/phpunit-bridge": "^4.4.18 || ^5.1.10", "symfony/yaml": "^5.0", - "thecodingmachine/phpstan-safe-rule": "^1.0", - "vimeo/psalm": "^4.0" + "thecodingmachine/phpstan-safe-rule": "^1.0" }, "bin": [ "bin/infection" @@ -757,7 +875,21 @@ "testing", "unit testing" ], - "time": "2020-11-20T17:15:57+00:00" + "support": { + "issues": "https://github.com/infection/infection/issues", + "source": "https://github.com/infection/infection/tree/0.21.0" + }, + "funding": [ + { + "url": "https://github.com/infection", + "type": "github" + }, + { + "url": "https://opencollective.com/infection", + "type": "open_collective" + } + ], + "time": "2021-01-26T22:10:01+00:00" }, { "name": "justinrainbow/json-schema", @@ -823,6 +955,10 @@ "json", "schema" ], + "support": { + "issues": "https://github.com/justinrainbow/json-schema/issues", + "source": "https://github.com/justinrainbow/json-schema/tree/5.2.10" + }, "time": "2020-05-27T16:41:55+00:00" }, { @@ -858,151 +994,11 @@ } ], "description": "Lcobucci's Coding Standard", - "time": "2020-09-05T21:36:16+00:00" - }, - { - "name": "lstrojny/functional-php", - "version": "1.14.1", - "source": { - "type": "git", - "url": "https://github.com/lstrojny/functional-php.git", - "reference": "9e8363e3cb9db924327f51b5804f4dfba03605aa" + "support": { + "issues": "https://github.com/lcobucci/coding-standard/issues", + "source": "https://github.com/lcobucci/coding-standard/tree/6.0.1" }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/lstrojny/functional-php/zipball/9e8363e3cb9db924327f51b5804f4dfba03605aa", - "reference": "9e8363e3cb9db924327f51b5804f4dfba03605aa", - "shasum": "" - }, - "require": { - "php": "~7" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.14", - "phpunit/phpunit": "~7", - "squizlabs/php_codesniffer": "~3.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Functional\\": "src/Functional" - }, - "files": [ - "src/Functional/Ary.php", - "src/Functional/Average.php", - "src/Functional/ButLast.php", - "src/Functional/Capture.php", - "src/Functional/ConstFunction.php", - "src/Functional/CompareOn.php", - "src/Functional/CompareObjectHashOn.php", - "src/Functional/Compose.php", - "src/Functional/Concat.php", - "src/Functional/Contains.php", - "src/Functional/Converge.php", - "src/Functional/Curry.php", - "src/Functional/CurryN.php", - "src/Functional/Difference.php", - "src/Functional/DropFirst.php", - "src/Functional/DropLast.php", - "src/Functional/Each.php", - "src/Functional/Equal.php", - "src/Functional/ErrorToException.php", - "src/Functional/Every.php", - "src/Functional/False.php", - "src/Functional/Falsy.php", - "src/Functional/Filter.php", - "src/Functional/First.php", - "src/Functional/FirstIndexOf.php", - "src/Functional/FlatMap.php", - "src/Functional/Flatten.php", - "src/Functional/Flip.php", - "src/Functional/GreaterThan.php", - "src/Functional/GreaterThanOrEqual.php", - "src/Functional/Group.php", - "src/Functional/Head.php", - "src/Functional/Id.php", - "src/Functional/IfElse.php", - "src/Functional/Identical.php", - "src/Functional/IndexesOf.php", - "src/Functional/Intersperse.php", - "src/Functional/Invoke.php", - "src/Functional/InvokeFirst.php", - "src/Functional/InvokeIf.php", - "src/Functional/InvokeLast.php", - "src/Functional/Invoker.php", - "src/Functional/Last.php", - "src/Functional/LastIndexOf.php", - "src/Functional/LessThan.php", - "src/Functional/LessThanOrEqual.php", - "src/Functional/LexicographicCompare.php", - "src/Functional/Map.php", - "src/Functional/Matching.php", - "src/Functional/Maximum.php", - "src/Functional/Memoize.php", - "src/Functional/Minimum.php", - "src/Functional/None.php", - "src/Functional/Noop.php", - "src/Functional/Not.php", - "src/Functional/OmitKeys.php", - "src/Functional/PartialAny.php", - "src/Functional/PartialLeft.php", - "src/Functional/PartialMethod.php", - "src/Functional/PartialRight.php", - "src/Functional/Partition.php", - "src/Functional/Pick.php", - "src/Functional/Pluck.php", - "src/Functional/Poll.php", - "src/Functional/Product.php", - "src/Functional/Ratio.php", - "src/Functional/ReduceLeft.php", - "src/Functional/ReduceRight.php", - "src/Functional/Reindex.php", - "src/Functional/Reject.php", - "src/Functional/Repeat.php", - "src/Functional/Retry.php", - "src/Functional/Select.php", - "src/Functional/SelectKeys.php", - "src/Functional/SequenceConstant.php", - "src/Functional/SequenceExponential.php", - "src/Functional/SequenceLinear.php", - "src/Functional/Some.php", - "src/Functional/Sort.php", - "src/Functional/Sum.php", - "src/Functional/SuppressError.php", - "src/Functional/Tap.php", - "src/Functional/Tail.php", - "src/Functional/TailRecursion.php", - "src/Functional/TakeLeft.php", - "src/Functional/TakeRight.php", - "src/Functional/True.php", - "src/Functional/Truthy.php", - "src/Functional/Unique.php", - "src/Functional/ValueToKey.php", - "src/Functional/With.php", - "src/Functional/Zip.php", - "src/Functional/ZipAll.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Lars Strojny", - "email": "lstrojny@php.net", - "homepage": "http://usrportage.de" - }, - { - "name": "Max Beutel", - "email": "nash12@gmail.com" - } - ], - "description": "Functional primitives for PHP", - "keywords": [ - "functional" - ], - "time": "2020-10-12T09:48:50+00:00" + "time": "2020-09-05T21:36:16+00:00" }, { "name": "mikey179/vfsstream", @@ -1048,6 +1044,11 @@ ], "description": "Virtual file system to mock the real file system in unit tests.", "homepage": "http://vfs.bovigo.org/", + "support": { + "issues": "https://github.com/bovigo/vfsStream/issues", + "source": "https://github.com/bovigo/vfsStream/tree/master", + "wiki": "https://github.com/bovigo/vfsStream/wiki" + }, "time": "2019-10-30T15:31:00+00:00" }, { @@ -1096,6 +1097,10 @@ "object", "object graph" ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" + }, "funding": [ { "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", @@ -1106,16 +1111,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.10.2", + "version": "v4.10.4", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "658f1be311a230e0907f5dfe0213742aff0596de" + "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/658f1be311a230e0907f5dfe0213742aff0596de", - "reference": "658f1be311a230e0907f5dfe0213742aff0596de", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c6d052fc58cb876152f89f532b95a8d7907e7f0e", + "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e", "shasum": "" }, "require": { @@ -1154,68 +1159,11 @@ "parser", "php" ], - "time": "2020-09-26T10:30:38+00:00" - }, - { - "name": "ocramius/package-versions", - "version": "1.9.0", - "source": { - "type": "git", - "url": "https://github.com/Ocramius/PackageVersions.git", - "reference": "94c9d42a466c57f91390cdd49c81313264f49d85" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/94c9d42a466c57f91390cdd49c81313264f49d85", - "reference": "94c9d42a466c57f91390cdd49c81313264f49d85", - "shasum": "" + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.4" }, - "require": { - "composer-plugin-api": "^1.1.0 || ^2.0", - "php": "^7.4.0" - }, - "require-dev": { - "composer/composer": "^1.9.3 || ^2.0@dev", - "doctrine/coding-standard": "^7.0.2", - "ext-zip": "^1.15.0", - "infection/infection": "^0.15.3", - "phpunit/phpunit": "^9.1.1", - "vimeo/psalm": "^3.9.3" - }, - "type": "composer-plugin", - "extra": { - "class": "PackageVersions\\Installer", - "branch-alias": { - "dev-master": "1.99.x-dev" - } - }, - "autoload": { - "psr-4": { - "PackageVersions\\": "src/PackageVersions" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - } - ], - "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", - "funding": [ - { - "url": "https://github.com/Ocramius", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ocramius/package-versions", - "type": "tidelift" - } - ], - "time": "2020-06-22T14:15:44+00:00" + "time": "2020-12-20T10:01:03+00:00" }, { "name": "ondram/ci-detector", @@ -1283,6 +1231,10 @@ "teamcity", "travis" ], + "support": { + "issues": "https://github.com/OndraM/ci-detector/issues", + "source": "https://github.com/OndraM/ci-detector/tree/main" + }, "time": "2020-09-04T11:21:14+00:00" }, { @@ -1339,20 +1291,24 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/master" + }, "time": "2020-06-27T14:33:11+00:00" }, { "name": "phar-io/version", - "version": "3.0.2", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "c6bb6825def89e0a32220f88337f8ceaf1975fa0" + "reference": "e4782611070e50613683d2b9a57730e9a3ba5451" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/c6bb6825def89e0a32220f88337f8ceaf1975fa0", - "reference": "c6bb6825def89e0a32220f88337f8ceaf1975fa0", + "url": "https://api.github.com/repos/phar-io/version/zipball/e4782611070e50613683d2b9a57730e9a3ba5451", + "reference": "e4782611070e50613683d2b9a57730e9a3ba5451", "shasum": "" }, "require": { @@ -1386,32 +1342,39 @@ } ], "description": "Library for handling version information and constraints", - "time": "2020-06-27T14:39:04+00:00" + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.0.4" + }, + "time": "2020-12-13T23:18:30+00:00" }, { "name": "phpbench/container", - "version": "1.2.1", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/phpbench/container.git", - "reference": "2f2b269b3b8cb9a0053cf98f1c3a84866fe7f0e2" + "reference": "04054b7c8cb30f948e5a289601c34834db58aa9f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/container/zipball/2f2b269b3b8cb9a0053cf98f1c3a84866fe7f0e2", - "reference": "2f2b269b3b8cb9a0053cf98f1c3a84866fe7f0e2", + "url": "https://api.github.com/repos/phpbench/container/zipball/04054b7c8cb30f948e5a289601c34834db58aa9f", + "reference": "04054b7c8cb30f948e5a289601c34834db58aa9f", "shasum": "" }, "require": { - "psr/container": "^1.0" + "psr/container": "^1.0", + "symfony/options-resolver": "^4.2 || ^5.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.36" + "friendsofphp/php-cs-fixer": "^2.16", + "phpstan/phpstan": "^0.12.52", + "phpunit/phpunit": "^8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1430,33 +1393,37 @@ } ], "description": "Simple, configurable, service container.", - "time": "2020-08-23T23:43:00+00:00" + "support": { + "issues": "https://github.com/phpbench/container/issues", + "source": "https://github.com/phpbench/container/tree/2.0.1" + }, + "time": "2020-11-21T10:55:32+00:00" }, { "name": "phpbench/dom", - "version": "0.2.0", + "version": "0.3.0", "source": { "type": "git", "url": "https://github.com/phpbench/dom.git", - "reference": "b135378dd0004c05ba5446aeddaf0b83339c1c4c" + "reference": "a126b32e83d0541f3c89befa1b166ba32d0048ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/dom/zipball/b135378dd0004c05ba5446aeddaf0b83339c1c4c", - "reference": "b135378dd0004c05ba5446aeddaf0b83339c1c4c", + "url": "https://api.github.com/repos/phpbench/dom/zipball/a126b32e83d0541f3c89befa1b166ba32d0048ab", + "reference": "a126b32e83d0541f3c89befa1b166ba32d0048ab", "shasum": "" }, "require": { "ext-dom": "*", - "php": "^5.4|^7.0" + "php": "^7.2|^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.6" + "phpunit/phpunit": "^8.0|^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "0.3-dev" } }, "autoload": { @@ -1475,37 +1442,41 @@ } ], "description": "DOM wrapper to simplify working with the PHP DOM implementation", - "time": "2016-02-27T12:15:56+00:00" + "support": { + "issues": "https://github.com/phpbench/dom/issues", + "source": "https://github.com/phpbench/dom/tree/0.3.0" + }, + "time": "2020-10-25T08:41:08+00:00" }, { "name": "phpbench/phpbench", - "version": "0.17.1", + "version": "1.0.0-alpha4", "source": { "type": "git", "url": "https://github.com/phpbench/phpbench.git", - "reference": "3211debc3afb9da79d796cf7471d52cad97b17f1" + "reference": "a0e8edfc1a308d79b4950648ece538cef7a6446e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/phpbench/zipball/3211debc3afb9da79d796cf7471d52cad97b17f1", - "reference": "3211debc3afb9da79d796cf7471d52cad97b17f1", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/a0e8edfc1a308d79b4950648ece538cef7a6446e", + "reference": "a0e8edfc1a308d79b4950648ece538cef7a6446e", "shasum": "" }, "require": { "beberlei/assert": "^2.4 || ^3.0", "doctrine/annotations": "^1.2.7", + "doctrine/lexer": "^1.2", "ext-dom": "*", "ext-json": "*", "ext-pcre": "*", "ext-reflection": "*", "ext-spl": "*", - "lstrojny/functional-php": "1.0 || ^1.2.3", - "php": "^7.2", - "phpbench/container": "~1.2", - "phpbench/dom": "~0.2.0", + "ext-tokenizer": "*", + "php": "^7.2 || ^8.0", + "phpbench/container": "^2.0", + "phpbench/dom": "~0.3.0", "seld/jsonlint": "^1.1", "symfony/console": "^4.2 || ^5.0", - "symfony/debug": "^4.2 || ^5.0", "symfony/filesystem": "^4.2 || ^5.0", "symfony/finder": "^4.2 || ^5.0", "symfony/options-resolver": "^4.2 || ^5.0", @@ -1513,15 +1484,16 @@ "webmozart/path-util": "^2.3" }, "require-dev": { - "doctrine/dbal": "^2.4", + "dantleech/invoke": "^1.2", "friendsofphp/php-cs-fixer": "^2.13.1", + "jangregor/phpstan-prophecy": "^0.8.1", "padraic/phar-updater": "^1.0", - "phpspec/prophecy": "^1.8", + "phpspec/prophecy": "^1.11", "phpstan/phpstan": "^0.12.7", - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^8.5.8 || ^9.0", + "symfony/var-dumper": "^4.0 || ^5.0" }, "suggest": { - "ext-curl": "For (web) reports extension", "ext-xdebug": "For Xdebug profiling extension." }, "bin": [ @@ -1536,7 +1508,6 @@ "autoload": { "psr-4": { "PhpBench\\": "lib/", - "PhpBench\\Extensions\\Dbal\\": "extensions/dbal/lib/", "PhpBench\\Extensions\\XDebug\\": "extensions/xdebug/lib/", "PhpBench\\Extensions\\Reports\\": "extensions/reports/lib/" } @@ -1552,7 +1523,11 @@ } ], "description": "PHP Benchmarking Framework", - "time": "2020-06-13T11:59:17+00:00" + "support": { + "issues": "https://github.com/phpbench/phpbench/issues", + "source": "https://github.com/phpbench/phpbench/tree/1.0.0-alpha4" + }, + "time": "2020-12-29T09:42:38+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -1601,6 +1576,10 @@ "reflection", "static analysis" ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, "time": "2020-06-27T09:03:43+00:00" }, { @@ -1653,6 +1632,10 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" + }, "time": "2020-09-03T19:13:55+00:00" }, { @@ -1698,20 +1681,24 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0" + }, "time": "2020-09-17T18:55:26+00:00" }, { "name": "phpspec/prophecy", - "version": "1.12.1", + "version": "1.12.2", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "8ce87516be71aae9b956f81906aaf0338e0d8a2d" + "reference": "245710e971a030f42e08f4912863805570f23d39" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/8ce87516be71aae9b956f81906aaf0338e0d8a2d", - "reference": "8ce87516be71aae9b956f81906aaf0338e0d8a2d", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/245710e971a030f42e08f4912863805570f23d39", + "reference": "245710e971a030f42e08f4912863805570f23d39", "shasum": "" }, "require": { @@ -1723,7 +1710,7 @@ }, "require-dev": { "phpspec/phpspec": "^6.0", - "phpunit/phpunit": "^8.0 || ^9.0 <9.3" + "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", "extra": { @@ -1761,20 +1748,24 @@ "spy", "stub" ], - "time": "2020-09-29T09:10:42+00:00" + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/1.12.2" + }, + "time": "2020-12-19T10:15:11+00:00" }, { "name": "phpstan/extension-installer", - "version": "1.0.5", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/phpstan/extension-installer.git", - "reference": "5c2da3846819f951385cb6a25d3277051481c48a" + "reference": "66c7adc9dfa38b6b5838a9fb728b68a7d8348051" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/5c2da3846819f951385cb6a25d3277051481c48a", - "reference": "5c2da3846819f951385cb6a25d3277051481c48a", + "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/66c7adc9dfa38b6b5838a9fb728b68a7d8348051", + "reference": "66c7adc9dfa38b6b5838a9fb728b68a7d8348051", "shasum": "" }, "require": { @@ -1784,13 +1775,9 @@ }, "require-dev": { "composer/composer": "^1.8", - "consistence/coding-standard": "^3.8", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "ergebnis/composer-normalize": "^2.0.2", - "phing/phing": "^2.16", + "phing/phing": "^2.16.3", "php-parallel-lint/php-parallel-lint": "^1.2.0", - "phpstan/phpstan-strict-rules": "^0.11", - "slevomat/coding-standard": "^5.0.4" + "phpstan/phpstan-strict-rules": "^0.11 || ^0.12" }, "type": "composer-plugin", "extra": { @@ -1806,7 +1793,11 @@ "MIT" ], "description": "Composer plugin for automatic installation of PHPStan extensions", - "time": "2020-08-30T12:06:42+00:00" + "support": { + "issues": "https://github.com/phpstan/extension-installer/issues", + "source": "https://github.com/phpstan/extension-installer/tree/1.1.0" + }, + "time": "2020-12-13T13:06:13+00:00" }, { "name": "phpstan/phpdoc-parser", @@ -1855,20 +1846,24 @@ "MIT" ], "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/master" + }, "time": "2020-08-03T20:32:43+00:00" }, { "name": "phpstan/phpstan", - "version": "0.12.57", + "version": "0.12.64", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "f9909d1d0c44b4cbaf72babcf80e8f14d6fdd55b" + "reference": "23eb1cb7ae125f45f1d0e48051bcf67a9a9b08aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f9909d1d0c44b4cbaf72babcf80e8f14d6fdd55b", - "reference": "f9909d1d0c44b4cbaf72babcf80e8f14d6fdd55b", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/23eb1cb7ae125f45f1d0e48051bcf67a9a9b08aa", + "reference": "23eb1cb7ae125f45f1d0e48051bcf67a9a9b08aa", "shasum": "" }, "require": { @@ -1897,6 +1892,10 @@ "MIT" ], "description": "PHPStan - PHP Static Analysis Tool", + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/0.12.64" + }, "funding": [ { "url": "https://github.com/ondrejmirtes", @@ -1911,35 +1910,31 @@ "type": "tidelift" } ], - "time": "2020-11-21T12:53:28+00:00" + "time": "2020-12-21T11:59:02+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", - "version": "0.12.5", + "version": "0.12.6", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-deprecation-rules.git", - "reference": "bfabc6a1b4617fbcbff43f03a4c04eae9bafae21" + "reference": "46dbd43c2db973d2876d6653e53f5c2cc3a01fbb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/bfabc6a1b4617fbcbff43f03a4c04eae9bafae21", - "reference": "bfabc6a1b4617fbcbff43f03a4c04eae9bafae21", + "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/46dbd43c2db973d2876d6653e53f5c2cc3a01fbb", + "reference": "46dbd43c2db973d2876d6653e53f5c2cc3a01fbb", "shasum": "" }, "require": { "php": "^7.1 || ^8.0", - "phpstan/phpstan": "^0.12.26" + "phpstan/phpstan": "^0.12.60" }, "require-dev": { - "consistence/coding-standard": "^3.0.1", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "ergebnis/composer-normalize": "^2.0.2", - "jakub-onderka/php-parallel-lint": "^1.0", - "phing/phing": "^2.16.0", + "phing/phing": "^2.16.3", + "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/phpstan-phpunit": "^0.12", - "phpunit/phpunit": "^7.0", - "slevomat/coding-standard": "^4.5.2" + "phpunit/phpunit": "^7.5.20" }, "type": "phpstan-extension", "extra": { @@ -1962,39 +1957,38 @@ "MIT" ], "description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.", - "time": "2020-07-21T14:52:30+00:00" + "support": { + "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues", + "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/0.12.6" + }, + "time": "2020-12-13T10:20:54+00:00" }, { "name": "phpstan/phpstan-phpunit", - "version": "0.12.16", + "version": "0.12.17", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-phpunit.git", - "reference": "1dd916d181b0539dea5cd37e91546afb8b107e17" + "reference": "432575b41cf2d4f44e460234acaf56119ed97d36" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/1dd916d181b0539dea5cd37e91546afb8b107e17", - "reference": "1dd916d181b0539dea5cd37e91546afb8b107e17", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/432575b41cf2d4f44e460234acaf56119ed97d36", + "reference": "432575b41cf2d4f44e460234acaf56119ed97d36", "shasum": "" }, "require": { "php": "^7.1 || ^8.0", - "phpstan/phpstan": "^0.12.33" + "phpstan/phpstan": "^0.12.60" }, "conflict": { "phpunit/phpunit": "<7.0" }, "require-dev": { - "consistence/coding-standard": "^3.5", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "ergebnis/composer-normalize": "^2.0.2", - "jakub-onderka/php-parallel-lint": "^1.0", - "phing/phing": "^2.16.0", - "phpstan/phpstan-strict-rules": "^0.12", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", - "satooshi/php-coveralls": "^1.0", - "slevomat/coding-standard": "^4.7.2" + "phing/phing": "^2.16.3", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-strict-rules": "^0.12.6", + "phpunit/phpunit": "^7.5.20" }, "type": "phpstan-extension", "extra": { @@ -2018,35 +2012,35 @@ "MIT" ], "description": "PHPUnit extensions and rules for PHPStan", - "time": "2020-08-05T13:28:50+00:00" + "support": { + "issues": "https://github.com/phpstan/phpstan-phpunit/issues", + "source": "https://github.com/phpstan/phpstan-phpunit/tree/0.12.17" + }, + "time": "2020-12-13T12:12:51+00:00" }, { "name": "phpstan/phpstan-strict-rules", - "version": "0.12.5", + "version": "0.12.7", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "334898a32217e4605e0f9cfa3d3fc3101bda26be" + "reference": "03807e3410747ec18cd5f05a6342384adcc423bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/334898a32217e4605e0f9cfa3d3fc3101bda26be", - "reference": "334898a32217e4605e0f9cfa3d3fc3101bda26be", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/03807e3410747ec18cd5f05a6342384adcc423bf", + "reference": "03807e3410747ec18cd5f05a6342384adcc423bf", "shasum": "" }, "require": { "php": "^7.1 || ^8.0", - "phpstan/phpstan": "^0.12.33" + "phpstan/phpstan": "^0.12.60" }, "require-dev": { - "consistence/coding-standard": "^3.0.1", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "ergebnis/composer-normalize": "^2.0.2", - "jakub-onderka/php-parallel-lint": "^1.0", - "phing/phing": "^2.16.0", - "phpstan/phpstan-phpunit": "^0.12", - "phpunit/phpunit": "^7.0", - "slevomat/coding-standard": "^4.5.2" + "phing/phing": "^2.16.3", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-phpunit": "^0.12.16", + "phpunit/phpunit": "^7.5.20" }, "type": "phpstan-extension", "extra": { @@ -2069,20 +2063,24 @@ "MIT" ], "description": "Extra strict and opinionated rules for PHPStan", - "time": "2020-08-30T15:42:06+00:00" + "support": { + "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/0.12.7" + }, + "time": "2020-12-13T13:27:14+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.3", + "version": "9.2.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "6b20e2055f7c29b56cb3870b3de7cc463d7add41" + "reference": "f3e026641cc91909d421802dd3ac7827ebfd97e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6b20e2055f7c29b56cb3870b3de7cc463d7add41", - "reference": "6b20e2055f7c29b56cb3870b3de7cc463d7add41", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f3e026641cc91909d421802dd3ac7827ebfd97e1", + "reference": "f3e026641cc91909d421802dd3ac7827ebfd97e1", "shasum": "" }, "require": { @@ -2096,7 +2094,7 @@ "sebastian/code-unit-reverse-lookup": "^2.0.2", "sebastian/complexity": "^2.0", "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0", + "sebastian/lines-of-code": "^1.0.3", "sebastian/version": "^3.0.1", "theseer/tokenizer": "^1.2.0" }, @@ -2136,13 +2134,17 @@ "testing", "xunit" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.5" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-10-30T10:46:41+00:00" + "time": "2020-11-28T06:44:49+00:00" }, { "name": "phpunit/php-file-iterator", @@ -2192,6 +2194,10 @@ "filesystem", "iterator" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2251,6 +2257,10 @@ "keywords": [ "process" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2306,6 +2316,10 @@ "keywords": [ "template" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2361,6 +2375,10 @@ "keywords": [ "timer" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2371,16 +2389,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.4.3", + "version": "9.5.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "9fa359ff5ddaa5eb2be2bedb08a6a5787a5807ab" + "reference": "8e16c225d57c3d6808014df6b1dd7598d0a5bbbe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9fa359ff5ddaa5eb2be2bedb08a6a5787a5807ab", - "reference": "9fa359ff5ddaa5eb2be2bedb08a6a5787a5807ab", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8e16c225d57c3d6808014df6b1dd7598d0a5bbbe", + "reference": "8e16c225d57c3d6808014df6b1dd7598d0a5bbbe", "shasum": "" }, "require": { @@ -2396,7 +2414,7 @@ "phar-io/version": "^3.0.2", "php": ">=7.3", "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2", + "phpunit/php-code-coverage": "^9.2.3", "phpunit/php-file-iterator": "^3.0.5", "phpunit/php-invoker": "^3.1.1", "phpunit/php-text-template": "^2.0.3", @@ -2427,7 +2445,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.4-dev" + "dev-master": "9.5-dev" } }, "autoload": { @@ -2456,6 +2474,10 @@ "testing", "xunit" ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.0" + }, "funding": [ { "url": "https://phpunit.de/donate.html", @@ -2466,7 +2488,7 @@ "type": "github" } ], - "time": "2020-11-10T12:53:30+00:00" + "time": "2020-12-04T05:05:53+00:00" }, { "name": "psr/container", @@ -2515,6 +2537,10 @@ "container-interop", "psr" ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/master" + }, "time": "2017-02-14T16:28:37+00:00" }, { @@ -2562,8 +2588,69 @@ "psr", "psr-3" ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.3" + }, "time": "2020-03-23T09:12:05+00:00" }, + { + "name": "sanmai/later", + "version": "0.1.2", + "source": { + "type": "git", + "url": "https://github.com/sanmai/later.git", + "reference": "9b659fecef2030193fd02402955bc39629d5606f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sanmai/later/zipball/9b659fecef2030193fd02402955bc39629d5606f", + "reference": "9b659fecef2030193fd02402955bc39629d5606f", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.13", + "infection/infection": ">=0.10.5", + "phan/phan": ">=2", + "php-coveralls/php-coveralls": "^2.0", + "phpstan/phpstan": ">=0.10", + "phpunit/phpunit": ">=7.4", + "vimeo/psalm": ">=2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Later\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Alexey Kopytko", + "email": "alexey@kopytko.com" + } + ], + "description": "Later: deferred wrapper object", + "support": { + "issues": "https://github.com/sanmai/later/issues", + "source": "https://github.com/sanmai/later/tree/0.1.2" + }, + "funding": [ + { + "url": "https://github.com/sanmai", + "type": "github" + } + ], + "time": "2021-01-02T10:26:44+00:00" + }, { "name": "sanmai/pipeline", "version": "v5.1.0", @@ -2617,6 +2704,10 @@ } ], "description": "General-purpose collections pipeline", + "support": { + "issues": "https://github.com/sanmai/pipeline/issues", + "source": "https://github.com/sanmai/pipeline/tree/v5.1.0" + }, "funding": [ { "url": "https://github.com/sanmai", @@ -2669,6 +2760,10 @@ ], "description": "Library for parsing CLI options", "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2721,6 +2816,10 @@ ], "description": "Collection of value objects that represent the PHP code units", "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2772,6 +2871,10 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2842,6 +2945,10 @@ "compare", "equality" ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2895,6 +3002,10 @@ ], "description": "Library for calculating the complexity of PHP code units", "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2957,6 +3068,10 @@ "unidiff", "unified diff" ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3016,6 +3131,10 @@ "environment", "hhvm" ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3089,6 +3208,10 @@ "export", "exporter" ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3149,6 +3272,10 @@ "keywords": [ "global state" ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.2" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3159,16 +3286,16 @@ }, { "name": "sebastian/lines-of-code", - "version": "1.0.2", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "acf76492a65401babcf5283296fa510782783a7a" + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/acf76492a65401babcf5283296fa510782783a7a", - "reference": "acf76492a65401babcf5283296fa510782783a7a", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", "shasum": "" }, "require": { @@ -3202,13 +3329,17 @@ ], "description": "Library for counting the lines of code in PHP source code", "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-10-26T17:03:56+00:00" + "time": "2020-11-28T06:42:11+00:00" }, { "name": "sebastian/object-enumerator", @@ -3255,6 +3386,10 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3306,6 +3441,10 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3365,6 +3504,10 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3416,6 +3559,10 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3468,6 +3615,10 @@ ], "description": "Collection of value objects that represent the types of the PHP type system", "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/2.3.1" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3517,6 +3668,10 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3572,6 +3727,10 @@ "parser", "validator" ], + "support": { + "issues": "https://github.com/Seldaek/jsonlint/issues", + "source": "https://github.com/Seldaek/jsonlint/tree/1.8.3" + }, "funding": [ { "url": "https://github.com/Seldaek", @@ -3629,6 +3788,10 @@ "MIT" ], "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", + "support": { + "issues": "https://github.com/slevomat/coding-standard/issues", + "source": "https://github.com/slevomat/coding-standard/tree/6.4.1" + }, "funding": [ { "url": "https://github.com/kukulich", @@ -3690,20 +3853,25 @@ "phpcs", "standards" ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, "time": "2020-10-23T02:01:07+00:00" }, { "name": "symfony/console", - "version": "v5.1.8", + "version": "v5.2.2", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "e0b2c29c0fa6a69089209bbe8fcff4df2a313d0e" + "reference": "d62ec79478b55036f65e2602e282822b8eaaff0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/e0b2c29c0fa6a69089209bbe8fcff4df2a313d0e", - "reference": "e0b2c29c0fa6a69089209bbe8fcff4df2a313d0e", + "url": "https://api.github.com/repos/symfony/console/zipball/d62ec79478b55036f65e2602e282822b8eaaff0a", + "reference": "d62ec79478b55036f65e2602e282822b8eaaff0a", "shasum": "" }, "require": { @@ -3762,74 +3930,17 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Console Component", + "description": "Eases the creation of beautiful and testable command line interfaces", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } + "keywords": [ + "cli", + "command line", + "console", + "terminal" ], - "time": "2020-10-24T12:01:57+00:00" - }, - { - "name": "symfony/debug", - "version": "v4.4.16", - "source": { - "type": "git", - "url": "https://github.com/symfony/debug.git", - "reference": "c87adf3fc1cd0bf4758316a3a150d50a8f957ef4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/c87adf3fc1cd0bf4758316a3a150d50a8f957ef4", - "reference": "c87adf3fc1cd0bf4758316a3a150d50a8f957ef4", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "psr/log": "~1.0", - "symfony/polyfill-php80": "^1.15" - }, - "conflict": { - "symfony/http-kernel": "<3.4" - }, - "require-dev": { - "symfony/http-kernel": "^3.4|^4.0|^5.0" + "support": { + "source": "https://github.com/symfony/console/tree/v5.2.2" }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Debug\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Debug Component", - "homepage": "https://symfony.com", "funding": [ { "url": "https://symfony.com/sponsor", @@ -3844,7 +3955,7 @@ "type": "tidelift" } ], - "time": "2020-10-24T11:50:19+00:00" + "time": "2021-01-27T10:15:41+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3894,6 +4005,9 @@ ], "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/master" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3912,16 +4026,16 @@ }, { "name": "symfony/filesystem", - "version": "v5.1.8", + "version": "v5.2.2", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "df08650ea7aee2d925380069c131a66124d79177" + "reference": "262d033b57c73e8b59cd6e68a45c528318b15038" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/df08650ea7aee2d925380069c131a66124d79177", - "reference": "df08650ea7aee2d925380069c131a66124d79177", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/262d033b57c73e8b59cd6e68a45c528318b15038", + "reference": "262d033b57c73e8b59cd6e68a45c528318b15038", "shasum": "" }, "require": { @@ -3951,8 +4065,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Filesystem Component", + "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v5.2.2" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3967,20 +4084,20 @@ "type": "tidelift" } ], - "time": "2020-10-24T12:01:57+00:00" + "time": "2021-01-27T10:01:46+00:00" }, { "name": "symfony/finder", - "version": "v5.1.8", + "version": "v5.2.2", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "e70eb5a69c2ff61ea135a13d2266e8914a67b3a0" + "reference": "196f45723b5e618bf0e23b97e96d11652696ea9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/e70eb5a69c2ff61ea135a13d2266e8914a67b3a0", - "reference": "e70eb5a69c2ff61ea135a13d2266e8914a67b3a0", + "url": "https://api.github.com/repos/symfony/finder/zipball/196f45723b5e618bf0e23b97e96d11652696ea9e", + "reference": "196f45723b5e618bf0e23b97e96d11652696ea9e", "shasum": "" }, "require": { @@ -4009,8 +4126,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Finder Component", + "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v5.2.2" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4025,25 +4145,26 @@ "type": "tidelift" } ], - "time": "2020-10-24T12:01:57+00:00" + "time": "2021-01-27T10:01:46+00:00" }, { "name": "symfony/options-resolver", - "version": "v5.1.8", + "version": "v5.2.1", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "c6a02905e4ffc7a1498e8ee019db2b477cd1cc02" + "reference": "87a2a4a766244e796dd9cb9d6f58c123358cd986" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/c6a02905e4ffc7a1498e8ee019db2b477cd1cc02", - "reference": "c6a02905e4ffc7a1498e8ee019db2b477cd1cc02", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/87a2a4a766244e796dd9cb9d6f58c123358cd986", + "reference": "87a2a4a766244e796dd9cb9d6f58c123358cd986", "shasum": "" }, "require": { "php": ">=7.2.5", "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-php73": "~1.0", "symfony/polyfill-php80": "^1.15" }, "type": "library", @@ -4076,6 +4197,9 @@ "configuration", "options" ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v5.2.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4090,20 +4214,20 @@ "type": "tidelift" } ], - "time": "2020-10-24T12:01:57+00:00" + "time": "2020-10-24T12:08:07+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.20.0", + "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41" + "reference": "c6c942b1ac76c82448322025e084cadc56048b4e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f4ba089a5b6366e453971d3aad5fe8e897b37f41", - "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e", + "reference": "c6c942b1ac76c82448322025e084cadc56048b4e", "shasum": "" }, "require": { @@ -4115,7 +4239,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.22-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4152,6 +4276,9 @@ "polyfill", "portable" ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4166,20 +4293,20 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-01-07T16:49:33+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.20.0", + "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c" + "reference": "267a9adeb8ecb8071040a740930e077cdfb987af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c", - "reference": "c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/267a9adeb8ecb8071040a740930e077cdfb987af", + "reference": "267a9adeb8ecb8071040a740930e077cdfb987af", "shasum": "" }, "require": { @@ -4191,7 +4318,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.22-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4230,6 +4357,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.22.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4244,20 +4374,20 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-01-07T16:49:33+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.20.0", + "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "727d1096295d807c309fb01a851577302394c897" + "reference": "6e971c891537eb617a00bb07a43d182a6915faba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/727d1096295d807c309fb01a851577302394c897", - "reference": "727d1096295d807c309fb01a851577302394c897", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/6e971c891537eb617a00bb07a43d182a6915faba", + "reference": "6e971c891537eb617a00bb07a43d182a6915faba", "shasum": "" }, "require": { @@ -4269,7 +4399,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.22-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4311,6 +4441,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4325,20 +4458,20 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-01-07T17:09:11+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.20.0", + "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "39d483bdf39be819deabf04ec872eb0b2410b531" + "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/39d483bdf39be819deabf04ec872eb0b2410b531", - "reference": "39d483bdf39be819deabf04ec872eb0b2410b531", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f377a3dd1fde44d37b9831d68dc8dea3ffd28e13", + "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13", "shasum": "" }, "require": { @@ -4350,7 +4483,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.22-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4388,6 +4521,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4402,20 +4538,20 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-01-07T16:49:33+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.20.0", + "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "8ff431c517be11c78c48a39a66d37431e26a6bed" + "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/8ff431c517be11c78c48a39a66d37431e26a6bed", - "reference": "8ff431c517be11c78c48a39a66d37431e26a6bed", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", + "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", "shasum": "" }, "require": { @@ -4424,7 +4560,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.22-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4464,6 +4600,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.22.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4478,20 +4617,20 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-01-07T16:49:33+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.20.0", + "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "e70aa8b064c5b72d3df2abd5ab1e90464ad009de" + "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/e70aa8b064c5b72d3df2abd5ab1e90464ad009de", - "reference": "e70aa8b064c5b72d3df2abd5ab1e90464ad009de", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91", + "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91", "shasum": "" }, "require": { @@ -4500,7 +4639,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.22-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4544,6 +4683,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4558,20 +4700,20 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-01-07T16:49:33+00:00" }, { "name": "symfony/process", - "version": "v5.1.8", + "version": "v5.2.2", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "f00872c3f6804150d6a0f73b4151daab96248101" + "reference": "313a38f09c77fbcdc1d223e57d368cea76a2fd2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/f00872c3f6804150d6a0f73b4151daab96248101", - "reference": "f00872c3f6804150d6a0f73b4151daab96248101", + "url": "https://api.github.com/repos/symfony/process/zipball/313a38f09c77fbcdc1d223e57d368cea76a2fd2f", + "reference": "313a38f09c77fbcdc1d223e57d368cea76a2fd2f", "shasum": "" }, "require": { @@ -4601,8 +4743,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Process Component", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v5.2.2" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4617,7 +4762,7 @@ "type": "tidelift" } ], - "time": "2020-10-24T12:01:57+00:00" + "time": "2021-01-27T10:15:41+00:00" }, { "name": "symfony/service-contracts", @@ -4679,6 +4824,9 @@ "interoperability", "standards" ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/master" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4697,16 +4845,16 @@ }, { "name": "symfony/string", - "version": "v5.1.8", + "version": "v5.2.2", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "a97573e960303db71be0dd8fda9be3bca5e0feea" + "reference": "c95468897f408dd0aca2ff582074423dd0455122" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/a97573e960303db71be0dd8fda9be3bca5e0feea", - "reference": "a97573e960303db71be0dd8fda9be3bca5e0feea", + "url": "https://api.github.com/repos/symfony/string/zipball/c95468897f408dd0aca2ff582074423dd0455122", + "reference": "c95468897f408dd0aca2ff582074423dd0455122", "shasum": "" }, "require": { @@ -4749,7 +4897,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony String component", + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", "homepage": "https://symfony.com", "keywords": [ "grapheme", @@ -4759,6 +4907,9 @@ "utf-8", "utf8" ], + "support": { + "source": "https://github.com/symfony/string/tree/v5.2.2" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4773,7 +4924,7 @@ "type": "tidelift" } ], - "time": "2020-10-24T12:01:57+00:00" + "time": "2021-01-25T15:14:59+00:00" }, { "name": "thecodingmachine/safe", @@ -4908,6 +5059,10 @@ "MIT" ], "description": "PHP core functions that throw exceptions instead of returning FALSE on error", + "support": { + "issues": "https://github.com/thecodingmachine/safe/issues", + "source": "https://github.com/thecodingmachine/safe/tree/v1.3.3" + }, "time": "2020-10-28T17:51:34+00:00" }, { @@ -4948,6 +5103,10 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/master" + }, "funding": [ { "url": "https://github.com/theseer", @@ -4961,12 +5120,12 @@ "version": "1.9.1", "source": { "type": "git", - "url": "https://github.com/webmozart/assert.git", + "url": "https://github.com/webmozarts/assert.git", "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", "shasum": "" }, @@ -5003,6 +5162,10 @@ "check", "validate" ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.9.1" + }, "time": "2020-07-08T17:02:28+00:00" }, { @@ -5049,19 +5212,28 @@ } ], "description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.", + "support": { + "issues": "https://github.com/webmozart/path-util/issues", + "source": "https://github.com/webmozart/path-util/tree/2.3.0" + }, "time": "2015-12-17T08:42:14+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "phpbench/phpbench": 15 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { "php": "^7.4 || ^8.0", + "ext-hash": "*", + "ext-json": "*", "ext-mbstring": "*", - "ext-openssl": "*" + "ext-openssl": "*", + "ext-sodium": "*" }, "platform-dev": [], - "plugin-api-version": "1.1.0" + "plugin-api-version": "2.0.0" } diff --git a/docs/configuration.md b/docs/configuration.md index 3afcf67a..3f40f7f9 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -82,7 +82,7 @@ use Lcobucci\JWT\Signer\Key\LocalFileReference; use Lcobucci\JWT\Signer\Key\InMemory; $configuration = Configuration::forAsymmetricSigner( - // You may use RSA or ECDSA and all their variations (256, 384, and 512) + // You may use RSA or ECDSA and all their variations (256, 384, and 512) and EdDSA over Curve25519 new Signer\Rsa\Sha256(), LocalFileReference::file(__DIR__ . '/my-private-key.pem'), InMemory::base64Encoded('mBC5v1sOKVvbdEitdSBenu59nfNfhwkedkJVNabosTw=') diff --git a/docs/extending-the-library.md b/docs/extending-the-library.md index b19a6a89..f0018484 100644 --- a/docs/extending-the-library.md +++ b/docs/extending-the-library.md @@ -51,20 +51,17 @@ You may customise and even create your own formatters: ```php use Lcobucci\JWT\ClaimsFormatter; use Lcobucci\JWT\Configuration; -use Lcobucci\JWT\Token\RegisteredClaims; +use Serializable; -final class UnixTimestampDates implements ClaimsFormatter +final class ClaimSerializer implements ClaimsFormatter { /** @inheritdoc */ public function formatClaims(array $claims): array { - foreach (RegisteredClaims::DATE_CLAIMS as $claim) { - if (! array_key_exists($claim, $claims)) { - continue; + foreach ($claims as $claim => $claimValue) { + if ($claimValue instanceof Serializable) { + $claims[$claim] = $claimValue->serialize(); } - - assert($claims[$claim] instanceof DateTimeImmutable); - $claims[$claim] = $claims[$claim]->getTimestamp(); } return $claims; @@ -74,7 +71,7 @@ final class UnixTimestampDates implements ClaimsFormatter $config = $container->get(Configuration::class); assert($config instanceof Configuration); -$config->builder(new UnixTimestampDates()); +$config->builder(new ClaimSerializer()); ``` The class `Lcobucci\JWT\Encoding\ChainedFormatter` allows for users to combine multiple formatters. @@ -172,6 +169,7 @@ To create your own implementation of constraint you must implement the `Lcobucci ```php use Lcobucci\JWT\Token; +use Lcobucci\JWT\UnencryptedToken; use Lcobucci\JWT\Validation\Constraint; use Lcobucci\JWT\Validation\ConstraintViolation; @@ -179,7 +177,7 @@ final class SubjectMustBeAValidUser implements Constraint { public function assert(Token $token): void { - if (! $token instanceof Token\Plain) { + if (! $token instanceof UnencryptedToken) { throw new ConstraintViolation('You should pass a plain token'); } diff --git a/docs/installation.md b/docs/installation.md index c756b000..012eb6c3 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -16,7 +16,7 @@ composer require lcobucci/jwt In order to be able to use the classes provided by this library you're also required to include [Composer]'s autoloader in your application: ```php -require 'vendor/bin/autoload.php'; +require 'vendor/autoload.php'; ``` !!! Tip diff --git a/docs/parsing-tokens.md b/docs/parsing-tokens.md index 55cafe30..0388d461 100644 --- a/docs/parsing-tokens.md +++ b/docs/parsing-tokens.md @@ -8,7 +8,7 @@ To parse a token you must create a new parser (easier when using the [configurat ```php use Lcobucci\JWT\Configuration; -use Lcobucci\JWT\Token\Plain; +use Lcobucci\JWT\UnencryptedToken; $config = $container->get(Configuration::class); assert($config instanceof Configuration); @@ -19,7 +19,7 @@ $token = $config->parser()->parse( . '2gSBz9EOsQRN9I-3iSxJoFt7NtgV6Rm0IL6a8CAwl3Q' ); -assert($token instanceof Plain); +assert($token instanceof UnencryptedToken); $token->headers(); // Retrieves the token headers $token->claims(); // Retrieves the token claims diff --git a/docs/validating-tokens.md b/docs/validating-tokens.md index 69c5b86b..c0d9c29d 100644 --- a/docs/validating-tokens.md +++ b/docs/validating-tokens.md @@ -15,14 +15,14 @@ This method goes through every single constraint in the set, groups all the viol ```php use Lcobucci\JWT\Configuration; -use Lcobucci\JWT\Token\Plain; +use Lcobucci\JWT\UnencryptedToken; use Lcobucci\JWT\Validation\RequiredConstraintsViolated; -$token = $container->get(Configuration::class); +$config = $container->get(Configuration::class); assert($config instanceof Configuration); $token = $config->parser()->parse('...'); -assert($token instanceof Plain); +assert($token instanceof UnencryptedToken); $constraints = $config->validationConstraints(); @@ -39,17 +39,17 @@ try { !!! Warning You **MUST** provide at least one constraint, otherwise `\Lcobucci\JWT\Validation\NoConstraintsGiven` exception will be thrown. -The difference here is that we'll always a get a `boolean` result and stop in the very first violation: +The difference here is that we'll always get a `boolean` result and stop in the very first violation: ```php use Lcobucci\JWT\Configuration; -use Lcobucci\JWT\Token\Plain; +use Lcobucci\JWT\UnencryptedToken; -$token = $container->get(Configuration::class); +$config = $container->get(Configuration::class); assert($config instanceof Configuration); $token = $config->parser()->parse('...'); -assert($token instanceof Plain); +assert($token instanceof UnencryptedToken); $constraints = $config->validationConstraints(); @@ -67,6 +67,7 @@ This library provides the following constraints: * `Lcobucci\JWT\Validation\Constraint\PermittedFor`: verifies if the claim `aud` contains the expected value * `Lcobucci\JWT\Validation\Constraint\RelatedTo`: verifies if the claim `sub` matches the expected value * `Lcobucci\JWT\Validation\Constraint\SignedWith`: verifies if the token was signed with the expected signer and key -* `Lcobucci\JWT\Validation\Constraint\ValidAt`: verifies the claims `iat`, `nbf`, and `exp` (supports leeway configuration) +* `Lcobucci\JWT\Validation\Constraint\StrictValidAt`: verifies presence and validity of the claims `iat`, `nbf`, and `exp` (supports leeway configuration) +* `Lcobucci\JWT\Validation\Constraint\LooseValidAt`: verifies the claims `iat`, `nbf`, and `exp`, when present (supports leeway configuration) You may also create your [own validation constraints](extending-the-library.md#validation-constraints). diff --git a/infection.json.dist b/infection.json.dist index ef8e6172..318f231c 100644 --- a/infection.json.dist +++ b/infection.json.dist @@ -8,9 +8,48 @@ }, "mutators": { "@default": true, - "@function_signature": true + "@function_signature": true, + "CastInt": { + "ignore": [ + "Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter::octetLength", + "Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter::readAsn1Integer" + ] + }, + "GreaterThan": { + "ignore": [ + "Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter::toAsn1", + "Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter::preparePositiveInteger", + "Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter::retrievePositiveInteger" + ] + }, + "InstanceOf_": { + "ignore": [ + "Lcobucci\\JWT\\Signer\\OpenSSL::freeKey" + ] + }, + "LessThanOrEqualTo": { + "ignore": [ + "Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter::preparePositiveInteger" + ] + }, + "LogicalNot": { + "ignoreSourceCodeByRegex": [ + "if \\(!function_exists\\('sodium_\\w+'\\)\\) \\{" + ] + }, + "MBString": { + "ignore": [ + "Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter" + ] + }, + "MethodCallRemoval": { + "ignore": [ + "Lcobucci\\JWT\\Signer\\OpenSSL::createSignature", + "Lcobucci\\JWT\\Signer\\OpenSSL::verifySignature" + ] + } }, - "minMsi": 93.28, - "minCoveredMsi": 93.28, + "minMsi": 100, + "minCoveredMsi": 100, "testFrameworkOptions": "--testsuite=unit" } diff --git a/src/Encoding/ChainedFormatter.php b/src/Encoding/ChainedFormatter.php index 8eed7e79..1670138b 100644 --- a/src/Encoding/ChainedFormatter.php +++ b/src/Encoding/ChainedFormatter.php @@ -20,6 +20,11 @@ public static function default(): self return new self(new UnifyAudience(), new MicrosecondBasedDateConversion()); } + public static function withUnixTimestampDates(): self + { + return new self(new UnifyAudience(), new UnixTimestampDates()); + } + /** @inheritdoc */ public function formatClaims(array $claims): array { diff --git a/src/Encoding/JoseEncoder.php b/src/Encoding/JoseEncoder.php index 9cb49c42..597d15f9 100644 --- a/src/Encoding/JoseEncoder.php +++ b/src/Encoding/JoseEncoder.php @@ -6,14 +6,10 @@ use JsonException; use Lcobucci\JWT\Decoder; use Lcobucci\JWT\Encoder; +use Lcobucci\JWT\SodiumBase64Polyfill; -use function base64_decode; -use function base64_encode; -use function is_string; use function json_decode; use function json_encode; -use function rtrim; -use function strtr; use const JSON_THROW_ON_ERROR; use const JSON_UNESCAPED_SLASHES; @@ -48,18 +44,17 @@ public function jsonDecode(string $json) public function base64UrlEncode(string $data): string { - return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); + return SodiumBase64Polyfill::bin2base64( + $data, + SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING + ); } public function base64UrlDecode(string $data): string { - // Padding isn't added back because it isn't strictly necessary for decoding with PHP - $decodedContent = base64_decode(strtr($data, '-_', '+/'), true); - - if (! is_string($decodedContent)) { - throw CannotDecodeContent::invalidBase64String(); - } - - return $decodedContent; + return SodiumBase64Polyfill::base642bin( + $data, + SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING + ); } } diff --git a/src/Encoding/UnixTimestampDates.php b/src/Encoding/UnixTimestampDates.php new file mode 100644 index 00000000..7d58389b --- /dev/null +++ b/src/Encoding/UnixTimestampDates.php @@ -0,0 +1,32 @@ +convertDate($claims[$claim]); + } + + return $claims; + } + + private function convertDate(DateTimeImmutable $date): int + { + return $date->getTimestamp(); + } +} diff --git a/src/Signer/Eddsa.php b/src/Signer/Eddsa.php new file mode 100644 index 00000000..03ad88bd --- /dev/null +++ b/src/Signer/Eddsa.php @@ -0,0 +1,36 @@ +contents()); + } catch (SodiumException $sodiumException) { + throw new InvalidKeyProvided($sodiumException->getMessage(), 0, $sodiumException); + } + } + + public function verify(string $expected, string $payload, Key $key): bool + { + try { + return sodium_crypto_sign_verify_detached($expected, $payload, $key->contents()); + } catch (SodiumException $sodiumException) { + throw new InvalidKeyProvided($sodiumException->getMessage(), 0, $sodiumException); + } + } +} diff --git a/src/Signer/Key/InMemory.php b/src/Signer/Key/InMemory.php index 2dbe9916..1a4df0f5 100644 --- a/src/Signer/Key/InMemory.php +++ b/src/Signer/Key/InMemory.php @@ -3,13 +3,12 @@ namespace Lcobucci\JWT\Signer\Key; -use Lcobucci\JWT\Encoding\CannotDecodeContent; use Lcobucci\JWT\Signer\Key; +use Lcobucci\JWT\SodiumBase64Polyfill; use SplFileObject; use Throwable; use function assert; -use function base64_decode; use function is_string; final class InMemory implements Key @@ -35,11 +34,10 @@ public static function plainText(string $contents, string $passphrase = ''): sel public static function base64Encoded(string $contents, string $passphrase = ''): self { - $decoded = base64_decode($contents, true); - - if ($decoded === false) { - throw CannotDecodeContent::invalidBase64String(); - } + $decoded = SodiumBase64Polyfill::base642bin( + $contents, + SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_ORIGINAL + ); return new self($decoded, $passphrase); } diff --git a/src/SodiumBase64Polyfill.php b/src/SodiumBase64Polyfill.php new file mode 100644 index 00000000..a00068db --- /dev/null +++ b/src/SodiumBase64Polyfill.php @@ -0,0 +1,88 @@ +clock = $clock; + $this->leeway = $this->guardLeeway($leeway); + } + + private function guardLeeway(?DateInterval $leeway): DateInterval + { + if ($leeway === null) { + return new DateInterval('PT0S'); + } + + if ($leeway->invert === 1) { + throw LeewayCannotBeNegative::create(); + } + + return $leeway; + } + + public function assert(Token $token): void + { + $now = $this->clock->now(); + + $this->assertIssueTime($token, $now->add($this->leeway)); + $this->assertMinimumTime($token, $now->add($this->leeway)); + $this->assertExpiration($token, $now->sub($this->leeway)); + } + + /** @throws ConstraintViolation */ + private function assertExpiration(Token $token, DateTimeInterface $now): void + { + if ($token->isExpired($now)) { + throw new ConstraintViolation('The token is expired'); + } + } + + /** @throws ConstraintViolation */ + private function assertMinimumTime(Token $token, DateTimeInterface $now): void + { + if (! $token->isMinimumTimeBefore($now)) { + throw new ConstraintViolation('The token cannot be used yet'); + } + } + + /** @throws ConstraintViolation */ + private function assertIssueTime(Token $token, DateTimeInterface $now): void + { + if (! $token->hasBeenIssuedBefore($now)) { + throw new ConstraintViolation('The token was issued in the future'); + } + } +} diff --git a/src/Validation/Constraint/SignedWith.php b/src/Validation/Constraint/SignedWith.php index 56acb2ef..1bc1e905 100644 --- a/src/Validation/Constraint/SignedWith.php +++ b/src/Validation/Constraint/SignedWith.php @@ -5,6 +5,7 @@ use Lcobucci\JWT\Signer; use Lcobucci\JWT\Token; +use Lcobucci\JWT\UnencryptedToken; use Lcobucci\JWT\Validation\Constraint; use Lcobucci\JWT\Validation\ConstraintViolation; @@ -21,7 +22,7 @@ public function __construct(Signer $signer, Signer\Key $key) public function assert(Token $token): void { - if (! $token instanceof Token\Plain) { + if (! $token instanceof UnencryptedToken) { throw new ConstraintViolation('You should pass a plain token'); } diff --git a/src/Validation/Constraint/StrictValidAt.php b/src/Validation/Constraint/StrictValidAt.php new file mode 100644 index 00000000..bb221092 --- /dev/null +++ b/src/Validation/Constraint/StrictValidAt.php @@ -0,0 +1,86 @@ +clock = $clock; + $this->leeway = $this->guardLeeway($leeway); + } + + private function guardLeeway(?DateInterval $leeway): DateInterval + { + if ($leeway === null) { + return new DateInterval('PT0S'); + } + + if ($leeway->invert === 1) { + throw LeewayCannotBeNegative::create(); + } + + return $leeway; + } + + public function assert(Token $token): void + { + if (! $token instanceof UnencryptedToken) { + throw new ConstraintViolation('You should pass a plain token'); + } + + $now = $this->clock->now(); + + $this->assertIssueTime($token, $now->add($this->leeway)); + $this->assertMinimumTime($token, $now->add($this->leeway)); + $this->assertExpiration($token, $now->sub($this->leeway)); + } + + /** @throws ConstraintViolation */ + private function assertExpiration(UnencryptedToken $token, DateTimeInterface $now): void + { + if (! $token->claims()->has(Token\RegisteredClaims::EXPIRATION_TIME)) { + throw new ConstraintViolation('"Expiration Time" claim missing'); + } + + if ($token->isExpired($now)) { + throw new ConstraintViolation('The token is expired'); + } + } + + /** @throws ConstraintViolation */ + private function assertMinimumTime(UnencryptedToken $token, DateTimeInterface $now): void + { + if (! $token->claims()->has(Token\RegisteredClaims::NOT_BEFORE)) { + throw new ConstraintViolation('"Not Before" claim missing'); + } + + if (! $token->isMinimumTimeBefore($now)) { + throw new ConstraintViolation('The token cannot be used yet'); + } + } + + /** @throws ConstraintViolation */ + private function assertIssueTime(UnencryptedToken $token, DateTimeInterface $now): void + { + if (! $token->claims()->has(Token\RegisteredClaims::ISSUED_AT)) { + throw new ConstraintViolation('"Issued At" claim missing'); + } + + if (! $token->hasBeenIssuedBefore($now)) { + throw new ConstraintViolation('The token was issued in the future'); + } + } +} diff --git a/src/Validation/Constraint/ValidAt.php b/src/Validation/Constraint/ValidAt.php index 61866864..db7f6baf 100644 --- a/src/Validation/Constraint/ValidAt.php +++ b/src/Validation/Constraint/ValidAt.php @@ -4,66 +4,22 @@ namespace Lcobucci\JWT\Validation\Constraint; use DateInterval; -use DateTimeInterface; use Lcobucci\Clock\Clock; use Lcobucci\JWT\Token; use Lcobucci\JWT\Validation\Constraint; -use Lcobucci\JWT\Validation\ConstraintViolation; +/** @deprecated Use \Lcobucci\JWT\Validation\Constraint\LooseValidAt */ final class ValidAt implements Constraint { - private Clock $clock; - private DateInterval $leeway; + private LooseValidAt $constraint; public function __construct(Clock $clock, ?DateInterval $leeway = null) { - $this->clock = $clock; - $this->leeway = $this->guardLeeway($leeway); - } - - private function guardLeeway(?DateInterval $leeway): DateInterval - { - if ($leeway === null) { - return new DateInterval('PT0S'); - } - - if ($leeway->invert === 1) { - throw LeewayCannotBeNegative::create(); - } - - return $leeway; + $this->constraint = new LooseValidAt($clock, $leeway); } public function assert(Token $token): void { - $now = $this->clock->now(); - - $this->assertIssueTime($token, $now->add($this->leeway)); - $this->assertMinimumTime($token, $now->add($this->leeway)); - $this->assertExpiration($token, $now->sub($this->leeway)); - } - - /** @throws ConstraintViolation */ - private function assertExpiration(Token $token, DateTimeInterface $now): void - { - if ($token->isExpired($now)) { - throw new ConstraintViolation('The token is expired'); - } - } - - /** @throws ConstraintViolation */ - private function assertMinimumTime(Token $token, DateTimeInterface $now): void - { - if (! $token->isMinimumTimeBefore($now)) { - throw new ConstraintViolation('The token cannot be used yet'); - } - } - - /** @throws ConstraintViolation */ - private function assertIssueTime(Token $token, DateTimeInterface $now): void - { - if (! $token->hasBeenIssuedBefore($now)) { - throw new ConstraintViolation('The token was issued in the future'); - } + $this->constraint->assert($token); } } diff --git a/test/_keys/Keys.php b/test/_keys/Keys.php index 87737230..93db8224 100644 --- a/test/_keys/Keys.php +++ b/test/_keys/Keys.php @@ -14,6 +14,9 @@ trait Keys /** @var array */ protected static array $ecdsaKeys; + /** @var array */ + protected static array $eddsaKeys; + /** @beforeClass */ public static function createRsaKeys(): void { @@ -39,4 +42,16 @@ public static function createEcdsaKeys(): void 'public2_ec512' => LocalFileReference::file(__DIR__ . '/ecdsa/public2_ec512.key'), ]; } + + /** @beforeClass */ + public static function createEddsaKeys(): void + { + static::$eddsaKeys = [ + 'private' => Key\InMemory::base64Encoded( + 'K3NWT0XqaH+4jgi42gQmHnFE+HTPVhFYi3u4DFJ3OpRHRMt/aGRBoKD/Pt5H/iYgGCla7Q04CdjOUpLSrjZhtg==' + ), + 'public1' => Key\InMemory::base64Encoded('R0TLf2hkQaCg/z7eR/4mIBgpWu0NOAnYzlKS0q42YbY='), + 'public2' => Key\InMemory::base64Encoded('8uLLzCdMrIWcOrAxS/fteYyJhWIGH+wav2fNz8NZhvI='), + ]; + } } diff --git a/test/functional/ES512TokenTest.php b/test/functional/ES512TokenTest.php index 2d921218..2b6a7004 100644 --- a/test/functional/ES512TokenTest.php +++ b/test/functional/ES512TokenTest.php @@ -35,6 +35,7 @@ * @covers \Lcobucci\JWT\Signer\Ecdsa\Sha512 * @covers \Lcobucci\JWT\Signer\InvalidKeyProvided * @covers \Lcobucci\JWT\Signer\OpenSSL + * @covers \Lcobucci\JWT\SodiumBase64Polyfill * @covers \Lcobucci\JWT\Validation\Validator * @covers \Lcobucci\JWT\Validation\RequiredConstraintsViolated * @covers \Lcobucci\JWT\Validation\Constraint\SignedWith diff --git a/test/functional/EcdsaTokenTest.php b/test/functional/EcdsaTokenTest.php index f8794e6c..d3fa0695 100644 --- a/test/functional/EcdsaTokenTest.php +++ b/test/functional/EcdsaTokenTest.php @@ -37,6 +37,7 @@ * @covers \Lcobucci\JWT\Signer\Ecdsa\Sha512 * @covers \Lcobucci\JWT\Signer\InvalidKeyProvided * @covers \Lcobucci\JWT\Signer\OpenSSL + * @covers \Lcobucci\JWT\SodiumBase64Polyfill * @covers \Lcobucci\JWT\Validation\Validator * @covers \Lcobucci\JWT\Validation\Constraint\SignedWith * @covers \Lcobucci\JWT\Validation\Validator diff --git a/test/functional/EddsaTokenTest.php b/test/functional/EddsaTokenTest.php new file mode 100644 index 00000000..54b77af1 --- /dev/null +++ b/test/functional/EddsaTokenTest.php @@ -0,0 +1,142 @@ +config = Configuration::forAsymmetricSigner( + new Eddsa(), + static::$eddsaKeys['private'], + static::$eddsaKeys['public1'] + ); + } + + /** @test */ + public function builderShouldRaiseExceptionWhenKeyIsInvalid(): void + { + $builder = $this->config->builder(); + + $this->expectException(InvalidKeyProvided::class); + $this->expectExceptionMessage('SODIUM_CRYPTO_SIGN_SECRETKEYBYTES'); + + $builder->identifiedBy('1') + ->permittedFor('http://client.abc.com') + ->issuedBy('http://api.abc.com') + ->withClaim('user', ['name' => 'testing', 'email' => 'testing@abc.com']) + ->getToken($this->config->signer(), InMemory::plainText('testing')); + } + + /** @test */ + public function builderCanGenerateAToken(): Token + { + $user = ['name' => 'testing', 'email' => 'testing@abc.com']; + $builder = $this->config->builder(); + + $token = $builder->identifiedBy('1') + ->permittedFor('http://client.abc.com') + ->permittedFor('http://client2.abc.com') + ->issuedBy('http://api.abc.com') + ->withClaim('user', $user) + ->withHeader('jki', '1234') + ->getToken($this->config->signer(), $this->config->signingKey()); + + self::assertEquals('1234', $token->headers()->get('jki')); + self::assertEquals('http://api.abc.com', $token->claims()->get(Token\RegisteredClaims::ISSUER)); + self::assertEquals($user, $token->claims()->get('user')); + + self::assertEquals( + ['http://client.abc.com', 'http://client2.abc.com'], + $token->claims()->get(Token\RegisteredClaims::AUDIENCE) + ); + + return $token; + } + + /** + * @test + * @depends builderCanGenerateAToken + */ + public function parserCanReadAToken(Token $generated): void + { + $read = $this->config->parser()->parse($generated->toString()); + assert($read instanceof Token\Plain); + + self::assertEquals($generated, $read); + self::assertEquals('testing', $read->claims()->get('user')['name']); + } + + /** + * @test + * @depends builderCanGenerateAToken + */ + public function signatureAssertionShouldRaiseExceptionWhenKeyIsNotRight(Token $token): void + { + $this->expectException(RequiredConstraintsViolated::class); + $this->expectExceptionMessage('The token violates some mandatory constraints'); + + $this->config->validator()->assert( + $token, + new SignedWith( + $this->config->signer(), + self::$eddsaKeys['public2'] + ) + ); + } + + /** + * @test + * @depends builderCanGenerateAToken + */ + public function signatureValidationShouldSucceedWhenKeyIsRight(Token $token): void + { + $constraint = new SignedWith( + $this->config->signer(), + $this->config->verificationKey() + ); + + self::assertTrue($this->config->validator()->validate($token, $constraint)); + } +} diff --git a/test/functional/HmacTokenTest.php b/test/functional/HmacTokenTest.php index d87771a6..f85cc975 100644 --- a/test/functional/HmacTokenTest.php +++ b/test/functional/HmacTokenTest.php @@ -29,6 +29,7 @@ * @covers \Lcobucci\JWT\Signer\Hmac * @covers \Lcobucci\JWT\Signer\Hmac\Sha256 * @covers \Lcobucci\JWT\Signer\Hmac\Sha512 + * @covers \Lcobucci\JWT\SodiumBase64Polyfill * @covers \Lcobucci\JWT\Validation\Validator * @covers \Lcobucci\JWT\Validation\RequiredConstraintsViolated * @covers \Lcobucci\JWT\Validation\Constraint\SignedWith diff --git a/test/functional/MaliciousTamperingPreventionTest.php b/test/functional/MaliciousTamperingPreventionTest.php index 6a46ec64..598901ef 100644 --- a/test/functional/MaliciousTamperingPreventionTest.php +++ b/test/functional/MaliciousTamperingPreventionTest.php @@ -60,6 +60,7 @@ public function createConfiguration(): void * @covers \Lcobucci\JWT\Signer\Hmac * @covers \Lcobucci\JWT\Signer\Hmac\Sha256 * @covers \Lcobucci\JWT\Signer\Hmac\Sha512 + * @covers \Lcobucci\JWT\SodiumBase64Polyfill * @covers \Lcobucci\JWT\Validation\Constraint\SignedWith * @covers \Lcobucci\JWT\Validation\Validator */ diff --git a/test/functional/RsaTokenTest.php b/test/functional/RsaTokenTest.php index 5ecb0844..ec0c709f 100644 --- a/test/functional/RsaTokenTest.php +++ b/test/functional/RsaTokenTest.php @@ -34,6 +34,7 @@ * @covers \Lcobucci\JWT\Signer\Rsa * @covers \Lcobucci\JWT\Signer\Rsa\Sha256 * @covers \Lcobucci\JWT\Signer\Rsa\Sha512 + * @covers \Lcobucci\JWT\SodiumBase64Polyfill * @covers \Lcobucci\JWT\Validation\Validator * @covers \Lcobucci\JWT\Validation\RequiredConstraintsViolated * @covers \Lcobucci\JWT\Validation\Constraint\SignedWith diff --git a/test/functional/UnsignedTokenTest.php b/test/functional/UnsignedTokenTest.php index f661bfd1..23bfa5d7 100644 --- a/test/functional/UnsignedTokenTest.php +++ b/test/functional/UnsignedTokenTest.php @@ -10,8 +10,8 @@ use Lcobucci\JWT\Validation\Constraint; use Lcobucci\JWT\Validation\Constraint\IdentifiedBy; use Lcobucci\JWT\Validation\Constraint\IssuedBy; +use Lcobucci\JWT\Validation\Constraint\LooseValidAt; use Lcobucci\JWT\Validation\Constraint\PermittedFor; -use Lcobucci\JWT\Validation\Constraint\ValidAt; use Lcobucci\JWT\Validation\ConstraintViolation; use Lcobucci\JWT\Validation\RequiredConstraintsViolated; use PHPUnit\Framework\TestCase; @@ -31,12 +31,13 @@ * @covers \Lcobucci\JWT\Token\Signature * @covers \Lcobucci\JWT\Signer\None * @covers \Lcobucci\JWT\Signer\Key\InMemory + * @covers \Lcobucci\JWT\SodiumBase64Polyfill * @covers \Lcobucci\JWT\Validation\RequiredConstraintsViolated * @covers \Lcobucci\JWT\Validation\Validator * @covers \Lcobucci\JWT\Validation\Constraint\IssuedBy * @covers \Lcobucci\JWT\Validation\Constraint\PermittedFor * @covers \Lcobucci\JWT\Validation\Constraint\IdentifiedBy - * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt + * @covers \Lcobucci\JWT\Validation\Constraint\LooseValidAt */ class UnsignedTokenTest extends TestCase { @@ -99,7 +100,7 @@ public function tokenValidationShouldPassWhenEverythingIsFine(Token $generated): new IdentifiedBy('1'), new PermittedFor('http://client.abc.com'), new IssuedBy('http://issuer.abc.com', 'http://api.abc.com'), - new ValidAt($clock), + new LooseValidAt($clock), ]; self::assertTrue($this->config->validator()->validate($generated, ...$constraints)); diff --git a/test/performance/EddsaBench.php b/test/performance/EddsaBench.php new file mode 100644 index 00000000..3d94cdee --- /dev/null +++ b/test/performance/EddsaBench.php @@ -0,0 +1,30 @@ +formatClaims($claims); + + self::assertSame('test', $formatted[RegisteredClaims::AUDIENCE]); + self::assertSame(1487285080, $formatted[RegisteredClaims::EXPIRATION_TIME]); } } diff --git a/test/unit/Encoding/JoseEncoderTest.php b/test/unit/Encoding/JoseEncoderTest.php index 12db5a36..568a6f07 100644 --- a/test/unit/Encoding/JoseEncoderTest.php +++ b/test/unit/Encoding/JoseEncoderTest.php @@ -103,6 +103,8 @@ public function jsonDecodeMustRaiseExceptionWhenAnErrorHasOccurred(): void * @test * * @covers ::base64UrlEncode + * + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::bin2base64() */ public function base64UrlEncodeMustReturnAUrlSafeBase64(): void { @@ -119,6 +121,8 @@ public function base64UrlEncodeMustReturnAUrlSafeBase64(): void * @test * * @covers ::base64UrlEncode + * + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::bin2base64() */ public function base64UrlEncodeMustEncodeBilboMessageProperly(): void { @@ -139,6 +143,8 @@ public function base64UrlEncodeMustEncodeBilboMessageProperly(): void * @test * * @covers ::base64UrlDecode + * + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::base642bin() */ public function base64UrlDecodeMustRaiseExceptionWhenInvalidBase64CharsAreUsed(): void { @@ -148,13 +154,15 @@ public function base64UrlDecodeMustRaiseExceptionWhenInvalidBase64CharsAreUsed() $this->expectExceptionCode(0); $this->expectExceptionMessage('Error while decoding from Base64Url, invalid base64 characters detected'); - $decoder->base64UrlDecode('áááááá'); + $decoder->base64UrlDecode('ááá'); } /** * @test * * @covers ::base64UrlDecode + * + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::base642bin() */ public function base64UrlDecodeMustReturnTheRightData(): void { @@ -170,6 +178,8 @@ public function base64UrlDecodeMustReturnTheRightData(): void * @test * * @covers ::base64UrlDecode + * + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::base642bin() */ public function base64UrlDecodeMustDecodeBilboMessageProperly(): void { diff --git a/test/unit/Encoding/UnixTimestampDatesTest.php b/test/unit/Encoding/UnixTimestampDatesTest.php new file mode 100644 index 00000000..f5c4fb8a --- /dev/null +++ b/test/unit/Encoding/UnixTimestampDatesTest.php @@ -0,0 +1,68 @@ + $issuedAt, + RegisteredClaims::NOT_BEFORE => $notBefore, + RegisteredClaims::EXPIRATION_TIME => $expiration, + 'testing' => 'test', + ]; + + $formatter = new UnixTimestampDates(); + $formatted = $formatter->formatClaims($claims); + + self::assertSame(1487285080, $formatted[RegisteredClaims::ISSUED_AT]); + self::assertSame(1487285080, $formatted[RegisteredClaims::NOT_BEFORE]); + self::assertSame(1487285080, $formatted[RegisteredClaims::EXPIRATION_TIME]); + self::assertSame('test', $formatted['testing']); // this should remain untouched + } + + /** + * @test + * + * @covers ::formatClaims + * @covers ::convertDate + */ + public function notAllDateClaimsNeedToBeConfigured(): void + { + $issuedAt = new DateTimeImmutable('@1487285080'); + $expiration = DateTimeImmutable::createFromFormat('U.u', '1487285080.123456'); + + $claims = [ + RegisteredClaims::ISSUED_AT => $issuedAt, + RegisteredClaims::EXPIRATION_TIME => $expiration, + 'testing' => 'test', + ]; + + $formatter = new UnixTimestampDates(); + $formatted = $formatter->formatClaims($claims); + + self::assertSame(1487285080, $formatted[RegisteredClaims::ISSUED_AT]); + self::assertSame(1487285080, $formatted[RegisteredClaims::EXPIRATION_TIME]); + self::assertSame('test', $formatted['testing']); // this should remain untouched + } +} diff --git a/test/unit/Signer/EddsaTest.php b/test/unit/Signer/EddsaTest.php new file mode 100644 index 00000000..b70def84 --- /dev/null +++ b/test/unit/Signer/EddsaTest.php @@ -0,0 +1,163 @@ +getSigner()->algorithmId()); + } + + /** + * @test + * + * @covers ::sign + * + * @uses \Lcobucci\JWT\Signer\Key\InMemory + */ + public function signShouldReturnAValidEddsaSignature(): void + { + $payload = 'testing'; + + $signer = $this->getSigner(); + $signature = $signer->sign($payload, self::$eddsaKeys['private']); + + $publicKey = self::$eddsaKeys['public1']->contents(); + + self::assertTrue(sodium_crypto_sign_verify_detached($signature, $payload, $publicKey)); + } + + /** + * @test + * + * @covers ::sign + * + * @uses \Lcobucci\JWT\Signer\Key\InMemory + */ + public function signShouldRaiseAnExceptionWhenKeyIsInvalid(): void + { + $signer = $this->getSigner(); + + $this->expectException(InvalidKeyProvided::class); + $this->expectExceptionCode(0); + $this->expectExceptionMessage('SODIUM_CRYPTO_SIGN_SECRETKEYBYTES'); + + $signer->sign('testing', InMemory::plainText('tooshort')); + } + + /** + * @test + * + * @covers ::verify + * + * @uses \Lcobucci\JWT\Signer\Key\InMemory + */ + public function verifyShouldReturnTrueWhenSignatureIsValid(): void + { + $payload = 'testing'; + $signature = sodium_crypto_sign_detached($payload, self::$eddsaKeys['private']->contents()); + + $signer = $this->getSigner(); + + self::assertTrue($signer->verify($signature, $payload, self::$eddsaKeys['public1'])); + } + + /** + * @test + * + * @covers ::verify + * + * @uses \Lcobucci\JWT\Signer\Key\InMemory + */ + public function verifyShouldRaiseAnExceptionWhenKeyIsNotParseable(): void + { + $signer = $this->getSigner(); + + $this->expectException(InvalidKeyProvided::class); + $this->expectExceptionCode(0); + $this->expectExceptionMessage('SODIUM_CRYPTO_SIGN_BYTES'); + + $signer->verify('testing', 'testing', InMemory::plainText('blablabla')); + } + + /** + * @see https://tools.ietf.org/html/rfc8037#appendix-A.4 + * + * @test + * + * @covers ::sign + * + * @uses \Lcobucci\JWT\Encoding\JoseEncoder + * @uses \Lcobucci\JWT\Signer\Key\InMemory + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::base642bin() + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::bin2base64() + */ + public function signatureOfRfcExample(): void + { + $signer = $this->getSigner(); + $encoder = new JoseEncoder(); + + $key = InMemory::plainText( + $encoder->base64UrlDecode('nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A') + . $encoder->base64UrlDecode('11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo') + ); + $payload = $encoder->base64UrlEncode('{"alg":"EdDSA"}') + . '.' + . $encoder->base64UrlEncode('Example of Ed25519 signing'); + $signature = $signer->sign($payload, $key); + + self::assertSame('eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc', $payload); + self::assertSame( + 'hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg', + $encoder->base64UrlEncode($signature) + ); + } + + /** + * @see https://tools.ietf.org/html/rfc8037#appendix-A.5 + * + * @test + * + * @covers ::verify + * + * @uses \Lcobucci\JWT\Encoding\JoseEncoder + * @uses \Lcobucci\JWT\Signer\Key\InMemory + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::base642bin() + */ + public function verificationOfRfcExample(): void + { + $signer = $this->getSigner(); + $encoder = new JoseEncoder(); + + $key = InMemory::plainText($encoder->base64UrlDecode('11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo')); + $payload = 'eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc'; + $signature = $encoder->base64UrlDecode( + 'hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg' + ); + + self::assertTrue($signer->verify($signature, $payload, $key)); + } + + private function getSigner(): Eddsa + { + return new Eddsa(); + } +} diff --git a/test/unit/Signer/Key/InMemoryTest.php b/test/unit/Signer/Key/InMemoryTest.php index 9039aa17..2a9716e9 100644 --- a/test/unit/Signer/Key/InMemoryTest.php +++ b/test/unit/Signer/Key/InMemoryTest.php @@ -27,6 +27,8 @@ public function configureRootDir(): void * * @covers ::base64Encoded * @covers \Lcobucci\JWT\Encoding\CannotDecodeContent + * + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::base642bin() */ public function exceptionShouldBeRaisedWhenInvalidBase64CharsAreUsed(): void { @@ -42,6 +44,8 @@ public function exceptionShouldBeRaisedWhenInvalidBase64CharsAreUsed(): void * @covers ::base64Encoded * @covers ::__construct * @covers ::contents + * + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::base642bin() */ public function base64EncodedShouldDecodeKeyContents(): void { diff --git a/test/unit/Signer/RsaTest.php b/test/unit/Signer/RsaTest.php index 9909b11c..b966cc7d 100644 --- a/test/unit/Signer/RsaTest.php +++ b/test/unit/Signer/RsaTest.php @@ -67,7 +67,7 @@ public function signShouldRaiseAnExceptionWhenKeyIsInvalid(): void $signer = $this->getSigner(); $this->expectException(CannotSignPayload::class); - $this->expectExceptionMessage('There was an error while creating the signature'); + $this->expectExceptionMessage('There was an error while creating the signature: error:'); $signer->sign('testing', InMemory::plainText($key)); } @@ -86,7 +86,7 @@ public function signShouldRaiseAnExceptionWhenKeyIsNotParseable(): void $signer = $this->getSigner(); $this->expectException(InvalidKeyProvided::class); - $this->expectExceptionMessage('It was not possible to parse your key'); + $this->expectExceptionMessage('It was not possible to parse your key, reason: error:'); $signer->sign('testing', InMemory::plainText('blablabla')); } diff --git a/test/unit/SodiumBase64PolyfillTest.php b/test/unit/SodiumBase64PolyfillTest.php new file mode 100644 index 00000000..be091948 --- /dev/null +++ b/test/unit/SodiumBase64PolyfillTest.php @@ -0,0 +1,139 @@ +testString = sodium_base642bin('I+o2tVq8ynY=', SODIUM_BASE64_VARIANT_ORIGINAL, ''); + } + + /** + * @test + * + * @coversNothing + */ + public function constantsMatchExtensionOnes(): void + { + self::assertSame( + SODIUM_BASE64_VARIANT_ORIGINAL, + SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_ORIGINAL + ); + self::assertSame( + SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING, + SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING + ); + self::assertSame( + SODIUM_BASE64_VARIANT_URLSAFE, + SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_URLSAFE + ); + self::assertSame( + SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING, + SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING + ); + } + + /** + * @test + * @dataProvider provideVariants + * + * @covers ::bin2base64 + * @covers ::bin2base64Fallback + */ + public function bin2base64(int $variant): void + { + $expected = sodium_bin2base64($this->testString, $variant); + + self::assertSame( + $expected, + SodiumBase64Polyfill::bin2base64($this->testString, $variant) + ); + + self::assertSame( + $expected, + SodiumBase64Polyfill::bin2base64Fallback($this->testString, $variant) + ); + } + + /** + * @test + * @dataProvider provideVariants + * + * @covers ::base642bin + * @covers ::base642binFallback + */ + public function base642binFallback(int $variant): void + { + self::assertSame( + $this->testString, + SodiumBase64Polyfill::base642bin( + sodium_bin2base64($this->testString, $variant), + $variant + ) + ); + + self::assertSame( + $this->testString, + SodiumBase64Polyfill::base642binFallback( + sodium_bin2base64($this->testString, $variant), + $variant + ) + ); + } + + /** @return int[][] */ + public function provideVariants(): array + { + return [ + [SODIUM_BASE64_VARIANT_ORIGINAL], + [SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING], + [SODIUM_BASE64_VARIANT_URLSAFE], + [SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING], + ]; + } + + /** + * @test + * + * @covers ::base642bin + * + * @uses \Lcobucci\JWT\Encoding\CannotDecodeContent::invalidBase64String() + */ + public function sodiumBase642BinRaisesExceptionOnInvalidBase64(): void + { + $this->expectException(CannotDecodeContent::class); + + SodiumBase64Polyfill::base642bin('ááá', SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING); + } + + /** + * @test + * + * @covers ::base642binFallback + * + * @uses \Lcobucci\JWT\Encoding\CannotDecodeContent::invalidBase64String() + */ + public function fallbackBase642BinRaisesExceptionOnInvalidBase64(): void + { + $this->expectException(CannotDecodeContent::class); + + SodiumBase64Polyfill::base642binFallback('ááá', SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING); + } +} diff --git a/test/unit/Token/BuilderTest.php b/test/unit/Token/BuilderTest.php index 31ba8b9f..2fbeedc3 100644 --- a/test/unit/Token/BuilderTest.php +++ b/test/unit/Token/BuilderTest.php @@ -89,7 +89,12 @@ public function getTokenShouldReturnACompletelyConfigureToken(): void $this->encoder->expects(self::exactly(3)) ->method('base64UrlEncode') - ->willReturnOnConsecutiveCalls('1', '2', '3'); + ->willReturnArgument(0); + + $this->signer->expects(self::once()) + ->method('sign') + ->with('1.2') + ->willReturn('3'); $builder = new Builder($this->encoder, new MicrosecondBasedDateConversion()); $token = $builder->identifiedBy('123456') diff --git a/test/unit/Validation/Constraint/LooseValidAtTest.php b/test/unit/Validation/Constraint/LooseValidAtTest.php new file mode 100644 index 00000000..b741a69b --- /dev/null +++ b/test/unit/Validation/Constraint/LooseValidAtTest.php @@ -0,0 +1,40 @@ +buildToken(); + $constraint = $this->buildValidAtConstraint($this->clock); + + $constraint->assert($token); + $this->addToAssertionCount(1); + } +} diff --git a/test/unit/Validation/Constraint/StrictValidAtTest.php b/test/unit/Validation/Constraint/StrictValidAtTest.php new file mode 100644 index 00000000..fb41bd28 --- /dev/null +++ b/test/unit/Validation/Constraint/StrictValidAtTest.php @@ -0,0 +1,117 @@ +expectException(ConstraintViolation::class); + $this->expectExceptionMessage('You should pass a plain token'); + + $constraint = $this->buildValidAtConstraint($this->clock); + $constraint->assert($this->createMock(Token::class)); + } + + /** + * @test + * + * @covers ::__construct + * @covers ::assert + * @covers ::guardLeeway + * @covers ::assertIssueTime + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + public function assertShouldRaiseExceptionWhenIatClaimIsMissing(): void + { + $this->expectException(ConstraintViolation::class); + $this->expectExceptionMessage('"Issued At" claim missing'); + + $constraint = $this->buildValidAtConstraint($this->clock); + $constraint->assert($this->buildToken()); + } + + /** + * @test + * + * @covers ::__construct + * @covers ::assert + * @covers ::guardLeeway + * @covers ::assertIssueTime + * @covers ::assertMinimumTime + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + public function assertShouldRaiseExceptionWhenNbfClaimIsMissing(): void + { + $this->expectException(ConstraintViolation::class); + $this->expectExceptionMessage('"Not Before" claim missing'); + + $now = $this->clock->now(); + $claims = [ + RegisteredClaims::ISSUED_AT => $now->modify('-5 seconds'), + ]; + + $constraint = $this->buildValidAtConstraint($this->clock); + $constraint->assert($this->buildToken($claims)); + } + + /** + * @test + * + * @covers ::__construct + * @covers ::assert + * @covers ::guardLeeway + * @covers ::assertIssueTime + * @covers ::assertMinimumTime + * @covers ::assertExpiration + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + public function assertShouldRaiseExceptionWhenExpClaimIsMissing(): void + { + $this->expectException(ConstraintViolation::class); + $this->expectExceptionMessage('"Expiration Time" claim missing'); + + $now = $this->clock->now(); + $claims = [ + RegisteredClaims::ISSUED_AT => $now->modify('-5 seconds'), + RegisteredClaims::NOT_BEFORE => $now->modify('-5 seconds'), + ]; + + $constraint = $this->buildValidAtConstraint($this->clock); + $constraint->assert($this->buildToken($claims)); + } +} diff --git a/test/unit/Validation/Constraint/ValidAtTest.php b/test/unit/Validation/Constraint/ValidAtTest.php index bddda7e3..321a4840 100644 --- a/test/unit/Validation/Constraint/ValidAtTest.php +++ b/test/unit/Validation/Constraint/ValidAtTest.php @@ -5,7 +5,6 @@ use DateInterval; use DateTimeImmutable; -use Lcobucci\Clock\Clock; use Lcobucci\Clock\FrozenClock; use Lcobucci\JWT\Token\RegisteredClaims; use Lcobucci\JWT\Validation\ConstraintViolation; @@ -13,214 +12,37 @@ /** @coversDefaultClass \Lcobucci\JWT\Validation\Constraint\ValidAt */ final class ValidAtTest extends ConstraintTestCase { - private Clock $clock; - - /** @before */ - public function createDependencies(): void - { - $this->clock = new FrozenClock(new DateTimeImmutable()); - } - - /** - * @test - * - * @covers ::__construct - * @covers ::guardLeeway - * @covers \Lcobucci\JWT\Validation\Constraint\LeewayCannotBeNegative - */ - public function constructShouldRaiseExceptionOnNegativeLeeway(): void - { - $leeway = new DateInterval('PT30S'); - $leeway->invert = 1; - - $this->expectException(LeewayCannotBeNegative::class); - $this->expectExceptionMessage('Leeway cannot be negative'); - - new ValidAt($this->clock, $leeway); - } - /** * @test * * @covers ::__construct - * @covers ::guardLeeway * @covers ::assert - * @covers ::assertExpiration - * @covers ::assertIssueTime - * @covers ::assertMinimumTime * * @uses \Lcobucci\JWT\Token\DataSet * @uses \Lcobucci\JWT\Token\Plain * @uses \Lcobucci\JWT\Token\Signature + * @uses \Lcobucci\JWT\Validation\Constraint\LooseValidAt */ - public function assertShouldRaiseExceptionWhenTokenIsExpired(): void + public function assertIsAProxyToLooseValidAt(): void { - $now = $this->clock->now(); + $clock = new FrozenClock(new DateTimeImmutable()); $claims = [ - RegisteredClaims::ISSUED_AT => $now->modify('-20 seconds'), - RegisteredClaims::NOT_BEFORE => $now->modify('-10 seconds'), - RegisteredClaims::EXPIRATION_TIME => $now->modify('-10 seconds'), + RegisteredClaims::ISSUED_AT => $clock->now(), + RegisteredClaims::NOT_BEFORE => $clock->now()->modify('+5 seconds'), + RegisteredClaims::EXPIRATION_TIME => $clock->now()->modify('15 seconds'), ]; - $this->expectException(ConstraintViolation::class); - $this->expectExceptionMessage('The token is expired'); + // @phpstan-ignore-next-line + $constraint = new ValidAt($clock, new DateInterval('PT1S')); - $constraint = new ValidAt($this->clock); + $clock->setTo($clock->now()->modify('+4 seconds')); $constraint->assert($this->buildToken($claims)); - } - - /** - * @test - * - * @covers ::__construct - * @covers ::guardLeeway - * @covers ::assert - * @covers ::assertIssueTime - * @covers ::assertMinimumTime - * - * @uses \Lcobucci\JWT\Token\DataSet - * @uses \Lcobucci\JWT\Token\Plain - * @uses \Lcobucci\JWT\Token\Signature - */ - public function assertShouldRaiseExceptionWhenMinimumTimeIsNotMet(): void - { - $now = $this->clock->now(); - - $claims = [ - RegisteredClaims::ISSUED_AT => $now->modify('-20 seconds'), - RegisteredClaims::NOT_BEFORE => $now->modify('+40 seconds'), - RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'), - ]; - - $this->expectException(ConstraintViolation::class); - $this->expectExceptionMessage('The token cannot be used yet'); - - $constraint = new ValidAt($this->clock); - $constraint->assert($this->buildToken($claims)); - } - - /** - * @test - * - * @covers ::__construct - * @covers ::guardLeeway - * @covers ::assert - * @covers ::assertIssueTime - * - * @uses \Lcobucci\JWT\Token\DataSet - * @uses \Lcobucci\JWT\Token\Plain - * @uses \Lcobucci\JWT\Token\Signature - */ - public function assertShouldRaiseExceptionWhenTokenWasIssuedInTheFuture(): void - { - $now = $this->clock->now(); - - $claims = [ - RegisteredClaims::ISSUED_AT => $now->modify('+20 seconds'), - RegisteredClaims::NOT_BEFORE => $now->modify('+40 seconds'), - RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'), - ]; + $this->addToAssertionCount(1); $this->expectException(ConstraintViolation::class); - $this->expectExceptionMessage('The token was issued in the future'); - - $constraint = new ValidAt($this->clock); - $constraint->assert($this->buildToken($claims)); - } - - /** - * @test - * - * @covers ::__construct - * @covers ::guardLeeway - * @covers ::assert - * @covers ::assertExpiration - * @covers ::assertIssueTime - * @covers ::assertMinimumTime - * - * @uses \Lcobucci\JWT\Token\DataSet - * @uses \Lcobucci\JWT\Token\Plain - * @uses \Lcobucci\JWT\Token\Signature - */ - public function assertShouldNotRaiseExceptionWhenLeewayIsUsed(): void - { - $now = $this->clock->now(); - $claims = [ - RegisteredClaims::ISSUED_AT => $now->modify('+5 seconds'), - RegisteredClaims::NOT_BEFORE => $now->modify('+5 seconds'), - RegisteredClaims::EXPIRATION_TIME => $now->modify('-5 seconds'), - ]; - - $constraint = new ValidAt($this->clock, new DateInterval('PT6S')); + $clock->setTo($clock->now()->modify('+20 seconds')); $constraint->assert($this->buildToken($claims)); - - $this->addToAssertionCount(1); - } - - /** - * @test - * - * @covers ::__construct - * @covers ::guardLeeway - * @covers ::assert - * @covers ::assertExpiration - * @covers ::assertIssueTime - * @covers ::assertMinimumTime - * - * @uses \Lcobucci\JWT\Token\DataSet - * @uses \Lcobucci\JWT\Token\Plain - * @uses \Lcobucci\JWT\Token\Signature - */ - public function assertShouldNotRaiseExceptionWhenTokenIsUsedInTheRightMoment(): void - { - $constraint = new ValidAt($this->clock); - $now = $this->clock->now(); - - $token = $this->buildToken( - [ - RegisteredClaims::ISSUED_AT => $now->modify('-40 seconds'), - RegisteredClaims::NOT_BEFORE => $now->modify('-20 seconds'), - RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'), - ] - ); - - $constraint->assert($token); - $this->addToAssertionCount(1); - - $token = $this->buildToken( - [ - RegisteredClaims::ISSUED_AT => $now, - RegisteredClaims::NOT_BEFORE => $now, - RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'), - ] - ); - - $constraint->assert($token); - $this->addToAssertionCount(1); - } - - /** - * @test - * - * @covers ::__construct - * @covers ::guardLeeway - * @covers ::assert - * @covers ::assertExpiration - * @covers ::assertIssueTime - * @covers ::assertMinimumTime - * - * @uses \Lcobucci\JWT\Token\DataSet - * @uses \Lcobucci\JWT\Token\Plain - * @uses \Lcobucci\JWT\Token\Signature - */ - public function assertShouldNotRaiseExceptionWhenTokenDoesNotHaveTimeClaims(): void - { - $token = $this->buildToken(); - $constraint = new ValidAt($this->clock); - - $constraint->assert($token); - $this->addToAssertionCount(1); } } diff --git a/test/unit/Validation/Constraint/ValidAtTestCase.php b/test/unit/Validation/Constraint/ValidAtTestCase.php new file mode 100644 index 00000000..81053e36 --- /dev/null +++ b/test/unit/Validation/Constraint/ValidAtTestCase.php @@ -0,0 +1,205 @@ +clock = new FrozenClock(new DateTimeImmutable()); + } + + abstract protected function buildValidAtConstraint(Clock $clock, ?DateInterval $leeway = null): Constraint; + + /** + * @test + * + * @covers ::__construct + * @covers ::guardLeeway + * @covers \Lcobucci\JWT\Validation\Constraint\LeewayCannotBeNegative + */ + final public function constructShouldRaiseExceptionOnNegativeLeeway(): void + { + $leeway = new DateInterval('PT30S'); + $leeway->invert = 1; + + $this->expectException(LeewayCannotBeNegative::class); + $this->expectExceptionMessage('Leeway cannot be negative'); + + $this->buildValidAtConstraint($this->clock, $leeway); + } + + /** + * @test + * + * @covers ::__construct + * @covers ::guardLeeway + * @covers ::assert + * @covers ::assertExpiration + * @covers ::assertIssueTime + * @covers ::assertMinimumTime + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + final public function assertShouldRaiseExceptionWhenTokenIsExpired(): void + { + $now = $this->clock->now(); + + $claims = [ + RegisteredClaims::ISSUED_AT => $now->modify('-20 seconds'), + RegisteredClaims::NOT_BEFORE => $now->modify('-10 seconds'), + RegisteredClaims::EXPIRATION_TIME => $now->modify('-10 seconds'), + ]; + + $this->expectException(ConstraintViolation::class); + $this->expectExceptionMessage('The token is expired'); + + $constraint = $this->buildValidAtConstraint($this->clock); + $constraint->assert($this->buildToken($claims)); + } + + /** + * @test + * + * @covers ::__construct + * @covers ::guardLeeway + * @covers ::assert + * @covers ::assertIssueTime + * @covers ::assertMinimumTime + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + final public function assertShouldRaiseExceptionWhenMinimumTimeIsNotMet(): void + { + $now = $this->clock->now(); + + $claims = [ + RegisteredClaims::ISSUED_AT => $now->modify('-20 seconds'), + RegisteredClaims::NOT_BEFORE => $now->modify('+40 seconds'), + RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'), + ]; + + $this->expectException(ConstraintViolation::class); + $this->expectExceptionMessage('The token cannot be used yet'); + + $constraint = $this->buildValidAtConstraint($this->clock); + $constraint->assert($this->buildToken($claims)); + } + + /** + * @test + * + * @covers ::__construct + * @covers ::guardLeeway + * @covers ::assert + * @covers ::assertIssueTime + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + final public function assertShouldRaiseExceptionWhenTokenWasIssuedInTheFuture(): void + { + $now = $this->clock->now(); + + $claims = [ + RegisteredClaims::ISSUED_AT => $now->modify('+20 seconds'), + RegisteredClaims::NOT_BEFORE => $now->modify('+40 seconds'), + RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'), + ]; + + $this->expectException(ConstraintViolation::class); + $this->expectExceptionMessage('The token was issued in the future'); + + $constraint = $this->buildValidAtConstraint($this->clock); + $constraint->assert($this->buildToken($claims)); + } + + /** + * @test + * + * @covers ::__construct + * @covers ::guardLeeway + * @covers ::assert + * @covers ::assertExpiration + * @covers ::assertIssueTime + * @covers ::assertMinimumTime + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + final public function assertShouldNotRaiseExceptionWhenLeewayIsUsed(): void + { + $now = $this->clock->now(); + + $claims = [ + RegisteredClaims::ISSUED_AT => $now->modify('+5 seconds'), + RegisteredClaims::NOT_BEFORE => $now->modify('+5 seconds'), + RegisteredClaims::EXPIRATION_TIME => $now->modify('-5 seconds'), + ]; + + $constraint = $this->buildValidAtConstraint($this->clock, new DateInterval('PT6S')); + $constraint->assert($this->buildToken($claims)); + + $this->addToAssertionCount(1); + } + + /** + * @test + * + * @covers ::__construct + * @covers ::guardLeeway + * @covers ::assert + * @covers ::assertExpiration + * @covers ::assertIssueTime + * @covers ::assertMinimumTime + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + final public function assertShouldNotRaiseExceptionWhenTokenIsUsedInTheRightMoment(): void + { + $constraint = $this->buildValidAtConstraint($this->clock); + $now = $this->clock->now(); + + $token = $this->buildToken( + [ + RegisteredClaims::ISSUED_AT => $now->modify('-40 seconds'), + RegisteredClaims::NOT_BEFORE => $now->modify('-20 seconds'), + RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'), + ] + ); + + $constraint->assert($token); + $this->addToAssertionCount(1); + + $token = $this->buildToken( + [ + RegisteredClaims::ISSUED_AT => $now, + RegisteredClaims::NOT_BEFORE => $now, + RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'), + ] + ); + + $constraint->assert($token); + $this->addToAssertionCount(1); + } +}