From 63146eb697567628a2645f7489b07fd3893a1ba9 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Fri, 22 Jul 2022 18:07:24 +0100 Subject: [PATCH 01/21] wip: cipher upgrade --- README.md | 11 +- composer.json | 8 +- composer.lock | 979 +++++++++++------- src/Authenticator.php | 74 +- src/InitVector.php | 18 - .../{AdminUri.php => AdminUriBase.php} | 4 +- ...actProviderUri.php => BaseProviderUri.php} | 8 +- .../{LoginUri.php => LoginUriBase.php} | 4 +- .../{LogoutUri.php => LogoutUriBase.php} | 6 +- .../{ProfileUri.php => ProfileUriBase.php} | 4 +- src/ResponseData/UserData.php | 4 +- src/Token.php | 51 +- test/phpunit/AuthenticatorTest.php | 332 +++--- .../phpunit/MalformedReponseDataException.php | 6 - .../ProviderUri/AbstractProviderUriTest.php | 16 +- test/phpunit/ProviderUri/AdminUriTest.php | 6 +- test/phpunit/ProviderUri/LoginUriTest.php | 12 +- test/phpunit/TokenTest.php | 64 +- 18 files changed, 932 insertions(+), 675 deletions(-) delete mode 100644 src/InitVector.php rename src/ProviderUri/{AdminUri.php => AdminUriBase.php} (82%) rename src/ProviderUri/{AbstractProviderUri.php => BaseProviderUri.php} (86%) rename src/ProviderUri/{LoginUri.php => LoginUriBase.php} (95%) rename src/ProviderUri/{LogoutUri.php => LogoutUriBase.php} (84%) rename src/ProviderUri/{ProfileUri.php => ProfileUriBase.php} (87%) delete mode 100644 test/phpunit/MalformedReponseDataException.php diff --git a/README.md b/README.md index 539097a..5970f8b 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,4 @@ else {

Log in

HTML; } -``` \ No newline at end of file +``` diff --git a/composer.json b/composer.json index b360806..78b30fa 100644 --- a/composer.json +++ b/composer.json @@ -3,13 +3,15 @@ "description": "PHP client library to implement Authwave in your application", "require": { - "php": ">=7.4", + "php": ">=8.1", "ext-openssl": "*", + "phpgt/cipher": "dev-master", "phpgt/http": "1.*", "phpgt/session": ">=1.1" }, "require-dev": { - "phpunit/phpunit": "8.*" + "phpunit/phpunit": "8.*", + "ext-sodium": "*" }, "autoload": { "psr-4": { @@ -21,4 +23,4 @@ "Authwave\\Test\\": "./test/phpunit" } } -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index 396bbef..bf392a9 100644 --- a/composer.lock +++ b/composer.lock @@ -1,73 +1,86 @@ { "_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": "33cc3f246595defd43ac4a235110f89a", "packages": [ { - "name": "phpgt/cookie", - "version": "v1.0.2", + "name": "phpgt/cipher", + "version": "dev-master", "source": { "type": "git", - "url": "https://github.com/PhpGt/Cookie.git", - "reference": "dd811b6f04becc7c1f8524d84ad02e4635f4a94e" + "url": "https://github.com/PhpGt/Cipher.git", + "reference": "5cef809acd0d77d24ea11ece9ba0e1ed4c8736fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhpGt/Cookie/zipball/dd811b6f04becc7c1f8524d84ad02e4635f4a94e", - "reference": "dd811b6f04becc7c1f8524d84ad02e4635f4a94e", + "url": "https://api.github.com/repos/PhpGt/Cipher/zipball/5cef809acd0d77d24ea11ece9ba0e1ed4c8736fd", + "reference": "5cef809acd0d77d24ea11ece9ba0e1ed4c8736fd", "shasum": "" }, "require": { - "php": ">=7.2" + "ext-sodium": "*", + "php": ">=8.0", + "phpgt/http": "v1.*" }, "require-dev": { - "phpunit/phpunit": "8.*" + "phpstan/phpstan": "v1.8.0", + "phpunit/phpunit": "v9.5.21" }, + "default-branch": true, "type": "library", "autoload": { "psr-4": { - "Gt\\Cookie\\": "./src" + "Gt\\Cipher\\": "./src" } }, "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], "authors": [ { "name": "Greg Bowler", - "email": "greg.bowler@g105b.com" + "email": "greg.bowler@g105b.com", + "homepage": "https://www.g105b.com", + "role": "Developer" } ], - "description": "Object oriented cookie handler.", - "time": "2019-07-17T20:11:11+00:00" + "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/master" + }, + "funding": [ + { + "url": "https://github.com/sponsors/PhpGt", + "type": "github" + } + ], + "time": "2022-07-20T11:29:08+00:00" }, { "name": "phpgt/http", - "version": "v1.0.3", + "version": "v1.1.4", "source": { "type": "git", "url": "https://github.com/PhpGt/Http.git", - "reference": "394ecdd2a2e2686a467cdf2f837c72105d68398f" + "reference": "80ed7269050bc0f2de2402c5461711dd2418f55f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhpGt/Http/zipball/394ecdd2a2e2686a467cdf2f837c72105d68398f", - "reference": "394ecdd2a2e2686a467cdf2f837c72105d68398f", + "url": "https://api.github.com/repos/PhpGt/Http/zipball/80ed7269050bc0f2de2402c5461711dd2418f55f", + "reference": "80ed7269050bc0f2de2402c5461711dd2418f55f", "shasum": "" }, "require": { - "php": ">=7.2", - "phpgt/cookie": "*", - "phpgt/input": "*", - "psr/http-message": "^1.0.1", - "willdurand/negotiation": "^2.3" + "php": ">=8.0", + "phpgt/input": "^v1", + "psr/http-message": "^v1.0.1", + "willdurand/negotiation": "v3.0.*" }, "require-dev": { - "phpunit/phpunit": "8.*" + "phpstan/phpstan": ">=0.12.64", + "phpunit/phpunit": "9.*" }, "type": "library", "autoload": { @@ -80,27 +93,39 @@ "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.1.4" + }, + "funding": [ + { + "url": "https://github.com/sponsors/PhpGt", + "type": "github" + } + ], + "time": "2021-06-23T15:45:09+00:00" }, { "name": "phpgt/input", - "version": "v1.1.2", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/PhpGt/Input.git", - "reference": "ae78931ffd5c4800f362db61bd86f8e545733e86" + "reference": "95136bcbe1c50c3d8db90eb3b1944d049ec2b2a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhpGt/Input/zipball/ae78931ffd5c4800f362db61bd86f8e545733e86", - "reference": "ae78931ffd5c4800f362db61bd86f8e545733e86", + "url": "https://api.github.com/repos/PhpGt/Input/zipball/95136bcbe1c50c3d8db90eb3b1944d049ec2b2a0", + "reference": "95136bcbe1c50c3d8db90eb3b1944d049ec2b2a0", "shasum": "" }, "require": { - "phpgt/http": "*" + "php": ">=8.0", + "phpgt/http": "^v1.1" }, "require-dev": { - "phpunit/phpunit": "8.*" + "phpstan/phpstan": "^v1.4", + "phpunit/phpunit": "^v9.5" }, "type": "library", "autoload": { @@ -113,27 +138,39 @@ "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.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/PhpGt", + "type": "github" + } + ], + "time": "2022-03-18T19:03:02+00:00" }, { "name": "phpgt/session", - "version": "v1.1.0", + "version": "v1.1.5", "source": { "type": "git", "url": "https://github.com/PhpGt/Session.git", - "reference": "97b793244a6cde7858f5bf07ecfad67ce4d6002b" + "reference": "4ec55d2e38117b095d525b3e8507bdb5efac5c8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhpGt/Session/zipball/97b793244a6cde7858f5bf07ecfad67ce4d6002b", - "reference": "97b793244a6cde7858f5bf07ecfad67ce4d6002b", + "url": "https://api.github.com/repos/PhpGt/Session/zipball/4ec55d2e38117b095d525b3e8507bdb5efac5c8a", + "reference": "4ec55d2e38117b095d525b3e8507bdb5efac5c8a", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=8.0", + "phpgt/typesafegetter": "^v1.2" }, "require-dev": { - "phpunit/phpunit": "^8.0" + "phpstan/phpstan": "^v1.4", + "phpunit/phpunit": "^9.5" }, "type": "library", "autoload": { @@ -146,7 +183,67 @@ "MIT" ], "description": "Encapsulated user sessions.", - "time": "2020-02-27T16:06:07+00:00" + "support": { + "issues": "https://github.com/PhpGt/Session/issues", + "source": "https://github.com/PhpGt/Session/tree/v1.1.5" + }, + "funding": [ + { + "url": "https://github.com/sponsors/PhpGt", + "type": "github" + } + ], + "time": "2022-03-08T15:40:23+00:00" + }, + { + "name": "phpgt/typesafegetter", + "version": "v1.2.4", + "source": { + "type": "git", + "url": "https://github.com/PhpGt/TypeSafeGetter.git", + "reference": "ebffd758e69b8a0eebcad30f3daf408915b9ddf3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PhpGt/TypeSafeGetter/zipball/ebffd758e69b8a0eebcad30f3daf408915b9ddf3", + "reference": "ebffd758e69b8a0eebcad30f3daf408915b9ddf3", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpstan/phpstan": "v1.8.0", + "phpunit/phpunit": "v9.5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Gt\\TypeSafeGetter\\": "./src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "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.2.4" + }, + "funding": [ + { + "url": "https://github.com/sponsors/PhpGt", + "type": "github" + } + ], + "time": "2022-07-08T17:17:42+00:00" }, { "name": "psr/http-message", @@ -196,32 +293,35 @@ "request", "response" ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/master" + }, "time": "2016-08-06T14:39:51+00:00" }, { "name": "willdurand/negotiation", - "version": "v2.3.1", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/willdurand/Negotiation.git", - "reference": "03436ededa67c6e83b9b12defac15384cb399dc9" + "reference": "04e14f38d4edfcc974114a07d2777d90c98f3d9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/willdurand/Negotiation/zipball/03436ededa67c6e83b9b12defac15384cb399dc9", - "reference": "03436ededa67c6e83b9b12defac15384cb399dc9", + "url": "https://api.github.com/repos/willdurand/Negotiation/zipball/04e14f38d4edfcc974114a07d2777d90c98f3d9c", + "reference": "04e14f38d4edfcc974114a07d2777d90c98f3d9c", "shasum": "" }, "require": { - "php": ">=5.4.0" + "php": ">=7.1.0" }, "require-dev": { - "phpunit/phpunit": "~4.5" + "symfony/phpunit-bridge": "^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -248,42 +348,42 @@ "header", "negotiation" ], - "time": "2017-05-14T17:21:12+00:00" + "support": { + "issues": "https://github.com/willdurand/Negotiation/issues", + "source": "https://github.com/willdurand/Negotiation/tree/3.0.0" + }, + "time": "2020-09-25T08:01:41+00:00" } ], "packages-dev": [ { "name": "doctrine/instantiator", - "version": "1.3.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "ae466f726242e637cebdd526a7d991b9433bacf1" + "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1", - "reference": "ae466f726242e637cebdd526a7d991b9433bacf1", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", + "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^6.0", + "doctrine/coding-standard": "^9", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.13", - "phpstan/phpstan-phpunit": "^0.11", - "phpstan/phpstan-shim": "^0.11", - "phpunit/phpunit": "^7.0" + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.22" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, "autoload": { "psr-4": { "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" @@ -297,7 +397,7 @@ { "name": "Marco Pivetta", "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" + "homepage": "https://ocramius.github.io/" } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", @@ -306,41 +406,60 @@ "constructor", "instantiate" ], - "time": "2019-10-21T16:45:58+00:00" + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.4.1" + }, + "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-03-03T08:28:38+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.9.5", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef" + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef", - "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.1 || ^8.0" }, - "replace": { - "myclabs/deep-copy": "self.version" + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" }, "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" + "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": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, "files": [ "src/DeepCopy/deep_copy.php" - ] + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -354,32 +473,43 @@ "object", "object graph" ], - "time": "2020-01-17T21:11:47+00:00" + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2022-03-03T13:19:32+00:00" }, { "name": "phar-io/manifest", - "version": "1.0.3", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", - "phar-io/version": "^2.0", - "php": "^5.6 || ^7.0" + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -409,24 +539,28 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2018-07-08T19:23:20+00:00" + "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": "phar-io/version", - "version": "2.0.1", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -456,32 +590,33 @@ } ], "description": "Library for handling version information and constraints", - "time": "2018-07-08T19:19:57+00:00" + "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": "phpdocumentor/reflection-common", - "version": "2.0.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a" + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a", - "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", "shasum": "" }, "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "~6" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.x-dev" + "dev-2.x": "2.x-dev" } }, "autoload": { @@ -508,32 +643,36 @@ "reflection", "static analysis" ], - "time": "2018-08-07T13:53:10+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.1.0", + "version": "5.3.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e" + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", - "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", "shasum": "" }, "require": { - "ext-filter": "^7.1", - "php": "^7.2", - "phpdocumentor/reflection-common": "^2.0", - "phpdocumentor/type-resolver": "^1.0", - "webmozart/assert": "^1" + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" }, "require-dev": { - "doctrine/instantiator": "^1", - "mockery/mockery": "^1" + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" }, "type": "library", "extra": { @@ -561,34 +700,38 @@ } ], "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" + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.1.0", + "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "7462d5f123dfc080dfdf26897032a6513644fc95" + "reference": "77a32518733312af16a44300404e945338981de3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/7462d5f123dfc080dfdf26897032a6513644fc95", - "reference": "7462d5f123dfc080dfdf26897032a6513644fc95", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", + "reference": "77a32518733312af16a44300404e945338981de3", "shasum": "" }, "require": { - "php": "^7.2", + "php": "^7.2 || ^8.0", "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "ext-tokenizer": "^7.2", - "mockery/mockery": "~1" + "ext-tokenizer": "*", + "psalm/phar": "^4.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-1.x": "1.x-dev" } }, "autoload": { @@ -607,37 +750,41 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "time": "2020-02-18T18:59:58+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" + }, + "time": "2022-03-15T21:29:03+00:00" }, { "name": "phpspec/prophecy", - "version": "v1.10.2", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9" + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/b4400efc9d206e83138e2bb97ed7f5b14b831cd9", - "reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13", + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13", "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" + "doctrine/instantiator": "^1.2", + "php": "^7.2 || ~8.0, <8.2", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { - "phpspec/phpspec": "^2.5 || ^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + "phpspec/phpspec": "^6.0 || ^7.0", + "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.10.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { @@ -670,29 +817,33 @@ "spy", "stub" ], - "time": "2020-01-20T15:57:02+00:00" + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.15.0" + }, + "time": "2021-12-08T12:19:24+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 +884,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 +944,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 +995,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 +1048,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,52 +1107,60 @@ "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.27", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "018b6ac3c8ab20916db85fa91bf6465acb64d1e0" + "reference": "df70070f2711b8fe8dcca0797c1239ede8c94be6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/018b6ac3c8ab20916db85fa91bf6465acb64d1e0", - "reference": "018b6ac3c8ab20916db85fa91bf6465acb64d1e0", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/df70070f2711b8fe8dcca0797c1239ede8c94be6", + "reference": "df70070f2711b8fe8dcca0797c1239ede8c94be6", "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", + "phpspec/prophecy": "^1.10.3", + "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/diff": "^3.0.2", - "sebastian/environment": "^4.2.2", - "sebastian/exporter": "^3.1.1", + "sebastian/environment": "^4.2.3", + "sebastian/exporter": "^3.1.2", "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": "*", @@ -1005,27 +1198,41 @@ "testing", "xunit" ], - "time": "2020-01-08T08:49:49+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.27" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-06-19T12:11:16+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 +1257,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.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" + "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", + "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", "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 +1307,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -1101,10 +1322,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 +1331,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.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T08:04:30+00:00" }, { "name": "sebastian/diff", - "version": "3.0.2", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" + "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", + "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=7.1" }, "require-dev": { "phpunit/phpunit": "^7.5 || ^8.0", @@ -1153,13 +1380,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 +1397,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.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:59:04+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 +1460,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.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" + "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", - "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0c32ea2e40dbf59de29f3b49bf375176ce7dd8db", + "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db", "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 +1537,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.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-11-11T13:51:24+00:00" }, { "name": "sebastian/global-state", - "version": "3.0.0", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4" + "reference": "de036ec91d55d2a9e0db2ba975b512cdb1c23921" }, "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/de036ec91d55d2a9e0db2ba975b512cdb1c23921", + "reference": "de036ec91d55d2a9e0db2ba975b512cdb1c23921", "shasum": "" }, "require": { - "php": "^7.2", + "php": ">=7.2", "sebastian/object-reflector": "^1.1.1", "sebastian/recursion-context": "^3.0" }, @@ -1344,24 +1601,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.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-02-10T06:55:38+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 +1658,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 +1713,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 +1761,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 +1776,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 +1828,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 +1884,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 +1937,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" + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/master" }, - "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" - ] - }, - "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,33 +1981,49 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2019-06-13T22:48:21+00:00" + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" }, { "name": "webmozart/assert", - "version": "1.7.0", + "version": "1.11.0", "source": { "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "aed98a490f9a8f78468232db345ab9cf606cf598" + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/aed98a490f9a8f78468232db345ab9cf606cf598", - "reference": "aed98a490f9a8f78468232db345ab9cf606cf598", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0", - "symfony/polyfill-ctype": "^1.8" + "ext-ctype": "*", + "php": "^7.2 || ^8.0" }, "conflict": { - "vimeo/psalm": "<3.6.0" + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" }, "require-dev": { - "phpunit/phpunit": "^4.8.36 || ^7.5.13" + "phpunit/phpunit": "^8.5.13" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -1766,18 +2045,24 @@ "check", "validate" ], - "time": "2020-02-14T12:15:55+00:00" + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "phpgt/cipher": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.4", - "ext-openssl": "*", - "ext-json": "*" + "php": ">=8.1", + "ext-openssl": "*" }, - "platform-dev": [] + "platform-dev": [], + "plugin-api-version": "2.2.0" } diff --git a/src/Authenticator.php b/src/Authenticator.php index ab1f471..48c9158 100644 --- a/src/Authenticator.php +++ b/src/Authenticator.php @@ -1,11 +1,11 @@ session = $this->session ?? new GlobalSessionContainer(); - if(!$session->contains(self::SESSION_KEY)) { + if(!$this->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()); + $this->session->set(self::SESSION_KEY, new SessionData()); } /** @var SessionData $sessionData*/ - $sessionData = $session->get(self::SESSION_KEY); + $sessionData = $this->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(); @@ -53,16 +42,14 @@ public function __construct( } public function isLoggedIn():bool { - $userData = null; - try { - $userData = $this->sessionData->getData(); + $this->sessionData->getData(); } catch(NotLoggedInException $exception) { return false; } - return isset($userData); + return true; } public function login(Token $token = null):void { @@ -91,9 +78,9 @@ public function logout(Token $token = null):void { $this->redirectHandler->redirect($this->getLogoutUri($token)); } - public function getUuid():string { + public function getId():string { $userData = $this->sessionData->getData(); - return $userData->getUuid(); + return $userData->getId(); } public function getEmail():string { @@ -106,16 +93,16 @@ public function getField(string $name):?string { return $userData->getField($name); } - public function getLoginUri(Token $token):AbstractProviderUri { - return new LoginUri( + public function getLoginUri(Token $token):BaseProviderUri { + return new LoginUriBase( $token, $this->currentUriPath, $this->authwaveHost ); } - private function getLogoutUri(Token $token):AbstractProviderUri { - return new LogoutUri( + private function getLogoutUri(Token $token):BaseProviderUri { + return new LogoutUriBase( $token, $this->currentUriPath, $this->authwaveHost @@ -123,7 +110,7 @@ private function getLogoutUri(Token $token):AbstractProviderUri { } public function getAdminUri():UriInterface { - return new AdminUri($this->authwaveHost); + return new AdminUriBase($this->authwaveHost); } public function getProfileUri(Token $token = null):UriInterface { @@ -131,23 +118,24 @@ public function getProfileUri(Token $token = null):UriInterface { $token = new Token($this->clientKey); } - return new ProfileUri( + return new ProfileUriBase( $token, - $this->getUuid(), + $this->getId(), $this->currentUriPath, $this->authwaveHost ); } private function completeAuth():void { - $responseCipher = $this->getResponseCipher(); + return; + $queryData = $this->getQueryData(); - if(!$responseCipher) { + if(!$queryData) { return; } $token = $this->sessionData->getToken(); - $userData = $token->decryptResponseCipher($responseCipher); + $userData = $token->decode($queryData); $this->session->set( self::SESSION_KEY, new SessionData($token, $userData) @@ -159,7 +147,7 @@ private function completeAuth():void { ); } - private function getResponseCipher():?string { + private function getQueryData():?string { $queryString = parse_url( $this->currentUriPath, PHP_URL_QUERY @@ -176,4 +164,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/AdminUriBase.php similarity index 82% rename from src/ProviderUri/AdminUri.php rename to src/ProviderUri/AdminUriBase.php index f8b1ae3..3c932b6 100644 --- a/src/ProviderUri/AdminUri.php +++ b/src/ProviderUri/AdminUriBase.php @@ -1,7 +1,7 @@ path = "/admin"; } -} \ No newline at end of file +} diff --git a/src/ProviderUri/AbstractProviderUri.php b/src/ProviderUri/BaseProviderUri.php similarity index 86% rename from src/ProviderUri/AbstractProviderUri.php rename to src/ProviderUri/BaseProviderUri.php index bdd63f4..404daf3 100644 --- a/src/ProviderUri/AbstractProviderUri.php +++ b/src/ProviderUri/BaseProviderUri.php @@ -5,7 +5,7 @@ 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"; @@ -35,12 +35,12 @@ protected function normaliseBaseUri(string $baseUri):Uri { protected function buildQuery( Token $token, string $currentPath, - string $message = null + string $message = "" ):string { return http_build_query([ - self::QUERY_STRING_CIPHER => (string)$token->generateRequestCipher($message), + self::QUERY_STRING_CIPHER => $token->generateRequestCipher($message), self::QUERY_STRING_INIT_VECTOR => (string)$token->getIv(), self::QUERY_STRING_CURRENT_PATH => bin2hex($currentPath), ]); } -} \ No newline at end of file +} diff --git a/src/ProviderUri/LoginUri.php b/src/ProviderUri/LoginUriBase.php similarity index 95% rename from src/ProviderUri/LoginUri.php rename to src/ProviderUri/LoginUriBase.php index a24a6ba..a36fdbe 100644 --- a/src/ProviderUri/LoginUri.php +++ b/src/ProviderUri/LoginUriBase.php @@ -10,7 +10,7 @@ * 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 LoginUriBase 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 @@ -29,4 +29,4 @@ public function __construct( parent::__construct($baseRemoteUri); $this->query = $this->buildQuery($token, $currentPath); } -} \ No newline at end of file +} diff --git a/src/ProviderUri/LogoutUri.php b/src/ProviderUri/LogoutUriBase.php similarity index 84% rename from src/ProviderUri/LogoutUri.php rename to src/ProviderUri/LogoutUriBase.php index c2ed192..f6c440c 100644 --- a/src/ProviderUri/LogoutUri.php +++ b/src/ProviderUri/LogoutUriBase.php @@ -3,7 +3,7 @@ use Authwave\Token; -class LogoutUri extends AbstractProviderUri { +class LogoutUriBase extends BaseProviderUri { public function __construct( Token $token, string $currentPath = "/", @@ -15,7 +15,7 @@ public function __construct( $this->query = $this->buildQuery( $token, $currentPath, - "action=logout" + "action=logout", ); } -} \ No newline at end of file +} diff --git a/src/ProviderUri/ProfileUri.php b/src/ProviderUri/ProfileUriBase.php similarity index 87% rename from src/ProviderUri/ProfileUri.php rename to src/ProviderUri/ProfileUriBase.php index 6ca73f7..8b217b9 100644 --- a/src/ProviderUri/ProfileUri.php +++ b/src/ProviderUri/ProfileUriBase.php @@ -3,7 +3,7 @@ use Authwave\Token; -class ProfileUri extends AbstractProviderUri { +class ProfileUriBase extends BaseProviderUri { public function __construct( Token $token, string $uuid, @@ -15,4 +15,4 @@ public function __construct( $this->path = "/profile"; $this->query = $this->buildQuery($token, $uuid); } -} \ No newline at end of file +} diff --git a/src/ResponseData/UserData.php b/src/ResponseData/UserData.php index 01ebd49..2ea4e77 100644 --- a/src/ResponseData/UserData.php +++ b/src/ResponseData/UserData.php @@ -19,7 +19,7 @@ public function __construct( parent::__construct($message); } - public function getUuid():string { + public function getId():string { return $this->uuid; } @@ -30,4 +30,4 @@ public function getEmail():string { public function getField(string $name):?string { return $this->fields->{$name} ?? null; } -} \ No newline at end of file +} diff --git a/src/Token.php b/src/Token.php index 223008a..6307a29 100644 --- a/src/Token.php +++ b/src/Token.php @@ -3,22 +3,25 @@ use Authwave\ResponseData\AbstractResponseData; use Authwave\ResponseData\UserData; +use Gt\Cipher\CipherText; +use Gt\Cipher\InitVector; +use Gt\Cipher\Key; +use Gt\Cipher\Message\EncryptedMessage; +use Gt\Cipher\Message\PlainTextMessage; use StdClass; class Token { - const ENCRYPTION_METHOD = "aes128"; - - private string $key; + private Key $key; private InitVector $secretIv; private InitVector $iv; public function __construct( - string $key, - InitVector $secretIv = null, - InitVector $iv = null + string $keyString, + InitVector $sessionIv = null, + InitVector $iv = null, ) { - $this->key = $key; - $this->secretIv = $secretIv ?? new InitVector(); + $this->key = new Key($keyString); + $this->secretIv = $sessionIv ?? new InitVector(); $this->iv = $iv ?? new InitVector(); } @@ -34,35 +37,21 @@ public function getIv():InitVector { * 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 generateRequestCipher(string $message = ""):CipherText { + $plainTextMessage = new PlainTextMessage($message, $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):AbstractResponseData { + $encryptedMessage = new EncryptedMessage( + $base64cipher, + $this->iv, ); + $decrypted = $encryptedMessage->decrypt($this->key); if(!$decrypted) { throw new ResponseCipherDecryptionException(); @@ -81,4 +70,4 @@ public function decryptResponseCipher(string $cipher):AbstractResponseData { $data->{"fields"} ?? new StdClass() ); } -} \ No newline at end of file +} diff --git a/test/phpunit/AuthenticatorTest.php b/test/phpunit/AuthenticatorTest.php index 5fba507..07678a2 100644 --- a/test/phpunit/AuthenticatorTest.php +++ b/test/phpunit/AuthenticatorTest.php @@ -2,17 +2,16 @@ namespace Authwave\Test; use Authwave\Authenticator; -use Authwave\InitVector; use Authwave\NotLoggedInException; -use Authwave\ProviderUri\AdminUri; -use Authwave\ProviderUri\LoginUri; -use Authwave\ProviderUri\LogoutUri; +use Authwave\ProviderUri\BaseProviderUri; +use Authwave\ProviderUri\LoginUriBase; use Authwave\RedirectHandler; use Authwave\ResponseData\AbstractResponseData; use Authwave\SessionData; use Authwave\SessionNotStartedException; use Authwave\Token; use Authwave\ResponseData\UserData; +use Gt\Http\Uri; use PHPUnit\Framework\TestCase; use Psr\Http\Message\UriInterface; @@ -53,6 +52,7 @@ public function testIsLoggedInTrueWhenSessionDataSet() { ->method("getData") ->willReturn($userData); + /** @noinspection PhpArrayWriteIsNotUsedInspection */ $_SESSION = [ Authenticator::SESSION_KEY => $sessionData ]; @@ -64,111 +64,6 @@ 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 = []; @@ -189,9 +84,9 @@ public function testLoginRedirectsWithCorrectQueryString() { ->willReturn($iv); $expectedQueryParts = [ - LoginUri::QUERY_STRING_CIPHER => $cipher, - LoginUri::QUERY_STRING_INIT_VECTOR => $ivString, - LoginUri::QUERY_STRING_CURRENT_PATH => bin2hex($currentPath), + LoginUriBase::QUERY_STRING_CIPHER => $cipher, + LoginUriBase::QUERY_STRING_INIT_VECTOR => $ivString, + LoginUriBase::QUERY_STRING_CURRENT_PATH => bin2hex($currentPath), ]; $expectedQuery = http_build_query($expectedQueryParts); @@ -205,50 +100,19 @@ public function testLoginRedirectsWithCorrectQueryString() { $sut = new Authenticator( $key, $currentPath, - LoginUri::DEFAULT_BASE_REMOTE_URI, + LoginUriBase::DEFAULT_BASE_REMOTE_URI, null, $redirectHandler ); $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->method("getId") + ->willReturn($exampleId); $sessionData = self::createMock(SessionData::class); $sessionData->method("getData") ->willReturn($userData); @@ -260,7 +124,7 @@ public function testGetUuid() { "test-key", "/" ); - self::assertEquals($expectedUuid, $sut->getUuid()); + self::assertEquals($exampleId, $sut->getId()); } public function testGetEmailThrowsExceptionWhenNotLoggedIn() { @@ -293,6 +157,35 @@ public function testGetEmail() { self::assertEquals($expectedEmail, $sut->getEmail()); } + public function testCompleteAuthNotAffectedByQueryString() { + $redirectHandler = self::createMock(RedirectHandler::class); + $redirectHandler->expects(self::never()) + ->method("redirect"); + $_SESSION = []; + + new Authenticator( + "test-key", + "/example-path/?filter=something", + LoginUriBase::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() { $currentUri = "/?" . Authenticator::RESPONSE_QUERY_PARAMETER @@ -335,7 +228,7 @@ public function testCompleteAuth() { new Authenticator( "test-key", $currentUri, - LoginUri::DEFAULT_BASE_REMOTE_URI, + LoginUriBase::DEFAULT_BASE_REMOTE_URI, null, $redirectHandler ); @@ -353,32 +246,149 @@ public function testCompleteAuth() { ); } - public function testCompleteAuthNotAffectedByQueryString() { + 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", + "/", + LoginUriBase::DEFAULT_BASE_REMOTE_URI, + null, + $redirectHandler + ); + + $sut->login(); + } + + public function testGetUuidThrowsExceptionWhenNotLoggedIn() { $_SESSION = []; + $sut = new Authenticator( + "test-key", + "/" + ); + self::expectException(NotLoggedInException::class); + $sut->getId(); + } + + 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(function(UriInterface $uri):bool { + if($uri->getHost() !== "login.authwave.com") { + return false; + } + + parse_str($uri->getQuery(), $queryParts); + /** @var SessionData $session */ + $session = $_SESSION[Authenticator::SESSION_KEY]; + $token = $session->getToken(); + $decrypted = $token->decode( + $queryParts[BaseProviderUri::QUERY_STRING_CIPHER] + ); + var_dump($decrypted);die(); + })); + + $sut = new Authenticator( + "test-key", + "/", + LoginUriBase::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", - "/example-path?filter=something", - LoginUri::DEFAULT_BASE_REMOTE_URI, + "/", + LoginUriBase::DEFAULT_BASE_REMOTE_URI, null, $redirectHandler ); + + self::assertEmpty($_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() === LoginUriBase::DEFAULT_BASE_REMOTE_URI + )); + + $sut = new Authenticator( "test-key", - "/example-path", - LoginUri::DEFAULT_BASE_REMOTE_URI + "/", + LoginUriBase::DEFAULT_BASE_REMOTE_URI, + null, + $redirectHandler ); - $sut = $auth->getAdminUri(); - self::assertEquals( - "/admin", - $sut->getPath() + $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(); } -} \ 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 @@ -willReturn("https://example.com"); $token = self::createMock(Token::class); - $sut = new LoginUri( + $sut = new LoginUriBase( $token, "", $baseUri @@ -35,7 +35,7 @@ public function testAuthUriWithNonStandardPort() { ->willReturn("http://localhost:8081"); $token = self::createMock(Token::class); - $sut = new LoginUri( + $sut = new LoginUriBase( $token, "", $baseUri @@ -54,7 +54,7 @@ public function testAuthUriWithNonStandardPort() { // But it should still default to HTTPS on localhost. public function testGetAuthUriHostnameLocalhostHttpsByDefault() { $token = self::createMock(Token::class); - $sut = new LoginUri( + $sut = new LoginUriBase( $token, "/", "localhost" @@ -69,7 +69,7 @@ public function testGetAuthUriHostnameLocalhostHttpsByDefault() { // We should be able to set the scheme to HTTP for localhost hostname only. public function testGetAuthUriHostnameLocalhostHttpAllowed() { $token = self::createMock(Token::class); - $sut = new LoginUri( + $sut = new LoginUriBase( $token, "/", "http://localhost" @@ -84,7 +84,7 @@ public function testGetAuthUriHostnameLocalhostHttpAllowed() { public function testGetAuthUriHostnameNotLocalhostHttpNotAllowed() { $token = self::createMock(Token::class); self::expectException(InsecureProtocolException::class); - new LoginUri( + new LoginUriBase( $token, "/", "http://localhost.com" @@ -98,7 +98,7 @@ public function testAuthUriHttpsInferred() { // Note on the line above, no scheme is passed in - we must assume https. $token = self::createMock(Token::class); - $sut = new LoginUri( + $sut = new LoginUriBase( $token, "/", $baseUri); @@ -108,4 +108,4 @@ public function testAuthUriHttpsInferred() { $sut->getScheme() ); } -} \ No newline at end of file +} diff --git a/test/phpunit/ProviderUri/AdminUriTest.php b/test/phpunit/ProviderUri/AdminUriTest.php index e2e5db7..fed1311 100644 --- a/test/phpunit/ProviderUri/AdminUriTest.php +++ b/test/phpunit/ProviderUri/AdminUriTest.php @@ -1,15 +1,15 @@ getPath() ); } -} \ No newline at end of file +} diff --git a/test/phpunit/ProviderUri/LoginUriTest.php b/test/phpunit/ProviderUri/LoginUriTest.php index 794c754..c97f22d 100644 --- a/test/phpunit/ProviderUri/LoginUriTest.php +++ b/test/phpunit/ProviderUri/LoginUriTest.php @@ -2,7 +2,7 @@ namespace Authwave\Test\ProviderUri; use Authwave\InitVector; -use Authwave\ProviderUri\LoginUri; +use Authwave\ProviderUri\LoginUriBase; use Authwave\Token; use PHPUnit\Framework\TestCase; use Psr\Http\Message\UriInterface; @@ -23,7 +23,7 @@ public function testQueryString() { ->willReturn($iv); $returnPath = "/examplePage"; - $sut = new LoginUri( + $sut = new LoginUriBase( $token, $returnPath, $baseUri @@ -32,17 +32,17 @@ public function testQueryString() { self::assertEquals( $mockCipherValue, - $queryParts[LoginUri::QUERY_STRING_CIPHER], + $queryParts[LoginUriBase::QUERY_STRING_CIPHER], ); self::assertEquals( $mockIvValue, - $queryParts[LoginUri::QUERY_STRING_INIT_VECTOR] + $queryParts[LoginUriBase::QUERY_STRING_INIT_VECTOR] ); self::assertEquals( bin2hex($returnPath), - $queryParts[LoginUri::QUERY_STRING_CURRENT_PATH] + $queryParts[LoginUriBase::QUERY_STRING_CURRENT_PATH] ); } -} \ No newline at end of file +} diff --git a/test/phpunit/TokenTest.php b/test/phpunit/TokenTest.php index d001a7b..b178bd5 100644 --- a/test/phpunit/TokenTest.php +++ b/test/phpunit/TokenTest.php @@ -1,67 +1,69 @@ 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() { @@ -93,9 +95,9 @@ public function testDecryptResponseCipher() { ); $cipher = base64_encode($cipher); $sut = new Token($clientKey, $secretIv, $iv); - $userData = $sut->decryptResponseCipher($cipher); + $userData = $sut->decode($cipher); self::assertInstanceOf(UserData::class, $userData); - self::assertEquals($uuid, $userData->getUuid()); + self::assertEquals($uuid, $userData->getId()); self::assertEquals($email, $userData->getEmail()); } -} \ No newline at end of file +} From f63836f9773fbb408645eea3041056fb4fed12ad Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Mon, 25 Jul 2022 17:55:19 +0100 Subject: [PATCH 02/21] build: upgrade to stable cipher --- composer.json | 2 +- composer.lock | 45 ++++++++++++++++++++++----------------------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/composer.json b/composer.json index 78b30fa..c78e634 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "require": { "php": ">=8.1", "ext-openssl": "*", - "phpgt/cipher": "dev-master", + "phpgt/cipher": "^1.0", "phpgt/http": "1.*", "phpgt/session": ">=1.1" }, diff --git a/composer.lock b/composer.lock index bf392a9..11f3d54 100644 --- a/composer.lock +++ b/composer.lock @@ -4,11 +4,11 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "33cc3f246595defd43ac4a235110f89a", + "content-hash": "4573800f16257b1e039dd85fe4abe2a9", "packages": [ { "name": "phpgt/cipher", - "version": "dev-master", + "version": "v1.0.0", "source": { "type": "git", "url": "https://github.com/PhpGt/Cipher.git", @@ -29,7 +29,6 @@ "phpstan/phpstan": "v1.8.0", "phpunit/phpunit": "v9.5.21" }, - "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -48,7 +47,7 @@ "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/master" + "source": "https://github.com/PhpGt/Cipher/tree/v1.0.0" }, "funding": [ { @@ -60,27 +59,27 @@ }, { "name": "phpgt/http", - "version": "v1.1.4", + "version": "v1.1.5", "source": { "type": "git", "url": "https://github.com/PhpGt/Http.git", - "reference": "80ed7269050bc0f2de2402c5461711dd2418f55f" + "reference": "1b93fa9489100d649f29f59226cc52cd7c0812cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhpGt/Http/zipball/80ed7269050bc0f2de2402c5461711dd2418f55f", - "reference": "80ed7269050bc0f2de2402c5461711dd2418f55f", + "url": "https://api.github.com/repos/PhpGt/Http/zipball/1b93fa9489100d649f29f59226cc52cd7c0812cb", + "reference": "1b93fa9489100d649f29f59226cc52cd7c0812cb", "shasum": "" }, "require": { "php": ">=8.0", "phpgt/input": "^v1", "psr/http-message": "^v1.0.1", - "willdurand/negotiation": "v3.0.*" + "willdurand/negotiation": "v3.1.0" }, "require-dev": { - "phpstan/phpstan": ">=0.12.64", - "phpunit/phpunit": "9.*" + "phpstan/phpstan": "v1.8.1", + "phpunit/phpunit": "v9.5.21" }, "type": "library", "autoload": { @@ -95,7 +94,7 @@ "description": "PSR-7 HTTP message implementation.", "support": { "issues": "https://github.com/PhpGt/Http/issues", - "source": "https://github.com/PhpGt/Http/tree/v1.1.4" + "source": "https://github.com/PhpGt/Http/tree/v1.1.5" }, "funding": [ { @@ -103,7 +102,7 @@ "type": "github" } ], - "time": "2021-06-23T15:45:09+00:00" + "time": "2022-07-25T14:31:55+00:00" }, { "name": "phpgt/input", @@ -300,16 +299,16 @@ }, { "name": "willdurand/negotiation", - "version": "3.0.0", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/willdurand/Negotiation.git", - "reference": "04e14f38d4edfcc974114a07d2777d90c98f3d9c" + "reference": "68e9ea0553ef6e2ee8db5c1d98829f111e623ec2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/willdurand/Negotiation/zipball/04e14f38d4edfcc974114a07d2777d90c98f3d9c", - "reference": "04e14f38d4edfcc974114a07d2777d90c98f3d9c", + "url": "https://api.github.com/repos/willdurand/Negotiation/zipball/68e9ea0553ef6e2ee8db5c1d98829f111e623ec2", + "reference": "68e9ea0553ef6e2ee8db5c1d98829f111e623ec2", "shasum": "" }, "require": { @@ -350,9 +349,9 @@ ], "support": { "issues": "https://github.com/willdurand/Negotiation/issues", - "source": "https://github.com/willdurand/Negotiation/tree/3.0.0" + "source": "https://github.com/willdurand/Negotiation/tree/3.1.0" }, - "time": "2020-09-25T08:01:41+00:00" + "time": "2022-01-30T20:08:53+00:00" } ], "packages-dev": [ @@ -2054,15 +2053,15 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "phpgt/cipher": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { "php": ">=8.1", "ext-openssl": "*" }, - "platform-dev": [], + "platform-dev": { + "ext-sodium": "*" + }, "plugin-api-version": "2.2.0" } From 1a6cbf38a61cf8ddd32ff7d8e4f9925cd77b6d6b Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Wed, 10 Aug 2022 08:36:37 +0100 Subject: [PATCH 03/21] feature: upgrade for 2022 provider --- src/Authenticator.php | 112 +++++++++--------- .../{AdminUriBase.php => AdminUri.php} | 4 +- src/ProviderUri/BaseProviderUri.php | 13 +- .../{LoginUriBase.php => LoginUri.php} | 10 +- .../{LogoutUriBase.php => LogoutUri.php} | 2 +- .../{ProfileUriBase.php => ProfileUri.php} | 4 +- src/ResponseData/AbstractResponseData.php | 14 --- src/ResponseData/UserData.php | 33 ------ src/SessionData.php | 17 +-- src/Token.php | 39 +++--- test/phpunit/AuthenticatorTest.php | 36 +++--- .../ProviderUri/AbstractProviderUriTest.php | 14 +-- test/phpunit/ProviderUri/AdminUriTest.php | 4 +- test/phpunit/ProviderUri/LoginUriTest.php | 10 +- test/phpunit/SessionDataTest.php | 6 +- test/phpunit/TokenTest.php | 4 +- 16 files changed, 141 insertions(+), 181 deletions(-) rename src/ProviderUri/{AdminUriBase.php => AdminUri.php} (73%) rename src/ProviderUri/{LoginUriBase.php => LoginUri.php} (87%) rename src/ProviderUri/{LogoutUriBase.php => LogoutUri.php} (89%) rename src/ProviderUri/{ProfileUriBase.php => ProfileUri.php} (81%) delete mode 100644 src/ResponseData/AbstractResponseData.php delete mode 100644 src/ResponseData/UserData.php diff --git a/src/Authenticator.php b/src/Authenticator.php index 48c9158..8782c97 100644 --- a/src/Authenticator.php +++ b/src/Authenticator.php @@ -2,54 +2,53 @@ namespace Authwave; use Authwave\ProviderUri\BaseProviderUri; -use Authwave\ProviderUri\AdminUriBase; -use Authwave\ProviderUri\LoginUriBase; -use Authwave\ProviderUri\LogoutUriBase; -use Authwave\ProviderUri\ProfileUriBase; +use Authwave\ProviderUri\AdminUri; +use Authwave\ProviderUri\LoginUri; +use Authwave\ProviderUri\LogoutUri; +use Authwave\ProviderUri\ProfileUri; +use Authwave\ResponseData\UserResponseData; +use Gt\Cipher\Key; +use Gt\Cipher\Message\EncryptedMessage; use Gt\Http\Uri; use Gt\Session\SessionContainer; use Psr\Http\Message\UriInterface; class Authenticator { - const SESSION_KEY = "AUTHWAVE_SESSION"; const RESPONSE_QUERY_PARAMETER = "AUTHWAVE_RESPONSE_DATA"; - const LOGIN_TYPE_DEFAULT = "login-default"; - const LOGIN_TYPE_ADMIN = "login-admin"; private SessionData $sessionData; + private User $user; public function __construct( private readonly string $clientKey, - private readonly string $currentUriPath, + private readonly Uri $currentUri, private readonly string $authwaveHost = "login.authwave.com", private ?SessionContainer $session = null, - private ?RedirectHandler $redirectHandler = null + private ?RedirectHandler $redirectHandler = null, ) { $this->session = $this->session ?? new GlobalSessionContainer(); - - if(!$this->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? - $this->session->set(self::SESSION_KEY, new SessionData()); - } - /** @var SessionData $sessionData*/ - $sessionData = $this->session->get(self::SESSION_KEY); - - $this->sessionData = $sessionData; $this->redirectHandler = $redirectHandler ?? new RedirectHandler(); + if($data = $this->session->get(SessionData::class)) { + $this->sessionData = $data; + + try { + $responseData = $this->sessionData->getData(); + if($responseData instanceof UserResponseData) { + $this->user = new User( + $responseData->getId(), + $responseData->getEmail(), + $responseData->getAllFields(), + ); + } + } + catch(NotLoggedInException) {} + } $this->completeAuth(); } public function isLoggedIn():bool { - try { - $this->sessionData->getData(); - } - catch(NotLoggedInException $exception) { - return false; - } - - return true; + return isset($this->user); } public function login(Token $token = null):void { @@ -62,8 +61,7 @@ public function login(Token $token = null):void { } $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)); } @@ -73,44 +71,36 @@ public function logout(Token $token = null):void { } $this->sessionData = new SessionData($token); - $this->session->set(self::SESSION_KEY, $this->sessionData); - + $this->session->set(SessionData::class, $this->sessionData); $this->redirectHandler->redirect($this->getLogoutUri($token)); } - public function getId():string { - $userData = $this->sessionData->getData(); - return $userData->getId(); - } - - public function getEmail():string { - $userData = $this->sessionData->getData(); - return $userData->getEmail(); - } + public function getUser():User { + if(!isset($this->user)) { + throw new NotLoggedInException(); + } - public function getField(string $name):?string { - $userData = $this->sessionData->getData(); - return $userData->getField($name); + return $this->user; } public function getLoginUri(Token $token):BaseProviderUri { - return new LoginUriBase( + return new LoginUri( $token, - $this->currentUriPath, + $this->currentUri, $this->authwaveHost ); } private function getLogoutUri(Token $token):BaseProviderUri { - return new LogoutUriBase( + return new LogoutUri( $token, - $this->currentUriPath, + $this->currentUri, $this->authwaveHost ); } public function getAdminUri():UriInterface { - return new AdminUriBase($this->authwaveHost); + return new AdminUri($this->authwaveHost); } public function getProfileUri(Token $token = null):UriInterface { @@ -118,38 +108,48 @@ public function getProfileUri(Token $token = null):UriInterface { $token = new Token($this->clientKey); } - return new ProfileUriBase( + return new ProfileUri( $token, - $this->getId(), - $this->currentUriPath, + $this->user->id, + $this->currentUri, $this->authwaveHost ); } private function completeAuth():void { - return; $queryData = $this->getQueryData(); if(!$queryData) { return; } + $token = $this->sessionData->getToken(); - $userData = $token->decode($queryData); + $secretSessionIv = $token->getSecretIv(); + $encrypted = new EncryptedMessage($queryData, $secretSessionIv); + $key = new Key($this->clientKey); + $decrypt = $encrypted->decrypt($key); + parse_str($decrypt, $data); + $userData = new UserResponseData( + $data["id"], + $data["email"], + $data["kvp"] ?? [], + ); + $this->session->set( - self::SESSION_KEY, + SessionData::class, new SessionData($token, $userData) ); $this->redirectHandler->redirect( - (new Uri($this->currentUriPath)) + (new Uri($this->currentUri)) ->withoutQueryValue(self::RESPONSE_QUERY_PARAMETER) ); } private function getQueryData():?string { $queryString = parse_url( - $this->currentUriPath, + $this->currentUri, PHP_URL_QUERY ); if(!$queryString) { diff --git a/src/ProviderUri/AdminUriBase.php b/src/ProviderUri/AdminUri.php similarity index 73% rename from src/ProviderUri/AdminUriBase.php rename to src/ProviderUri/AdminUri.php index 3c932b6..8e0cd6f 100644 --- a/src/ProviderUri/AdminUriBase.php +++ b/src/ProviderUri/AdminUri.php @@ -1,12 +1,12 @@ normaliseBaseUri($baseRemoteUri); parent::__construct($baseRemoteUri); - $this->path = "/admin"; + $this->path = "/admin/"; } } diff --git a/src/ProviderUri/BaseProviderUri.php b/src/ProviderUri/BaseProviderUri.php index 404daf3..3f8fcb4 100644 --- a/src/ProviderUri/BaseProviderUri.php +++ b/src/ProviderUri/BaseProviderUri.php @@ -7,9 +7,9 @@ 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_CIPHER = "cipher"; + const QUERY_STRING_INIT_VECTOR = "iv"; + const QUERY_STRING_CURRENT_PATH = "path"; protected function normaliseBaseUri(string $baseUri):Uri { $scheme = parse_url($baseUri, PHP_URL_SCHEME) @@ -25,7 +25,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()); } @@ -35,10 +36,10 @@ protected function normaliseBaseUri(string $baseUri):Uri { protected function buildQuery( Token $token, string $currentPath, - string $message = "" + string $message = "", ):string { return http_build_query([ - self::QUERY_STRING_CIPHER => $token->generateRequestCipher($message), + self::QUERY_STRING_CIPHER => (string)$token->generateRequestCipher($message), self::QUERY_STRING_INIT_VECTOR => (string)$token->getIv(), self::QUERY_STRING_CURRENT_PATH => bin2hex($currentPath), ]); diff --git a/src/ProviderUri/LoginUriBase.php b/src/ProviderUri/LoginUri.php similarity index 87% rename from src/ProviderUri/LoginUriBase.php rename to src/ProviderUri/LoginUri.php index a36fdbe..0e6eae5 100644 --- a/src/ProviderUri/LoginUriBase.php +++ b/src/ProviderUri/LoginUri.php @@ -10,7 +10,7 @@ * 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 LoginUriBase extends BaseProviderUri { +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 @@ -22,11 +22,15 @@ class LoginUriBase extends BaseProviderUri { */ public function __construct( Token $token, - string $currentPath = "/", + string $currentPath, 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, + $currentPath, + "action=login" + ); } } diff --git a/src/ProviderUri/LogoutUriBase.php b/src/ProviderUri/LogoutUri.php similarity index 89% rename from src/ProviderUri/LogoutUriBase.php rename to src/ProviderUri/LogoutUri.php index f6c440c..9f7922c 100644 --- a/src/ProviderUri/LogoutUriBase.php +++ b/src/ProviderUri/LogoutUri.php @@ -3,7 +3,7 @@ use Authwave\Token; -class LogoutUriBase extends BaseProviderUri { +class LogoutUri extends BaseProviderUri { public function __construct( Token $token, string $currentPath = "/", diff --git a/src/ProviderUri/ProfileUriBase.php b/src/ProviderUri/ProfileUri.php similarity index 81% rename from src/ProviderUri/ProfileUriBase.php rename to src/ProviderUri/ProfileUri.php index 8b217b9..6e743e9 100644 --- a/src/ProviderUri/ProfileUriBase.php +++ b/src/ProviderUri/ProfileUri.php @@ -3,7 +3,7 @@ use Authwave\Token; -class ProfileUriBase extends BaseProviderUri { +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); } } diff --git a/src/ResponseData/AbstractResponseData.php b/src/ResponseData/AbstractResponseData.php deleted file mode 100644 index a3e4df6..0000000 --- a/src/ResponseData/AbstractResponseData.php +++ /dev/null @@ -1,14 +0,0 @@ -message = $message; - } - - public function getMessage():?string { - return $this->message; - } -} \ No newline at end of file diff --git a/src/ResponseData/UserData.php b/src/ResponseData/UserData.php deleted file mode 100644 index 2ea4e77..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 getId():string { - return $this->uuid; - } - - public function getEmail():string { - return $this->email; - } - - public function getField(string $name):?string { - return $this->fields->{$name} ?? null; - } -} 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 6307a29..7c93fc8 100644 --- a/src/Token.php +++ b/src/Token.php @@ -1,18 +1,19 @@ key = new Key($keyString); - $this->secretIv = $sessionIv ?? new InitVector(); + $this->secretSessionIv = $sessionIv ?? new InitVector(); $this->iv = $iv ?? new InitVector(); + Log::debug("Created login information:\n\tsecretSessionIV\t$this->secretSessionIv\n\t\tiv\t$this->iv"); } 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 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 { - $plainTextMessage = new PlainTextMessage($message, $this->getIv()); +// 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 decode(string $base64cipher):AbstractResponseData { + + public function decode(string $base64cipher):BaseResponseData { $encryptedMessage = new EncryptedMessage( $base64cipher, $this->iv, @@ -64,7 +71,7 @@ public function decode(string $base64cipher):AbstractResponseData { throw new InvalidUserDataSerializationException(); } - return new UserData( + return new UserResponseData( $data->{"uuid"}, $data->{"email"}, $data->{"fields"} ?? new StdClass() diff --git a/test/phpunit/AuthenticatorTest.php b/test/phpunit/AuthenticatorTest.php index 07678a2..e89ff73 100644 --- a/test/phpunit/AuthenticatorTest.php +++ b/test/phpunit/AuthenticatorTest.php @@ -4,13 +4,13 @@ use Authwave\Authenticator; use Authwave\NotLoggedInException; use Authwave\ProviderUri\BaseProviderUri; -use Authwave\ProviderUri\LoginUriBase; +use Authwave\ProviderUri\LoginUri; 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\Http\Uri; use PHPUnit\Framework\TestCase; use Psr\Http\Message\UriInterface; @@ -46,7 +46,7 @@ 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") @@ -84,9 +84,9 @@ public function testLoginRedirectsWithCorrectQueryString() { ->willReturn($iv); $expectedQueryParts = [ - LoginUriBase::QUERY_STRING_CIPHER => $cipher, - LoginUriBase::QUERY_STRING_INIT_VECTOR => $ivString, - LoginUriBase::QUERY_STRING_CURRENT_PATH => bin2hex($currentPath), + LoginUri::QUERY_STRING_CIPHER => $cipher, + LoginUri::QUERY_STRING_INIT_VECTOR => $ivString, + LoginUri::QUERY_STRING_CURRENT_PATH => bin2hex($currentPath), ]; $expectedQuery = http_build_query($expectedQueryParts); @@ -100,7 +100,7 @@ public function testLoginRedirectsWithCorrectQueryString() { $sut = new Authenticator( $key, $currentPath, - LoginUriBase::DEFAULT_BASE_REMOTE_URI, + LoginUri::DEFAULT_BASE_REMOTE_URI, null, $redirectHandler ); @@ -110,7 +110,7 @@ public function testLoginRedirectsWithCorrectQueryString() { public function testGetUuid() { $exampleId = uniqid("example-id-"); - $userData = self::createMock(UserData::class); + $userData = self::createMock(UserResponseData::class); $userData->method("getId") ->willReturn($exampleId); $sessionData = self::createMock(SessionData::class); @@ -140,7 +140,7 @@ public function testGetEmailThrowsExceptionWhenNotLoggedIn() { 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); @@ -166,7 +166,7 @@ public function testCompleteAuthNotAffectedByQueryString() { new Authenticator( "test-key", "/example-path/?filter=something", - LoginUriBase::DEFAULT_BASE_REMOTE_URI, + LoginUri::DEFAULT_BASE_REMOTE_URI, null, $redirectHandler ); @@ -228,7 +228,7 @@ public function testCompleteAuth() { new Authenticator( "test-key", $currentUri, - LoginUriBase::DEFAULT_BASE_REMOTE_URI, + LoginUri::DEFAULT_BASE_REMOTE_URI, null, $redirectHandler ); @@ -241,7 +241,7 @@ public function testCompleteAuth() { $newSessionData ); self::assertInstanceOf( - AbstractResponseData::class, + BaseResponseData::class, $newSessionData->getData() ); } @@ -259,7 +259,7 @@ public function testLoginDoesNothingWhenAlreadyLoggedIn() { $sut = new Authenticator( "test-key", "/", - LoginUriBase::DEFAULT_BASE_REMOTE_URI, + LoginUri::DEFAULT_BASE_REMOTE_URI, null, $redirectHandler ); @@ -304,7 +304,7 @@ public function testLogoutCallsLogoutUri() { $sut = new Authenticator( "test-key", "/", - LoginUriBase::DEFAULT_BASE_REMOTE_URI, + LoginUri::DEFAULT_BASE_REMOTE_URI, null, $redirectHandler ); @@ -342,7 +342,7 @@ public function testCompleteAuthFromLogoutClearsSession() { new Authenticator( "test-key", "/", - LoginUriBase::DEFAULT_BASE_REMOTE_URI, + LoginUri::DEFAULT_BASE_REMOTE_URI, null, $redirectHandler ); @@ -357,13 +357,13 @@ public function testLoginRedirects() { $redirectHandler->expects(self::once()) ->method("redirect") ->with(self::callback(fn(UriInterface $uri) => - $uri->getHost() === LoginUriBase::DEFAULT_BASE_REMOTE_URI + $uri->getHost() === LoginUri::DEFAULT_BASE_REMOTE_URI )); $sut = new Authenticator( "test-key", "/", - LoginUriBase::DEFAULT_BASE_REMOTE_URI, + LoginUri::DEFAULT_BASE_REMOTE_URI, null, $redirectHandler ); diff --git a/test/phpunit/ProviderUri/AbstractProviderUriTest.php b/test/phpunit/ProviderUri/AbstractProviderUriTest.php index c264fd6..99a39b2 100644 --- a/test/phpunit/ProviderUri/AbstractProviderUriTest.php +++ b/test/phpunit/ProviderUri/AbstractProviderUriTest.php @@ -3,7 +3,7 @@ use Authwave\InitVector; use Authwave\InsecureProtocolException; -use Authwave\ProviderUri\LoginUriBase; +use Authwave\ProviderUri\LoginUri; use Authwave\Token; use PHPUnit\Framework\TestCase; use Psr\Http\Message\UriInterface; @@ -15,7 +15,7 @@ public function testAuthUriHttps() { ->willReturn("https://example.com"); $token = self::createMock(Token::class); - $sut = new LoginUriBase( + $sut = new LoginUri( $token, "", $baseUri @@ -35,7 +35,7 @@ public function testAuthUriWithNonStandardPort() { ->willReturn("http://localhost:8081"); $token = self::createMock(Token::class); - $sut = new LoginUriBase( + $sut = new LoginUri( $token, "", $baseUri @@ -54,7 +54,7 @@ public function testAuthUriWithNonStandardPort() { // But it should still default to HTTPS on localhost. public function testGetAuthUriHostnameLocalhostHttpsByDefault() { $token = self::createMock(Token::class); - $sut = new LoginUriBase( + $sut = new LoginUri( $token, "/", "localhost" @@ -69,7 +69,7 @@ public function testGetAuthUriHostnameLocalhostHttpsByDefault() { // We should be able to set the scheme to HTTP for localhost hostname only. public function testGetAuthUriHostnameLocalhostHttpAllowed() { $token = self::createMock(Token::class); - $sut = new LoginUriBase( + $sut = new LoginUri( $token, "/", "http://localhost" @@ -84,7 +84,7 @@ public function testGetAuthUriHostnameLocalhostHttpAllowed() { public function testGetAuthUriHostnameNotLocalhostHttpNotAllowed() { $token = self::createMock(Token::class); self::expectException(InsecureProtocolException::class); - new LoginUriBase( + new LoginUri( $token, "/", "http://localhost.com" @@ -98,7 +98,7 @@ public function testAuthUriHttpsInferred() { // Note on the line above, no scheme is passed in - we must assume https. $token = self::createMock(Token::class); - $sut = new LoginUriBase( + $sut = new LoginUri( $token, "/", $baseUri); diff --git a/test/phpunit/ProviderUri/AdminUriTest.php b/test/phpunit/ProviderUri/AdminUriTest.php index fed1311..3d32da5 100644 --- a/test/phpunit/ProviderUri/AdminUriTest.php +++ b/test/phpunit/ProviderUri/AdminUriTest.php @@ -1,12 +1,12 @@ getPath() diff --git a/test/phpunit/ProviderUri/LoginUriTest.php b/test/phpunit/ProviderUri/LoginUriTest.php index c97f22d..d7d819a 100644 --- a/test/phpunit/ProviderUri/LoginUriTest.php +++ b/test/phpunit/ProviderUri/LoginUriTest.php @@ -2,7 +2,7 @@ namespace Authwave\Test\ProviderUri; use Authwave\InitVector; -use Authwave\ProviderUri\LoginUriBase; +use Authwave\ProviderUri\LoginUri; use Authwave\Token; use PHPUnit\Framework\TestCase; use Psr\Http\Message\UriInterface; @@ -23,7 +23,7 @@ public function testQueryString() { ->willReturn($iv); $returnPath = "/examplePage"; - $sut = new LoginUriBase( + $sut = new LoginUri( $token, $returnPath, $baseUri @@ -32,17 +32,17 @@ public function testQueryString() { self::assertEquals( $mockCipherValue, - $queryParts[LoginUriBase::QUERY_STRING_CIPHER], + $queryParts[LoginUri::QUERY_STRING_CIPHER], ); self::assertEquals( $mockIvValue, - $queryParts[LoginUriBase::QUERY_STRING_INIT_VECTOR] + $queryParts[LoginUri::QUERY_STRING_INIT_VECTOR] ); self::assertEquals( bin2hex($returnPath), - $queryParts[LoginUriBase::QUERY_STRING_CURRENT_PATH] + $queryParts[LoginUri::QUERY_STRING_CURRENT_PATH] ); } } 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 b178bd5..3e5ddf8 100644 --- a/test/phpunit/TokenTest.php +++ b/test/phpunit/TokenTest.php @@ -4,7 +4,7 @@ use Authwave\InvalidUserDataSerializationException; use Authwave\ResponseCipherDecryptionException; use Authwave\Token; -use Authwave\ResponseData\UserData; +use Authwave\ResponseData\UserResponseData; use Gt\Cipher\InitVector; use Gt\Cipher\Key; use Gt\Cipher\Message\DecryptionFailureException; @@ -96,7 +96,7 @@ public function testDecryptResponseCipher() { $cipher = base64_encode($cipher); $sut = new Token($clientKey, $secretIv, $iv); $userData = $sut->decode($cipher); - self::assertInstanceOf(UserData::class, $userData); + self::assertInstanceOf(UserResponseData::class, $userData); self::assertEquals($uuid, $userData->getId()); self::assertEquals($email, $userData->getEmail()); } From 36842ed9d243e63374be42632c06a8019514394c Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Thu, 11 Aug 2022 13:27:38 +0100 Subject: [PATCH 04/21] feature: response data classes --- composer.lock | 12 +++++------ src/ResponseData/BaseResponseData.php | 14 +++++++++++++ src/ResponseData/UserResponseData.php | 30 +++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 src/ResponseData/BaseResponseData.php create mode 100644 src/ResponseData/UserResponseData.php diff --git a/composer.lock b/composer.lock index 11f3d54..16d2673 100644 --- a/composer.lock +++ b/composer.lock @@ -1121,16 +1121,16 @@ }, { "name": "phpunit/phpunit", - "version": "8.5.27", + "version": "8.5.28", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "df70070f2711b8fe8dcca0797c1239ede8c94be6" + "reference": "8f2d1c9c7b30382459c871467853da1a6e44fbd4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/df70070f2711b8fe8dcca0797c1239ede8c94be6", - "reference": "df70070f2711b8fe8dcca0797c1239ede8c94be6", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8f2d1c9c7b30382459c871467853da1a6e44fbd4", + "reference": "8f2d1c9c7b30382459c871467853da1a6e44fbd4", "shasum": "" }, "require": { @@ -1199,7 +1199,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.27" + "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.28" }, "funding": [ { @@ -1211,7 +1211,7 @@ "type": "github" } ], - "time": "2022-06-19T12:11:16+00:00" + "time": "2022-07-29T09:20:50+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", diff --git a/src/ResponseData/BaseResponseData.php b/src/ResponseData/BaseResponseData.php new file mode 100644 index 0000000..eea9406 --- /dev/null +++ b/src/ResponseData/BaseResponseData.php @@ -0,0 +1,14 @@ +message = $message; + } + + public function getMessage():?string { + return $this->message; + } +} 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; + } +} From b0e50336f40919be29ced5ece26ae0abf9c3246e Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Thu, 11 Aug 2022 13:28:20 +0100 Subject: [PATCH 05/21] feature: user class --- src/User.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/User.php diff --git a/src/User.php b/src/User.php new file mode 100644 index 0000000..1c5784b --- /dev/null +++ b/src/User.php @@ -0,0 +1,14 @@ +kvp[$key] ?? null; + } +} From 9111729200e303b5970d0850862fa6eeb9a9f08d Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Fri, 12 Aug 2022 10:26:06 +0100 Subject: [PATCH 06/21] test: fix tests --- src/Authenticator.php | 11 +- src/Token.php | 5 +- test/phpunit/AuthenticatorTest.php | 129 ++++++++++------------ test/phpunit/ProviderUri/AdminUriTest.php | 2 +- test/phpunit/TokenTest.php | 41 +++---- 5 files changed, 89 insertions(+), 99 deletions(-) diff --git a/src/Authenticator.php b/src/Authenticator.php index 8782c97..704ed91 100644 --- a/src/Authenticator.php +++ b/src/Authenticator.php @@ -18,10 +18,11 @@ class Authenticator { private SessionData $sessionData; private User $user; + private Uri $currentUri; public function __construct( private readonly string $clientKey, - private readonly Uri $currentUri, + string|Uri $currentUri, private readonly string $authwaveHost = "login.authwave.com", private ?SessionContainer $session = null, private ?RedirectHandler $redirectHandler = null, @@ -44,6 +45,11 @@ public function __construct( catch(NotLoggedInException) {} } + if(is_string($currentUri)) { + $currentUri = new Uri($currentUri); + } + $this->currentUri = $currentUri; + $this->completeAuth(); } @@ -123,6 +129,9 @@ private function completeAuth():void { return; } + if(!isset($this->sessionData)) { + return; + } $token = $this->sessionData->getToken(); $secretSessionIv = $token->getSecretIv(); diff --git a/src/Token.php b/src/Token.php index 7c93fc8..bcee8a8 100644 --- a/src/Token.php +++ b/src/Token.php @@ -24,7 +24,6 @@ public function __construct( $this->key = new Key($keyString); $this->secretSessionIv = $sessionIv ?? new InitVector(); $this->iv = $iv ?? new InitVector(); - Log::debug("Created login information:\n\tsecretSessionIV\t$this->secretSessionIv\n\t\tiv\t$this->iv"); } public function getIv():InitVector { @@ -72,9 +71,9 @@ public function decode(string $base64cipher):BaseResponseData { } return new UserResponseData( - $data->{"uuid"}, + $data->{"uuid"} ?? $data->{"id"}, $data->{"email"}, - $data->{"fields"} ?? new StdClass() + $data->{"fields"} ?? [] ); } } diff --git a/test/phpunit/AuthenticatorTest.php b/test/phpunit/AuthenticatorTest.php index e89ff73..7d6503e 100644 --- a/test/phpunit/AuthenticatorTest.php +++ b/test/phpunit/AuthenticatorTest.php @@ -11,6 +11,7 @@ use Authwave\SessionNotStartedException; use Authwave\Token; use Authwave\ResponseData\UserResponseData; +use Gt\Cipher\InitVector; use Gt\Http\Uri; use PHPUnit\Framework\TestCase; use Psr\Http\Message\UriInterface; @@ -24,18 +25,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( @@ -54,7 +43,7 @@ public function testIsLoggedInTrueWhenSessionDataSet() { /** @noinspection PhpArrayWriteIsNotUsedInspection */ $_SESSION = [ - Authenticator::SESSION_KEY => $sessionData + SessionData::class => $sessionData ]; $sut = new Authenticator( @@ -118,13 +107,13 @@ public function testGetUuid() { ->willReturn($userData); $_SESSION = [ - Authenticator::SESSION_KEY => $sessionData, + SessionData::class => $sessionData, ]; $sut = new Authenticator( "test-key", "/" ); - self::assertEquals($exampleId, $sut->getId()); + self::assertEquals($exampleId, $sut->getUser()->id); } public function testGetEmailThrowsExceptionWhenNotLoggedIn() { @@ -134,7 +123,7 @@ public function testGetEmailThrowsExceptionWhenNotLoggedIn() { "/" ); self::expectException(NotLoggedInException::class); - $sut->getEmail(); + $sut->getUser()->email; } public function testGetEmail() { @@ -148,13 +137,13 @@ 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() { @@ -181,7 +170,7 @@ public function testGetAdminUri() { ); $sut = $auth->getAdminUri(); self::assertEquals( - "/admin", + "/admin/", $sut->getPath() ); } @@ -193,10 +182,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 @@ -205,9 +195,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 = http_build_query([ + "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()) @@ -216,17 +219,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, @@ -234,7 +242,7 @@ public function testCompleteAuth() { ); /** @var SessionData $newSessionData */ - $newSessionData = $_SESSION[Authenticator::SESSION_KEY]; + $newSessionData = $_SESSION[SessionData::class]; self::assertNotSame($sessionData, $newSessionData); self::assertInstanceOf( SessionData::class, @@ -249,7 +257,7 @@ public function testCompleteAuth() { public function testLoginDoesNothingWhenAlreadyLoggedIn() { $sessionData = self::createMock(SessionData::class); $_SESSION = [ - Authenticator::SESSION_KEY => $sessionData, + SessionData::class => $sessionData, ]; $redirectHandler = self::createMock(RedirectHandler::class); @@ -264,7 +272,12 @@ public function testLoginDoesNothingWhenAlreadyLoggedIn() { $redirectHandler ); - $sut->login(); + $token = self::createMock(Token::class); + $token->method("generateRequestCipher") + ->willReturn("example-request-cipher"); + $token->method("getIv") + ->willReturn("0123456789"); + $sut->login($token); } public function testGetUuidThrowsExceptionWhenNotLoggedIn() { @@ -274,13 +287,13 @@ public function testGetUuidThrowsExceptionWhenNotLoggedIn() { "/" ); self::expectException(NotLoggedInException::class); - $sut->getId(); + $sut->getUser()->id; } public function testLogoutCallsLogoutUri() { $sessionData = self::createMock(SessionData::class); $_SESSION = [ - Authenticator::SESSION_KEY => $sessionData + SessionData::class => $sessionData ]; $redirectHandler = self::createMock(RedirectHandler::class); @@ -308,46 +321,13 @@ public function testLogoutCallsLogoutUri() { 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); + $token->method("generateRequestCipher") + ->willReturn("example-request-cipher"); + $token->method("getIv") + ->willReturn("01234567890"); + $sut->logout($token); + self::assertNotEmpty($_SESSION); } public function testLoginRedirects() { @@ -367,7 +347,13 @@ public function testLoginRedirects() { null, $redirectHandler ); - $sut->login(); + + $token = self::createMock(Token::class); + $token->method("generateRequestCipher") + ->willReturn("example-request-cipher"); + $token->method("getIv") + ->willReturn("01234567890"); + $sut->login($token); } public function testLoginRedirectsLocalhost() { @@ -389,6 +375,11 @@ public function testLoginRedirectsLocalhost() { null, $redirectHandler ); - $sut->login(); + $token = self::createMock(Token::class); + $token->method("generateRequestCipher") + ->willReturn("example-request-cipher"); + $token->method("getIv") + ->willReturn("01234567890"); + $sut->login($token); } } diff --git a/test/phpunit/ProviderUri/AdminUriTest.php b/test/phpunit/ProviderUri/AdminUriTest.php index 3d32da5..da82940 100644 --- a/test/phpunit/ProviderUri/AdminUriTest.php +++ b/test/phpunit/ProviderUri/AdminUriTest.php @@ -8,7 +8,7 @@ class AdminUriTest extends TestCase { public function testPathAccount() { $sut = new AdminUri("example.com"); self::assertEquals( - "/admin", + "/admin/", $sut->getPath() ); } diff --git a/test/phpunit/TokenTest.php b/test/phpunit/TokenTest.php index 3e5ddf8..d8751c3 100644 --- a/test/phpunit/TokenTest.php +++ b/test/phpunit/TokenTest.php @@ -67,35 +67,26 @@ public function testDecryptResponseCipherBadJson() { } 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( + serialize((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->decode($cipher); + $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()); From 3cebe9ce026e4bda73f72a278676a16caaac2707 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Fri, 12 Aug 2022 10:29:42 +0100 Subject: [PATCH 07/21] docs: explain unit tests --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 5970f8b..f8709fb 100644 --- a/README.md +++ b/README.md @@ -58,3 +58,12 @@ else { HTML; } ``` + +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 +``` From 8c8c08aacac19b22afbbfb528e76ef09e23749ef Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Wed, 17 Aug 2022 12:26:02 +0100 Subject: [PATCH 08/21] ci: update step versions --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66b6de3..d90bc29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,18 +7,18 @@ jobs: runs-on: [ubuntu-latest] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Cache PHP dependencies - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: vendor key: ${{ runner.OS }}-build-${{ hashFiles('**/composer.lock') }} - - uses: php-actions/composer@v1 + - uses: php-actions/composer@v6 - name: Upload build for test runner - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3 with: name: build-artifact path: ./ @@ -28,9 +28,9 @@ jobs: needs: [build] steps: - - uses: actions/download-artifact@v1 + - uses: actions/download-artifact@v3 with: name: build-artifact path: ./ - - uses: php-actions/phpunit@v1 \ No newline at end of file + - uses: php-actions/phpunit@v3 From 00a8b9135fc125e53368e08acd6880f6ae5aa524 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Wed, 17 Aug 2022 12:30:41 +0100 Subject: [PATCH 09/21] ci: upgrade workflow --- .github/workflows/ci.yml | 49 ++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d90bc29..50a433e 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@v3 + - uses: actions/checkout@v2 - - name: Cache PHP dependencies - uses: actions/cache@v3 + - name: Cache Composer dependencies + uses: actions/cache@v2 with: - path: vendor - key: ${{ runner.OS }}-build-${{ hashFiles('**/composer.lock') }} + path: /tmp/composer-cache + key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }} - - uses: php-actions/composer@v6 + - 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@v3 + - name: Upload build archive for test runners + uses: actions/upload-artifact@v2 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@v3 + - uses: actions/download-artifact@v2 with: name: build-artifact - path: ./ + path: /tmp/github-actions - - uses: php-actions/phpunit@v3 + - 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 From 798705a92f4c590002d3fdfffdccba1a98fb4f22 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Wed, 17 Aug 2022 12:31:06 +0100 Subject: [PATCH 10/21] ci: bump step versions --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 50a433e..ceebd0b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,10 +7,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Cache Composer dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: /tmp/composer-cache key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }} @@ -24,7 +24,7 @@ jobs: run: mkdir /tmp/github-actions/ && tar -cvf /tmp/github-actions/build.tar ./ - name: Upload build archive for test runners - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: build-artifact path: /tmp/github-actions @@ -34,7 +34,7 @@ jobs: needs: [ composer ] steps: - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: build-artifact path: /tmp/github-actions From ea05c76b93b2c44f7bda0c4f4db490702269bb37 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Wed, 17 Aug 2022 13:05:26 +0100 Subject: [PATCH 11/21] test: fix test warnings caused by mocks --- test/phpunit/AuthenticatorTest.php | 54 ++++++++++++++++++----- test/phpunit/ProviderUri/LoginUriTest.php | 11 +++-- 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/test/phpunit/AuthenticatorTest.php b/test/phpunit/AuthenticatorTest.php index 7d6503e..c1f0fce 100644 --- a/test/phpunit/AuthenticatorTest.php +++ b/test/phpunit/AuthenticatorTest.php @@ -11,6 +11,7 @@ use Authwave\SessionNotStartedException; use Authwave\Token; use Authwave\ResponseData\UserResponseData; +use Gt\Cipher\CipherText; use Gt\Cipher\InitVector; use Gt\Http\Uri; use PHPUnit\Framework\TestCase; @@ -59,7 +60,9 @@ public function testLoginRedirectsWithCorrectQueryString() { $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); @@ -73,7 +76,7 @@ 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), ]; @@ -256,6 +259,8 @@ public function testCompleteAuth() { public function testLoginDoesNothingWhenAlreadyLoggedIn() { $sessionData = self::createMock(SessionData::class); + $sessionData->method("getData") + ->willReturn(self::createMock(UserResponseData::class)); $_SESSION = [ SessionData::class => $sessionData, ]; @@ -273,10 +278,17 @@ public function testLoginDoesNothingWhenAlreadyLoggedIn() { ); $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("example-request-cipher"); + ->willReturn($requestCipher); $token->method("getIv") - ->willReturn("0123456789"); + ->willReturn($iv); $sut->login($token); } @@ -306,12 +318,12 @@ public function testLogoutCallsLogoutUri() { parse_str($uri->getQuery(), $queryParts); /** @var SessionData $session */ - $session = $_SESSION[Authenticator::SESSION_KEY]; + $session = $_SESSION[SessionData::class]; $token = $session->getToken(); $decrypted = $token->decode( $queryParts[BaseProviderUri::QUERY_STRING_CIPHER] ); - var_dump($decrypted);die(); + return (bool)$decrypted; })); $sut = new Authenticator( @@ -322,10 +334,16 @@ public function testLogoutCallsLogoutUri() { $redirectHandler ); $token = self::createMock(Token::class); + $cipherString = "example-request-cipher"; + $cipher = self::createMock(CipherText::class); + $cipher->method("__toString")->willReturn($cipherString); $token->method("generateRequestCipher") - ->willReturn("example-request-cipher"); + ->willReturn($cipher); + $ivBytes = "01234567890"; + $iv = self::createMock(InitVector::class); + $iv->method("__toString")->willReturn($ivBytes); $token->method("getIv") - ->willReturn("01234567890"); + ->willReturn($iv); $sut->logout($token); self::assertNotEmpty($_SESSION); } @@ -349,10 +367,16 @@ public function testLoginRedirects() { ); $token = self::createMock(Token::class); + $cipherString = "example-request-cipher"; + $cipher = self::createMock(CipherText::class); + $cipher->method("__toString")->willReturn($cipherString); $token->method("generateRequestCipher") - ->willReturn("example-request-cipher"); + ->willReturn($cipher); + $ivBytes = "01234567890"; + $iv = self::createMock(InitVector::class); + $iv->method("__toString")->willReturn($ivBytes); $token->method("getIv") - ->willReturn("01234567890"); + ->willReturn($iv); $sut->login($token); } @@ -376,10 +400,16 @@ public function testLoginRedirectsLocalhost() { $redirectHandler ); $token = self::createMock(Token::class); + $cipherString = "example-request-cipher"; + $cipher = self::createMock(CipherText::class); + $cipher->method("__toString")->willReturn($cipherString); $token->method("generateRequestCipher") - ->willReturn("example-request-cipher"); + ->willReturn($cipher); + $ivBytes = "01234567890"; + $iv = self::createMock(InitVector::class); + $iv->method("__toString")->willReturn($ivBytes); $token->method("getIv") - ->willReturn("01234567890"); + ->willReturn($iv); $sut->login($token); } } diff --git a/test/phpunit/ProviderUri/LoginUriTest.php b/test/phpunit/ProviderUri/LoginUriTest.php index d7d819a..7af25d2 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], ); From a19260d177f00c2dc5fc24182837e7b10e7c6f0c Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Wed, 17 Aug 2022 13:17:27 +0100 Subject: [PATCH 12/21] feature: switch to json serialisation for language interoperability --- src/Token.php | 6 ++---- test/phpunit/TokenTest.php | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Token.php b/src/Token.php index bcee8a8..0cbcebc 100644 --- a/src/Token.php +++ b/src/Token.php @@ -63,10 +63,8 @@ public function decode(string $base64cipher):BaseResponseData { throw new ResponseCipherDecryptionException(); } - $data = @unserialize( - $decrypted - ); - if($data === false) { + $data = json_decode($decrypted); + if($data === null) { throw new InvalidUserDataSerializationException(); } diff --git a/test/phpunit/TokenTest.php b/test/phpunit/TokenTest.php index d8751c3..b792063 100644 --- a/test/phpunit/TokenTest.php +++ b/test/phpunit/TokenTest.php @@ -72,7 +72,7 @@ public function testDecryptResponseCipher() { $uuid = "abcdef"; $email = "person@example.com"; $cipher = sodium_crypto_secretbox( - serialize((object)[ + json_encode((object)[ "id" => $uuid, "email" => $email, ]), From 62ec6fbd2627b19cd65396919e0130e95a91958b Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Wed, 17 Aug 2022 13:38:35 +0100 Subject: [PATCH 13/21] feature: switch to json serialisation for language interoperability --- src/Authenticator.php | 10 +++++----- test/phpunit/AuthenticatorTest.php | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Authenticator.php b/src/Authenticator.php index 704ed91..8891ea3 100644 --- a/src/Authenticator.php +++ b/src/Authenticator.php @@ -137,12 +137,12 @@ private function completeAuth():void { $secretSessionIv = $token->getSecretIv(); $encrypted = new EncryptedMessage($queryData, $secretSessionIv); $key = new Key($this->clientKey); - $decrypt = $encrypted->decrypt($key); - parse_str($decrypt, $data); + $decrypted = $encrypted->decrypt($key); + $data = json_decode($decrypted); $userData = new UserResponseData( - $data["id"], - $data["email"], - $data["kvp"] ?? [], + $data->{"id"}, + $data->{"email"}, + $data->{"kvp"} ?? [], ); $this->session->set( diff --git a/test/phpunit/AuthenticatorTest.php b/test/phpunit/AuthenticatorTest.php index c1f0fce..afcf817 100644 --- a/test/phpunit/AuthenticatorTest.php +++ b/test/phpunit/AuthenticatorTest.php @@ -200,7 +200,7 @@ public function testCompleteAuthNotLoggedIn() { public function testCompleteAuth() { $keyBytes = str_repeat("0", SODIUM_CRYPTO_SECRETBOX_KEYBYTES); $ivBytes = str_repeat("1", SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); - $plainTextMessage = http_build_query([ + $plainTextMessage = json_encode([ "id" => 123, "email" => "person@example.com", ]); From 1f60dfe03f643e1e1afc1b64c90b5e1948a391f3 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Mon, 30 Oct 2023 17:46:16 +0000 Subject: [PATCH 14/21] feature: logout uri --- composer.json | 3 +- composer.lock | 900 ++++++++++++---------- src/Authenticator.php | 36 +- src/ProviderUri/BaseProviderUri.php | 11 +- src/ProviderUri/LoginUri.php | 6 +- src/ProviderUri/LogoutUri.php | 5 +- src/Token.php | 6 +- test/phpunit/AuthenticatorTest.php | 2 +- test/phpunit/ProviderUri/LoginUriTest.php | 2 +- 9 files changed, 553 insertions(+), 418 deletions(-) diff --git a/composer.json b/composer.json index c78e634..f9b4734 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,8 @@ "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.*", diff --git a/composer.lock b/composer.lock index 16d2673..825b0d5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,66 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4573800f16257b1e039dd85fe4abe2a9", + "content-hash": "f1ed6a0c38c839b72dfec0981f5aa820", "packages": [ { - "name": "phpgt/cipher", + "name": "phpgt/async", "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/PhpGt/Async.git", + "reference": "3d2bdeca8cafc8573b416da3ac591d5d88f6dea9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PhpGt/Async/zipball/3d2bdeca8cafc8573b416da3ac591d5d88f6dea9", + "reference": "3d2bdeca8cafc8573b416da3ac591d5d88f6dea9", + "shasum": "" + }, + "require": { + "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": "5cef809acd0d77d24ea11ece9ba0e1ed4c8736fd" + "reference": "611ff35dbcb73b65d31e902fa15d76b49ba11d46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhpGt/Cipher/zipball/5cef809acd0d77d24ea11ece9ba0e1ed4c8736fd", - "reference": "5cef809acd0d77d24ea11ece9ba0e1ed4c8736fd", + "url": "https://api.github.com/repos/PhpGt/Cipher/zipball/611ff35dbcb73b65d31e902fa15d76b49ba11d46", + "reference": "611ff35dbcb73b65d31e902fa15d76b49ba11d46", "shasum": "" }, "require": { @@ -26,8 +72,8 @@ "phpgt/http": "v1.*" }, "require-dev": { - "phpstan/phpstan": "v1.8.0", - "phpunit/phpunit": "v9.5.21" + "phpstan/phpstan": "^v1.8", + "phpunit/phpunit": "^v9.5" }, "type": "library", "autoload": { @@ -47,7 +93,7 @@ "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.0" + "source": "https://github.com/PhpGt/Cipher/tree/v1.0.1" }, "funding": [ { @@ -55,33 +101,165 @@ "type": "github" } ], - "time": "2022-07-20T11:29:08+00:00" + "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": { + "phpmd/phpmd": "^2.13", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.1", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Gt\\DataObject\\": "./src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Bowler", + "email": "greg.bowler@g105b.com" + } + ], + "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.1.5", + "version": "v1.2.2", "source": { "type": "git", "url": "https://github.com/PhpGt/Http.git", - "reference": "1b93fa9489100d649f29f59226cc52cd7c0812cb" + "reference": "de03587d63d5a582140a3e9e9b06e506ffbc8ca3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhpGt/Http/zipball/1b93fa9489100d649f29f59226cc52cd7c0812cb", - "reference": "1b93fa9489100d649f29f59226cc52cd7c0812cb", + "url": "https://api.github.com/repos/PhpGt/Http/zipball/de03587d63d5a582140a3e9e9b06e506ffbc8ca3", + "reference": "de03587d63d5a582140a3e9e9b06e506ffbc8ca3", "shasum": "" }, "require": { - "php": ">=8.0", - "phpgt/input": "^v1", - "psr/http-message": "^v1.0.1", - "willdurand/negotiation": "v3.1.0" + "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": { - "phpstan/phpstan": "v1.8.1", - "phpunit/phpunit": "v9.5.21" + "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" @@ -94,7 +272,7 @@ "description": "PSR-7 HTTP message implementation.", "support": { "issues": "https://github.com/PhpGt/Http/issues", - "source": "https://github.com/PhpGt/Http/tree/v1.1.5" + "source": "https://github.com/PhpGt/Http/tree/v1.2.2" }, "funding": [ { @@ -102,29 +280,31 @@ "type": "github" } ], - "time": "2022-07-25T14:31:55+00:00" + "time": "2023-07-20T15:04:03+00:00" }, { "name": "phpgt/input", - "version": "v1.2.0", + "version": "v1.2.3", "source": { "type": "git", "url": "https://github.com/PhpGt/Input.git", - "reference": "95136bcbe1c50c3d8db90eb3b1944d049ec2b2a0" + "reference": "7e82009922c42531e57e45641072b7c416d726c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhpGt/Input/zipball/95136bcbe1c50c3d8db90eb3b1944d049ec2b2a0", - "reference": "95136bcbe1c50c3d8db90eb3b1944d049ec2b2a0", + "url": "https://api.github.com/repos/PhpGt/Input/zipball/7e82009922c42531e57e45641072b7c416d726c3", + "reference": "7e82009922c42531e57e45641072b7c416d726c3", "shasum": "" }, "require": { - "php": ">=8.0", + "php": ">=8.1", "phpgt/http": "^v1.1" }, "require-dev": { - "phpstan/phpstan": "^v1.4", - "phpunit/phpunit": "^v9.5" + "phpmd/phpmd": "^2.13", + "phpstan/phpstan": "^v1.10", + "phpunit/phpunit": "^10.1", + "squizlabs/php_codesniffer": "^3.7" }, "type": "library", "autoload": { @@ -139,7 +319,168 @@ "description": "Encapsulated user input.", "support": { "issues": "https://github.com/PhpGt/Input/issues", - "source": "https://github.com/PhpGt/Input/tree/v1.2.0" + "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/json", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/PhpGt/Json.git", + "reference": "8938b374d550bc6114bf1d4e5c1cbe3283868e58" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PhpGt/Json/zipball/8938b374d550bc6114bf1d4e5c1cbe3283868e58", + "reference": "8938b374d550bc6114bf1d4e5c1cbe3283868e58", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=8.1", + "phpgt/dataobject": "^1.0.7" + }, + "require-dev": { + "phpmd/phpmd": "^2.13", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.1", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Gt\\Json\\": "./src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "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": "phpgt/logger", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/PhpGt/Logger.git", + "reference": "9cbb7c986941bff513aedf80dfa256497aff8dd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PhpGt/Logger/zipball/9cbb7c986941bff513aedf80dfa256497aff8dd2", + "reference": "9cbb7c986941bff513aedf80dfa256497aff8dd2", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "phpstan/phpstan": ">=0.12.64", + "phpunit/phpunit": "9.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Gt\\Logger\\": "./src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Bowler", + "email": "greg.bowler@g105b.com" + } + ], + "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": "2021-09-21T14:18:11+00:00" + }, + { + "name": "phpgt/promise", + "version": "v2.2.3", + "source": { + "type": "git", + "url": "https://github.com/PhpGt/Promise.git", + "reference": "907b3a450f3252077f80b50289f73ab930ca2cdc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PhpGt/Promise/zipball/907b3a450f3252077f80b50289f73ab930ca2cdc", + "reference": "907b3a450f3252077f80b50289f73ab930ca2cdc", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpmd/phpmd": "^2.13", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.1", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Gt\\Promise\\": "./src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "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": [ { @@ -147,29 +488,83 @@ "type": "github" } ], - "time": "2022-03-18T19:03:02+00:00" + "time": "2023-07-01T10:59:01+00:00" + }, + { + "name": "phpgt/propfunc", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/PhpGt/PropFunc.git", + "reference": "091213649e89ff22d1ef640b46fbee5215c65520" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PhpGt/PropFunc/zipball/091213649e89ff22d1ef640b46fbee5215c65520", + "reference": "091213649e89ff22d1ef640b46fbee5215c65520", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpstan/phpstan": ">=0.12", + "phpunit/phpunit": ">=9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Gt\\PropFunc\\": "./src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Bowler", + "email": "greg.bowler@g105b.com", + "homepage": "https://www.g105b.com", + "role": "Developer" + } + ], + "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": "2021-03-23T12:46:44+00:00" }, { "name": "phpgt/session", - "version": "v1.1.5", + "version": "v1.2.1", "source": { "type": "git", "url": "https://github.com/PhpGt/Session.git", - "reference": "4ec55d2e38117b095d525b3e8507bdb5efac5c8a" + "reference": "b2e21a3147f135aee9dc16b04e352c28898d78fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhpGt/Session/zipball/4ec55d2e38117b095d525b3e8507bdb5efac5c8a", - "reference": "4ec55d2e38117b095d525b3e8507bdb5efac5c8a", + "url": "https://api.github.com/repos/PhpGt/Session/zipball/b2e21a3147f135aee9dc16b04e352c28898d78fa", + "reference": "b2e21a3147f135aee9dc16b04e352c28898d78fa", "shasum": "" }, "require": { "php": ">=8.0", - "phpgt/typesafegetter": "^v1.2" + "phpgt/typesafegetter": "^1.3" }, "require-dev": { - "phpstan/phpstan": "^v1.4", - "phpunit/phpunit": "^9.5" + "phpmd/phpmd": "^2.13", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.1", + "squizlabs/php_codesniffer": "^3.7" }, "type": "library", "autoload": { @@ -184,7 +579,7 @@ "description": "Encapsulated user sessions.", "support": { "issues": "https://github.com/PhpGt/Session/issues", - "source": "https://github.com/PhpGt/Session/tree/v1.1.5" + "source": "https://github.com/PhpGt/Session/tree/v1.2.1" }, "funding": [ { @@ -192,28 +587,30 @@ "type": "github" } ], - "time": "2022-03-08T15:40:23+00:00" + "time": "2023-05-25T13:15:56+00:00" }, { "name": "phpgt/typesafegetter", - "version": "v1.2.4", + "version": "v1.3.2", "source": { "type": "git", "url": "https://github.com/PhpGt/TypeSafeGetter.git", - "reference": "ebffd758e69b8a0eebcad30f3daf408915b9ddf3" + "reference": "f760c05a37b1cc188dcbf800c5fdfab8a926b4b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhpGt/TypeSafeGetter/zipball/ebffd758e69b8a0eebcad30f3daf408915b9ddf3", - "reference": "ebffd758e69b8a0eebcad30f3daf408915b9ddf3", + "url": "https://api.github.com/repos/PhpGt/TypeSafeGetter/zipball/f760c05a37b1cc188dcbf800c5fdfab8a926b4b0", + "reference": "f760c05a37b1cc188dcbf800c5fdfab8a926b4b0", "shasum": "" }, "require": { "php": ">=8.0" }, "require-dev": { - "phpstan/phpstan": "v1.8.0", - "phpunit/phpunit": "v9.5.21" + "phpmd/phpmd": "^2.13", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.1", + "squizlabs/php_codesniffer": "^3.7" }, "type": "library", "autoload": { @@ -234,7 +631,7 @@ "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.2.4" + "source": "https://github.com/PhpGt/TypeSafeGetter/tree/v1.3.2" }, "funding": [ { @@ -242,29 +639,29 @@ "type": "github" } ], - "time": "2022-07-08T17:17:42+00:00" + "time": "2023-04-28T14:42:27+00:00" }, { "name": "psr/http-message", - "version": "1.0.1", + "version": "2.0", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -279,7 +676,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for HTTP messages", @@ -293,9 +690,9 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/master" + "source": "https://github.com/php-fig/http-message/tree/2.0" }, - "time": "2016-08-06T14:39:51+00:00" + "time": "2023-04-04T09:54:51+00:00" }, { "name": "willdurand/negotiation", @@ -357,30 +754,30 @@ "packages-dev": [ { "name": "doctrine/instantiator", - "version": "1.4.1", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9", + "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.22" + "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", "autoload": { @@ -407,7 +804,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.1" + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" }, "funding": [ { @@ -423,20 +820,20 @@ "type": "tidelift" } ], - "time": "2022-03-03T08:28:38+00:00" + "time": "2022-12-30T00:15:36+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.11.0", + "version": "1.11.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", "shasum": "" }, "require": { @@ -474,7 +871,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" }, "funding": [ { @@ -482,7 +879,7 @@ "type": "tidelift" } ], - "time": "2022-03-03T13:19:32+00:00" + "time": "2023-03-08T13:26:56+00:00" }, { "name": "phar-io/manifest", @@ -595,233 +992,6 @@ }, "time": "2022-02-21T01:04:05+00:00" }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" - }, - "time": "2021-10-19T17:43:47+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.6.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "77a32518733312af16a44300404e945338981de3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", - "reference": "77a32518733312af16a44300404e945338981de3", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.0" - }, - "require-dev": { - "ext-tokenizer": "*", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" - }, - "time": "2022-03-15T21:29:03+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.15.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13", - "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.2", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^6.0 || ^7.0", - "phpunit/phpunit": "^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.15.0" - }, - "time": "2021-12-08T12:19:24+00:00" - }, { "name": "phpunit/php-code-coverage", "version": "7.0.15", @@ -1121,16 +1291,16 @@ }, { "name": "phpunit/phpunit", - "version": "8.5.28", + "version": "8.5.34", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "8f2d1c9c7b30382459c871467853da1a6e44fbd4" + "reference": "622d0186707f39a4ae71df3bcf42d759bb868854" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8f2d1c9c7b30382459c871467853da1a6e44fbd4", - "reference": "8f2d1c9c7b30382459c871467853da1a6e44fbd4", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/622d0186707f39a4ae71df3bcf42d759bb868854", + "reference": "622d0186707f39a4ae71df3bcf42d759bb868854", "shasum": "" }, "require": { @@ -1145,15 +1315,14 @@ "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", "php": ">=7.2", - "phpspec/prophecy": "^1.10.3", "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.3", - "sebastian/exporter": "^3.1.2", + "sebastian/exporter": "^3.1.5", "sebastian/global-state": "^3.0.0", "sebastian/object-enumerator": "^3.0.3", "sebastian/resource-operations": "^2.0.1", @@ -1161,9 +1330,9 @@ "sebastian/version": "^2.0.1" }, "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" @@ -1199,7 +1368,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.28" + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.34" }, "funding": [ { @@ -1209,9 +1379,13 @@ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" } ], - "time": "2022-07-29T09:20:50+00:00" + "time": "2023-09-19T05:20:51+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -1270,16 +1444,16 @@ }, { "name": "sebastian/comparator", - "version": "3.0.3", + "version": "3.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" + "reference": "1dc7ceb4a24aede938c7af2a9ed1de09609ca770" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1dc7ceb4a24aede938c7af2a9ed1de09609ca770", + "reference": "1dc7ceb4a24aede938c7af2a9ed1de09609ca770", "shasum": "" }, "require": { @@ -1332,7 +1506,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.5" }, "funding": [ { @@ -1340,20 +1514,20 @@ "type": "github" } ], - "time": "2020-11-30T08:04:30+00:00" + "time": "2022-09-14T12:31:48+00:00" }, { "name": "sebastian/diff", - "version": "3.0.3", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" + "reference": "6296a0c086dd0117c1b78b059374d7fcbe7545ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/6296a0c086dd0117c1b78b059374d7fcbe7545ae", + "reference": "6296a0c086dd0117c1b78b059374d7fcbe7545ae", "shasum": "" }, "require": { @@ -1398,7 +1572,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/diff/tree/3.0.4" }, "funding": [ { @@ -1406,7 +1580,7 @@ "type": "github" } ], - "time": "2020-11-30T07:59:04+00:00" + "time": "2023-05-07T05:30:20+00:00" }, { "name": "sebastian/environment", @@ -1473,16 +1647,16 @@ }, { "name": "sebastian/exporter", - "version": "3.1.4", + "version": "3.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db" + "reference": "73a9676f2833b9a7c36968f9d882589cd75511e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0c32ea2e40dbf59de29f3b49bf375176ce7dd8db", - "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/73a9676f2833b9a7c36968f9d882589cd75511e6", + "reference": "73a9676f2833b9a7c36968f9d882589cd75511e6", "shasum": "" }, "require": { @@ -1538,7 +1712,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.4" + "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.5" }, "funding": [ { @@ -1546,20 +1720,20 @@ "type": "github" } ], - "time": "2021-11-11T13:51:24+00:00" + "time": "2022-09-14T06:00:17+00:00" }, { "name": "sebastian/global-state", - "version": "3.0.2", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "de036ec91d55d2a9e0db2ba975b512cdb1c23921" + "reference": "66783ce213de415b451b904bfef9dda0cf9aeae0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/de036ec91d55d2a9e0db2ba975b512cdb1c23921", - "reference": "de036ec91d55d2a9e0db2ba975b512cdb1c23921", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/66783ce213de415b451b904bfef9dda0cf9aeae0", + "reference": "66783ce213de415b451b904bfef9dda0cf9aeae0", "shasum": "" }, "require": { @@ -1602,7 +1776,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.2" + "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.3" }, "funding": [ { @@ -1610,7 +1784,7 @@ "type": "github" } ], - "time": "2022-02-10T06:55:38+00:00" + "time": "2023-08-02T09:23:32+00:00" }, { "name": "sebastian/object-enumerator", @@ -1991,64 +2165,6 @@ } ], "time": "2021-07-28T10:34:58+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" } ], "aliases": [], @@ -2063,5 +2179,5 @@ "platform-dev": { "ext-sodium": "*" }, - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.3.0" } diff --git a/src/Authenticator.php b/src/Authenticator.php index 8891ea3..deb6777 100644 --- a/src/Authenticator.php +++ b/src/Authenticator.php @@ -10,27 +10,33 @@ use Gt\Cipher\Key; use Gt\Cipher\Message\EncryptedMessage; use Gt\Http\Uri; +use Gt\Logger\Log; use Gt\Session\SessionContainer; use Psr\Http\Message\UriInterface; class Authenticator { + const SESSION_STORE_KEY = "AUTHWAVE_CONSUMER_SESSION"; const RESPONSE_QUERY_PARAMETER = "AUTHWAVE_RESPONSE_DATA"; private SessionData $sessionData; private User $user; private Uri $currentUri; + private readonly string $deploymentId; + private readonly string $secret; public function __construct( - private readonly string $clientKey, + string $clientKey, string|Uri $currentUri, private readonly string $authwaveHost = "login.authwave.com", private ?SessionContainer $session = null, private ?RedirectHandler $redirectHandler = null, ) { + [$this->deploymentId, $this->secret] = explode("_", $clientKey); + $this->session = $this->session ?? new GlobalSessionContainer(); $this->redirectHandler = $redirectHandler ?? new RedirectHandler(); - if($data = $this->session->get(SessionData::class)) { - $this->sessionData = $data; + if($sessionData = $this->session->get(SessionData::class)) { + $this->sessionData = $sessionData; try { $responseData = $this->sessionData->getData(); @@ -63,7 +69,8 @@ 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); @@ -72,8 +79,9 @@ public function login(Token $token = null):void { } 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); @@ -93,7 +101,8 @@ public function getLoginUri(Token $token):BaseProviderUri { return new LoginUri( $token, $this->currentUri, - $this->authwaveHost + $this->deploymentId, + $this->authwaveHost, ); } @@ -101,7 +110,8 @@ private function getLogoutUri(Token $token):BaseProviderUri { return new LogoutUri( $token, $this->currentUri, - $this->authwaveHost + $this->deploymentId, + $this->authwaveHost, ); } @@ -111,7 +121,7 @@ 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( @@ -130,13 +140,15 @@ private function completeAuth():void { } if(!isset($this->sessionData)) { + die("No session data"); + $this->tidyResponseData(); return; } $token = $this->sessionData->getToken(); $secretSessionIv = $token->getSecretIv(); $encrypted = new EncryptedMessage($queryData, $secretSessionIv); - $key = new Key($this->clientKey); + $key = new Key($this->secret); $decrypted = $encrypted->decrypt($key); $data = json_decode($decrypted); $userData = new UserResponseData( @@ -150,9 +162,13 @@ private function completeAuth():void { new SessionData($token, $userData) ); + $this->tidyResponseData(); + } + + private function tidyResponseData():void { $this->redirectHandler->redirect( (new Uri($this->currentUri)) - ->withoutQueryValue(self::RESPONSE_QUERY_PARAMETER) + ->withoutQueryValue(self::RESPONSE_QUERY_PARAMETER) ); } diff --git a/src/ProviderUri/BaseProviderUri.php b/src/ProviderUri/BaseProviderUri.php index 3f8fcb4..a3b1cb3 100644 --- a/src/ProviderUri/BaseProviderUri.php +++ b/src/ProviderUri/BaseProviderUri.php @@ -7,9 +7,10 @@ abstract class BaseProviderUri extends Uri { const DEFAULT_BASE_REMOTE_URI = "login.authwave.com"; - const QUERY_STRING_CIPHER = "cipher"; - const QUERY_STRING_INIT_VECTOR = "iv"; - const QUERY_STRING_CURRENT_PATH = "path"; + const QUERY_STRING_CIPHER = "c"; + const QUERY_STRING_INIT_VECTOR = "i"; + 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) @@ -35,13 +36,15 @@ protected function normaliseBaseUri(string $baseUri):Uri { protected function buildQuery( Token $token, + string $deploymentId, string $currentPath, 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), ]); } } diff --git a/src/ProviderUri/LoginUri.php b/src/ProviderUri/LoginUri.php index 0e6eae5..5df9a81 100644 --- a/src/ProviderUri/LoginUri.php +++ b/src/ProviderUri/LoginUri.php @@ -15,22 +15,22 @@ 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 $deploymentId, string $baseRemoteUri = self::DEFAULT_BASE_REMOTE_URI ) { $baseRemoteUri = $this->normaliseBaseUri($baseRemoteUri); parent::__construct($baseRemoteUri); $this->query = $this->buildQuery( $token, + $deploymentId, $currentPath, - "action=login" + "action=login", ); } } diff --git a/src/ProviderUri/LogoutUri.php b/src/ProviderUri/LogoutUri.php index 9f7922c..c05e9fd 100644 --- a/src/ProviderUri/LogoutUri.php +++ b/src/ProviderUri/LogoutUri.php @@ -6,14 +6,15 @@ 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", ); diff --git a/src/Token.php b/src/Token.php index 0cbcebc..61aa5e5 100644 --- a/src/Token.php +++ b/src/Token.php @@ -8,8 +8,6 @@ use Gt\Cipher\Key; use Gt\Cipher\Message\EncryptedMessage; use Gt\Cipher\Message\PlainTextMessage; -use Gt\Logger\Log; -use StdClass; class Token { private Key $key; @@ -17,11 +15,11 @@ class Token { private InitVector $iv; public function __construct( - string $keyString, + string $secret, InitVector $sessionIv = null, InitVector $iv = null, ) { - $this->key = new Key($keyString); + $this->key = new Key($secret); $this->secretSessionIv = $sessionIv ?? new InitVector(); $this->iv = $iv ?? new InitVector(); } diff --git a/test/phpunit/AuthenticatorTest.php b/test/phpunit/AuthenticatorTest.php index afcf817..31b5653 100644 --- a/test/phpunit/AuthenticatorTest.php +++ b/test/phpunit/AuthenticatorTest.php @@ -78,7 +78,7 @@ public function testLoginRedirectsWithCorrectQueryString() { $expectedQueryParts = [ 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); diff --git a/test/phpunit/ProviderUri/LoginUriTest.php b/test/phpunit/ProviderUri/LoginUriTest.php index 7af25d2..3b56f50 100644 --- a/test/phpunit/ProviderUri/LoginUriTest.php +++ b/test/phpunit/ProviderUri/LoginUriTest.php @@ -45,7 +45,7 @@ public function testQueryString() { self::assertEquals( bin2hex($returnPath), - $queryParts[LoginUri::QUERY_STRING_CURRENT_PATH] + $queryParts[LoginUri::QUERY_STRING_CURRENT_URI] ); } } From 7478bda54a8ff735cdab4b5991f828ee6a850e41 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Mon, 20 Nov 2023 18:06:33 +0000 Subject: [PATCH 15/21] feature: fake login for #14 --- src/Authenticator.php | 48 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/Authenticator.php b/src/Authenticator.php index deb6777..25f009d 100644 --- a/src/Authenticator.php +++ b/src/Authenticator.php @@ -7,8 +7,10 @@ use Authwave\ProviderUri\LogoutUri; use Authwave\ProviderUri\ProfileUri; use Authwave\ResponseData\UserResponseData; +use Gt\Cipher\InitVector; use Gt\Cipher\Key; use Gt\Cipher\Message\EncryptedMessage; +use Gt\Cipher\Message\PlainTextMessage; use Gt\Http\Uri; use Gt\Logger\Log; use Gt\Session\SessionContainer; @@ -17,6 +19,7 @@ class Authenticator { const SESSION_STORE_KEY = "AUTHWAVE_CONSUMER_SESSION"; const RESPONSE_QUERY_PARAMETER = "AUTHWAVE_RESPONSE_DATA"; + const FAKE_EMAIL = "fakelogin@authwave.com"; private SessionData $sessionData; private User $user; @@ -84,9 +87,47 @@ public function logout(Token $token = null):void { $token = new Token($this->secret); } - $this->sessionData = new SessionData($token); - $this->session->set(SessionData::class, $this->sessionData); - $this->redirectHandler->redirect($this->getLogoutUri($token)); + if($this->user->email === self::FAKE_EMAIL) { + $this->session->remove(SessionData::class); + unset($this->user); + } + else { + $this->redirectHandler->redirect($this->getLogoutUri($token)); + $this->sessionData = new SessionData($token); + $this->session->set(SessionData::class, $this->sessionData); + } + } + + public function fakeLogin(string $userId, string $redirectTo = "/"):void { + $secretIv = new InitVector(); + $token = new Token($this->secret, $secretIv); + $sessionData = new SessionData($token); + $this->session->set(SessionData::class, $sessionData); + + $userData = new UserResponseData( + $userId, + 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, + ]); + $uri = new Uri("$redirectTo?$queryString"); + $this->redirectHandler->redirect($uri); } public function getUser():User { @@ -140,7 +181,6 @@ private function completeAuth():void { } if(!isset($this->sessionData)) { - die("No session data"); $this->tidyResponseData(); return; } From d3bf341521ed68fe6a14e03f9719a9b5ec1b9d02 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Mon, 20 Nov 2023 21:21:18 +0000 Subject: [PATCH 16/21] tweak: check property is set before access --- src/Authenticator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Authenticator.php b/src/Authenticator.php index 25f009d..9633494 100644 --- a/src/Authenticator.php +++ b/src/Authenticator.php @@ -87,7 +87,7 @@ public function logout(Token $token = null):void { $token = new Token($this->secret); } - if($this->user->email === self::FAKE_EMAIL) { + if(isset($this->user) && $this->user->email === self::FAKE_EMAIL) { $this->session->remove(SessionData::class); unset($this->user); } From a6cc9402ab282347a910f1d5159fce690bbe40ac Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Sun, 4 Feb 2024 20:19:43 +0000 Subject: [PATCH 17/21] tweak: get all kvp --- src/User.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/User.php b/src/User.php index 1c5784b..06fe970 100644 --- a/src/User.php +++ b/src/User.php @@ -11,4 +11,8 @@ public function __construct( public function getData(string $key):?string { return $this->kvp[$key] ?? null; } + + public function getKvp():array { + return $this->kvp; + } } From 7d840530c256a9fa466849f0a6a0527c6f1d775d Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Sun, 4 Feb 2024 21:28:27 +0000 Subject: [PATCH 18/21] tweak: override and remove session data on logout --- src/Authenticator.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Authenticator.php b/src/Authenticator.php index 9633494..52ed98f 100644 --- a/src/Authenticator.php +++ b/src/Authenticator.php @@ -92,9 +92,10 @@ public function logout(Token $token = null):void { unset($this->user); } else { - $this->redirectHandler->redirect($this->getLogoutUri($token)); $this->sessionData = new SessionData($token); $this->session->set(SessionData::class, $this->sessionData); + $this->session->remove(SessionData::class); + $this->redirectHandler->redirect($this->getLogoutUri($token)); } } From 3fca12086ffdbeecb65fa08c9730d3f05c643454 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Fri, 10 May 2024 15:14:05 +0100 Subject: [PATCH 19/21] feature: append existing query string --- src/Authenticator.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Authenticator.php b/src/Authenticator.php index 52ed98f..cac120b 100644 --- a/src/Authenticator.php +++ b/src/Authenticator.php @@ -127,6 +127,10 @@ public function fakeLogin(string $userId, string $redirectTo = "/"):void { $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); } From 9c8d79c78a48e719d6ed548410badb446cba2952 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Thu, 8 Aug 2024 10:16:12 +0100 Subject: [PATCH 20/21] tweak: pass in email you would like --- src/Authenticator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Authenticator.php b/src/Authenticator.php index cac120b..2c00e76 100644 --- a/src/Authenticator.php +++ b/src/Authenticator.php @@ -99,7 +99,7 @@ public function logout(Token $token = null):void { } } - public function fakeLogin(string $userId, string $redirectTo = "/"):void { + public function fakeLogin(string $userId, ?string $email = null, string $redirectTo = "/"):void { $secretIv = new InitVector(); $token = new Token($this->secret, $secretIv); $sessionData = new SessionData($token); @@ -107,7 +107,7 @@ public function fakeLogin(string $userId, string $redirectTo = "/"):void { $userData = new UserResponseData( $userId, - self::FAKE_EMAIL, + $email ?? self::FAKE_EMAIL, ); $this->session->set( From 8d3cfe6e0f147f5383dc675d6c75473a71351a36 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Tue, 5 Aug 2025 17:23:39 +0100 Subject: [PATCH 21/21] tweak: allow multiple underscores --- src/Authenticator.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Authenticator.php b/src/Authenticator.php index 2c00e76..3af6259 100644 --- a/src/Authenticator.php +++ b/src/Authenticator.php @@ -34,7 +34,8 @@ public function __construct( private ?SessionContainer $session = null, private ?RedirectHandler $redirectHandler = null, ) { - [$this->deploymentId, $this->secret] = explode("_", $clientKey); + $this->deploymentId = substr($clientKey, 0, strrpos($clientKey, "_")); + $this->secret = substr(strrchr($clientKey, "_"), 1); $this->session = $this->session ?? new GlobalSessionContainer(); $this->redirectHandler = $redirectHandler ?? new RedirectHandler();