diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66b6de3..ceebd0b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,34 +3,49 @@ name: CI on: [push] jobs: - build: - runs-on: [ubuntu-latest] + composer: + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - - name: Cache PHP dependencies - uses: actions/cache@v1 + - name: Cache Composer dependencies + uses: actions/cache@v3 with: - path: vendor - key: ${{ runner.OS }}-build-${{ hashFiles('**/composer.lock') }} + path: /tmp/composer-cache + key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }} - - uses: php-actions/composer@v1 + - name: Composer + uses: php-actions/composer@v6 + with: + php_version: '8.1' + + - name: Archive build + run: mkdir /tmp/github-actions/ && tar -cvf /tmp/github-actions/build.tar ./ - - name: Upload build for test runner - uses: actions/upload-artifact@v1 + - name: Upload build archive for test runners + uses: actions/upload-artifact@v3 with: name: build-artifact - path: ./ + path: /tmp/github-actions - test: - runs-on: [ubuntu-latest] - needs: [build] + phpunit: + runs-on: ubuntu-latest + needs: [ composer ] steps: - - uses: actions/download-artifact@v1 + - uses: actions/download-artifact@v3 with: name: build-artifact - path: ./ + path: /tmp/github-actions - - uses: php-actions/phpunit@v1 \ No newline at end of file + - name: Extract build archive + run: tar -xvf /tmp/github-actions/build.tar ./ + + - name: PHP Unit tests + uses: php-actions/phpunit@v3 + with: + php_version: '8.1' + php_extensions: xdebug + configuration: test/phpunit/phpunit.xml + bootstrap: vendor/autoload.php diff --git a/README.md b/README.md index 539097a..f8709fb 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ To use this repository, your application must be registered to obtain a client k Basic usage ----------- -With the following PHP code below, you can display a log in button that, when clicked, changes to a log out button and displays a greeting to the logged in user. +With the following PHP code below, you can display a login button that, when clicked, changes to a logout button and displays a greeting to the logged-in user. ```php isLoggedIn()) { +// Available methods: +// $auth->getId(); // a unique string in ULID format - use this to store in your database +// $auth->getEmail(); // the current email of the user (could change) +// $auth->getField($name); // any extra field your application is configured to take upon user signup + echo <<You are logged in as {$auth->getEmail()}

Log out

@@ -52,4 +57,13 @@ else {

Log in

HTML; } -``` \ No newline at end of file +``` + +Running unit tests +------------------ + +From the root directory, unit tests can be ran with the following command: + +```bash +vendor/bin/phpunit test/phpunit --coverage-clover test/phpunit/_coverage --whitelist src +``` diff --git a/composer.json b/composer.json index b360806..f9b4734 100644 --- a/composer.json +++ b/composer.json @@ -3,13 +3,16 @@ "description": "PHP client library to implement Authwave in your application", "require": { - "php": ">=7.4", + "php": ">=8.1", "ext-openssl": "*", + "phpgt/cipher": "^1.0", "phpgt/http": "1.*", - "phpgt/session": ">=1.1" + "phpgt/session": ">=1.1", + "phpgt/logger": "^1.0" }, "require-dev": { - "phpunit/phpunit": "8.*" + "phpunit/phpunit": "8.*", + "ext-sodium": "*" }, "autoload": { "psr-4": { @@ -21,4 +24,4 @@ "Authwave\\Test\\": "./test/phpunit" } } -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index 396bbef..825b0d5 100644 --- a/composer.lock +++ b/composer.lock @@ -1,35 +1,198 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f740eced9a0ec10945d216e31b732e26", + "content-hash": "f1ed6a0c38c839b72dfec0981f5aa820", "packages": [ { - "name": "phpgt/cookie", - "version": "v1.0.2", + "name": "phpgt/async", + "version": "v1.0.0", "source": { "type": "git", - "url": "https://github.com/PhpGt/Cookie.git", - "reference": "dd811b6f04becc7c1f8524d84ad02e4635f4a94e" + "url": "https://github.com/PhpGt/Async.git", + "reference": "3d2bdeca8cafc8573b416da3ac591d5d88f6dea9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhpGt/Cookie/zipball/dd811b6f04becc7c1f8524d84ad02e4635f4a94e", - "reference": "dd811b6f04becc7c1f8524d84ad02e4635f4a94e", + "url": "https://api.github.com/repos/PhpGt/Async/zipball/3d2bdeca8cafc8573b416da3ac591d5d88f6dea9", + "reference": "3d2bdeca8cafc8573b416da3ac591d5d88f6dea9", "shasum": "" }, "require": { - "php": ">=7.2" + "ext-dom": "*", + "php": ">=7.4", + "phpgt/promise": "^2.0" + }, + "require-dev": { + "phpstan/phpstan": "^v1.8", + "phpunit/phpunit": "^v9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Gt\\Async\\": "./src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Promise-based non-blocking operations.", + "support": { + "issues": "https://github.com/PhpGt/Async/issues", + "source": "https://github.com/PhpGt/Async/tree/v1.0.0" + }, + "funding": [ + { + "url": "https://github.com/phpgt", + "type": "github" + } + ], + "time": "2023-01-19T11:11:58+00:00" + }, + { + "name": "phpgt/cipher", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/PhpGt/Cipher.git", + "reference": "611ff35dbcb73b65d31e902fa15d76b49ba11d46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PhpGt/Cipher/zipball/611ff35dbcb73b65d31e902fa15d76b49ba11d46", + "reference": "611ff35dbcb73b65d31e902fa15d76b49ba11d46", + "shasum": "" + }, + "require": { + "ext-sodium": "*", + "php": ">=8.0", + "phpgt/http": "v1.*" + }, + "require-dev": { + "phpstan/phpstan": "^v1.8", + "phpunit/phpunit": "^v9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Gt\\Cipher\\": "./src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "authors": [ + { + "name": "Greg Bowler", + "email": "greg.bowler@g105b.com", + "homepage": "https://www.g105b.com", + "role": "Developer" + } + ], + "description": "Two-way encryption of messages for secure plain text transmission.", + "support": { + "issues": "https://github.com/PhpGt/Cipher/issues", + "source": "https://github.com/PhpGt/Cipher/tree/v1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/PhpGt", + "type": "github" + } + ], + "time": "2023-05-12T11:14:40+00:00" + }, + { + "name": "phpgt/curl", + "version": "v3.1.1", + "source": { + "type": "git", + "url": "https://github.com/PhpGt/Curl.git", + "reference": "a7e0856d3735f8f69d6d5fbf2f6f26664e55e3b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PhpGt/Curl/zipball/a7e0856d3735f8f69d6d5fbf2f6f26664e55e3b7", + "reference": "a7e0856d3735f8f69d6d5fbf2f6f26664e55e3b7", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "php": ">=8.1", + "phpgt/json": "^1.2" + }, + "require-dev": { + "phpmd/phpmd": "^2.13", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^10.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Gt\\Curl\\": "./src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Bowler", + "email": "greg.bowler@g105b.com" + } + ], + "description": "cURL object wrapper.", + "keywords": [ + "curl", + "curl_multi", + "http", + "interface" + ], + "support": { + "issues": "https://github.com/PhpGt/Curl/issues", + "source": "https://github.com/PhpGt/Curl/tree/v3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/PhpGt", + "type": "github" + } + ], + "time": "2023-04-29T17:28:12+00:00" + }, + { + "name": "phpgt/dataobject", + "version": "v1.0.7", + "source": { + "type": "git", + "url": "https://github.com/PhpGt/DataObject.git", + "reference": "3b05cc3b64a0f2b763c8e550bc6c03b71474cd1a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PhpGt/DataObject/zipball/3b05cc3b64a0f2b763c8e550bc6c03b71474cd1a", + "reference": "3b05cc3b64a0f2b763c8e550bc6c03b71474cd1a", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=8.0", + "phpgt/typesafegetter": "^1.3" }, "require-dev": { - "phpunit/phpunit": "8.*" + "phpmd/phpmd": "^2.13", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.1", + "squizlabs/php_codesniffer": "^3.7" }, "type": "library", "autoload": { "psr-4": { - "Gt\\Cookie\\": "./src" + "Gt\\DataObject\\": "./src" } }, "notification-url": "https://packagist.org/downloads/", @@ -42,34 +205,61 @@ "email": "greg.bowler@g105b.com" } ], - "description": "Object oriented cookie handler.", - "time": "2019-07-17T20:11:11+00:00" + "description": " Structured, type-safe, immutable data transfer.", + "support": { + "issues": "https://github.com/PhpGt/DataObject/issues", + "source": "https://github.com/PhpGt/DataObject/tree/v1.0.7" + }, + "funding": [ + { + "url": "https://github.com/phpgt", + "type": "github" + } + ], + "time": "2023-07-13T09:45:07+00:00" }, { "name": "phpgt/http", - "version": "v1.0.3", + "version": "v1.2.2", "source": { "type": "git", "url": "https://github.com/PhpGt/Http.git", - "reference": "394ecdd2a2e2686a467cdf2f837c72105d68398f" + "reference": "de03587d63d5a582140a3e9e9b06e506ffbc8ca3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhpGt/Http/zipball/394ecdd2a2e2686a467cdf2f837c72105d68398f", - "reference": "394ecdd2a2e2686a467cdf2f837c72105d68398f", + "url": "https://api.github.com/repos/PhpGt/Http/zipball/de03587d63d5a582140a3e9e9b06e506ffbc8ca3", + "reference": "de03587d63d5a582140a3e9e9b06e506ffbc8ca3", "shasum": "" }, "require": { - "php": ">=7.2", - "phpgt/cookie": "*", - "phpgt/input": "*", - "psr/http-message": "^1.0.1", - "willdurand/negotiation": "^2.3" + "ext-curl": "*", + "ext-dom": "*", + "ext-fileinfo": "*", + "php": ">=8.1", + "phpgt/async": "^1.0", + "phpgt/curl": "^3.1", + "phpgt/input": "^1.2", + "phpgt/json": "^1.2", + "phpgt/promise": "^2.2", + "phpgt/propfunc": "^1.0", + "phpgt/typesafegetter": "^1.3", + "psr/http-message": "^2.0", + "willdurand/negotiation": "3.1.0" }, "require-dev": { - "phpunit/phpunit": "8.*" + "phpmd/phpmd": "^2.13", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.1", + "squizlabs/php_codesniffer": "^3.7" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.3-dev", + "dev-ci": "1.2.3-dev" + } + }, "autoload": { "psr-4": { "Gt\\Http\\": "./src" @@ -80,27 +270,41 @@ "MIT" ], "description": "PSR-7 HTTP message implementation.", - "time": "2019-11-28T12:05:15+00:00" + "support": { + "issues": "https://github.com/PhpGt/Http/issues", + "source": "https://github.com/PhpGt/Http/tree/v1.2.2" + }, + "funding": [ + { + "url": "https://github.com/sponsors/PhpGt", + "type": "github" + } + ], + "time": "2023-07-20T15:04:03+00:00" }, { "name": "phpgt/input", - "version": "v1.1.2", + "version": "v1.2.3", "source": { "type": "git", "url": "https://github.com/PhpGt/Input.git", - "reference": "ae78931ffd5c4800f362db61bd86f8e545733e86" + "reference": "7e82009922c42531e57e45641072b7c416d726c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhpGt/Input/zipball/ae78931ffd5c4800f362db61bd86f8e545733e86", - "reference": "ae78931ffd5c4800f362db61bd86f8e545733e86", + "url": "https://api.github.com/repos/PhpGt/Input/zipball/7e82009922c42531e57e45641072b7c416d726c3", + "reference": "7e82009922c42531e57e45641072b7c416d726c3", "shasum": "" }, "require": { - "phpgt/http": "*" + "php": ">=8.1", + "phpgt/http": "^v1.1" }, "require-dev": { - "phpunit/phpunit": "8.*" + "phpmd/phpmd": "^2.13", + "phpstan/phpstan": "^v1.10", + "phpunit/phpunit": "^10.1", + "squizlabs/php_codesniffer": "^3.7" }, "type": "library", "autoload": { @@ -113,67 +317,97 @@ "MIT" ], "description": "Encapsulated user input.", - "time": "2019-11-29T11:22:35+00:00" + "support": { + "issues": "https://github.com/PhpGt/Input/issues", + "source": "https://github.com/PhpGt/Input/tree/v1.2.3" + }, + "funding": [ + { + "url": "https://github.com/sponsors/PhpGt", + "type": "github" + } + ], + "time": "2023-05-02T17:16:00+00:00" }, { - "name": "phpgt/session", - "version": "v1.1.0", + "name": "phpgt/json", + "version": "v1.2.1", "source": { "type": "git", - "url": "https://github.com/PhpGt/Session.git", - "reference": "97b793244a6cde7858f5bf07ecfad67ce4d6002b" + "url": "https://github.com/PhpGt/Json.git", + "reference": "8938b374d550bc6114bf1d4e5c1cbe3283868e58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhpGt/Session/zipball/97b793244a6cde7858f5bf07ecfad67ce4d6002b", - "reference": "97b793244a6cde7858f5bf07ecfad67ce4d6002b", + "url": "https://api.github.com/repos/PhpGt/Json/zipball/8938b374d550bc6114bf1d4e5c1cbe3283868e58", + "reference": "8938b374d550bc6114bf1d4e5c1cbe3283868e58", "shasum": "" }, "require": { - "php": ">=7.2" + "ext-json": "*", + "php": ">=8.1", + "phpgt/dataobject": "^1.0.7" }, "require-dev": { - "phpunit/phpunit": "^8.0" + "phpmd/phpmd": "^2.13", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.1", + "squizlabs/php_codesniffer": "^3.7" }, "type": "library", "autoload": { "psr-4": { - "Gt\\Session\\": "./src" + "Gt\\Json\\": "./src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Encapsulated user sessions.", - "time": "2020-02-27T16:06:07+00:00" + "authors": [ + { + "name": "Greg Bowler", + "email": "greg.bowler@g105b.com" + } + ], + "description": " Structured, type-safe, immutable JSON objects.", + "support": { + "issues": "https://github.com/PhpGt/Json/issues", + "source": "https://github.com/PhpGt/Json/tree/v1.2.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/PhpGt", + "type": "github" + } + ], + "time": "2023-07-13T13:20:08+00:00" }, { - "name": "psr/http-message", - "version": "1.0.1", + "name": "phpgt/logger", + "version": "v1.0.0", "source": { "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "url": "https://github.com/PhpGt/Logger.git", + "reference": "9cbb7c986941bff513aedf80dfa256497aff8dd2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/PhpGt/Logger/zipball/9cbb7c986941bff513aedf80dfa256497aff8dd2", + "reference": "9cbb7c986941bff513aedf80dfa256497aff8dd2", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=7.4" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "require-dev": { + "phpstan/phpstan": ">=0.12.64", + "phpunit/phpunit": "9.*" }, + "type": "library", "autoload": { "psr-4": { - "Psr\\Http\\Message\\": "src/" + "Gt\\Logger\\": "./src" } }, "notification-url": "https://packagist.org/downloads/", @@ -182,111 +416,105 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Greg Bowler", + "email": "greg.bowler@g105b.com" } ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" + "description": "PSR-3 logger and implementation.", + "support": { + "issues": "https://github.com/PhpGt/Logger/issues", + "source": "https://github.com/PhpGt/Logger/tree/v1.0.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/PhpGt", + "type": "github" + } ], - "time": "2016-08-06T14:39:51+00:00" + "time": "2021-09-21T14:18:11+00:00" }, { - "name": "willdurand/negotiation", - "version": "v2.3.1", + "name": "phpgt/promise", + "version": "v2.2.3", "source": { "type": "git", - "url": "https://github.com/willdurand/Negotiation.git", - "reference": "03436ededa67c6e83b9b12defac15384cb399dc9" + "url": "https://github.com/PhpGt/Promise.git", + "reference": "907b3a450f3252077f80b50289f73ab930ca2cdc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/willdurand/Negotiation/zipball/03436ededa67c6e83b9b12defac15384cb399dc9", - "reference": "03436ededa67c6e83b9b12defac15384cb399dc9", + "url": "https://api.github.com/repos/PhpGt/Promise/zipball/907b3a450f3252077f80b50289f73ab930ca2cdc", + "reference": "907b3a450f3252077f80b50289f73ab930ca2cdc", "shasum": "" }, "require": { - "php": ">=5.4.0" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "~4.5" + "phpmd/phpmd": "^2.13", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.1", + "squizlabs/php_codesniffer": "^3.7" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3-dev" - } - }, "autoload": { "psr-4": { - "Negotiation\\": "src/Negotiation" + "Gt\\Promise\\": "./src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ + "description": "Pleasantly work with asynchronous code.", + "keywords": [ + "W3C", + "async", + "asynchronous", + "callback", + "concurrency", + "concurrent", + "deferred", + "promise", + "then" + ], + "support": { + "issues": "https://github.com/PhpGt/Promise/issues", + "source": "https://github.com/PhpGt/Promise/tree/v2.2.3" + }, + "funding": [ { - "name": "William Durand", - "email": "will+git@drnd.me" + "url": "https://github.com/sponsors/PhpGt", + "type": "github" } ], - "description": "Content Negotiation tools for PHP provided as a standalone library.", - "homepage": "http://williamdurand.fr/Negotiation/", - "keywords": [ - "accept", - "content", - "format", - "header", - "negotiation" - ], - "time": "2017-05-14T17:21:12+00:00" - } - ], - "packages-dev": [ + "time": "2023-07-01T10:59:01+00:00" + }, { - "name": "doctrine/instantiator", - "version": "1.3.0", + "name": "phpgt/propfunc", + "version": "v1.0.1", "source": { "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "ae466f726242e637cebdd526a7d991b9433bacf1" + "url": "https://github.com/PhpGt/PropFunc.git", + "reference": "091213649e89ff22d1ef640b46fbee5215c65520" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1", - "reference": "ae466f726242e637cebdd526a7d991b9433bacf1", + "url": "https://api.github.com/repos/PhpGt/PropFunc/zipball/091213649e89ff22d1ef640b46fbee5215c65520", + "reference": "091213649e89ff22d1ef640b46fbee5215c65520", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=8.0" }, "require-dev": { - "doctrine/coding-standard": "^6.0", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.13", - "phpstan/phpstan-phpunit": "^0.11", - "phpstan/phpstan-shim": "^0.11", - "phpunit/phpunit": "^7.0" + "phpstan/phpstan": ">=0.12", + "phpunit/phpunit": ">=9.3" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, "autoload": { "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + "Gt\\PropFunc\\": "./src" } }, "notification-url": "https://packagist.org/downloads/", @@ -295,198 +523,206 @@ ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" + "name": "Greg Bowler", + "email": "greg.bowler@g105b.com", + "homepage": "https://www.g105b.com", + "role": "Developer" } ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" + "description": "Property accessor and mutator functions.", + "support": { + "issues": "https://github.com/PhpGt/PropFunc/issues", + "source": "https://github.com/PhpGt/PropFunc/tree/v1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/PhpGt", + "type": "github" + } ], - "time": "2019-10-21T16:45:58+00:00" + "time": "2021-03-23T12:46:44+00:00" }, { - "name": "myclabs/deep-copy", - "version": "1.9.5", + "name": "phpgt/session", + "version": "v1.2.1", "source": { "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef" + "url": "https://github.com/PhpGt/Session.git", + "reference": "b2e21a3147f135aee9dc16b04e352c28898d78fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef", - "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef", + "url": "https://api.github.com/repos/PhpGt/Session/zipball/b2e21a3147f135aee9dc16b04e352c28898d78fa", + "reference": "b2e21a3147f135aee9dc16b04e352c28898d78fa", "shasum": "" }, "require": { - "php": "^7.1" - }, - "replace": { - "myclabs/deep-copy": "self.version" + "php": ">=8.0", + "phpgt/typesafegetter": "^1.3" }, "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" + "phpmd/phpmd": "^2.13", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.1", + "squizlabs/php_codesniffer": "^3.7" }, "type": "library", "autoload": { "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, - "files": [ - "src/DeepCopy/deep_copy.php" - ] + "Gt\\Session\\": "./src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" + "description": "Encapsulated user sessions.", + "support": { + "issues": "https://github.com/PhpGt/Session/issues", + "source": "https://github.com/PhpGt/Session/tree/v1.2.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/PhpGt", + "type": "github" + } ], - "time": "2020-01-17T21:11:47+00:00" + "time": "2023-05-25T13:15:56+00:00" }, { - "name": "phar-io/manifest", - "version": "1.0.3", + "name": "phpgt/typesafegetter", + "version": "v1.3.2", "source": { "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" + "url": "https://github.com/PhpGt/TypeSafeGetter.git", + "reference": "f760c05a37b1cc188dcbf800c5fdfab8a926b4b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "url": "https://api.github.com/repos/PhpGt/TypeSafeGetter/zipball/f760c05a37b1cc188dcbf800c5fdfab8a926b4b0", + "reference": "f760c05a37b1cc188dcbf800c5fdfab8a926b4b0", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-phar": "*", - "phar-io/version": "^2.0", - "php": "^5.6 || ^7.0" + "php": ">=8.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "require-dev": { + "phpmd/phpmd": "^2.13", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.1", + "squizlabs/php_codesniffer": "^3.7" }, + "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Gt\\TypeSafeGetter\\": "./src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, + "name": "Greg Bowler", + "email": "greg.bowler@g105b.com" + } + ], + "description": "An interface for objects that expose type-safe getter methods.", + "support": { + "issues": "https://github.com/PhpGt/TypeSafeGetter/issues", + "source": "https://github.com/PhpGt/TypeSafeGetter/tree/v1.3.2" + }, + "funding": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "url": "https://github.com/sponsors/PhpGt", + "type": "github" } ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2018-07-08T19:23:20+00:00" + "time": "2023-04-28T14:42:27+00:00" }, { - "name": "phar-io/version", - "version": "2.0.1", + "name": "psr/http-message", + "version": "2.0", "source": { "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Library for handling version information and constraints", - "time": "2018-07-08T19:19:57+00:00" + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" }, { - "name": "phpdocumentor/reflection-common", - "version": "2.0.0", + "name": "willdurand/negotiation", + "version": "3.1.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a" + "url": "https://github.com/willdurand/Negotiation.git", + "reference": "68e9ea0553ef6e2ee8db5c1d98829f111e623ec2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a", - "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a", + "url": "https://api.github.com/repos/willdurand/Negotiation/zipball/68e9ea0553ef6e2ee8db5c1d98829f111e623ec2", + "reference": "68e9ea0553ef6e2ee8db5c1d98829f111e623ec2", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.1.0" }, "require-dev": { - "phpunit/phpunit": "~6" + "symfony/phpunit-bridge": "^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src/" + "Negotiation\\": "src/Negotiation" } }, "notification-url": "https://packagist.org/downloads/", @@ -495,204 +731,288 @@ ], "authors": [ { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" + "name": "William Durand", + "email": "will+git@drnd.me" } ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", + "description": "Content Negotiation tools for PHP provided as a standalone library.", + "homepage": "http://williamdurand.fr/Negotiation/", "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" + "accept", + "content", + "format", + "header", + "negotiation" ], - "time": "2018-08-07T13:53:10+00:00" - }, + "support": { + "issues": "https://github.com/willdurand/Negotiation/issues", + "source": "https://github.com/willdurand/Negotiation/tree/3.1.0" + }, + "time": "2022-01-30T20:08:53+00:00" + } + ], + "packages-dev": [ { - "name": "phpdocumentor/reflection-docblock", - "version": "5.1.0", + "name": "doctrine/instantiator", + "version": "1.5.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e" + "url": "https://github.com/doctrine/instantiator.git", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", - "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", "shasum": "" }, "require": { - "ext-filter": "^7.1", - "php": "^7.2", - "phpdocumentor/reflection-common": "^2.0", - "phpdocumentor/type-resolver": "^1.0", - "webmozart/assert": "^1" + "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/instantiator": "^1", - "mockery/mockery": "^1" + "doctrine/coding-standard": "^9 || ^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" } + ], + "time": "2022-12-30T00:15:36+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, + "type": "library", "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], "psr-4": { - "phpDocumentor\\Reflection\\": "src" + "DeepCopy\\": "src/DeepCopy/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" } ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2020-02-22T12:28:44+00:00" + "time": "2023-03-08T13:26:56+00:00" }, { - "name": "phpdocumentor/type-resolver", - "version": "1.1.0", + "name": "phar-io/manifest", + "version": "2.0.3", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "7462d5f123dfc080dfdf26897032a6513644fc95" + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/7462d5f123dfc080dfdf26897032a6513644fc95", - "reference": "7462d5f123dfc080dfdf26897032a6513644fc95", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", "shasum": "" }, "require": { - "php": "^7.2", - "phpdocumentor/reflection-common": "^2.0" - }, - "require-dev": { - "ext-tokenizer": "^7.2", - "mockery/mockery": "~1" + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" } ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "time": "2020-02-18T18:59:58+00:00" + "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/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" }, { - "name": "phpspec/prophecy", - "version": "v1.10.2", + "name": "phar-io/version", + "version": "3.2.1", "source": { "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9" + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/b4400efc9d206e83138e2bb97ed7f5b14b831cd9", - "reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", - "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^2.5 || ^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + "php": "^7.2 || ^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10.x-dev" - } - }, "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" }, { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" } ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "time": "2020-01-20T15:57:02+00:00" + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "7.0.10", + "version": "7.0.15", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf" + "reference": "819f92bba8b001d4363065928088de22f25a3a48" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f1884187926fbb755a9aaf0b3836ad3165b478bf", - "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/819f92bba8b001d4363065928088de22f25a3a48", + "reference": "819f92bba8b001d4363065928088de22f25a3a48", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", - "php": "^7.2", + "php": ">=7.2", "phpunit/php-file-iterator": "^2.0.2", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.1.1", + "phpunit/php-token-stream": "^3.1.3 || ^4.0", "sebastian/code-unit-reverse-lookup": "^1.0.1", "sebastian/environment": "^4.2.2", "sebastian/version": "^2.0.1", @@ -733,27 +1053,37 @@ "testing", "xunit" ], - "time": "2019-11-20T13:55:58+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.15" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-07-26T12:20:09+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "2.0.2", + "version": "2.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "050bedf145a257b1ff02746c31894800e5122946" + "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", - "reference": "050bedf145a257b1ff02746c31894800e5122946", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5", + "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -783,7 +1113,17 @@ "filesystem", "iterator" ], - "time": "2018-09-13T20:33:42+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:42:26+00:00" }, { "name": "phpunit/php-text-template", @@ -824,27 +1164,31 @@ "keywords": [ "template" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" + }, "time": "2015-06-21T13:50:34+00:00" }, { "name": "phpunit/php-timer", - "version": "2.1.2", + "version": "2.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "1038454804406b0b5f5f520358e78c1c2f71501e" + "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e", - "reference": "1038454804406b0b5f5f520358e78c1c2f71501e", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", + "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -873,33 +1217,43 @@ "keywords": [ "timer" ], - "time": "2019-06-07T04:22:29+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T08:20:02+00:00" }, { "name": "phpunit/php-token-stream", - "version": "3.1.1", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff" + "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff", - "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/a853a0e183b9db7eed023d7933a858fa1c8d25a3", + "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": "^7.1" + "php": "^7.3 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -922,56 +1276,63 @@ "keywords": [ "tokenizer" ], - "time": "2019-09-17T06:23:10+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", + "source": "https://github.com/sebastianbergmann/php-token-stream/tree/master" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "abandoned": true, + "time": "2020-08-04T08:28:15+00:00" }, { "name": "phpunit/phpunit", - "version": "8.5.2", + "version": "8.5.34", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "018b6ac3c8ab20916db85fa91bf6465acb64d1e0" + "reference": "622d0186707f39a4ae71df3bcf42d759bb868854" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/018b6ac3c8ab20916db85fa91bf6465acb64d1e0", - "reference": "018b6ac3c8ab20916db85fa91bf6465acb64d1e0", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/622d0186707f39a4ae71df3bcf42d759bb868854", + "reference": "622d0186707f39a4ae71df3bcf42d759bb868854", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.2.0", + "doctrine/instantiator": "^1.3.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.9.1", - "phar-io/manifest": "^1.0.3", - "phar-io/version": "^2.0.1", - "php": "^7.2", - "phpspec/prophecy": "^1.8.1", - "phpunit/php-code-coverage": "^7.0.7", - "phpunit/php-file-iterator": "^2.0.2", + "myclabs/deep-copy": "^1.10.0", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.2", + "phpunit/php-code-coverage": "^7.0.12", + "phpunit/php-file-iterator": "^2.0.4", "phpunit/php-text-template": "^1.2.1", "phpunit/php-timer": "^2.1.2", - "sebastian/comparator": "^3.0.2", + "sebastian/comparator": "^3.0.5", "sebastian/diff": "^3.0.2", - "sebastian/environment": "^4.2.2", - "sebastian/exporter": "^3.1.1", + "sebastian/environment": "^4.2.3", + "sebastian/exporter": "^3.1.5", "sebastian/global-state": "^3.0.0", "sebastian/object-enumerator": "^3.0.3", "sebastian/resource-operations": "^2.0.1", "sebastian/type": "^1.1.3", "sebastian/version": "^2.0.1" }, - "require-dev": { - "ext-pdo": "*" - }, "suggest": { - "ext-soap": "*", - "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0.0" + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage", + "phpunit/php-invoker": "To allow enforcing time limits" }, "bin": [ "phpunit" @@ -1005,27 +1366,46 @@ "testing", "xunit" ], - "time": "2020-01-08T08:49:49+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.34" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2023-09-19T05:20:51+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", + "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": ">=5.6" }, "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -1050,29 +1430,39 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T08:15:22+00:00" }, { "name": "sebastian/comparator", - "version": "3.0.2", + "version": "3.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" + "reference": "1dc7ceb4a24aede938c7af2a9ed1de09609ca770" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1dc7ceb4a24aede938c7af2a9ed1de09609ca770", + "reference": "1dc7ceb4a24aede938c7af2a9ed1de09609ca770", "shasum": "" }, "require": { - "php": "^7.1", + "php": ">=7.1", "sebastian/diff": "^3.0", "sebastian/exporter": "^3.1" }, "require-dev": { - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -1090,6 +1480,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -1101,10 +1495,6 @@ { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" } ], "description": "Provides the functionality to compare PHP values for equality", @@ -1114,24 +1504,34 @@ "compare", "equality" ], - "time": "2018-07-12T15:12:46+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:31:48+00:00" }, { "name": "sebastian/diff", - "version": "3.0.2", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" + "reference": "6296a0c086dd0117c1b78b059374d7fcbe7545ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/6296a0c086dd0117c1b78b059374d7fcbe7545ae", + "reference": "6296a0c086dd0117c1b78b059374d7fcbe7545ae", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "require-dev": { "phpunit/phpunit": "^7.5 || ^8.0", @@ -1153,13 +1553,13 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], "description": "Diff implementation", @@ -1170,24 +1570,34 @@ "unidiff", "unified diff" ], - "time": "2019-02-04T06:01:07+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/3.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-05-07T05:30:20+00:00" }, { "name": "sebastian/environment", - "version": "4.2.3", + "version": "4.2.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368" + "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/464c90d7bdf5ad4e8a6aea15c091fec0603d4368", - "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", + "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "require-dev": { "phpunit/phpunit": "^7.5" @@ -1223,29 +1633,39 @@ "environment", "hhvm" ], - "time": "2019-11-20T08:46:58+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:53:42+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.2", + "version": "3.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" + "reference": "73a9676f2833b9a7c36968f9d882589cd75511e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", - "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/73a9676f2833b9a7c36968f9d882589cd75511e6", + "reference": "73a9676f2833b9a7c36968f9d882589cd75511e6", "shasum": "" }, "require": { - "php": "^7.0", + "php": ">=7.0", "sebastian/recursion-context": "^3.0" }, "require-dev": { "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -1290,24 +1710,34 @@ "export", "exporter" ], - "time": "2019-09-14T09:02:43+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T06:00:17+00:00" }, { "name": "sebastian/global-state", - "version": "3.0.0", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4" + "reference": "66783ce213de415b451b904bfef9dda0cf9aeae0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", - "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/66783ce213de415b451b904bfef9dda0cf9aeae0", + "reference": "66783ce213de415b451b904bfef9dda0cf9aeae0", "shasum": "" }, "require": { - "php": "^7.2", + "php": ">=7.2", "sebastian/object-reflector": "^1.1.1", "sebastian/recursion-context": "^3.0" }, @@ -1344,24 +1774,34 @@ "keywords": [ "global state" ], - "time": "2019-02-01T05:30:01+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-02T09:23:32+00:00" }, { "name": "sebastian/object-enumerator", - "version": "3.0.3", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", + "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", "shasum": "" }, "require": { - "php": "^7.0", + "php": ">=7.0", "sebastian/object-reflector": "^1.1.1", "sebastian/recursion-context": "^3.0" }, @@ -1391,24 +1831,34 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03T12:35:26+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:40:27+00:00" }, { "name": "sebastian/object-reflector", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" + "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", + "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.0" }, "require-dev": { "phpunit/phpunit": "^6.0" @@ -1436,24 +1886,34 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29T09:07:27+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:37:18+00:00" }, { "name": "sebastian/recursion-context", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", + "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.0" }, "require-dev": { "phpunit/phpunit": "^6.0" @@ -1474,14 +1934,14 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, { "name": "Adam Harvey", "email": "aharvey@php.net" @@ -1489,24 +1949,34 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03T06:23:57+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:34:24+00:00" }, { "name": "sebastian/resource-operations", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" + "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", + "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "type": "library", "extra": { @@ -1531,24 +2001,34 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2018-10-04T04:07:39+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:30:19+00:00" }, { "name": "sebastian/type", - "version": "1.1.3", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3" + "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/3aaaa15fa71d27650d62a948be022fe3b48541a3", - "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0150cfbc4495ed2df3872fb31b26781e4e077eb4", + "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4", "shasum": "" }, "require": { - "php": "^7.2" + "php": ">=7.2" }, "require-dev": { "phpunit/phpunit": "^8.2" @@ -1577,7 +2057,17 @@ ], "description": "Collection of value objects that represent the types of the PHP type system", "homepage": "https://github.com/sebastianbergmann/type", - "time": "2019-07-02T08:10:15+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/1.1.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:25:11+00:00" }, { "name": "sebastian/version", @@ -1620,85 +2110,31 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03T07:35:21+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.14.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", - "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.14-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/master" }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "time": "2020-01-13T11:15:53+00:00" + "time": "2016-10-03T07:35:21+00:00" }, { "name": "theseer/tokenizer", - "version": "1.1.3", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", - "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -1718,55 +2154,17 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2019-06-13T22:48:21+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.7.0", - "source": { - "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "aed98a490f9a8f78468232db345ab9cf606cf598" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/aed98a490f9a8f78468232db345ab9cf606cf598", - "reference": "aed98a490f9a8f78468232db345ab9cf606cf598", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "vimeo/psalm": "<3.6.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.36 || ^7.5.13" - }, - "type": "library", - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ + "funding": [ { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" + "url": "https://github.com/theseer", + "type": "github" } ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "time": "2020-02-14T12:15:55+00:00" + "time": "2021-07-28T10:34:58+00:00" } ], "aliases": [], @@ -1775,9 +2173,11 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.4", - "ext-openssl": "*", - "ext-json": "*" + "php": ">=8.1", + "ext-openssl": "*" + }, + "platform-dev": { + "ext-sodium": "*" }, - "platform-dev": [] + "plugin-api-version": "2.3.0" } diff --git a/src/Authenticator.php b/src/Authenticator.php index ab1f471..3af6259 100644 --- a/src/Authenticator.php +++ b/src/Authenticator.php @@ -1,68 +1,70 @@ deploymentId = substr($clientKey, 0, strrpos($clientKey, "_")); + $this->secret = substr(strrchr($clientKey, "_"), 1); + + $this->session = $this->session ?? new GlobalSessionContainer(); + $this->redirectHandler = $redirectHandler ?? new RedirectHandler(); + if($sessionData = $this->session->get(SessionData::class)) { + $this->sessionData = $sessionData; + + try { + $responseData = $this->sessionData->getData(); + if($responseData instanceof UserResponseData) { + $this->user = new User( + $responseData->getId(), + $responseData->getEmail(), + $responseData->getAllFields(), + ); + } + } + catch(NotLoggedInException) {} } - if(!$session->contains(self::SESSION_KEY)) { -// TODO: If there is no Token or UserData in the SessionData, do we even -// need to store it to the current session at all? - $session->set(self::SESSION_KEY, new SessionData()); + if(is_string($currentUri)) { + $currentUri = new Uri($currentUri); } - /** @var SessionData $sessionData*/ - $sessionData = $session->get(self::SESSION_KEY); - - $this->clientKey = $clientKey; - $this->currentUriPath = $currentUriPath; - $this->authwaveHost = $authwaveHost; - $this->session = $session; - $this->sessionData = $sessionData; - $this->redirectHandler = $redirectHandler ?? new RedirectHandler(); + $this->currentUri = $currentUri; $this->completeAuth(); } public function isLoggedIn():bool { - $userData = null; - - try { - $userData = $this->sessionData->getData(); - } - catch(NotLoggedInException $exception) { - return false; - } - - return isset($userData); + return isset($this->user); } public function login(Token $token = null):void { @@ -71,54 +73,92 @@ public function login(Token $token = null):void { } if(is_null($token)) { - $token = new Token($this->clientKey); + Log::debug("Created new CONSUMER token"); + $token = new Token($this->secret); } $this->sessionData = new SessionData($token); - $this->session->set(self::SESSION_KEY, $this->sessionData); - + $this->session->set(SessionData::class, $this->sessionData); $this->redirectHandler->redirect($this->getLoginUri($token)); } public function logout(Token $token = null):void { +// TODO: Log out needs implementing - ApplicationDeploymentNotFoundException throws! if(is_null($token)) { - $token = new Token($this->clientKey); + $token = new Token($this->secret); } - $this->sessionData = new SessionData($token); - $this->session->set(self::SESSION_KEY, $this->sessionData); - - $this->redirectHandler->redirect($this->getLogoutUri($token)); + if(isset($this->user) && $this->user->email === self::FAKE_EMAIL) { + $this->session->remove(SessionData::class); + unset($this->user); + } + else { + $this->sessionData = new SessionData($token); + $this->session->set(SessionData::class, $this->sessionData); + $this->session->remove(SessionData::class); + $this->redirectHandler->redirect($this->getLogoutUri($token)); + } } - public function getUuid():string { - $userData = $this->sessionData->getData(); - return $userData->getUuid(); - } + public function fakeLogin(string $userId, ?string $email = null, string $redirectTo = "/"):void { + $secretIv = new InitVector(); + $token = new Token($this->secret, $secretIv); + $sessionData = new SessionData($token); + $this->session->set(SessionData::class, $sessionData); - public function getEmail():string { - $userData = $this->sessionData->getData(); - return $userData->getEmail(); + $userData = new UserResponseData( + $userId, + $email ?? self::FAKE_EMAIL, + ); + + $this->session->set( + SessionData::class, + new SessionData($token, $userData) + ); + + $message = new PlainTextMessage( + json_encode([ + "id" => $userData->getId(), + "email" => $userData->getEmail(), + ]), + $secretIv, + ); + + $cipherText = $message->encrypt(new Key($this->secret)); + $queryString = http_build_query([ + "AUTHWAVE_RESPONSE_DATA" => (string)$cipherText, + ]); + if($currentQuery = $this->currentUri->getQuery()) { + $queryString .= "&"; + $queryString .= $currentQuery; + } + $uri = new Uri("$redirectTo?$queryString"); + $this->redirectHandler->redirect($uri); } - public function getField(string $name):?string { - $userData = $this->sessionData->getData(); - return $userData->getField($name); + public function getUser():User { + if(!isset($this->user)) { + throw new NotLoggedInException(); + } + + return $this->user; } - public function getLoginUri(Token $token):AbstractProviderUri { + public function getLoginUri(Token $token):BaseProviderUri { return new LoginUri( $token, - $this->currentUriPath, - $this->authwaveHost + $this->currentUri, + $this->deploymentId, + $this->authwaveHost, ); } - private function getLogoutUri(Token $token):AbstractProviderUri { + private function getLogoutUri(Token $token):BaseProviderUri { return new LogoutUri( $token, - $this->currentUriPath, - $this->authwaveHost + $this->currentUri, + $this->deploymentId, + $this->authwaveHost, ); } @@ -128,40 +168,59 @@ public function getAdminUri():UriInterface { public function getProfileUri(Token $token = null):UriInterface { if(is_null($token)) { - $token = new Token($this->clientKey); + $token = new Token($this->secret); } return new ProfileUri( $token, - $this->getUuid(), - $this->currentUriPath, + $this->user->id, + $this->currentUri, $this->authwaveHost ); } private function completeAuth():void { - $responseCipher = $this->getResponseCipher(); + $queryData = $this->getQueryData(); + + if(!$queryData) { + return; + } - if(!$responseCipher) { + if(!isset($this->sessionData)) { + $this->tidyResponseData(); return; } $token = $this->sessionData->getToken(); - $userData = $token->decryptResponseCipher($responseCipher); + $secretSessionIv = $token->getSecretIv(); + $encrypted = new EncryptedMessage($queryData, $secretSessionIv); + $key = new Key($this->secret); + $decrypted = $encrypted->decrypt($key); + $data = json_decode($decrypted); + $userData = new UserResponseData( + $data->{"id"}, + $data->{"email"}, + $data->{"kvp"} ?? [], + ); + $this->session->set( - self::SESSION_KEY, + SessionData::class, new SessionData($token, $userData) ); + $this->tidyResponseData(); + } + + private function tidyResponseData():void { $this->redirectHandler->redirect( - (new Uri($this->currentUriPath)) - ->withoutQueryValue(self::RESPONSE_QUERY_PARAMETER) + (new Uri($this->currentUri)) + ->withoutQueryValue(self::RESPONSE_QUERY_PARAMETER) ); } - private function getResponseCipher():?string { + private function getQueryData():?string { $queryString = parse_url( - $this->currentUriPath, + $this->currentUri, PHP_URL_QUERY ); if(!$queryString) { @@ -176,4 +235,4 @@ private function getResponseCipher():?string { return $queryParts[self::RESPONSE_QUERY_PARAMETER]; } -} \ No newline at end of file +} diff --git a/src/InitVector.php b/src/InitVector.php deleted file mode 100644 index 7978666..0000000 --- a/src/InitVector.php +++ /dev/null @@ -1,18 +0,0 @@ -bytes = random_bytes($length); - } - - public function getBytes():string { - return $this->bytes; - } - - public function __toString():string { - return bin2hex($this->bytes); - } -} \ No newline at end of file diff --git a/src/ProviderUri/AdminUri.php b/src/ProviderUri/AdminUri.php index f8b1ae3..8e0cd6f 100644 --- a/src/ProviderUri/AdminUri.php +++ b/src/ProviderUri/AdminUri.php @@ -1,12 +1,12 @@ normaliseBaseUri($baseRemoteUri); parent::__construct($baseRemoteUri); - $this->path = "/admin"; + $this->path = "/admin/"; } -} \ No newline at end of file +} diff --git a/src/ProviderUri/AbstractProviderUri.php b/src/ProviderUri/BaseProviderUri.php similarity index 72% rename from src/ProviderUri/AbstractProviderUri.php rename to src/ProviderUri/BaseProviderUri.php index bdd63f4..a3b1cb3 100644 --- a/src/ProviderUri/AbstractProviderUri.php +++ b/src/ProviderUri/BaseProviderUri.php @@ -5,11 +5,12 @@ use Authwave\Token; use Gt\Http\Uri; -abstract class AbstractProviderUri extends Uri { +abstract class BaseProviderUri extends Uri { const DEFAULT_BASE_REMOTE_URI = "login.authwave.com"; const QUERY_STRING_CIPHER = "c"; const QUERY_STRING_INIT_VECTOR = "i"; - const QUERY_STRING_CURRENT_PATH = "p"; + const QUERY_STRING_CURRENT_URI = "u"; + const QUERY_STRING_DEPLOYMENT_ID = "d"; protected function normaliseBaseUri(string $baseUri):Uri { $scheme = parse_url($baseUri, PHP_URL_SCHEME) @@ -25,7 +26,8 @@ protected function normaliseBaseUri(string $baseUri):Uri { ->withPort($port); if($uri->getHost() !== "localhost" - && $uri->getScheme() !== "https") { + && $uri->getHost() !== "127.0.0.127" + && $uri->getScheme() !== "https") { throw new InsecureProtocolException($uri->getScheme()); } @@ -34,13 +36,15 @@ protected function normaliseBaseUri(string $baseUri):Uri { protected function buildQuery( Token $token, + string $deploymentId, string $currentPath, - string $message = null + string $message = "", ):string { return http_build_query([ self::QUERY_STRING_CIPHER => (string)$token->generateRequestCipher($message), + self::QUERY_STRING_DEPLOYMENT_ID => $deploymentId, self::QUERY_STRING_INIT_VECTOR => (string)$token->getIv(), - self::QUERY_STRING_CURRENT_PATH => bin2hex($currentPath), + self::QUERY_STRING_CURRENT_URI => bin2hex($currentPath), ]); } -} \ No newline at end of file +} diff --git a/src/ProviderUri/LoginUri.php b/src/ProviderUri/LoginUri.php index a24a6ba..5df9a81 100644 --- a/src/ProviderUri/LoginUri.php +++ b/src/ProviderUri/LoginUri.php @@ -10,23 +10,27 @@ * pass the secret IV to the provider, encrypted with the API key. The secret * IV is only ever stored in the client's session, and is unique to the session. */ -class LoginUri extends AbstractProviderUri { +class LoginUri extends BaseProviderUri { /** * @param Token $token This must be the same instance of the Token when * creating Authenticator for the first time as it is when checking the * response from the Authwave provider (store in a session). - * @param string $clientId - * @param string $currentPath * @param string $baseRemoteUri The base URI of the application. This is the * URI authority with optional scheme, as localhost allows http:// */ public function __construct( Token $token, - string $currentPath = "/", + string $currentPath, + string $deploymentId, string $baseRemoteUri = self::DEFAULT_BASE_REMOTE_URI ) { $baseRemoteUri = $this->normaliseBaseUri($baseRemoteUri); parent::__construct($baseRemoteUri); - $this->query = $this->buildQuery($token, $currentPath); + $this->query = $this->buildQuery( + $token, + $deploymentId, + $currentPath, + "action=login", + ); } -} \ No newline at end of file +} diff --git a/src/ProviderUri/LogoutUri.php b/src/ProviderUri/LogoutUri.php index c2ed192..c05e9fd 100644 --- a/src/ProviderUri/LogoutUri.php +++ b/src/ProviderUri/LogoutUri.php @@ -3,19 +3,20 @@ use Authwave\Token; -class LogoutUri extends AbstractProviderUri { +class LogoutUri extends BaseProviderUri { public function __construct( Token $token, - string $currentPath = "/", + string $currentPath, + string $deploymentId, string $baseRemoteUri = self::DEFAULT_BASE_REMOTE_URI ) { $baseRemoteUri = $this->normaliseBaseUri($baseRemoteUri); - parent::__construct($baseRemoteUri); $this->query = $this->buildQuery( $token, + $deploymentId, $currentPath, - "action=logout" + "action=logout", ); } -} \ No newline at end of file +} diff --git a/src/ProviderUri/ProfileUri.php b/src/ProviderUri/ProfileUri.php index 6ca73f7..6e743e9 100644 --- a/src/ProviderUri/ProfileUri.php +++ b/src/ProviderUri/ProfileUri.php @@ -3,7 +3,7 @@ use Authwave\Token; -class ProfileUri extends AbstractProviderUri { +class ProfileUri extends BaseProviderUri { public function __construct( Token $token, string $uuid, @@ -12,7 +12,7 @@ public function __construct( ) { $baseRemoteUri = $this->normaliseBaseUri($baseRemoteUri); parent::__construct($baseRemoteUri); - $this->path = "/profile"; + $this->path = "/profile/"; $this->query = $this->buildQuery($token, $uuid); } -} \ No newline at end of file +} diff --git a/src/ResponseData/AbstractResponseData.php b/src/ResponseData/BaseResponseData.php similarity index 85% rename from src/ResponseData/AbstractResponseData.php rename to src/ResponseData/BaseResponseData.php index a3e4df6..eea9406 100644 --- a/src/ResponseData/AbstractResponseData.php +++ b/src/ResponseData/BaseResponseData.php @@ -1,7 +1,7 @@ message; } -} \ No newline at end of file +} diff --git a/src/ResponseData/UserData.php b/src/ResponseData/UserData.php deleted file mode 100644 index 01ebd49..0000000 --- a/src/ResponseData/UserData.php +++ /dev/null @@ -1,33 +0,0 @@ -uuid = $uuid; - $this->email = $email; - $this->fields = $fields; - - parent::__construct($message); - } - - public function getUuid():string { - return $this->uuid; - } - - public function getEmail():string { - return $this->email; - } - - public function getField(string $name):?string { - return $this->fields->{$name} ?? null; - } -} \ No newline at end of file diff --git a/src/ResponseData/UserResponseData.php b/src/ResponseData/UserResponseData.php new file mode 100644 index 0000000..abcbc1b --- /dev/null +++ b/src/ResponseData/UserResponseData.php @@ -0,0 +1,30 @@ +uuid; + } + + public function getEmail():string { + return $this->email; + } + + public function getField(string $name):?string { + return $this->kvp[$name] ?? null; + } + + /** @return array */ + public function getAllFields():array { + return $this->kvp; + } +} diff --git a/src/SessionData.php b/src/SessionData.php index dee8c41..43ef6ba 100644 --- a/src/SessionData.php +++ b/src/SessionData.php @@ -1,19 +1,14 @@ token = $token; - $this->data = $data; } public function getToken():Token { @@ -24,11 +19,11 @@ public function getToken():Token { return $this->token; } - public function getData():AbstractResponseData { + public function getData():BaseResponseData { if(!isset($this->data)) { throw new NotLoggedInException(); } return $this->data; } -} \ No newline at end of file +} diff --git a/src/Token.php b/src/Token.php index 223008a..61aa5e5 100644 --- a/src/Token.php +++ b/src/Token.php @@ -1,24 +1,26 @@ key = $key; - $this->secretIv = $secretIv ?? new InitVector(); + $this->key = new Key($secret); + $this->secretSessionIv = $sessionIv ?? new InitVector(); $this->iv = $iv ?? new InitVector(); } @@ -26,59 +28,48 @@ public function getIv():InitVector { return $this->iv; } -/** - * The request cipher is sent to the remote provider in the querystring. It - * consists of the token's secret IV, encrypted with the client key, along with - * an optional message. The secret IV is required for two-way encryption. The - * remote provider will decrypt the secret and use it as the key if encrypting a - * response cipher, which will be sent back to the client application in the - * querystring. - */ - public function generateRequestCipher(string $message = null):string { - $data = $this->secretIv; - if($message) { - $data .= "|" . $message; - } - - $rawCipher = openssl_encrypt( - $data, - self::ENCRYPTION_METHOD, - $this->key, - 0, - $this->iv->getBytes() - ); - - return base64_encode($rawCipher); + public function getSecretIv():InitVector { + return $this->secretSessionIv; } + /** + * The request cipher is sent to the remote provider in the querystring. + * It consists of the token's secret IV, encrypted with the client key, + * along with an optional message. The secret IV is required for two-way + * encryption. The remote provider will decrypt the secret and use it as + * the key if encrypting a response cipher, which will be sent back to + * the client application in the querystring. + */ + public function generateRequestCipher(string $message = ""):CipherText { +// TODO: $message should probably always be a KVP, so we can build up the querystring message here without concatenating. + $plainTextMessage = new PlainTextMessage($message . "&secretIv=" . $this->getSecretIv(), $this->getIv()); + return $plainTextMessage->encrypt($this->key); + } // The response cipher is send from the remote provider back to the client // application after a successful authentication and includes a serialised // UserData object, encrypted using the secret IV, which was created when // encrypting the original request cipher. - public function decryptResponseCipher(string $cipher):AbstractResponseData { - $decrypted = openssl_decrypt( - base64_decode($cipher), - self::ENCRYPTION_METHOD, - $this->key, - 0, - $this->secretIv->getBytes() + + public function decode(string $base64cipher):BaseResponseData { + $encryptedMessage = new EncryptedMessage( + $base64cipher, + $this->iv, ); + $decrypted = $encryptedMessage->decrypt($this->key); if(!$decrypted) { throw new ResponseCipherDecryptionException(); } - $data = @unserialize( - $decrypted - ); - if($data === false) { + $data = json_decode($decrypted); + if($data === null) { throw new InvalidUserDataSerializationException(); } - return new UserData( - $data->{"uuid"}, + return new UserResponseData( + $data->{"uuid"} ?? $data->{"id"}, $data->{"email"}, - $data->{"fields"} ?? new StdClass() + $data->{"fields"} ?? [] ); } -} \ No newline at end of file +} diff --git a/src/User.php b/src/User.php new file mode 100644 index 0000000..06fe970 --- /dev/null +++ b/src/User.php @@ -0,0 +1,18 @@ +kvp[$key] ?? null; + } + + public function getKvp():array { + return $this->kvp; + } +} diff --git a/test/phpunit/AuthenticatorTest.php b/test/phpunit/AuthenticatorTest.php index 5fba507..31b5653 100644 --- a/test/phpunit/AuthenticatorTest.php +++ b/test/phpunit/AuthenticatorTest.php @@ -2,17 +2,18 @@ namespace Authwave\Test; use Authwave\Authenticator; -use Authwave\InitVector; use Authwave\NotLoggedInException; -use Authwave\ProviderUri\AdminUri; +use Authwave\ProviderUri\BaseProviderUri; use Authwave\ProviderUri\LoginUri; -use Authwave\ProviderUri\LogoutUri; use Authwave\RedirectHandler; -use Authwave\ResponseData\AbstractResponseData; +use Authwave\ResponseData\BaseResponseData; use Authwave\SessionData; use Authwave\SessionNotStartedException; use Authwave\Token; -use Authwave\ResponseData\UserData; +use Authwave\ResponseData\UserResponseData; +use Gt\Cipher\CipherText; +use Gt\Cipher\InitVector; +use Gt\Http\Uri; use PHPUnit\Framework\TestCase; use Psr\Http\Message\UriInterface; @@ -25,18 +26,6 @@ public function testConstructWithDefaultSessionNotStarted() { ); } - public function testConstructWithDefaultSession() { - $_SESSION = []; - new Authenticator( - "test-key", - "/" - ); - self::assertArrayHasKey( - Authenticator::SESSION_KEY, - $_SESSION - ); - } - public function testIsLoggedInFalseByDefault() { $_SESSION = []; $sut = new Authenticator( @@ -47,14 +36,15 @@ public function testIsLoggedInFalseByDefault() { } public function testIsLoggedInTrueWhenSessionDataSet() { - $userData = self::createMock(UserData::class); + $userData = self::createMock(UserResponseData::class); $sessionData = self::createMock(SessionData::class); $sessionData->expects(self::once()) ->method("getData") ->willReturn($userData); + /** @noinspection PhpArrayWriteIsNotUsedInspection */ $_SESSION = [ - Authenticator::SESSION_KEY => $sessionData + SessionData::class => $sessionData ]; $sut = new Authenticator( @@ -64,118 +54,15 @@ public function testIsLoggedInTrueWhenSessionDataSet() { self::assertTrue($sut->isLoggedIn()); } - public function testLogoutCallsLogoutUri() { - $sessionData = self::createMock(SessionData::class); - $_SESSION = [ - Authenticator::SESSION_KEY => $sessionData - ]; - - $redirectHandler = self::createMock(RedirectHandler::class); - $redirectHandler->expects(self::once()) - ->method("redirect") - ->with(self::callback(fn(UriInterface $uri) => - $uri->getHost() === "login.authwave.com" - && $uri->getPath() === "/logout" - )); - - $sut = new Authenticator( - "test-key", - "/", - LoginUri::DEFAULT_BASE_REMOTE_URI, - null, - $redirectHandler - ); - $sut->logout(); - self::assertNotEmpty($_SESSION); - } - - public function testCompleteAuthFromLogoutClearsSession() { - $token = self::createMock(Token::class); - - $sessionData = self::createMock(SessionData::class); - $sessionData->method("getToken") - ->willReturn($token); - - $_SESSION = [ - Authenticator::SESSION_KEY => $sessionData, - ]; - - $responseCipher = "abcdef"; - - $currentUri = "/example-page-" . uniqid(); - $currentUri .= "?"; - $currentUri .= http_build_query([ - Authenticator::RESPONSE_QUERY_PARAMETER => $responseCipher, - ]); - - $redirectHandler = self::createMock(RedirectHandler::class); - $redirectHandler->expects(self::once()) - ->method("redirect") - ->with(self::callback(fn(UriInterface $uri) => - $uri->getHost() == "" - && $uri->getPath() == $currentUri - )); - - new Authenticator( - "test-key", - "/", - LoginUri::DEFAULT_BASE_REMOTE_URI, - null, - $redirectHandler - ); - - self::assertEmpty($_SESSION); - } - - public function testLoginRedirects() { - $_SESSION = []; - - $redirectHandler = self::createMock(RedirectHandler::class); - $redirectHandler->expects(self::once()) - ->method("redirect") - ->with(self::callback(fn(UriInterface $uri) => - $uri->getHost() === LoginUri::DEFAULT_BASE_REMOTE_URI - )); - - $sut = new Authenticator( - "test-key", - "/", - LoginUri::DEFAULT_BASE_REMOTE_URI, - null, - $redirectHandler - ); - $sut->login(); - } - - public function testLoginRedirectsLocalhost() { - $_SESSION = []; - - $redirectHandler = self::createMock(RedirectHandler::class); - $redirectHandler->expects(self::once()) - ->method("redirect") - ->with(self::callback(fn(UriInterface $uri) => - $uri->getScheme() === "http" - && $uri->getHost() === "localhost" - && $uri->getPort() === 8081 - )); - - $sut = new Authenticator( - "test-key", - "/", - "http://localhost:8081", - null, - $redirectHandler - ); - $sut->login(); - } - public function testLoginRedirectsWithCorrectQueryString() { $_SESSION = []; $key = uniqid("key-"); $currentPath = uniqid("/path/"); - $cipher = "example-cipher"; + $cipherString = "example-cipher"; + $cipher = self::createMock(CipherText::class); + $cipher->method("__toString")->willReturn($cipherString); $ivString = "example-iv"; $iv = self::createMock(InitVector::class); @@ -189,9 +76,9 @@ public function testLoginRedirectsWithCorrectQueryString() { ->willReturn($iv); $expectedQueryParts = [ - LoginUri::QUERY_STRING_CIPHER => $cipher, + LoginUri::QUERY_STRING_CIPHER => (string)$cipher, LoginUri::QUERY_STRING_INIT_VECTOR => $ivString, - LoginUri::QUERY_STRING_CURRENT_PATH => bin2hex($currentPath), + LoginUri::QUERY_STRING_CURRENT_URI => bin2hex($currentPath), ]; $expectedQuery = http_build_query($expectedQueryParts); @@ -212,55 +99,24 @@ public function testLoginRedirectsWithCorrectQueryString() { $sut->login($token); } - public function testLoginDoesNothingWhenAlreadyLoggedIn() { - $sessionData = self::createMock(SessionData::class); - $_SESSION = [ - Authenticator::SESSION_KEY => $sessionData, - ]; - - $redirectHandler = self::createMock(RedirectHandler::class); - $redirectHandler->expects(self::never()) - ->method("redirect"); - - $sut = new Authenticator( - "test-key", - "/", - LoginUri::DEFAULT_BASE_REMOTE_URI, - null, - $redirectHandler - ); - - $sut->login(); - } - - public function testGetUuidThrowsExceptionWhenNotLoggedIn() { - $_SESSION = []; - $sut = new Authenticator( - "test-key", - "/" - ); - self::expectException(NotLoggedInException::class); - $sut->getUuid(); - } - public function testGetUuid() { - $expectedUuid = uniqid("example-uuid-"); + $exampleId = uniqid("example-id-"); - $userData = self::createMock(UserData::class); - $userData->method("getUuid") - ->willReturn($expectedUuid); + $userData = self::createMock(UserResponseData::class); + $userData->method("getId") + ->willReturn($exampleId); $sessionData = self::createMock(SessionData::class); $sessionData->method("getData") ->willReturn($userData); $_SESSION = [ - Authenticator::SESSION_KEY => $sessionData, + SessionData::class => $sessionData, ]; $sut = new Authenticator( "test-key", "/" ); - self::assertEquals($expectedUuid, $sut->getUuid()); + self::assertEquals($exampleId, $sut->getUser()->id); } public function testGetEmailThrowsExceptionWhenNotLoggedIn() { @@ -270,13 +126,13 @@ public function testGetEmailThrowsExceptionWhenNotLoggedIn() { "/" ); self::expectException(NotLoggedInException::class); - $sut->getEmail(); + $sut->getUser()->email; } public function testGetEmail() { $expectedEmail = "example@example.com"; - $userData = self::createMock(UserData::class); + $userData = self::createMock(UserResponseData::class); $userData->method("getEmail") ->willReturn($expectedEmail); $sessionData = self::createMock(SessionData::class); @@ -284,13 +140,42 @@ public function testGetEmail() { ->willReturn($userData); $_SESSION = [ - Authenticator::SESSION_KEY => $sessionData, + SessionData::class => $sessionData, ]; $sut = new Authenticator( "test-key", "/" ); - self::assertEquals($expectedEmail, $sut->getEmail()); + self::assertEquals($expectedEmail, $sut->getUser()->email); + } + + public function testCompleteAuthNotAffectedByQueryString() { + $redirectHandler = self::createMock(RedirectHandler::class); + $redirectHandler->expects(self::never()) + ->method("redirect"); + $_SESSION = []; + + new Authenticator( + "test-key", + "/example-path/?filter=something", + LoginUri::DEFAULT_BASE_REMOTE_URI, + null, + $redirectHandler + ); + } + + public function testGetAdminUri() { + $_SESSION = []; + $auth = new Authenticator( + "test-key", + "/example-path", + BaseProviderUri::DEFAULT_BASE_REMOTE_URI + ); + $sut = $auth->getAdminUri(); + self::assertEquals( + "/admin/", + $sut->getPath() + ); } public function testCompleteAuthNotLoggedIn() { @@ -300,10 +185,11 @@ public function testCompleteAuthNotLoggedIn() { $_SESSION = []; self::expectException(NotLoggedInException::class); - new Authenticator( + $sut = new Authenticator( "test-key", $currentUri ); + $sut->getUser(); } // When the remote provider redirects back to the client application, a query @@ -312,9 +198,22 @@ public function testCompleteAuthNotLoggedIn() { // decrypt properly, and should throw an exception to prevent unauthorised // access. public function testCompleteAuth() { + $keyBytes = str_repeat("0", SODIUM_CRYPTO_SECRETBOX_KEYBYTES); + $ivBytes = str_repeat("1", SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); + $plainTextMessage = json_encode([ + "id" => 123, + "email" => "person@example.com", + ]); + $encryptedMessage = sodium_crypto_secretbox( + $plainTextMessage, + $ivBytes, + $keyBytes, + ); + $currentUri = "/my-page?filter=example&" . Authenticator::RESPONSE_QUERY_PARAMETER - . "=0123456789abcdef"; + . "=" + . base64_encode($encryptedMessage); $redirectHandler = self::createMock(RedirectHandler::class); $redirectHandler->expects(self::once()) @@ -323,17 +222,22 @@ public function testCompleteAuth() { $uri->getQuery() === "filter=example" && $uri->getPath() === "/my-page" )); + $iv = self::createMock(InitVector::class); + $iv->method("getBytes")->willReturn($ivBytes); $token = self::createMock(Token::class); + $token->method("getSecretIv") + ->willReturn($iv); $sessionData = self::createMock(SessionData::class); $sessionData->method("getToken") ->willReturn($token); $_SESSION = [ - Authenticator::SESSION_KEY => $sessionData, + SessionData::class => $sessionData, ]; + new Authenticator( - "test-key", + $keyBytes, $currentUri, LoginUri::DEFAULT_BASE_REMOTE_URI, null, @@ -341,44 +245,171 @@ public function testCompleteAuth() { ); /** @var SessionData $newSessionData */ - $newSessionData = $_SESSION[Authenticator::SESSION_KEY]; + $newSessionData = $_SESSION[SessionData::class]; self::assertNotSame($sessionData, $newSessionData); self::assertInstanceOf( SessionData::class, $newSessionData ); self::assertInstanceOf( - AbstractResponseData::class, + BaseResponseData::class, $newSessionData->getData() ); } - public function testCompleteAuthNotAffectedByQueryString() { + public function testLoginDoesNothingWhenAlreadyLoggedIn() { + $sessionData = self::createMock(SessionData::class); + $sessionData->method("getData") + ->willReturn(self::createMock(UserResponseData::class)); + $_SESSION = [ + SessionData::class => $sessionData, + ]; + $redirectHandler = self::createMock(RedirectHandler::class); $redirectHandler->expects(self::never()) ->method("redirect"); + + $sut = new Authenticator( + "test-key", + "/", + LoginUri::DEFAULT_BASE_REMOTE_URI, + null, + $redirectHandler + ); + + $token = self::createMock(Token::class); + $requestCipherString = "example-request-cipher"; + $requestCipher = self::createMock(CipherText::class); + $requestCipher->method("__toString")->willReturn($requestCipherString); + $ivBytes = "0123456789"; + $iv = self::createMock(InitVector::class); + $iv->method("__toString")->willReturn($ivBytes); + + $token->method("generateRequestCipher") + ->willReturn($requestCipher); + $token->method("getIv") + ->willReturn($iv); + $sut->login($token); + } + + public function testGetUuidThrowsExceptionWhenNotLoggedIn() { $_SESSION = []; + $sut = new Authenticator( + "test-key", + "/" + ); + self::expectException(NotLoggedInException::class); + $sut->getUser()->id; + } - new Authenticator( + public function testLogoutCallsLogoutUri() { + $sessionData = self::createMock(SessionData::class); + $_SESSION = [ + SessionData::class => $sessionData + ]; + + $redirectHandler = self::createMock(RedirectHandler::class); + $redirectHandler->expects(self::once()) + ->method("redirect") + ->with(self::callback(function(UriInterface $uri):bool { + if($uri->getHost() !== "login.authwave.com") { + return false; + } + + parse_str($uri->getQuery(), $queryParts); + /** @var SessionData $session */ + $session = $_SESSION[SessionData::class]; + $token = $session->getToken(); + $decrypted = $token->decode( + $queryParts[BaseProviderUri::QUERY_STRING_CIPHER] + ); + return (bool)$decrypted; + })); + + $sut = new Authenticator( "test-key", - "/example-path?filter=something", + "/", LoginUri::DEFAULT_BASE_REMOTE_URI, null, $redirectHandler ); + $token = self::createMock(Token::class); + $cipherString = "example-request-cipher"; + $cipher = self::createMock(CipherText::class); + $cipher->method("__toString")->willReturn($cipherString); + $token->method("generateRequestCipher") + ->willReturn($cipher); + $ivBytes = "01234567890"; + $iv = self::createMock(InitVector::class); + $iv->method("__toString")->willReturn($ivBytes); + $token->method("getIv") + ->willReturn($iv); + $sut->logout($token); + self::assertNotEmpty($_SESSION); } - public function testGetAdminUri() { + public function testLoginRedirects() { $_SESSION = []; - $auth = new Authenticator( + + $redirectHandler = self::createMock(RedirectHandler::class); + $redirectHandler->expects(self::once()) + ->method("redirect") + ->with(self::callback(fn(UriInterface $uri) => + $uri->getHost() === LoginUri::DEFAULT_BASE_REMOTE_URI + )); + + $sut = new Authenticator( "test-key", - "/example-path", - LoginUri::DEFAULT_BASE_REMOTE_URI + "/", + LoginUri::DEFAULT_BASE_REMOTE_URI, + null, + $redirectHandler ); - $sut = $auth->getAdminUri(); - self::assertEquals( - "/admin", - $sut->getPath() + + $token = self::createMock(Token::class); + $cipherString = "example-request-cipher"; + $cipher = self::createMock(CipherText::class); + $cipher->method("__toString")->willReturn($cipherString); + $token->method("generateRequestCipher") + ->willReturn($cipher); + $ivBytes = "01234567890"; + $iv = self::createMock(InitVector::class); + $iv->method("__toString")->willReturn($ivBytes); + $token->method("getIv") + ->willReturn($iv); + $sut->login($token); + } + + public function testLoginRedirectsLocalhost() { + $_SESSION = []; + + $redirectHandler = self::createMock(RedirectHandler::class); + $redirectHandler->expects(self::once()) + ->method("redirect") + ->with(self::callback(fn(UriInterface $uri) => + $uri->getScheme() === "http" + && $uri->getHost() === "localhost" + && $uri->getPort() === 8081 + )); + + $sut = new Authenticator( + "test-key", + "/", + "http://localhost:8081", + null, + $redirectHandler ); + $token = self::createMock(Token::class); + $cipherString = "example-request-cipher"; + $cipher = self::createMock(CipherText::class); + $cipher->method("__toString")->willReturn($cipherString); + $token->method("generateRequestCipher") + ->willReturn($cipher); + $ivBytes = "01234567890"; + $iv = self::createMock(InitVector::class); + $iv->method("__toString")->willReturn($ivBytes); + $token->method("getIv") + ->willReturn($iv); + $sut->login($token); } -} \ No newline at end of file +} diff --git a/test/phpunit/MalformedReponseDataException.php b/test/phpunit/MalformedReponseDataException.php deleted file mode 100644 index a8660bb..0000000 --- a/test/phpunit/MalformedReponseDataException.php +++ /dev/null @@ -1,6 +0,0 @@ -getScheme() ); } -} \ No newline at end of file +} diff --git a/test/phpunit/ProviderUri/AdminUriTest.php b/test/phpunit/ProviderUri/AdminUriTest.php index e2e5db7..da82940 100644 --- a/test/phpunit/ProviderUri/AdminUriTest.php +++ b/test/phpunit/ProviderUri/AdminUriTest.php @@ -8,8 +8,8 @@ class AdminUriTest extends TestCase { public function testPathAccount() { $sut = new AdminUri("example.com"); self::assertEquals( - "/admin", + "/admin/", $sut->getPath() ); } -} \ No newline at end of file +} diff --git a/test/phpunit/ProviderUri/LoginUriTest.php b/test/phpunit/ProviderUri/LoginUriTest.php index 794c754..3b56f50 100644 --- a/test/phpunit/ProviderUri/LoginUriTest.php +++ b/test/phpunit/ProviderUri/LoginUriTest.php @@ -1,15 +1,18 @@ method("__toString")->willReturn($cipherString); $mockIvValue = str_repeat("0", 16); $iv = self::createMock(InitVector::class); $iv->method("__toString") @@ -18,7 +21,7 @@ public function testQueryString() { $baseUri = self::createMock(UriInterface::class); $token = self::createMock(Token::class); $token->method("generateRequestCipher") - ->willReturn($mockCipherValue); + ->willReturn($cipher); $token->method("getIv") ->willReturn($iv); @@ -31,7 +34,7 @@ public function testQueryString() { parse_str($sut->getQuery(), $queryParts); self::assertEquals( - $mockCipherValue, + $cipher, $queryParts[LoginUri::QUERY_STRING_CIPHER], ); @@ -42,7 +45,7 @@ public function testQueryString() { self::assertEquals( bin2hex($returnPath), - $queryParts[LoginUri::QUERY_STRING_CURRENT_PATH] + $queryParts[LoginUri::QUERY_STRING_CURRENT_URI] ); } -} \ No newline at end of file +} diff --git a/test/phpunit/SessionDataTest.php b/test/phpunit/SessionDataTest.php index 8b8d07b..160776c 100644 --- a/test/phpunit/SessionDataTest.php +++ b/test/phpunit/SessionDataTest.php @@ -4,7 +4,7 @@ use Authwave\NotLoggedInException; use Authwave\SessionData; use Authwave\Token; -use Authwave\ResponseData\UserData; +use Authwave\ResponseData\UserResponseData; use PHPUnit\Framework\TestCase; class SessionDataTest extends TestCase { @@ -28,8 +28,8 @@ public function testGetUserDataNull() { public function testGetUserData() { $token = self::createMock(Token::class); - $userData = self::createMock(UserData::class); + $userData = self::createMock(UserResponseData::class); $sut = new SessionData($token, $userData); self::assertSame($userData, $sut->getData()); } -} \ No newline at end of file +} diff --git a/test/phpunit/TokenTest.php b/test/phpunit/TokenTest.php index d001a7b..b792063 100644 --- a/test/phpunit/TokenTest.php +++ b/test/phpunit/TokenTest.php @@ -1,101 +1,94 @@ generateRequestCipher(); $cipher2 = $token->generateRequestCipher(); - self::assertSame($cipher1, $cipher2); + self::assertEquals($cipher1, $cipher2); } - public function testGenerateRequestCipherDifferentForDifferentTokenSameDetails() { - $key = "test-key"; + public function testGenerateRequestCipher_differentForDifferentTokenSameDetails():void { + $key = str_repeat("0", 32); $token1 = new Token($key); $token2 = new Token($key); $cipher1 = $token1->generateRequestCipher(); $cipher2 = $token2->generateRequestCipher(); - self::assertNotSame($cipher1, $cipher2); + self::assertNotEquals($cipher1, $cipher2); } - public function testGetIv() { + public function testGetIv():void { $iv = self::createMock(InitVector::class); $sut = new Token("", null, $iv); self::assertSame($iv, $sut->getIv()); } - public function testDecryptResponseCipherInvalid() { - $cipher = "0123456789abcdef"; - $sut = new Token("test-key"); - self::expectException(ResponseCipherDecryptionException::class); - $sut->decryptResponseCipher($cipher); + public function testDecrypt_responseCipherInvalid() { + $key = str_repeat("0", 32); + $sut = new Token($key); + self::expectException(DecryptionFailureException::class); + $sut->decode("not a real cipher"); } public function testDecryptResponseCipherBadJson() { - $key = uniqid("test-key-"); - $secretIv = self::createMock(InitVector::class); - $secretIv->method("getBytes") - ->willReturn(str_repeat("0", 16)); + $keyString = str_repeat("0", SODIUM_CRYPTO_SECRETBOX_KEYBYTES); + $sessionIv = self::createMock(InitVector::class); + $sessionIv->method("getBytes") + ->willReturn(str_repeat("a", SODIUM_CRYPTO_SECRETBOX_NONCEBYTES)); $iv = self::createMock(InitVector::class); $iv->method("getBytes") - ->willReturn(str_repeat("f", 16)); - $cipher = openssl_encrypt( + ->willReturn(str_repeat("f", SODIUM_CRYPTO_SECRETBOX_NONCEBYTES)); + + $nonce = $iv->getBytes(); + $manualCipherString = sodium_crypto_secretbox( "{badly-formed: json]", - Token::ENCRYPTION_METHOD, - implode("|", [$key, $secretIv->getBytes()]), - 0, - $iv->getBytes() + $nonce, + $keyString, ); - $cipher = base64_encode($cipher); - $sut = new Token($key, $secretIv, $iv); + $decryptedCipherString = sodium_crypto_secretbox_open($manualCipherString, $nonce, $keyString); + + $base64Cipher = base64_encode($manualCipherString); + $sut = new Token($keyString, $sessionIv, $iv); self::expectException(InvalidUserDataSerializationException::class); - $sut->decryptResponseCipher($cipher); + $sut->decode($base64Cipher); } public function testDecryptResponseCipher() { - $clientKey = uniqid("test-key-"); -// SecretIv is stored in the client application's session only. + $clientKeyBytes = str_repeat("0", SODIUM_CRYPTO_SECRETBOX_KEYBYTES); + $ivBytes = str_repeat("1", SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); + $uuid = "abcdef"; + $email = "person@example.com"; + $cipher = sodium_crypto_secretbox( + json_encode((object)[ + "id" => $uuid, + "email" => $email, + ]), + $ivBytes, + $clientKeyBytes, + ); + $secretIv = self::createMock(InitVector::class); - $secretIv->method("getBytes") - ->willReturn(str_repeat("0", 16)); $iv = self::createMock(InitVector::class); $iv->method("getBytes") - ->willReturn(str_repeat("f", 16)); + ->willReturn($ivBytes); - $uuid = "aabb-ccdd-eeff"; - $email = "user@example.com"; - $serialized = serialize((object)[ - "uuid" => $uuid, - "email" => $email, - "fields" => (object)[ - "example1" => "value1", - ] - ]); - - $cipher = openssl_encrypt( - $serialized, - Token::ENCRYPTION_METHOD, - $clientKey, - 0, - $secretIv->getBytes() - ); - $cipher = base64_encode($cipher); - $sut = new Token($clientKey, $secretIv, $iv); - $userData = $sut->decryptResponseCipher($cipher); - self::assertInstanceOf(UserData::class, $userData); - self::assertEquals($uuid, $userData->getUuid()); + $sut = new Token($clientKeyBytes, $secretIv, $iv); + $userData = $sut->decode(base64_encode($cipher)); + self::assertInstanceOf(UserResponseData::class, $userData); + self::assertEquals($uuid, $userData->getId()); self::assertEquals($email, $userData->getEmail()); } -} \ No newline at end of file +}