diff --git a/.gitignore b/.gitignore index 8d7f3f7..1bcb3fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/.phpunit.result.cache /clover.xml /coveralls-upload.json /docs/html/ @@ -5,4 +6,3 @@ /vendor/ /zf-mkdoc-theme.tgz /zf-mkdoc-theme/ -.idea diff --git a/.travis.yml b/.travis.yml index 0dee3e5..9eb9fcf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,3 @@ -sudo: false - language: php cache: @@ -19,6 +17,7 @@ matrix: - php: 7.1 env: - DEPS=locked + - LEGACY_DEPS="phpunit/phpunit" - CS_CHECK=true - TEST_COVERAGE=true - php: 7.1 @@ -33,7 +32,13 @@ matrix: - php: 7.2 env: - DEPS=latest - - php: nightly + - php: 7.3 + env: + - DEPS=lowest + - php: 7.3 + env: + - DEPS=locked + - php: 7.3 env: - DEPS=latest @@ -41,9 +46,10 @@ before_install: - if [[ $TEST_COVERAGE != 'true' ]]; then phpenv config-rm xdebug.ini || return 0 ; fi install: - - travis_retry composer install $COMPOSER_ARGS + - travis_retry composer install $COMPOSER_ARGS --ignore-platform-reqs + - if [[ $LEGACY_DEPS != '' ]]; then travis_retry composer update $COMPOSER_ARGS --with-dependencies $LEGACY_DEPS ; fi - if [[ $DEPS == 'latest' ]]; then travis_retry composer update $COMPOSER_ARGS ; fi - - if [[ $DEPS == 'lowest' ]]; then travis_retry composer update --prefer-lowest --prefer-stable $COMPOSER_ARGS ; fi + - if [[ $DEPS == 'lowest' ]]; then travis_retry composer update $COMPOSER_ARGS --prefer-lowest --prefer-stable ; fi - if [[ $TEST_COVERAGE == 'true' ]]; then travis_retry composer require --dev $COMPOSER_ARGS $COVERAGE_DEPS ; fi - stty cols 120 && composer show @@ -56,4 +62,3 @@ after_script: notifications: email: false - diff --git a/CHANGELOG.md b/CHANGELOG.md index c8d469d..7d0ddb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,173 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. +## 2.0.1 - TBD + +### Added + +- Nothing. + +### Changed + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + +## 2.0.0 - 2019-12-28 + +### Added + +- Nothing. + +### Changed + +- [#69](https://github.com/zendframework/zend-expressive-authentication-oauth2/pull/69) updates the minimum supported version of league/oauth-server to ^8.0. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + +## 1.3.0 - 2019-12-28 + +### Added + +- [#62](https://github.com/zendframework/zend-expressive-authentication-oauth2/pull/62) adds the ability to configure and add event listeners for the underlying league/oauth2 implementation. See the [event listeners configuration documentation](https://docs.zendframework.com/zend-expressive-authentication-oauth2/intro/#configure-event-listeners) for more information. + +### Changed + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + +## 1.2.1 - 2019-12-28 + +### Added + +- Nothing. + +### Changed + +- [#55](https://github.com/zendframework/zend-expressive-authentication-oauth2/pull/55) changes how the `OAuth2Adapter` validates when a client ID is present. Previously, if a client ID was present, but not a user ID, it would attempt to pull a user from the user factory using the client ID, which was incorrect. With this release, it no longer does that. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- [#71](https://github.com/zendframework/zend-expressive-authentication-oauth2/pull/71) adds a check to `AccessTokenRepository` to verify that a row was returned before checking if a token was revoked, raising an exception if not. + +- [#72](https://github.com/zendframework/zend-expressive-authentication-oauth2/pull/72) updates the database schema in provided examples to reflect actual requirements. + +## 1.2.0 - 2019-09-01 + +### Added + +- [#63](https://github.com/zendframework/zend-expressive-authentication-oauth2/pull/63) + registers the `ConfigProvider` with the package. If you are using zend-component-installer + it will be added to your configuration during the installation. + +- [#64](https://github.com/zendframework/zend-expressive-authentication-oauth2/pull/64) + adds support for PHP 7.3. + +### Changed + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + +## 1.1.0 - 2018-11-19 + +### Added + +- Nothing. + +### Changed + +- [#58](https://github.com/zendframework/zend-expressive-authentication-oauth2/pull/58) + Upgrades the `league/oauth2-server` library to 7.3.0 in order to use it with + [Swoole](https://www.swoole.co.uk/). This is provided by `league/oauth2-server` + thanks to [#960 AuthorizationServer stateless](https://github.com/thephpleague/oauth2-server/pull/960) + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + +## 1.0.1 - 2018-10-31 + +### Added + +- Nothing. + +### Changed + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- [#52](https://github.com/zendframework/zend-expressive-authentication-oauth2/issues/52) + Wrong factory mapped to AuthorizationHandler +- [#54](https://github.com/zendframework/zend-expressive-authentication-oauth2/pull/54) + Fixed "WWW-Authenticate" header value format + ## 1.0.0 - 2018-10-04 ### Added diff --git a/LICENSE.md b/LICENSE.md index 4d6af70..ed2bafe 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (c) 2017-2018, Zend Technologies USA, Inc. +Copyright (c) 2017-2019, Zend Technologies USA, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, diff --git a/README.md b/README.md index e793ffd..aaa8e09 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # OAuth2 server middleware for Expressive and PSR-7 applications +> ## Repository abandoned 2019-12-31 +> +> This repository has moved to [mezzio/mezzio-authentication-oauth2](https://github.com/mezzio/mezzio-authentication-oauth2). + [![Build Status](https://secure.travis-ci.org/zendframework/zend-expressive-authentication-oauth2.svg?branch=master)](https://secure.travis-ci.org/zendframework/zend-expressive-authentication-oauth2) [![Coverage Status](https://coveralls.io/repos/github/zendframework/zend-expressive-authentication-oauth2/badge.svg?branch=master)](https://coveralls.io/github/zendframework/zend-expressive-authentication-oauth2?branch=master) @@ -28,10 +32,10 @@ $ composer require zendframework/zend-expressive-authentication-oauth2 ## Documentation -Documentation is [in the doc tree](doc/book/), and can be compiled using [mkdocs](http://www.mkdocs.org): +Browse the documentation online at https://docs.zendframework.com/zend-expressive-authentication-oauth2/ -```bash -$ mkdocs build -``` +## Support -You may also [browse the documentation online](https://docs.zendframework.com/zend-expressive-authentication-oauth2/). +* [Issues](https://github.com/zendframework/zend-expressive-authentication-oauth2/issues/) +* [Chat](https://zendframework-slack.herokuapp.com/) +* [Forum](https://discourse.zendframework.com/) diff --git a/composer.json b/composer.json index 8bd66a7..180b8d4 100644 --- a/composer.json +++ b/composer.json @@ -17,22 +17,23 @@ "issues": "https://github.com/zendframework/zend-expressive-authentication-oauth2/issues", "source": "https://github.com/zendframework/zend-expressive-authentication-oauth2", "rss": "https://github.com/zendframework/zend-expressive-authentication-oauth2/releases.atom", - "slack": "https://zendframework-slack.herokuapp.com", + "chat": "https://zendframework-slack.herokuapp.com", "forum": "https://discourse.zendframework.com/c/questions/expressive" }, "require": { "php": "^7.1", - "league/oauth2-server": "^6.0.2", + "league/oauth2-server": "^8.0.0", "psr/container": "^1.0", "psr/http-message": "^1.0.1", "psr/http-server-middleware": "^1.0", "zendframework/zend-expressive-authentication": "^1.0" }, "require-dev": { - "phpunit/phpunit": "^7.0.1", + "phpunit/phpunit": "^7.5.15 || ^8.3.4", "roave/security-advisories": "dev-master", "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-diactoros": "^1.4.0" + "zendframework/zend-diactoros": "^1.4.0 || ^2.0", + "zendframework/zend-servicemanager": "^3.0.0" }, "conflict": { "container-interop/container-interop": "<1.2.0" @@ -52,7 +53,11 @@ }, "extra": { "branch-alias": { - "dev-master": "0.4.x-dev" + "dev-master": "2.0.x-dev", + "dev-develop": "2.1.x-dev" + }, + "zf": { + "config-provider": "Zend\\Expressive\\Authentication\\OAuth2\\ConfigProvider" } }, "bin": [ diff --git a/composer.lock b/composer.lock index e04b4b8..1915661 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d8f6be31fe18e6a9ab7a576fb39fd9f2", + "content-hash": "607e2fc1e526e325c6d9b176d6250da4", "packages": [ { "name": "defuse/php-encryption", @@ -71,33 +71,30 @@ }, { "name": "lcobucci/jwt", - "version": "3.2.4", + "version": "3.3.1", "source": { "type": "git", "url": "https://github.com/lcobucci/jwt.git", - "reference": "c9704b751315d21735dc98d78d4f37bd73596da7" + "reference": "a11ec5f4b4d75d1fcd04e133dede4c317aac9e18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/jwt/zipball/c9704b751315d21735dc98d78d4f37bd73596da7", - "reference": "c9704b751315d21735dc98d78d4f37bd73596da7", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/a11ec5f4b4d75d1fcd04e133dede4c317aac9e18", + "reference": "a11ec5f4b4d75d1fcd04e133dede4c317aac9e18", "shasum": "" }, "require": { + "ext-mbstring": "*", "ext-openssl": "*", - "php": ">=5.5" + "php": "^5.6 || ^7.0" }, "require-dev": { - "mdanter/ecc": "~0.3.1", "mikey179/vfsstream": "~1.5", "phpmd/phpmd": "~2.2", "phpunit/php-invoker": "~1.1", - "phpunit/phpunit": "~4.5", + "phpunit/phpunit": "^5.7 || ^7.3", "squizlabs/php_codesniffer": "~2.3" }, - "suggest": { - "mdanter/ecc": "Required to use Elliptic Curves based algorithms." - }, "type": "library", "extra": { "branch-alias": { @@ -125,20 +122,20 @@ "JWS", "jwt" ], - "time": "2018-08-03T11:23:50+00:00" + "time": "2019-05-24T18:30:49+00:00" }, { "name": "league/event", - "version": "2.1.2", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/thephpleague/event.git", - "reference": "e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd" + "reference": "d2cc124cf9a3fab2bb4ff963307f60361ce4d119" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/event/zipball/e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd", - "reference": "e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd", + "url": "https://api.github.com/repos/thephpleague/event/zipball/d2cc124cf9a3fab2bb4ff963307f60361ce4d119", + "reference": "d2cc124cf9a3fab2bb4ff963307f60361ce4d119", "shasum": "" }, "require": { @@ -146,7 +143,7 @@ }, "require-dev": { "henrikbjorn/phpspec-code-coverage": "~1.0.1", - "phpspec/phpspec": "~2.0.0" + "phpspec/phpspec": "^2.2" }, "type": "library", "extra": { @@ -175,38 +172,41 @@ "event", "listener" ], - "time": "2015-05-21T12:24:47+00:00" + "time": "2018-11-26T11:52:41+00:00" }, { "name": "league/oauth2-server", - "version": "6.1.1", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/thephpleague/oauth2-server.git", - "reference": "a0cabb573c7cd5ee01803daec992d6ee3677c4ae" + "reference": "e1dc4d708c56fcfa205be4bb1862b6d525b4baac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/oauth2-server/zipball/a0cabb573c7cd5ee01803daec992d6ee3677c4ae", - "reference": "a0cabb573c7cd5ee01803daec992d6ee3677c4ae", + "url": "https://api.github.com/repos/thephpleague/oauth2-server/zipball/e1dc4d708c56fcfa205be4bb1862b6d525b4baac", + "reference": "e1dc4d708c56fcfa205be4bb1862b6d525b4baac", "shasum": "" }, "require": { - "defuse/php-encryption": "^2.1", + "defuse/php-encryption": "^2.2.1", + "ext-json": "*", "ext-openssl": "*", - "lcobucci/jwt": "^3.1", - "league/event": "^2.1", - "paragonie/random_compat": "^2.0", - "php": ">=5.6.0", - "psr/http-message": "^1.0" + "lcobucci/jwt": "^3.3.1", + "league/event": "^2.2", + "php": ">=7.1.0", + "psr/http-message": "^1.0.1" }, "replace": { "league/oauth2server": "*", "lncd/oauth2": "*" }, "require-dev": { - "phpunit/phpunit": "^4.8.38 || ^5.7.21", - "zendframework/zend-diactoros": "^1.0" + "phpstan/phpstan": "^0.11.8", + "phpstan/phpstan-phpunit": "^0.11.2", + "phpunit/phpunit": "^7.5.13 || ^8.2.3", + "roave/security-advisories": "dev-master", + "zendframework/zend-diactoros": "^2.1.2" }, "type": "library", "autoload": { @@ -224,6 +224,12 @@ "email": "hello@alexbilbie.com", "homepage": "http://www.alexbilbie.com", "role": "Developer" + }, + { + "name": "Andy Millington", + "email": "andrew@noexceptions.io", + "homepage": "https://www.noexceptions.io", + "role": "Developer" } ], "description": "A lightweight and powerful OAuth 2.0 authorization and resource server library with support for all the core specification grants. This library will allow you to secure your API with OAuth and allow your applications users to approve apps that want to access their data from your API.", @@ -243,37 +249,33 @@ "secure", "server" ], - "time": "2017-12-23T23:33:42+00:00" + "time": "2019-07-13T18:58:26+00:00" }, { "name": "paragonie/random_compat", - "version": "v2.0.17", + "version": "v9.99.99", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d" + "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/29af24f25bab834fcbb38ad2a69fa93b867e070d", - "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", + "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", "shasum": "" }, "require": { - "php": ">=5.2.0" + "php": "^7" }, "require-dev": { - "phpunit/phpunit": "4.*|5.*" + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" }, "suggest": { "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." }, "type": "library", - "autoload": { - "files": [ - "lib/random.php" - ] - }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" @@ -292,7 +294,7 @@ "pseudorandom", "random" ], - "time": "2018-07-04T16:31:37+00:00" + "time": "2018-07-02T15:55:56+00:00" }, { "name": "psr/container", @@ -395,16 +397,16 @@ }, { "name": "psr/http-server-handler", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-server-handler.git", - "reference": "439d92054dc06097f2406ec074a2627839955a02" + "reference": "aff2f80e33b7f026ec96bb42f63242dc50ffcae7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/439d92054dc06097f2406ec074a2627839955a02", - "reference": "439d92054dc06097f2406ec074a2627839955a02", + "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/aff2f80e33b7f026ec96bb42f63242dc50ffcae7", + "reference": "aff2f80e33b7f026ec96bb42f63242dc50ffcae7", "shasum": "" }, "require": { @@ -444,20 +446,20 @@ "response", "server" ], - "time": "2018-01-22T17:04:15+00:00" + "time": "2018-10-30T16:46:14+00:00" }, { "name": "psr/http-server-middleware", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-server-middleware.git", - "reference": "ea17eb1fb2c8df6db919cc578451a8013c6a0ae5" + "reference": "2296f45510945530b9dceb8bcedb5cb84d40c5f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/ea17eb1fb2c8df6db919cc578451a8013c6a0ae5", - "reference": "ea17eb1fb2c8df6db919cc578451a8013c6a0ae5", + "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/2296f45510945530b9dceb8bcedb5cb84d40c5f5", + "reference": "2296f45510945530b9dceb8bcedb5cb84d40c5f5", "shasum": "" }, "require": { @@ -497,20 +499,20 @@ "request", "response" ], - "time": "2018-01-22T17:08:31+00:00" + "time": "2018-10-30T17:12:04+00:00" }, { "name": "zendframework/zend-expressive-authentication", - "version": "1.0.0", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-expressive-authentication.git", - "reference": "52594067ffc149cd7defb87388e2c33707b5203f" + "reference": "d602ab47649cd550ffc9c745cb6de862d6c8a862" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-expressive-authentication/zipball/52594067ffc149cd7defb87388e2c33707b5203f", - "reference": "52594067ffc149cd7defb87388e2c33707b5203f", + "url": "https://api.github.com/repos/zendframework/zend-expressive-authentication/zipball/d602ab47649cd550ffc9c745cb6de862d6c8a862", + "reference": "d602ab47649cd550ffc9c745cb6de862d6c8a862", "shasum": "" }, "require": { @@ -536,7 +538,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "0.4.x-dev" + "dev-master": "1.1.x-dev", + "dev-develop": "1.2.x-dev" }, "zf": { "config-provider": "Zend\\Expressive\\Authentication\\ConfigProvider" @@ -562,33 +565,67 @@ "zend-expressive", "zf" ], - "time": "2018-08-27T15:09:06+00:00" + "time": "2019-03-05T17:50:29+00:00" } ], "packages-dev": [ + { + "name": "container-interop/container-interop", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/container-interop/container-interop.git", + "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8", + "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8", + "shasum": "" + }, + "require": { + "psr/container": "^1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Interop\\Container\\": "src/Interop/Container/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", + "homepage": "https://github.com/container-interop/container-interop", + "abandoned": "psr/container", + "time": "2017-02-14T19:40:03+00:00" + }, { "name": "doctrine/instantiator", - "version": "1.1.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1", + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1", "shasum": "" }, "require": { "php": "^7.1" }, "require-dev": { - "athletic/athletic": "~0.1.8", + "doctrine/coding-standard": "^6.0", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "^6.2.3", - "squizlabs/php_codesniffer": "^3.0.2" + "phpbench/phpbench": "^0.13", + "phpstan/phpstan-phpunit": "^0.11", + "phpstan/phpstan-shim": "^0.11", + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { @@ -613,25 +650,25 @@ } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", "keywords": [ "constructor", "instantiate" ], - "time": "2017-07-22T11:58:36+00:00" + "time": "2019-10-21T16:45:58+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.8.1", + "version": "1.9.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" + "reference": "579bb7356d91f9456ccd505f24ca8b667966a0a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", - "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/579bb7356d91f9456ccd505f24ca8b667966a0a7", + "reference": "579bb7356d91f9456ccd505f24ca8b667966a0a7", "shasum": "" }, "require": { @@ -666,7 +703,7 @@ "object", "object graph" ], - "time": "2018-06-11T23:09:50+00:00" + "time": "2019-12-15T19:12:40+00:00" }, { "name": "phar-io/manifest", @@ -772,35 +809,33 @@ }, { "name": "phpdocumentor/reflection-common", - "version": "1.0.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a", + "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a", "shasum": "" }, "require": { - "php": ">=5.5" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^4.6" + "phpunit/phpunit": "~6" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] + "phpDocumentor\\Reflection\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -822,31 +857,32 @@ "reflection", "static analysis" ], - "time": "2017-09-11T18:02:19+00:00" + "time": "2018-08-07T13:53:10+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.3.0", + "version": "4.3.4", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08" + "reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/da3fd972d6bafd628114f7e7e036f45944b62e9c", + "reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c", "shasum": "" }, "require": { "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", + "phpdocumentor/reflection-common": "^1.0.0 || ^2.0.0", + "phpdocumentor/type-resolver": "~0.4 || ^1.0.0", "webmozart/assert": "^1.0" }, "require-dev": { - "doctrine/instantiator": "~1.0.5", + "doctrine/instantiator": "^1.0.5", "mockery/mockery": "^1.0", + "phpdocumentor/type-resolver": "0.4.*", "phpunit/phpunit": "^6.4" }, "type": "library", @@ -873,41 +909,40 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-11-30T07:14:17+00:00" + "time": "2019-12-28T18:55:12+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.4.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", + "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" + "php": "^7.1", + "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" + "ext-tokenizer": "^7.1", + "mockery/mockery": "~1", + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -920,42 +955,43 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-07-14T14:27:02+00:00" + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "time": "2019-08-22T18:11:29+00:00" }, { "name": "phpspec/prophecy", - "version": "1.8.0", + "version": "1.10.1", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" + "reference": "cbe1df668b3fe136bcc909126a0f529a78d4cbbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/cbe1df668b3fe136bcc909126a0f529a78d4cbbc", + "reference": "cbe1df668b3fe136bcc909126a0f529a78d4cbbc", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", + "sebastian/comparator": "^1.2.3|^2.0|^3.0", "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", + "phpspec/phpspec": "^2.5 || ^3.2", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8.x-dev" + "dev-master": "1.10.x-dev" } }, "autoload": { - "psr-0": { - "Prophecy\\": "src/" + "psr-4": { + "Prophecy\\": "src/Prophecy" } }, "notification-url": "https://packagist.org/downloads/", @@ -983,44 +1019,44 @@ "spy", "stub" ], - "time": "2018-08-05T17:53:17+00:00" + "time": "2019-12-22T21:05:45+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "6.0.7", + "version": "7.0.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "865662550c384bc1db7e51d29aeda1c2c161d69a" + "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/865662550c384bc1db7e51d29aeda1c2c161d69a", - "reference": "865662550c384bc1db7e51d29aeda1c2c161d69a", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f1884187926fbb755a9aaf0b3836ad3165b478bf", + "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", - "php": "^7.1", - "phpunit/php-file-iterator": "^2.0", + "php": "^7.2", + "phpunit/php-file-iterator": "^2.0.2", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.0", + "phpunit/php-token-stream": "^3.1.1", "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.1", + "sebastian/environment": "^4.2.2", "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1" + "theseer/tokenizer": "^1.1.3" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^8.2.2" }, "suggest": { - "ext-xdebug": "^2.6.0" + "ext-xdebug": "^2.7.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.0-dev" + "dev-master": "7.0-dev" } }, "autoload": { @@ -1046,7 +1082,7 @@ "testing", "xunit" ], - "time": "2018-06-01T07:51:50+00:00" + "time": "2019-11-20T13:55:58+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1141,16 +1177,16 @@ }, { "name": "phpunit/php-timer", - "version": "2.0.0", + "version": "2.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f" + "reference": "1038454804406b0b5f5f520358e78c1c2f71501e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b8454ea6958c3dee38453d3bd571e023108c91f", - "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e", + "reference": "1038454804406b0b5f5f520358e78c1c2f71501e", "shasum": "" }, "require": { @@ -1162,7 +1198,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.1-dev" } }, "autoload": { @@ -1186,20 +1222,20 @@ "keywords": [ "timer" ], - "time": "2018-02-01T13:07:23+00:00" + "time": "2019-06-07T04:22:29+00:00" }, { "name": "phpunit/php-token-stream", - "version": "3.0.0", + "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "21ad88bbba7c3d93530d93994e0a33cd45f02ace" + "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/21ad88bbba7c3d93530d93994e0a33cd45f02ace", - "reference": "21ad88bbba7c3d93530d93994e0a33cd45f02ace", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff", + "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff", "shasum": "" }, "require": { @@ -1212,7 +1248,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -1235,57 +1271,56 @@ "keywords": [ "tokenizer" ], - "time": "2018-02-01T13:16:43+00:00" + "time": "2019-09-17T06:23:10+00:00" }, { "name": "phpunit/phpunit", - "version": "7.3.5", + "version": "8.5.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "7b331efabbb628c518c408fdfcaf571156775de2" + "reference": "7870c78da3c5e4883eaef36ae47853ebb3cb86f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7b331efabbb628c518c408fdfcaf571156775de2", - "reference": "7b331efabbb628c518c408fdfcaf571156775de2", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7870c78da3c5e4883eaef36ae47853ebb3cb86f2", + "reference": "7870c78da3c5e4883eaef36ae47853ebb3cb86f2", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.1", + "doctrine/instantiator": "^1.2.0", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", - "myclabs/deep-copy": "^1.7", - "phar-io/manifest": "^1.0.2", - "phar-io/version": "^2.0", - "php": "^7.1", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^6.0.7", - "phpunit/php-file-iterator": "^2.0.1", + "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", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.0", - "sebastian/comparator": "^3.0", - "sebastian/diff": "^3.0", - "sebastian/environment": "^3.1", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^2.0", + "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/global-state": "^3.0.0", "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^1.0", + "sebastian/resource-operations": "^2.0.1", + "sebastian/type": "^1.1.3", "sebastian/version": "^2.0.1" }, - "conflict": { - "phpunit/phpunit-mock-objects": "*" - }, "require-dev": { "ext-pdo": "*" }, "suggest": { "ext-soap": "*", "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0" + "phpunit/php-invoker": "^2.0.0" }, "bin": [ "phpunit" @@ -1293,7 +1328,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "7.3-dev" + "dev-master": "8.5-dev" } }, "autoload": { @@ -1319,30 +1354,95 @@ "testing", "xunit" ], - "time": "2018-09-08T15:14:29+00:00" + "time": "2019-12-25T14:49:39+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "time": "2019-04-30T12:38:16+00:00" }, { "name": "roave/security-advisories", "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/Roave/SecurityAdvisories.git", + "reference": "44a677c8e06241a66409ae6e4820dc166fc09ab2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/44a677c8e06241a66409ae6e4820dc166fc09ab2", + "reference": "44a677c8e06241a66409ae6e4820dc166fc09ab2", + "shasum": "" + }, "conflict": { "3f/pygmentize": "<1.2", "adodb/adodb-php": "<5.20.12", + "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", "amphp/artax": "<1.0.6|>=2,<2.0.6", "amphp/http": "<1.0.1", + "api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6", "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", "aws/aws-sdk-php": ">=3,<3.2.1", "brightlocal/phpwhois": "<=4.2.5", "bugsnag/bugsnag-laravel": ">=2,<2.0.2", - "cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.0.15|>=3.1,<3.1.4|>=3.4,<3.4.14|>=3.5,<3.5.17|>=3.6,<3.6.4", + "cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.5.18|>=3.6,<3.6.15|>=3.7,<3.7.7", "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4", "cartalyst/sentry": "<=2.1.6", "codeigniter/framework": "<=3.0.6", - "composer/composer": "<=1.0.0-alpha11", + "composer/composer": "<=1-alpha.11", "contao-components/mediaelement": ">=2.14.2,<2.21.1", - "contao/core": ">=2,<3.5.35", - "contao/core-bundle": ">=4,<4.4.18|>=4.5,<4.5.8", + "contao/core": ">=2,<3.5.39", + "contao/core-bundle": ">=4,<4.4.46|>=4.5,<4.8.6", "contao/listing-bundle": ">=4,<4.4.8", - "contao/newsletter-bundle": ">=4,<4.1", + "datadog/dd-trace": ">=0.30,<0.30.2", "david-garcia/phpwhois": "<=4.3.1", "doctrine/annotations": ">=1,<1.2.7", "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2", @@ -1354,18 +1454,23 @@ "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1", "dompdf/dompdf": ">=0.6,<0.6.2", - "drupal/core": ">=7,<7.59|>=8,<8.4.8|>=8.5,<8.5.3", - "drupal/drupal": ">=7,<7.59|>=8,<8.4.8|>=8.5,<8.5.3", - "erusev/parsedown": "<1.7", - "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.3|>=5.4,<5.4.11.3|>=2017.8,<2017.8.1.1|>=2017.12,<2017.12.2.1", + "drupal/core": ">=7,<8.7.11|>=8.8,<8.8.1", + "drupal/drupal": ">=7,<8.7.11|>=8.8,<8.8.1", + "erusev/parsedown": "<1.7.2", + "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.4", + "ezsystems/ezpublish-kernel": ">=5.3,<5.3.12.1|>=5.4,<5.4.13.1|>=6,<6.7.9.1|>=6.8,<6.13.5.1|>=7,<7.2.4.1|>=7.3,<7.3.2.1", + "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.6|>=5.4,<5.4.12.3|>=2011,<2017.12.4.3|>=2018.6,<2018.6.1.4|>=2018.9,<2018.9.1.3", + "ezsystems/repository-forms": ">=2.3,<2.3.2.1", "ezyang/htmlpurifier": "<4.1.1", "firebase/php-jwt": "<2", + "fooman/tcpdf": "<6.2.22", + "fossar/tcpdf-parser": "<6.2.22", "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", "fuel/core": "<1.8.1", "gree/jose": "<=2.2", "gregwar/rst": "<1.0.3", - "guzzlehttp/guzzle": ">=6,<6.2.1|>=4.0.0-rc2,<4.2.4|>=5,<5.3.1", + "guzzlehttp/guzzle": ">=4-rc.2,<4.2.4|>=5,<5.3.1|>=6,<6.2.1", "illuminate/auth": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.10", "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30", "illuminate/database": ">=4,<4.0.99|>=4.1,<4.1.29", @@ -1376,11 +1481,13 @@ "jsmitty12/phpwhois": "<5.1", "kazist/phpwhois": "<=4.2.6", "kreait/firebase-php": ">=3.2,<3.8.1", + "la-haute-societe/tcpdf": "<6.2.22", "laravel/framework": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30", "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", - "magento/magento1ce": "<1.9.3.9", - "magento/magento1ee": ">=1.9,<1.14.3.2", - "magento/product-community-edition": ">=2,<2.2.5", + "league/commonmark": "<0.18.3", + "magento/magento1ce": "<1.9.4.3", + "magento/magento1ee": ">=1,<1.14.4.3", + "magento/product-community-edition": ">=2,<2.2.10|>=2.3,<2.3.2-p.2", "monolog/monolog": ">=1.8,<1.12", "namshi/jose": "<2.2", "onelogin/php-saml": "<2.10.4", @@ -1391,62 +1498,85 @@ "pagarme/pagarme-php": ">=0,<3", "paragonie/random_compat": "<2", "paypal/merchant-sdk-php": "<3.12", - "phpmailer/phpmailer": ">=5,<5.2.24", + "pear/archive_tar": "<1.4.4", + "phpmailer/phpmailer": ">=5,<5.2.27|>=6,<6.0.6", + "phpoffice/phpexcel": "<=1.8.1", + "phpoffice/phpspreadsheet": "<=1.5", "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3", "phpwhois/phpwhois": "<=4.2.5", "phpxmlrpc/extras": "<0.6.1", - "propel/propel": ">=2.0.0-alpha1,<=2.0.0-alpha7", + "propel/propel": ">=2-alpha.1,<=2-alpha.7", "propel/propel1": ">=1,<=1.7.1", "pusher/pusher-php-server": "<2.2.1", + "robrichards/xmlseclibs": ">=1,<3.0.4", "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", + "scheb/two-factor-bundle": ">=0,<3.26|>=4,<4.11", "sensiolabs/connect": "<4.2.3", "serluck/phpwhois": "<=4.2.6", "shopware/shopware": "<5.3.7", "silverstripe/cms": ">=3,<=3.0.11|>=3.1,<3.1.11", "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", - "silverstripe/framework": ">=3,<3.3", + "silverstripe/framework": ">=3,<3.6.7|>=3.7,<3.7.3|>=4,<4.4", + "silverstripe/graphql": ">=2,<2.0.5|>=3,<3.1.2", + "silverstripe/registry": ">=2.1,<2.1.2|>=2.2,<2.2.1", + "silverstripe/restfulserver": ">=1,<1.0.9|>=2,<2.0.4", "silverstripe/userforms": "<3", "simple-updates/phpwhois": "<=1", "simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4", - "simplesamlphp/simplesamlphp": "<1.15.2", + "simplesamlphp/simplesamlphp": "<1.17.8", "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", "slim/slim": "<2.6", "smarty/smarty": "<3.1.33", "socalnick/scn-social-auth": "<1.15.2", + "spoonity/tcpdf": "<6.2.22", "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", "stormpath/sdk": ">=0,<9.9.99", + "studio-42/elfinder": "<2.1.48", "swiftmailer/swiftmailer": ">=4,<5.4.5", "sylius/admin-bundle": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2", - "sylius/sylius": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2", - "symfony/dependency-injection": ">=2,<2.0.17", - "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", - "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2", - "symfony/http-foundation": ">=2,<2.7.49|>=2.8,<2.8.44|>=3,<3.3.18|>=3.4,<3.4.14|>=4,<4.0.14|>=4.1,<4.1.3", - "symfony/http-kernel": ">=2,<2.3.29|>=2.4,<2.5.12|>=2.6,<2.6.8", + "sylius/grid": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1", + "sylius/grid-bundle": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1", + "sylius/sylius": ">=1,<1.1.18|>=1.2,<1.2.17|>=1.3,<1.3.12|>=1.4,<1.4.4", + "symfony/cache": ">=3.1,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8", + "symfony/dependency-injection": ">=2,<2.0.17|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", + "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "symfony/http-foundation": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8", + "symfony/http-kernel": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8", "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", + "symfony/mime": ">=4.3,<4.3.8", + "symfony/phpunit-bridge": ">=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "symfony/polyfill": ">=1,<1.10", + "symfony/polyfill-php55": ">=1,<1.10", + "symfony/proxy-manager-bridge": ">=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", "symfony/routing": ">=2,<2.0.19", - "symfony/security": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/security": ">=2,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", "symfony/security-bundle": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<2.8.37|>=3,<3.3.17|>=3.4,<3.4.7|>=4,<4.0.7", "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", "symfony/security-guard": ">=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", - "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8", "symfony/serializer": ">=2,<2.0.11", - "symfony/symfony": ">=2,<2.7.49|>=2.8,<2.8.44|>=3,<3.3.18|>=3.4,<3.4.14|>=4,<4.0.14|>=4.1,<4.1.3", + "symfony/symfony": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8", "symfony/translation": ">=2,<2.0.17", "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3", + "symfony/var-exporter": ">=4.2,<4.2.12|>=4.3,<4.3.8", "symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4", "symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7", + "tecnickcom/tcpdf": "<6.2.22", "thelia/backoffice-default-template": ">=2.1,<2.1.2", - "thelia/thelia": ">=2.1.0-beta1,<2.1.3|>=2.1,<2.1.2", + "thelia/thelia": ">=2.1-beta.1,<2.1.3", "theonedemon/phpwhois": "<=4.2.5", "titon/framework": ">=0,<9.9.99", "truckersmp/phpwhois": "<=4.3.1", - "twig/twig": "<1.20", - "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.30|>=8,<8.7.17|>=9,<9.3.2", - "typo3/cms-core": ">=8,<8.7.17|>=9,<9.3.2", + "twig/twig": "<1.38|>=2,<2.7", + "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.30|>=9,<9.5.12|>=10,<10.2.1", + "typo3/cms-core": ">=8,<8.7.30|>=9,<9.5.12|>=10,<10.2.1", "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.10|>=3.1,<3.1.7|>=3.2,<3.2.7|>=3.3,<3.3.5", "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4", + "typo3/phar-stream-wrapper": ">=1,<2.1.1|>=3,<3.1.1", + "ua-parser/uap-php": "<3.8", + "wallabag/tcpdf": "<6.2.22", "willdurand/js-translation-bundle": "<2.1.1", "yiisoft/yii": ">=1.1.14,<1.1.15", "yiisoft/yii2": "<2.0.15", @@ -1460,6 +1590,7 @@ "zendframework/zend-captcha": ">=2,<2.4.9|>=2.5,<2.5.2", "zendframework/zend-crypt": ">=2,<2.4.9|>=2.5,<2.5.2", "zendframework/zend-db": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.10|>=2.3,<2.3.5", + "zendframework/zend-developer-tools": ">=1.2.2,<1.2.3", "zendframework/zend-diactoros": ">=1,<1.8.4", "zendframework/zend-feed": ">=1,<2.10.3", "zendframework/zend-form": ">=2,<2.2.7|>=2.3,<2.3.1", @@ -1494,7 +1625,7 @@ } ], "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", - "time": "2018-09-17T20:20:31+00:00" + "time": "2019-12-26T14:16:40+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -1607,23 +1738,23 @@ }, { "name": "sebastian/diff", - "version": "3.0.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "366541b989927187c4ca70490a35615d3fef2dce" + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/366541b989927187c4ca70490a35615d3fef2dce", - "reference": "366541b989927187c4ca70490a35615d3fef2dce", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", "shasum": "" }, "require": { "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^7.0", + "phpunit/phpunit": "^7.5 || ^8.0", "symfony/process": "^2 || ^3.3 || ^4" }, "type": "library", @@ -1659,32 +1790,35 @@ "unidiff", "unified diff" ], - "time": "2018-06-10T07:54:39+00:00" + "time": "2019-02-04T06:01:07+00:00" }, { "name": "sebastian/environment", - "version": "3.1.0", + "version": "4.2.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" + "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/464c90d7bdf5ad4e8a6aea15c091fec0603d4368", + "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368", "shasum": "" }, "require": { - "php": "^7.0" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^6.1" + "phpunit/phpunit": "^7.5" + }, + "suggest": { + "ext-posix": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -1709,20 +1843,20 @@ "environment", "hhvm" ], - "time": "2017-07-01T08:51:00+00:00" + "time": "2019-11-20T08:46:58+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.0", + "version": "3.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" + "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", + "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", "shasum": "" }, "require": { @@ -1749,6 +1883,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -1757,17 +1895,13 @@ "name": "Volker Dusch", "email": "github@wallbash.com" }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, { "name": "Adam Harvey", "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], "description": "Provides the functionality to export PHP variables for visualization", @@ -1776,27 +1910,30 @@ "export", "exporter" ], - "time": "2017-04-03T13:19:02+00:00" + "time": "2019-09-14T09:02:43+00:00" }, { "name": "sebastian/global-state", - "version": "2.0.0", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" + "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", + "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", "shasum": "" }, "require": { - "php": "^7.0" + "php": "^7.2", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "ext-dom": "*", + "phpunit/phpunit": "^8.0" }, "suggest": { "ext-uopz": "*" @@ -1804,7 +1941,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1827,7 +1964,7 @@ "keywords": [ "global state" ], - "time": "2017-04-27T15:39:26+00:00" + "time": "2019-02-01T05:30:01+00:00" }, { "name": "sebastian/object-enumerator", @@ -1976,25 +2113,25 @@ }, { "name": "sebastian/resource-operations", - "version": "1.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", "shasum": "" }, "require": { - "php": ">=5.6.0" + "php": "^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -2014,7 +2151,53 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2015-07-28T20:34:47+00:00" + "time": "2018-10-04T04:07:39+00:00" + }, + { + "name": "sebastian/type", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/3aaaa15fa71d27650d62a948be022fe3b48541a3", + "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3", + "shasum": "" + }, + "require": { + "php": "^7.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "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" }, { "name": "sebastian/version", @@ -2061,16 +2244,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "2.9.1", + "version": "2.9.2", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "dcbed1074f8244661eecddfc2a675430d8d33f62" + "reference": "2acf168de78487db620ab4bc524135a13cfe6745" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/dcbed1074f8244661eecddfc2a675430d8d33f62", - "reference": "dcbed1074f8244661eecddfc2a675430d8d33f62", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/2acf168de78487db620ab4bc524135a13cfe6745", + "reference": "2acf168de78487db620ab4bc524135a13cfe6745", "shasum": "" }, "require": { @@ -2135,20 +2318,78 @@ "phpcs", "standards" ], - "time": "2017-05-22T02:43:20+00:00" + "time": "2018-11-07T22:31:41+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.13.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", + "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.13-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": "2019-11-27T13:56:44+00:00" }, { "name": "theseer/tokenizer", - "version": "1.1.0", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", "shasum": "" }, "require": { @@ -2175,35 +2416,33 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2017-04-07T12:08:54+00:00" + "time": "2019-06-13T22:48:21+00:00" }, { "name": "webmozart/assert", - "version": "1.3.0", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a" + "reference": "573381c0a64f155a0d9a23f4b0c797194805b925" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a", + "url": "https://api.github.com/repos/webmozart/assert/zipball/573381c0a64f155a0d9a23f4b0c797194805b925", + "reference": "573381c0a64f155a0d9a23f4b0c797194805b925", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^5.3.3 || ^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "vimeo/psalm": "<3.6.0" }, "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" + "phpunit/phpunit": "^4.8.36 || ^7.5.13" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -2225,7 +2464,7 @@ "check", "validate" ], - "time": "2018-01-29T19:49:41+00:00" + "time": "2019-11-24T13:36:37+00:00" }, { "name": "zendframework/zend-coding-standard", @@ -2258,38 +2497,42 @@ }, { "name": "zendframework/zend-diactoros", - "version": "1.8.6", + "version": "2.2.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-diactoros.git", - "reference": "20da13beba0dde8fb648be3cc19765732790f46e" + "reference": "de5847b068362a88684a55b0dbb40d85986cfa52" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/20da13beba0dde8fb648be3cc19765732790f46e", - "reference": "20da13beba0dde8fb648be3cc19765732790f46e", + "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/de5847b068362a88684a55b0dbb40d85986cfa52", + "reference": "de5847b068362a88684a55b0dbb40d85986cfa52", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", + "php": "^7.1", + "psr/http-factory": "^1.0", "psr/http-message": "^1.0" }, "provide": { + "psr/http-factory-implementation": "1.0", "psr/http-message-implementation": "1.0" }, "require-dev": { + "ext-curl": "*", "ext-dom": "*", "ext-libxml": "*", + "http-interop/http-factory-tests": "^0.5.0", "php-http/psr7-integration-tests": "dev-master", - "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7", - "zendframework/zend-coding-standard": "~1.0" + "phpunit/phpunit": "^7.0.2", + "zendframework/zend-coding-standard": "~1.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8.x-dev", - "dev-develop": "1.9.x-dev", - "dev-release-2.0": "2.0.x-dev" + "dev-master": "2.1.x-dev", + "dev-develop": "2.2.x-dev", + "dev-release-1.8": "1.8.x-dev" } }, "autoload": { @@ -2309,16 +2552,129 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-2-Clause" + "BSD-3-Clause" ], "description": "PSR HTTP Message implementations", - "homepage": "https://github.com/zendframework/zend-diactoros", "keywords": [ "http", "psr", "psr-7" ], - "time": "2018-09-05T19:29:37+00:00" + "time": "2019-11-13T19:16:13+00:00" + }, + { + "name": "zendframework/zend-servicemanager", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-servicemanager.git", + "reference": "a1ed6140d0d3ee803fec96582593ed024950067b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/a1ed6140d0d3ee803fec96582593ed024950067b", + "reference": "a1ed6140d0d3ee803fec96582593ed024950067b", + "shasum": "" + }, + "require": { + "container-interop/container-interop": "^1.2", + "php": "^5.6 || ^7.0", + "psr/container": "^1.0", + "zendframework/zend-stdlib": "^3.2.1" + }, + "provide": { + "container-interop/container-interop-implementation": "^1.2", + "psr/container-implementation": "^1.0" + }, + "require-dev": { + "mikey179/vfsstream": "^1.6.5", + "ocramius/proxy-manager": "^1.0 || ^2.0", + "phpbench/phpbench": "^0.13.0", + "phpunit/phpunit": "^5.7.25 || ^6.4.4", + "zendframework/zend-coding-standard": "~1.0.0" + }, + "suggest": { + "ocramius/proxy-manager": "ProxyManager 1.* to handle lazy initialization of services", + "zendframework/zend-stdlib": "zend-stdlib ^2.5 if you wish to use the MergeReplaceKey or MergeRemoveKey features in Config instances" + }, + "bin": [ + "bin/generate-deps-for-config-factory", + "bin/generate-factory-for-class" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev", + "dev-develop": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\ServiceManager\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Factory-Driven Dependency Injection Container", + "keywords": [ + "PSR-11", + "ZendFramework", + "dependency-injection", + "di", + "dic", + "service-manager", + "servicemanager", + "zf" + ], + "time": "2018-12-22T06:05:09+00:00" + }, + { + "name": "zendframework/zend-stdlib", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-stdlib.git", + "reference": "66536006722aff9e62d1b331025089b7ec71c065" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/66536006722aff9e62d1b331025089b7ec71c065", + "reference": "66536006722aff9e62d1b331025089b7ec71c065", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpbench/phpbench": "^0.13", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", + "zendframework/zend-coding-standard": "~1.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2.x-dev", + "dev-develop": "3.3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Stdlib\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "SPL extensions, array utilities, error handlers, and more", + "keywords": [ + "ZendFramework", + "stdlib", + "zf" + ], + "time": "2018-08-28T21:34:05+00:00" } ], "aliases": [], diff --git a/data/oauth2.sql b/data/oauth2.sql index 508e667..f971783 100644 --- a/data/oauth2.sql +++ b/data/oauth2.sql @@ -1,66 +1,90 @@ -CREATE TABLE oauth_auth_codes ( - id VARCHAR(100), - user_id INTEGER, - client_id INTEGER, - scopes TEXT NULL, - revoked BOOLEAN, - expires_at TIMESTAMP NULL, - PRIMARY KEY(id) -); +-- +-- Table structure for table `oauth_access_tokens` +-- -CREATE TABLE oauth_access_tokens ( - id VARCHAR(100), - user_id VARCHAR(40) NULL, - client_id VARCHAR(40), - name VARCHAR(255) NULL, - scopes TEXT NULL, - revoked BOOLEAN, - created_at TIMESTAMP NULL, - updated_at TIMESTAMP NULL, - expires_at TIMESTAMP NULL, - PRIMARY KEY(id) +CREATE TABLE `oauth_access_tokens` ( + `id` varchar(100) PRIMARY KEY NOT NULL, + `user_id` int(10) DEFAULT NULL, + `client_id` int(10) NOT NULL, + `name` varchar(255) DEFAULT NULL, + `scopes` text, + `revoked` tinyint(1) NOT NULL DEFAULT '0', + `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` datetime DEFAULT NULL, + `expires_at` datetime NOT NULL ); -CREATE INDEX idx1_oauth_access_tokens ON oauth_access_tokens(user_id); - -CREATE TABLE oauth_refresh_tokens ( - id VARCHAR(100), - access_token_id VARCHAR(100), - revoked BOOLEAN, - expires_at TIMESTAMP NULL, - PRIMARY KEY(id) + +CREATE INDEX `IDX_CA42527CA76ED39519EB6921BDA26CCD` ON oauth_access_tokens (`user_id`,`client_id`); +CREATE INDEX `IDX_CA42527CA76ED395` ON oauth_access_tokens (`user_id`); +CREATE INDEX `IDX_CA42527C19EB6921` ON oauth_access_tokens (`client_id`); + +-- +-- Table structure for table `oauth_auth_codes` +-- + +CREATE TABLE `oauth_auth_codes` ( + `id` varchar(100) PRIMARY KEY NOT NULL, + `user_id` int(10) DEFAULT NULL, + `client_id` int(10) NOT NULL, + `scopes` text, + `revoked` tinyint(1) NOT NULL DEFAULT '0', + `expires_at` datetime DEFAULT NULL ); -CREATE INDEX idx1_oauth_refresh_tokens ON oauth_refresh_tokens(access_token_id); - -CREATE TABLE oauth_clients ( - name VARCHAR(40) NOT NULL, - user_id INTEGER NULL, - secret VARCHAR(100) NULL, - redirect VARCHAR(255), - personal_access_client BOOLEAN, - password_client BOOLEAN, - revoked BOOLEAN, - created_at TIMESTAMP NULL, - updated_at TIMESTAMP NULL, - PRIMARY KEY (name) + +CREATE INDEX `IDX_BB493F83A76ED395` ON oauth_auth_codes (`user_id`); +CREATE INDEX `IDX_BB493F8319EB6921` ON oauth_auth_codes (`client_id`); + +-- +-- Table structure for table `oauth_clients` +-- + +CREATE TABLE `oauth_clients` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + `user_id` int(10) DEFAULT NULL, + `name` varchar(100) NOT NULL, + `secret` varchar(100) DEFAULT NULL, + `redirect` varchar(255) DEFAULT NULL, + `personal_access_client` tinyint(1) DEFAULT NULL, + `password_client` tinyint(1) DEFAULT NULL, + `revoked` tinyint(1) DEFAULT NULL, + `created_at` datetime DEFAULT CURRENT_TIMESTAMP, + `updated_at` datetime DEFAULT NULL ); -CREATE INDEX idx1_oauth_clients ON oauth_clients(user_id); -CREATE TABLE oauth_personal_access_clients ( - client_id INTEGER, - created_at TIMESTAMP NULL, - updated_at TIMESTAMP NULL +CREATE INDEX `IDX_13CE81015E237E06A76ED395BDA26CCD` ON oauth_clients (`name`,`user_id`); +CREATE INDEX `IDX_13CE8101A76ED395` ON oauth_clients (`user_id`); + +-- +-- Table structure for table `oauth_refresh_tokens` +-- + +CREATE TABLE `oauth_refresh_tokens` ( + `id` varchar(100) PRIMARY KEY NOT NULL, + `access_token_id` varchar(100) NOT NULL, + `revoked` tinyint(1) NOT NULL DEFAULT '0', + `expires_at` datetime NOT NULL ); -CREATE INDEX idx1_oauth_personal_access_clients ON oauth_personal_access_clients(client_id); - -CREATE TABLE oauth_users ( - username VARCHAR(40) NOT NULL, - password VARCHAR(100) NOT NULL, - first_name VARCHAR(80), - last_name VARCHAR(80), - PRIMARY KEY (username) + +CREATE INDEX `IDX_5AB6872CCB2688BDA26CCD` ON oauth_refresh_tokens (`access_token_id`); + +-- +-- Table structure for table `oauth_scopes` +-- + +CREATE TABLE `oauth_scopes` ( + `id` varchar(100) PRIMARY KEY NOT NULL ); -CREATE TABLE oauth_scopes ( - id VARCHAR(30) NOT NULL, - PRIMARY KEY (id) +-- +-- Table structure for table `oauth_users` +-- + +CREATE TABLE `oauth_users` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + `username` varchar(320) UNIQUE NOT NULL, + `password` varchar(100) NOT NULL, + `first_name` varchar(80) DEFAULT NULL, + `last_name` varchar(80) DEFAULT NULL ); + +CREATE INDEX `UNIQ_93804FF8F85E0677` ON oauth_users (`username`); diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index ec1829f..36c9f2a 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -7,7 +7,7 @@ read/subscribe to the following resources: - [Coding Standards](https://github.com/zendframework/zend-coding-standard) - [Forums](https://discourse.zendframework.com/c/contributors) - - [Slack](https://zendframework-slack.herokuapp.com) + - [Chat](https://zendframework-slack.herokuapp.com) - [Code of Conduct](CODE_OF_CONDUCT.md) If you are working on new features or refactoring diff --git a/docs/ISSUE_TEMPLATE.md b/docs/ISSUE_TEMPLATE.md index eaaad0f..1826652 100644 --- a/docs/ISSUE_TEMPLATE.md +++ b/docs/ISSUE_TEMPLATE.md @@ -1,5 +1,5 @@ - [ ] I was not able to find an [open](https://github.com/zendframework/zend-expressive-authentication-oauth2/issues?q=is%3Aopen) or [closed](https://github.com/zendframework/zend-expressive-authentication-oauth2/issues?q=is%3Aclosed) issue matching what I'm seeing. - - [ ] This is not a question. (Questions should be asked on [slack](https://zendframework.slack.com/) ([Signup for Slack here](https://zendframework-slack.herokuapp.com/)) or our [forums](https://discourse.zendframework.com/).) + - [ ] This is not a question. (Questions should be asked on [chat](https://zendframework.slack.com/) ([Signup here](https://zendframework-slack.herokuapp.com/)) or our [forums](https://discourse.zendframework.com/).) Provide a narrative description of what you are trying to accomplish. diff --git a/docs/SUPPORT.md b/docs/SUPPORT.md index daa20e7..ae71cc0 100644 --- a/docs/SUPPORT.md +++ b/docs/SUPPORT.md @@ -3,13 +3,13 @@ Zend Framework offers three support channels: - For real-time questions, use our - [Slack](https://zendframework-slack.herokuapp.com) + [chat](https://zendframework-slack.herokuapp.com) - For detailed questions (e.g., those requiring examples) use our [forums](https://discourse.zendframework.com/c/questions/expressive) - To report issues, use this repository's [issue tracker](https://github.com/zendframework/zend-expressive-authentication-oauth2/issues/new) -**DO NOT** use the issue tracker to ask questions; use Slack or the forums for +**DO NOT** use the issue tracker to ask questions; use chat or the forums for that. Questions posed to the issue tracker will be closed. When reporting an issue, please include the following details: diff --git a/docs/book/index.html b/docs/book/index.html deleted file mode 100644 index 6ec4e7c..0000000 --- a/docs/book/index.html +++ /dev/null @@ -1,9 +0,0 @@ -
-
-

zendframework/zend-expressive-authentication-oauth2

- -

OAuth2 (server) for Expressive and PSR-7 applications.

- -
$ composer require zendframework/zend-expressive-authentication-oauth2
-
-
diff --git a/docs/book/v1/authorization-server.md b/docs/book/v1/authorization-server.md index ab998fc..1981f87 100644 --- a/docs/book/v1/authorization-server.md +++ b/docs/book/v1/authorization-server.md @@ -29,7 +29,7 @@ to obtain an access token or authorization code. This endpoint **MUST** accept `GET` requests and should: - Validate the request (especially for a valid client id and redirect url). - + - Make sure the user is authenticated (for example, by showing a login prompt if needed). diff --git a/docs/book/v1/intro.md b/docs/book/v1/intro.md index 8df0477..16dbf3b 100644 --- a/docs/book/v1/intro.md +++ b/docs/book/v1/intro.md @@ -81,6 +81,20 @@ The `private_key` and `public_key` values contains the paths to the previous generated pair of keys. The `encryption_key` contains the encryption key value as a string, as stored in the `data/oauth/encryption.key` file. +By default both key files are checked for correct permissions (chmod 400, 440, +600, 640 or 660 is expected, and 600 or 660 is recommended). In case the +environment/operating system (e.g. Windows) does not support such a permissions, +the check can be disabled: + +```php + // ... + 'private_key' => [ + 'key_or_path' => __DIR__ . '/../data/oauth/private.key', + 'key_permissions_check' => false, + ], + // ... +``` + The `access_token_expire` value is the time-to-live (TTL) value of the access token. The time period is represented using the [DateInterval](http://php.net/manual/en/class.dateinterval.php) format in PHP. The default value is `P1D` (1 day). @@ -112,6 +126,50 @@ grants are configured to be available. If you would like to disable any of the supplied grants, change the value for the grant to `null`. Additionally, you can extend this array to add your own custom grants. +### Configure Event Listeners + +- **Since 1.3.0** + +_Optional_ The `event_listeners` and `event_listener_providers` arrays may be used to enable event listeners for events published by `league\oauth2-server`. See the [Authorization Server Domain Events documentation](https://oauth2.thephpleague.com/authorization-server/events/). The possible event names can be found [in `League\OAuth2\Server\RequestEvent`](https://github.com/thephpleague/oauth2-server/blob/0b0b43d43342c0909b3b32fb7a09d502c368d2ec/src/RequestEvent.php#L17-L22). + +#### Event Listeners + +The `event_listeners` key must contain an array of arrays. Each array element must contain at least 2 elements and may include a 3rd element. These roughly correspond to the arguments passed to [`League\Event\ListenerAcceptorInterface::addListener()`](https://github.com/thephpleague/event/blob/d2cc124cf9a3fab2bb4ff963307f60361ce4d119/src/ListenerAcceptorInterface.php#L43). The first element must be a string -- either the [wildcard (`*`)](https://event.thephpleague.com/2.0/listeners/wildcard/) or a [single event name](https://event.thephpleague.com/2.0/events/named/). The second element must be either a callable, a concrete instance of `League\Event\ListenerInterface`, or a string pointing to your listener service instance in the container. The third element is optional, and must be an integer if provided. + +See the [documentation for callable listeners](https://event.thephpleague.com/2.0/listeners/callables/). + +#### Event Listener Providers + +The `event_listener_providers` key must contain an array. Each array element must contain either a concrete instance of `League\Event\ListenerProviderInterface` or a string pointing to your container service instance of a listener provider. + +See the [documentation for listener providers](https://event.thephpleague.com/2.0/listeners/providers/). + +Example config: + +```php +return [ + 'event_listeners' => [ + // using a container service + [ + \League\OAuth2\Server\RequestEvent::CLIENT_AUTHENTICATION_FAILED, + \My\Event\Listener\Service::class, + ], + // using a callable + [ + \League\OAuth2\Server\RequestEvent::ACCESS_TOKEN_ISSUED, + function (\League\OAuth2\Server\RequestEvent $event) { + // do something + }, + ], + ], + 'event_listener_providers' => [ + \My\Event\ListenerProvider\Service::class, + ], +]; +``` + +## OAuth2 Database + You need to provide an OAuth2 database yourself, or generate a [SQLite](https://www.sqlite.org) database with the following command (using `sqlite3` for GNU/Linux): @@ -138,7 +196,7 @@ For security reason, the client `secret` and the user `password` are stored using the `bcrypt` algorithm as used by the [password_hash](http://php.net/manual/en/function.password-hash.php) function. -## Configure OAuth2 routes +## Configure OAuth2 Routes As the final step, in order to use the OAuth2 server you need to configure the routes for the **token endpoint** and **authorization**. diff --git a/mkdocs.yml b/mkdocs.yml index bc2d4e0..4b5b479 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,7 +1,7 @@ docs_dir: docs/book site_dir: docs/html pages: - - index.md + - Home: index.md - Introduction: v1/intro.md - Usage: v1/usage.md - "Authorization server": v1/authorization-server.md @@ -21,7 +21,6 @@ pages: - "_grant_authcode": grant/auth_code.md - "_grant_implicit": grant/implicit.md - "_grant_refreshtoken": grant/refresh_token.md -site_name: OAuth2 middleware +site_name: zend-expressive-authentication-oauth2 site_description: 'OAuth2 (server) for Expressive and PSR-7 applications.' repo_url: 'https://github.com/zendframework/zend-expressive-authentication-oauth2' -copyright: 'Copyright (c) 2015-2018 Zend Technologies USA Inc.' diff --git a/src/AuthorizationServerFactory.php b/src/AuthorizationServerFactory.php index 1ecbc76..b4ad06f 100644 --- a/src/AuthorizationServerFactory.php +++ b/src/AuthorizationServerFactory.php @@ -11,20 +11,30 @@ namespace Zend\Expressive\Authentication\OAuth2; use DateInterval; +use League\Event\ListenerProviderInterface; + use League\OAuth2\Server\AuthorizationServer; -use League\OAuth2\Server\Grant; -use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface; -use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface; -use League\OAuth2\Server\Repositories\UserRepositoryInterface; use Psr\Container\ContainerInterface; -use Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException; +/** + * Factory for OAuth AuthorizationServer + * + * Initializes a new AuthorizationServer with required params from config. + * Then configured grant types are enabled with configured access token + * expiry. Then any optionally configured event listeners are attached to the + * AuthorizationServer. + */ class AuthorizationServerFactory { use ConfigTrait; use CryptKeyTrait; use RepositoryTrait; + /** + * @param ContainerInterface $container + * + * @return AuthorizationServer + */ public function __invoke(ContainerInterface $container) : AuthorizationServer { $clientRepository = $this->getClientRepository($container); @@ -46,7 +56,7 @@ public function __invoke(ContainerInterface $container) : AuthorizationServer $accessTokenInterval = new DateInterval($this->getAccessTokenExpire($container)); foreach ($grants as $grant) { - // Config may set this grant to null. Continue on if grant has been disabled + // Config may set this grant to null. Continue on if grant has been disabled if (empty($grant)) { continue; } @@ -57,6 +67,76 @@ public function __invoke(ContainerInterface $container) : AuthorizationServer ); } + // add listeners if configured + $this->addListeners($authServer, $container); + + // add listener providers if configured + $this->addListenerProviders($authServer, $container); + return $authServer; } + + /** + * Optionally add event listeners + * + * @param AuthorizationServer $authServer + * @param ContainerInterface $container + */ + private function addListeners( + AuthorizationServer $authServer, + ContainerInterface $container + ): void { + $listeners = $this->getListenersConfig($container); + + foreach ($listeners as $idx => $listenerConfig) { + $event = $listenerConfig[0]; + $listener = $listenerConfig[1]; + $priority = $listenerConfig[2] ?? null; + if (is_string($listener)) { + if (! $container->has($listener)) { + throw new Exception\InvalidConfigException(sprintf( + 'The second element of event_listeners config at ' . + 'index "%s" is a string and therefore expected to ' . + 'be available as a service key in the container. ' . + 'A service named "%s" was not found.', + $idx, + $listener + )); + } + $listener = $container->get($listener); + } + $authServer->getEmitter() + ->addListener($event, $listener, $priority); + } + } + + /** + * Optionally add event listener providers + * + * @param AuthorizationServer $authServer + * @param ContainerInterface $container + */ + private function addListenerProviders( + AuthorizationServer $authServer, + ContainerInterface $container + ): void { + $providers = $this->getListenerProvidersConfig($container); + + foreach ($providers as $idx => $provider) { + if (is_string($provider)) { + if (! $container->has($provider)) { + throw new Exception\InvalidConfigException(sprintf( + 'The event_listener_providers config at ' . + 'index "%s" is a string and therefore expected to ' . + 'be available as a service key in the container. ' . + 'A service named "%s" was not found.', + $idx, + $provider + )); + } + $provider = $container->get($provider); + } + $authServer->getEmitter()->useListenerProvider($provider); + } + } } diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index d8950e3..80b0410 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -66,7 +66,7 @@ public function getDependencies() : array ], 'factories' => [ AuthorizationMiddleware::class => AuthorizationMiddlewareFactory::class, - AuthorizationHandler::class => AuthorizationServerFactory::class, + AuthorizationHandler::class => AuthorizationHandlerFactory::class, TokenEndpointHandler::class => TokenEndpointHandlerFactory::class, OAuth2Adapter::class => OAuth2AdapterFactory::class, AuthorizationServer::class => AuthorizationServerFactory::class, diff --git a/src/ConfigTrait.php b/src/ConfigTrait.php index 163e9bf..1b75670 100644 --- a/src/ConfigTrait.php +++ b/src/ConfigTrait.php @@ -103,4 +103,46 @@ protected function getGrantsConfig(ContainerInterface $container) : array return $config['grants']; } + + /** + * @param ContainerInterface $container + * + * @return array + */ + protected function getListenersConfig(ContainerInterface $container) : array + { + $config = $container->get('config')['authentication'] ?? []; + + if (empty($config['event_listeners'])) { + return []; + } + if (! is_array($config['event_listeners'])) { + throw new InvalidConfigException( + 'The event_listeners config must be an array value' + ); + } + + return $config['event_listeners']; + } + + /** + * @param ContainerInterface $container + * + * @return array + */ + protected function getListenerProvidersConfig(ContainerInterface $container) : array + { + $config = $container->get('config')['authentication'] ?? []; + + if (empty($config['event_listener_providers'])) { + return []; + } + if (! is_array($config['event_listener_providers'])) { + throw new InvalidConfigException( + 'The event_listener_providers config must be an array value' + ); + } + + return $config['event_listener_providers']; + } } diff --git a/src/OAuth2Adapter.php b/src/OAuth2Adapter.php index 455fcc2..c24659c 100644 --- a/src/OAuth2Adapter.php +++ b/src/OAuth2Adapter.php @@ -29,6 +29,11 @@ class OAuth2Adapter implements AuthenticationInterface */ protected $responseFactory; + /** + * @var callable + */ + protected $userFactory; + public function __construct( ResourceServer $resourceServer, callable $responseFactory, @@ -56,9 +61,9 @@ public function authenticate(ServerRequestInterface $request) : ?UserInterface $result = $this->resourceServer->validateAuthenticatedRequest($request); $userId = $result->getAttribute('oauth_user_id', null); $clientId = $result->getAttribute('oauth_client_id', null); - if (isset($userId) || isset($clientId)) { + if (isset($userId)) { return ($this->userFactory)( - $userId ?? $clientId, + $userId, [], [ 'oauth_user_id' => $userId, @@ -82,7 +87,7 @@ public function unauthorizedResponse(ServerRequestInterface $request) : Response return ($this->responseFactory)() ->withHeader( 'WWW-Authenticate', - 'Bearer token-example' + 'Bearer realm="OAuth2 token"' ) ->withStatus(401); } diff --git a/src/Repository/Pdo/AccessTokenRepository.php b/src/Repository/Pdo/AccessTokenRepository.php index cac3249..03aa739 100644 --- a/src/Repository/Pdo/AccessTokenRepository.php +++ b/src/Repository/Pdo/AccessTokenRepository.php @@ -12,6 +12,7 @@ use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\ClientEntityInterface; +use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; use Zend\Expressive\Authentication\OAuth2\Entity\AccessTokenEntity; @@ -114,6 +115,9 @@ public function isAccessTokenRevoked($tokenId) return false; } $row = $sth->fetch(); + if (! is_array($row)) { + throw OAuthServerException::invalidRefreshToken(); + } return array_key_exists('revoked', $row) ? (bool) $row['revoked'] : false; } diff --git a/src/Repository/Pdo/ClientRepository.php b/src/Repository/Pdo/ClientRepository.php index f6613af..83597a0 100644 --- a/src/Repository/Pdo/ClientRepository.php +++ b/src/Repository/Pdo/ClientRepository.php @@ -1,7 +1,7 @@ pdo->prepare( - 'SELECT * FROM oauth_clients WHERE name = :clientIdentifier' + $clientData = $this->getClientData($clientIdentifier); + + if (empty($clientData)) { + return null; + } + + return new ClientEntity( + $clientIdentifier, + $clientData['name'] ?? '', + $clientData['redirect'] ?? '' ); - $sth->bindParam(':clientIdentifier', $clientIdentifier); + } + + /** + * {@inheritDoc} + */ + public function validateClient($clientIdentifier, $clientSecret, $grantType) : bool + { + $clientData = $this->getClientData($clientIdentifier); - if (false === $sth->execute()) { - return; + if (empty($clientData)) { + return false; } - $row = $sth->fetch(); - if (empty($row) || ! $this->isGranted($row, $grantType)) { - return; + + if (! $this->isGranted($clientData, $grantType)) { + return false; } - if ($mustValidateSecret - && (empty($row['secret']) || ! password_verify((string) $clientSecret, $row['secret'])) - ) { - return; + if (empty($clientData['secret']) || ! password_verify((string) $clientSecret, $clientData['secret'])) { + return false; } - return new ClientEntity($clientIdentifier, $row['name'], $row['redirect']); + return true; } /** * Check the grantType for the client value, stored in $row * - * @param array $row + * @param array $row * @param string $grantType + * * @return bool */ - protected function isGranted(array $row, string $grantType) : bool + protected function isGranted(array $row, string $grantType = null) : bool { switch ($grantType) { case 'authorization_code': @@ -64,4 +79,24 @@ protected function isGranted(array $row, string $grantType) : bool return true; } } + + private function getClientData(string $clientIdentifier) : ?array + { + $statement = $this->pdo->prepare( + 'SELECT * FROM oauth_clients WHERE name = :clientIdentifier' + ); + $statement->bindParam(':clientIdentifier', $clientIdentifier); + + if ($statement->execute() === false) { + return null; + } + + $row = $statement->fetch(); + + if (empty($row)) { + return null; + } + + return $row; + } } diff --git a/test/AuthorizationHandlerFactoryTest.php b/test/AuthorizationHandlerFactoryTest.php index 47c6ecb..50c5581 100644 --- a/test/AuthorizationHandlerFactoryTest.php +++ b/test/AuthorizationHandlerFactoryTest.php @@ -17,8 +17,10 @@ use Psr\Http\Message\ResponseInterface; use stdClass; use TypeError; +use Zend\Expressive\Authentication\OAuth2\ConfigProvider; use Zend\Expressive\Authentication\OAuth2\AuthorizationHandler; use Zend\Expressive\Authentication\OAuth2\AuthorizationHandlerFactory; +use Zend\ServiceManager\ServiceManager; /** * @covers \Zend\Expressive\Authentication\OAuth2\AuthorizationHandlerFactory @@ -34,7 +36,7 @@ class AuthorizationHandlerFactoryTest extends TestCase /** @var ResponseInterface|ObjectProphecy */ private $response; - public function setUp() + protected function setUp() : void { $this->container = $this->prophesize(ContainerInterface::class); $this->authServer = $this->prophesize(AuthorizationServer::class); @@ -108,4 +110,19 @@ public function testFactoryReturnsInstanceWhenAppropriateDependenciesArePresentI $middleware = $factory($this->container->reveal()); $this->assertInstanceOf(AuthorizationHandler::class, $middleware); } + + public function testConfigProvider() + { + $authServer = $this->prophesize(AuthorizationServer::class)->reveal(); + $responseFactory = function () { + return $this->prophesize(ResponseInterface::class)->reveal(); + }; + + $container = new ServiceManager((new ConfigProvider())->getDependencies()); + $container->setService(AuthorizationServer::class, $authServer); + $container->setService(ResponseInterface::class, $responseFactory); + + $authHandler = $container->get(AuthorizationHandler::class); + $this->assertInstanceOf(AuthorizationHandler::class, $authHandler); + } } diff --git a/test/AuthorizationMiddlewareFactoryTest.php b/test/AuthorizationMiddlewareFactoryTest.php index 32c536e..cc7e92b 100644 --- a/test/AuthorizationMiddlewareFactoryTest.php +++ b/test/AuthorizationMiddlewareFactoryTest.php @@ -34,7 +34,7 @@ class AuthorizationMiddlewareFactoryTest extends TestCase /** @var ResponseInterface|ObjectProphecy */ private $response; - public function setUp() + protected function setUp() : void { $this->container = $this->prophesize(ContainerInterface::class); $this->authServer = $this->prophesize(AuthorizationServer::class); diff --git a/test/AuthorizationMiddlewareTest.php b/test/AuthorizationMiddlewareTest.php index d5d0eb7..4b292a4 100644 --- a/test/AuthorizationMiddlewareTest.php +++ b/test/AuthorizationMiddlewareTest.php @@ -44,7 +44,7 @@ class AuthorizationMiddlewareTest extends TestCase /** @var ServerRequestInterface|ObjectProphecy */ private $serverRequest; - public function setUp() + protected function setUp() : void { $this->authServer = $this->prophesize(AuthorizationServer::class); $this->response = $this->prophesize(ResponseInterface::class); diff --git a/test/AuthorizationServerFactoryTest.php b/test/AuthorizationServerFactoryTest.php index ef1df13..05d9898 100644 --- a/test/AuthorizationServerFactoryTest.php +++ b/test/AuthorizationServerFactoryTest.php @@ -10,22 +10,32 @@ namespace ZendTest\Expressive\Authentication\OAuth2; +use League\Event\ListenerProviderInterface; + use League\OAuth2\Server\AuthorizationServer; +use League\OAuth2\Server\Grant\PasswordGrant; use League\OAuth2\Server\Grant\ClientCredentialsGrant; use League\OAuth2\Server\Grant\GrantTypeInterface; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; use League\OAuth2\Server\Repositories\ClientRepositoryInterface; use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; use PHPUnit\Framework\TestCase; +use Prophecy\Prophecy\ObjectProphecy; + use Psr\Container\ContainerInterface; use Zend\Expressive\Authentication\OAuth2\AuthorizationServerFactory; +use League\OAuth2\Server\RequestEvent; +use League\Event\ListenerInterface; use function array_merge; use function array_slice; use function in_array; +use Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException; + class AuthorizationServerFactoryTest extends TestCase { + public function testInvoke() { $mockContainer = $this->prophesize(ContainerInterface::class); @@ -41,10 +51,8 @@ public function testInvoke() 'encryption_key' => 'iALlwJ1sH77dmFCJFo+pMdM6Af4bF/hCca1EDDx7MwE=', 'access_token_expire' => 'P1D', 'grants' => [ - ClientCredentialsGrant::class - => ClientCredentialsGrant::class, - PasswordGrant::class - => PasswordGrant::class, + ClientCredentialsGrant::class => ClientCredentialsGrant::class, + PasswordGrant::class => PasswordGrant::class, ], ] ]; @@ -66,4 +74,183 @@ public function testInvoke() $this->assertInstanceOf(AuthorizationServer::class, $result); } + + /** + * @return ObjectProphecy + */ + private function getContainerMock(): ObjectProphecy + { + $mockContainer = $this->prophesize(ContainerInterface::class); + $mockClientRepo = $this->prophesize(ClientRepositoryInterface::class); + $mockAccessTokenRepo = $this->prophesize(AccessTokenRepositoryInterface::class); + $mockScopeRepo = $this->prophesize(ScopeRepositoryInterface::class); + $mockClientGrant = $this->prophesize(GrantTypeInterface::class); + $mockPasswordGrant = $this->prophesize(GrantTypeInterface::class); + + $mockContainer->has(ClientRepositoryInterface::class)->willReturn(true); + $mockContainer->has(AccessTokenRepositoryInterface::class)->willReturn(true); + $mockContainer->has(ScopeRepositoryInterface::class)->willReturn(true); + + $mockContainer->get(ClientRepositoryInterface::class)->willReturn($mockClientRepo->reveal()); + $mockContainer->get(AccessTokenRepositoryInterface::class)->willReturn($mockAccessTokenRepo->reveal()); + $mockContainer->get(ScopeRepositoryInterface::class)->willReturn($mockScopeRepo->reveal()); + $mockContainer->get(ClientCredentialsGrant::class)->willReturn($mockClientGrant->reveal()); + $mockContainer->get(PasswordGrant::class)->willReturn($mockPasswordGrant->reveal()); + + return $mockContainer; + } + + public function testInvokeWithNullGrant() + { + $mockContainer = $this->getContainerMock(); + + $config = [ + 'authentication' => [ + 'private_key' => __DIR__ . '/TestAsset/private.key', + 'encryption_key' => 'iALlwJ1sH77dmFCJFo+pMdM6Af4bF/hCca1EDDx7MwE=', + 'access_token_expire' => 'P1D', + 'grants' => [ + ClientCredentialsGrant::class => null, + PasswordGrant::class => PasswordGrant::class, + ], + ], + ]; + + $mockContainer->get('config')->willReturn($config); + + $factory = new AuthorizationServerFactory(); + + $result = $factory($mockContainer->reveal()); + + $this->assertInstanceOf(AuthorizationServer::class, $result); + } + + public function testInvokeWithListenerConfig() + { + $mockContainer = $this->getContainerMock(); + $mockListener = $this->prophesize(ListenerInterface::class); + $mockContainer->has(ListenerInterface::class)->willReturn(true); + $mockContainer->get(ListenerInterface::class)->willReturn($mockListener->reveal()); + + $config = [ + 'authentication' => [ + 'private_key' => __DIR__ . '/TestAsset/private.key', + 'encryption_key' => 'iALlwJ1sH77dmFCJFo+pMdM6Af4bF/hCca1EDDx7MwE=', + 'access_token_expire' => 'P1D', + 'grants' => [ + ClientCredentialsGrant::class => ClientCredentialsGrant::class, + ], + 'event_listeners' => [ + [ + RequestEvent::CLIENT_AUTHENTICATION_FAILED, + function (RequestEvent $event) { + // do something + }, + ], + [ + RequestEvent::CLIENT_AUTHENTICATION_FAILED, + ListenerInterface::class, + ], + ], + ], + ]; + + $mockContainer->get('config')->willReturn($config); + + $factory = new AuthorizationServerFactory(); + + $result = $factory($mockContainer->reveal()); + + $this->assertInstanceOf(AuthorizationServer::class, $result); + } + + public function testInvokeWithListenerConfigMissingServiceThrowsException() + { + $mockContainer = $this->getContainerMock(); + $mockListener = $this->prophesize(ListenerInterface::class); + $mockContainer->has(ListenerInterface::class)->willReturn(false); + + $config = [ + 'authentication' => [ + 'private_key' => __DIR__ . '/TestAsset/private.key', + 'encryption_key' => 'iALlwJ1sH77dmFCJFo+pMdM6Af4bF/hCca1EDDx7MwE=', + 'access_token_expire' => 'P1D', + 'grants' => [ + ClientCredentialsGrant::class => ClientCredentialsGrant::class, + ], + 'event_listeners' => [ + [ + RequestEvent::CLIENT_AUTHENTICATION_FAILED, + ListenerInterface::class, + ], + ], + ], + ]; + + $mockContainer->get('config')->willReturn($config); + + $factory = new AuthorizationServerFactory(); + + $this->expectException(InvalidConfigException::class); + + $result = $factory($mockContainer->reveal()); + } + + public function testInvokeWithListenerProviderConfig() + { + $mockContainer = $this->getContainerMock(); + $mockProvider = $this->prophesize(ListenerProviderInterface::class); + $mockContainer->has(ListenerProviderInterface::class)->willReturn(true); + $mockContainer->get(ListenerProviderInterface::class)->willReturn($mockProvider->reveal()); + + $config = [ + 'authentication' => [ + 'private_key' => __DIR__ . '/TestAsset/private.key', + 'encryption_key' => 'iALlwJ1sH77dmFCJFo+pMdM6Af4bF/hCca1EDDx7MwE=', + 'access_token_expire' => 'P1D', + 'grants' => [ + ClientCredentialsGrant::class => ClientCredentialsGrant::class, + ], + 'event_listener_providers' => [ + ListenerProviderInterface::class + ], + ], + ]; + + $mockContainer->get('config')->willReturn($config); + + $factory = new AuthorizationServerFactory(); + + $result = $factory($mockContainer->reveal()); + + $this->assertInstanceOf(AuthorizationServer::class, $result); + } + + public function testInvokeWithListenerProviderConfigMissingServiceThrowsException() + { + $mockContainer = $this->getContainerMock(); + $mockProvider = $this->prophesize(ListenerProviderInterface::class); + $mockContainer->has(ListenerProviderInterface::class)->willReturn(false); + + $config = [ + 'authentication' => [ + 'private_key' => __DIR__ . '/TestAsset/private.key', + 'encryption_key' => 'iALlwJ1sH77dmFCJFo+pMdM6Af4bF/hCca1EDDx7MwE=', + 'access_token_expire' => 'P1D', + 'grants' => [ + ClientCredentialsGrant::class => ClientCredentialsGrant::class, + ], + 'event_listener_providers' => [ + ListenerProviderInterface::class, + ], + ], + ]; + + $mockContainer->get('config')->willReturn($config); + + $factory = new AuthorizationServerFactory(); + + $this->expectException(InvalidConfigException::class); + $factory($mockContainer->reveal()); + } } diff --git a/test/ConfigTraitTest.php b/test/ConfigTraitTest.php index f1e9659..2808660 100644 --- a/test/ConfigTraitTest.php +++ b/test/ConfigTraitTest.php @@ -13,10 +13,11 @@ use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use Zend\Expressive\Authentication\OAuth2\ConfigTrait; +use Zend\Expressive\Authentication\OAuth2\Exception; class ConfigTraitTest extends TestCase { - public function setUp() + protected function setUp() : void { $this->trait = $trait = new class { use ConfigTrait; @@ -42,14 +43,13 @@ public function proxy(string $name, ContainerInterface $container) ->willReturn($this->config); } - /** - * @expectedException Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException - */ public function testGetPrivateKeyWhenNoConfigPresentWillResultInAnException() { $this->container ->get('config') ->willReturn([]); + + $this->expectException(Exception\InvalidConfigException::class); $this->trait->proxy('getPrivateKey', $this->container->reveal()); } @@ -59,14 +59,13 @@ public function testGetPrivateKey() $this->assertEquals($this->config['authentication']['private_key'], $result); } - /** - * @expectedException Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException - */ public function testGetEncryptionKeyNoConfig() { $this->container ->get('config') ->willReturn([]); + + $this->expectException(Exception\InvalidConfigException::class); $this->trait->proxy('getEncryptionKey', $this->container->reveal()); } @@ -76,14 +75,13 @@ public function testGetEncryptionKey() $this->assertEquals($this->config['authentication']['encryption_key'], $result); } - /** - * @expectedException Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException - */ public function testGetAccessTokenExpireNoConfig() { $this->container ->get('config') ->willReturn([]); + + $this->expectException(Exception\InvalidConfigException::class); $this->trait->proxy('getAccessTokenExpire', $this->container->reveal()); } @@ -93,14 +91,13 @@ public function testGetAccessTokenExpire() $this->assertEquals($this->config['authentication']['access_token_expire'], $result); } - /** - * @expectedException Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException - */ public function testGetRefreshTokenExpireNoConfig() { $this->container ->get('config') ->willReturn([]); + + $this->expectException(Exception\InvalidConfigException::class); $this->trait->proxy('getRefreshTokenExpire', $this->container->reveal()); } @@ -110,14 +107,13 @@ public function testGetRefreshTokenExpire() $this->assertEquals($this->config['authentication']['refresh_token_expire'], $result); } - /** - * @expectedException Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException - */ public function testGetAuthCodeExpireNoConfig() { $this->container ->get('config') ->willReturn([]); + + $this->expectException(Exception\InvalidConfigException::class); $this->trait->proxy('getAuthCodeExpire', $this->container->reveal()); } @@ -127,20 +123,16 @@ public function testGetAuthCodeExpire() $this->assertEquals($this->config['authentication']['auth_code_expire'], $result); } - /** - * @expectedException Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException - */ public function testGetGrantsConfigNoConfig() { $this->container ->get('config') ->willReturn([]); + + $this->expectException(Exception\InvalidConfigException::class); $this->trait->proxy('getGrantsConfig', $this->container->reveal()); } - /** - * @expectedException Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException - */ public function testGetGrantsConfigNoArrayValue() { $this->container @@ -151,6 +143,7 @@ public function testGetGrantsConfigNoArrayValue() ], ]); + $this->expectException(Exception\InvalidConfigException::class); $this->trait->proxy('getGrantsConfig', $this->container->reveal()); } @@ -159,4 +152,82 @@ public function testGetGrantsConfig() $result = $this->trait->proxy('getGrantsConfig', $this->container->reveal()); $this->assertEquals($this->config['authentication']['grants'], $result); } + + public function testGetListenersConfigNoConfig() + { + $this->container + ->get('config') + ->willReturn([]); + $result = $this->trait + ->proxy('getListenersConfig', $this->container->reveal()); + $this->assertInternalType('array', $result); + } + + /** + * @expectedException Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException + */ + public function testGetListenersConfigNoArrayValue() + { + $this->container + ->get('config') + ->willReturn([ + 'authentication' => [ + 'event_listeners' => 'xxx', + ], + ]); + + $this->trait->proxy('getListenersConfig', $this->container->reveal()); + } + + public function testGetListenersConfig() + { + $this->container->get('config') + ->willReturn([ + 'authentication' => [ + 'event_listeners' => $expected = [['xxx']], + ], + ]); + $result = $this->trait + ->proxy('getListenersConfig', $this->container->reveal()); + $this->assertEquals($expected, $result); + } + + public function testGetListenerProvidersConfigNoConfig() + { + $this->container + ->get('config') + ->willReturn([]); + $result = $this->trait + ->proxy('getListenerProvidersConfig', $this->container->reveal()); + $this->assertInternalType('array', $result); + } + + /** + * @expectedException Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException + */ + public function testGetListenerProvidersConfigNoArrayValue() + { + $this->container + ->get('config') + ->willReturn([ + 'authentication' => [ + 'event_listener_providers' => 'xxx', + ], + ]); + + $this->trait->proxy('getListenerProvidersConfig', $this->container->reveal()); + } + + public function testGetListenerProvidersConfig() + { + $this->container->get('config') + ->willReturn([ + 'authentication' => [ + 'event_listener_providers' => $expected = ['xxx'], + ], + ]); + $result = $this->trait + ->proxy('getListenerProvidersConfig', $this->container->reveal()); + $this->assertEquals($expected, $result); + } } diff --git a/test/Entity/ClientEntityTest.php b/test/Entity/ClientEntityTest.php index 7283c30..210707c 100644 --- a/test/Entity/ClientEntityTest.php +++ b/test/Entity/ClientEntityTest.php @@ -16,7 +16,7 @@ class ClientEntityTest extends TestCase { - public function setUp() + protected function setUp() : void { $this->entity = new ClientEntity('foo', 'bar', 'http://localhost'); } diff --git a/test/Entity/RevokableTraitTest.php b/test/Entity/RevokableTraitTest.php index bc043f2..b2964b1 100644 --- a/test/Entity/RevokableTraitTest.php +++ b/test/Entity/RevokableTraitTest.php @@ -15,7 +15,7 @@ class RevokableTraitTest extends TestCase { - public function setUp() + protected function setUp() : void { $this->trait = $this->getMockForTrait(RevokableTrait::class); } diff --git a/test/Entity/ScopeEntityTest.php b/test/Entity/ScopeEntityTest.php index 6088db8..2a70dfb 100644 --- a/test/Entity/ScopeEntityTest.php +++ b/test/Entity/ScopeEntityTest.php @@ -16,7 +16,7 @@ class ScopeEntityTest extends TestCase { - public function setUp() + protected function setUp() : void { $this->entity = new ScopeEntity(); } diff --git a/test/Entity/TimestampableTraitTest.php b/test/Entity/TimestampableTraitTest.php index 56cd7fe..3c1c9f7 100644 --- a/test/Entity/TimestampableTraitTest.php +++ b/test/Entity/TimestampableTraitTest.php @@ -16,7 +16,7 @@ class TimestampableTraitTest extends TestCase { - public function setUp() + protected function setUp() : void { $this->trait = $this->getMockForTrait(TimestampableTrait::class); } diff --git a/test/Entity/UserEntityTest.php b/test/Entity/UserEntityTest.php index 3420496..c46186a 100644 --- a/test/Entity/UserEntityTest.php +++ b/test/Entity/UserEntityTest.php @@ -10,22 +10,24 @@ namespace ZendTest\Expressive\Authentication\OAuth2\Entity; +use ArgumentCountError; use League\OAuth2\Server\Entities\UserEntityInterface; use PHPUnit\Framework\TestCase; use Zend\Expressive\Authentication\OAuth2\Entity\UserEntity; class UserEntityTest extends TestCase { - public function setUp() + /** @var UserEntity */ + private $entity; + + protected function setUp() : void { $this->entity = new UserEntity('foo'); } - /** - * @expectedException ArgumentCountError - */ public function testConstructorWithoutParamWillResultInAnException() { + $this->expectException(ArgumentCountError::class); $entity = new UserEntity(); } diff --git a/test/OAuth2AdapterFactoryTest.php b/test/OAuth2AdapterFactoryTest.php index c9c6a7e..50acbd9 100644 --- a/test/OAuth2AdapterFactoryTest.php +++ b/test/OAuth2AdapterFactoryTest.php @@ -18,6 +18,7 @@ use stdClass; use TypeError; use Zend\Expressive\Authentication\AuthenticationInterface; +use Zend\Expressive\Authentication\OAuth2\Exception; use Zend\Expressive\Authentication\OAuth2\OAuth2Adapter; use Zend\Expressive\Authentication\OAuth2\OAuth2AdapterFactory; use Zend\Expressive\Authentication\UserInterface; @@ -36,7 +37,7 @@ class OAuth2AdapterFactoryTest extends TestCase /** @var callable */ private $responseFactory; - public function setUp() + protected function setUp() : void { $this->container = $this->prophesize(ContainerInterface::class); $this->resourceServer = $this->prophesize(ResourceServer::class); @@ -60,13 +61,12 @@ public function testConstructor() $this->assertInstanceOf(OAuth2AdapterFactory::class, $factory); } - /** - * @expectedException \Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException - */ public function testInvokeWithEmptyContainer() { $factory = new OAuth2AdapterFactory(); - $oauth2Adapter = $factory($this->container->reveal()); + + $this->expectException(Exception\InvalidConfigException::class); + $factory($this->container->reveal()); } public function testFactoryRaisesTypeErrorForNonCallableResponseFactory() diff --git a/test/OAuth2AdapterTest.php b/test/OAuth2AdapterTest.php index 51dd4a3..5bced61 100644 --- a/test/OAuth2AdapterTest.php +++ b/test/OAuth2AdapterTest.php @@ -14,6 +14,7 @@ use League\OAuth2\Server\ResourceServer; use PHPUnit\Framework\TestCase; use Prophecy\Argument; +use Prophecy\Prophecy\ObjectProphecy; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Zend\Expressive\Authentication\AuthenticationInterface; @@ -32,7 +33,10 @@ class OAuth2AdapterTest extends TestCase /** @var callable */ private $responseFactory; - public function setUp() + /** @var callable */ + private $userFactory; + + protected function setUp() : void { $this->resourceServer = $this->prophesize(ResourceServer::class); $this->response = $this->prophesize(ResponseInterface::class); @@ -122,7 +126,7 @@ public function testAuthenticateReturnsAUserIfTheResourceServerProducesAUserId() $this->assertSame([], $user->getRoles()); } - public function testAuthenticateReturnsAClientIfTheResourceServerProducesAClientId() + public function testAuthenticateReturnsNullIfTheResourceServerProducesAClientIdOnly() { $request = $this->prophesize(ServerRequestInterface::class); $request->getAttribute('oauth_user_id', null)->willReturn(null); @@ -141,10 +145,7 @@ public function testAuthenticateReturnsAClientIfTheResourceServerProducesAClient ); $user = $adapter->authenticate($request->reveal()); - - $this->assertInstanceOf(UserInterface::class, $user); - $this->assertSame('some-identifier', $user->getIdentity()); - $this->assertSame([], $user->getRoles()); + $this->assertNull($user); } public function testUnauthorizedResponseProducesAResponseWithAWwwAuthenticateHeader() @@ -152,7 +153,7 @@ public function testUnauthorizedResponseProducesAResponseWithAWwwAuthenticateHea $request = $this->prophesize(ServerRequestInterface::class)->reveal(); $this->response - ->withHeader('WWW-Authenticate', 'Bearer token-example') + ->withHeader('WWW-Authenticate', 'Bearer realm="OAuth2 token"') ->will([$this->response, 'reveal']); $this->response ->withStatus(401) diff --git a/test/Pdo/OAuth2PdoMiddlewareTest.php b/test/Pdo/OAuth2PdoMiddlewareTest.php index e1858f4..338766d 100644 --- a/test/Pdo/OAuth2PdoMiddlewareTest.php +++ b/test/Pdo/OAuth2PdoMiddlewareTest.php @@ -1,7 +1,7 @@ response = new Response(); $this->pdoService = new PdoService('sqlite:' . self::DB_FILE); @@ -188,7 +191,7 @@ public function testProcessClientCredentialGrant() $this->assertEquals(200, $response->getStatusCode()); $content = json_decode((string) $response->getBody()); $this->assertEquals('Bearer', $content->token_type); - $this->assertInternalType("int", $content->expires_in); + $this->assertIsInt($content->expires_in); $this->assertNotEmpty($content->access_token); } @@ -236,7 +239,7 @@ public function testProcessPasswordGrant() $this->assertEquals(200, $response->getStatusCode()); $content = json_decode((string) $response->getBody()); $this->assertEquals('Bearer', $content->token_type); - $this->assertInternalType("int", $content->expires_in); + $this->assertIsInt($content->expires_in); $this->assertNotEmpty($content->access_token); $this->assertNotEmpty($content->refresh_token); } @@ -269,6 +272,17 @@ public function testProcessGetAuthorizationCode() 'scope' => 'test', 'state' => $state ]; + + $codeVerifier = new S256Verifier(); + + $params['code_challenge_method'] = $codeVerifier->getMethod(); + $params['code_verifier'] = self::CODE_VERIFIER; + $params['code_challenge'] = strtr( + rtrim(base64_encode(hash('sha256', self::CODE_VERIFIER, true)), '='), + '+/', + '-_' + ); + $request = $this->buildServerRequest( 'GET', '/auth_code?' . http_build_query($params), @@ -324,8 +338,10 @@ public function testProcessFromAuthorizationCode(string $code) 'client_id' => 'client_test2', 'client_secret' => 'test', 'redirect_uri' => '/redirect', - 'code' => $code + 'code' => $code, + 'code_verifier' => self::CODE_VERIFIER, ]; + $request = $this->buildServerRequest( 'POST', '/access_token', @@ -344,7 +360,7 @@ public function testProcessFromAuthorizationCode(string $code) $this->assertEquals(200, $response->getStatusCode()); $content = json_decode((string) $response->getBody()); $this->assertEquals('Bearer', $content->token_type); - $this->assertInternalType("int", $content->expires_in); + $this->assertIsInt($content->expires_in); $this->assertNotEmpty($content->access_token); $this->assertNotEmpty($content->refresh_token); @@ -443,7 +459,7 @@ public function testProcessRefreshTokenGrant(string $refreshToken) $this->assertEquals(200, $response->getStatusCode()); $content = json_decode((string) $response->getBody()); $this->assertEquals('Bearer', $content->token_type); - $this->assertInternalType("int", $content->expires_in); + $this->assertIsInt($content->expires_in); $this->assertNotEmpty($content->access_token); $this->assertNotEmpty($content->refresh_token); } diff --git a/test/Repository/Pdo/AbstractRepositoryTest.php b/test/Repository/Pdo/AbstractRepositoryTest.php index 455be99..de275d1 100644 --- a/test/Repository/Pdo/AbstractRepositoryTest.php +++ b/test/Repository/Pdo/AbstractRepositoryTest.php @@ -16,7 +16,7 @@ class AbstractRepositoryTest extends TestCase { - public function setUp() + protected function setUp() : void { $this->pdo = $this->prophesize(PdoService::class); } diff --git a/test/Repository/Pdo/AccessTokenRepositoryFactoryTest.php b/test/Repository/Pdo/AccessTokenRepositoryFactoryTest.php index fad93cf..27807b0 100644 --- a/test/Repository/Pdo/AccessTokenRepositoryFactoryTest.php +++ b/test/Repository/Pdo/AccessTokenRepositoryFactoryTest.php @@ -23,7 +23,7 @@ class AccessTokenRepositoryFactoryTest extends TestCase */ private $container; - public function setUp() + protected function setUp() : void { $this->container = $this->prophesize(ContainerInterface::class); $this->pdo = $this->prophesize(PdoService::class); diff --git a/test/Repository/Pdo/AccessTokenRepositoryTest.php b/test/Repository/Pdo/AccessTokenRepositoryTest.php index b4fa4b9..85b61b9 100644 --- a/test/Repository/Pdo/AccessTokenRepositoryTest.php +++ b/test/Repository/Pdo/AccessTokenRepositoryTest.php @@ -15,6 +15,7 @@ use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\ScopeEntityInterface; use League\OAuth2\Server\Entities\Traits\AccessTokenTrait; +use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException; use PDOStatement; use PHPUnit\Framework\TestCase; @@ -32,7 +33,7 @@ class AccessTokenRepositoryTest extends TestCase */ private $repo; - public function setUp() + protected function setUp() : void { $this->pdo = $this->prophesize(PdoService::class); $this->repo = new AccessTokenRepository($this->pdo->reveal()); @@ -133,6 +134,21 @@ public function testIsAccessTokenRevokedReturnsTrueWhenRowRevokedFlagIsTrue() $this->assertTrue($this->repo->isAccessTokenRevoked('token_id')); } + public function testIsAcessTokenRevokedRaisesExceptionWhenTokenIdDontExists() + { + $statement = $this->prophesize(PDOStatement::class); + $statement->bindParam(':tokenId', 'token_id')->shouldBeCalled(); + $statement->execute()->willReturn(true)->shouldBeCalled(); + $statement->fetch()->willReturn(false)->shouldBeCalled(); + + $this->pdo + ->prepare(Argument::containingString('SELECT revoked FROM oauth_access_tokens')) + ->will([$statement, 'reveal']); + + $this->expectException(OAuthServerException::class); + $this->repo->isAccessTokenRevoked('token_id'); + } + public function testRevokeAccessToken() { $statement = $this->prophesize(PDOStatement::class); diff --git a/test/Repository/Pdo/AuthCodeRepositoryFactoryTest.php b/test/Repository/Pdo/AuthCodeRepositoryFactoryTest.php index 9dd51f9..2897242 100644 --- a/test/Repository/Pdo/AuthCodeRepositoryFactoryTest.php +++ b/test/Repository/Pdo/AuthCodeRepositoryFactoryTest.php @@ -23,7 +23,7 @@ class AuthCodeRepositoryFactoryTest extends TestCase */ private $container; - public function setUp() + protected function setUp() : void { $this->container = $this->prophesize(ContainerInterface::class); $this->pdo = $this->prophesize(PdoService::class); diff --git a/test/Repository/Pdo/AuthCodeRepositoryTest.php b/test/Repository/Pdo/AuthCodeRepositoryTest.php index 6a78144..64e297a 100644 --- a/test/Repository/Pdo/AuthCodeRepositoryTest.php +++ b/test/Repository/Pdo/AuthCodeRepositoryTest.php @@ -25,7 +25,7 @@ class AuthCodeRepositoryTest extends TestCase { - public function setUp() + protected function setUp() : void { $this->pdo = $this->prophesize(PdoService::class); $this->repo = new AuthCodeRepository($this->pdo->reveal()); diff --git a/test/Repository/Pdo/ClientRepositoryFactoryTest.php b/test/Repository/Pdo/ClientRepositoryFactoryTest.php index 60874c4..e1a3072 100644 --- a/test/Repository/Pdo/ClientRepositoryFactoryTest.php +++ b/test/Repository/Pdo/ClientRepositoryFactoryTest.php @@ -23,7 +23,7 @@ class ClientRepositoryFactoryTest extends TestCase */ private $container; - public function setUp() + protected function setUp() : void { $this->container = $this->prophesize(ContainerInterface::class); $this->pdo = $this->prophesize(PdoService::class); diff --git a/test/Repository/Pdo/ClientRepositoryTest.php b/test/Repository/Pdo/ClientRepositoryTest.php index 312601e..6ae6636 100644 --- a/test/Repository/Pdo/ClientRepositoryTest.php +++ b/test/Repository/Pdo/ClientRepositoryTest.php @@ -1,7 +1,7 @@ pdo = $this->prophesize(PdoService::class); $this->repo = new ClientRepository($this->pdo->reveal()); @@ -36,10 +36,7 @@ public function testGetClientEntityReturnsNullIfStatementExecutionReturnsFalse() ->will([$statement, 'reveal']); $this->assertNull( - $this->repo ->getClientEntity( - 'client_id', - 'grant_type' - ) + $this->repo ->getClientEntity('client_id') ); } @@ -59,10 +56,45 @@ public function testGetClientEntityReturnsNullIfNoRowReturned() $client = $this->prophesize(ClientEntityInterface::class); $this->assertNull( - $this->repo ->getClientEntity( - 'client_id', - 'grant_type' - ) + $this->repo ->getClientEntity('client_id') + ); + } + + public function testGetClientEntityReturnsCorrectEntity() + { + $name = 'foo'; + $redirect = 'bar'; + + $statement = $this->prophesize(PDOStatement::class); + $statement->bindParam(':clientIdentifier', 'client_id')->shouldBeCalled(); + $statement->execute()->will(function () use ($statement, $name, $redirect) { + $statement->fetch()->willReturn([ + 'name' => $name, + 'redirect' => $redirect, + ]); + return null; + }); + + $this->pdo + ->prepare(Argument::containingString('SELECT * FROM oauth_clients')) + ->will([$statement, 'reveal']); + + $this->prophesize(ClientEntityInterface::class); + + /** @var ClientEntityInterface $client */ + $client = $this->repo->getClientEntity('client_id'); + + $this->assertInstanceOf( + ClientEntityInterface::class, + $client + ); + $this->assertEquals( + $name, + $client->getName() + ); + $this->assertEquals( + [$redirect], + $client->getRedirectUri() ); } @@ -82,10 +114,34 @@ public function invalidGrants() ]; } + public function testValidateClientReturnsFalseIfNoRowReturned() + { + $statement = $this->prophesize(PDOStatement::class); + $statement->bindParam(':clientIdentifier', 'client_id')->shouldBeCalled(); + $statement->execute()->will(function () use ($statement) { + $statement->fetch()->willReturn([]); + return null; + }); + + $this->pdo + ->prepare(Argument::containingString('SELECT * FROM oauth_clients')) + ->will([$statement, 'reveal']); + + $client = $this->prophesize(ClientEntityInterface::class); + + $this->assertFalse( + $this->repo->validateClient( + 'client_id', + '', + 'password' + ) + ); + } + /** * @dataProvider invalidGrants */ - public function testGetClientEntityReturnsNullIfRowIndicatesNotGranted(string $grantType, array $rowReturned) + public function testValidateClientReturnsFalseIfRowIndicatesNotGranted(string $grantType, array $rowReturned) { $statement = $this->prophesize(PDOStatement::class); $statement->bindParam(':clientIdentifier', 'client_id')->shouldBeCalled(); @@ -100,22 +156,23 @@ public function testGetClientEntityReturnsNullIfRowIndicatesNotGranted(string $g $client = $this->prophesize(ClientEntityInterface::class); - $this->assertNull( - $this->repo ->getClientEntity( + $this->assertFalse( + $this->repo ->validateClient( 'client_id', + '', $grantType ) ); } - public function testGetClientReturnsNullForNonMatchingClientSecret() + public function testValidateClientReturnsFalseForNonMatchingClientSecret() { $statement = $this->prophesize(PDOStatement::class); $statement->bindParam(':clientIdentifier', 'client_id')->shouldBeCalled(); $statement->execute()->will(function () use ($statement) { $statement->fetch()->willReturn([ 'password_client' => true, - 'secret' => 'unknown password', + 'secret' => 'bar', ]); return null; }); @@ -126,17 +183,16 @@ public function testGetClientReturnsNullForNonMatchingClientSecret() $client = $this->prophesize(ClientEntityInterface::class); - $this->assertNull( - $this->repo ->getClientEntity( + $this->assertFalse( + $this->repo ->validateClient( 'client_id', - 'password_client', - 'password', - true + 'foo', + 'password' ) ); } - public function testGetClientReturnsNullForEmptyClientSecret() + public function testValidateClientReturnsFalseForEmptyClientSecret() { $statement = $this->prophesize(PDOStatement::class); $statement->bindParam(':clientIdentifier', 'client_id')->shouldBeCalled(); @@ -154,12 +210,11 @@ public function testGetClientReturnsNullForEmptyClientSecret() $client = $this->prophesize(ClientEntityInterface::class); - $this->assertNull( - $this->repo ->getClientEntity( + $this->assertFalse( + $this->repo ->validateClient( 'client_id', - 'password_client', - 'password', - true + 'foo', + 'password' ) ); } diff --git a/test/Repository/Pdo/PdoServiceFactoryTest.php b/test/Repository/Pdo/PdoServiceFactoryTest.php index 5e60c7c..cc7c9fe 100644 --- a/test/Repository/Pdo/PdoServiceFactoryTest.php +++ b/test/Repository/Pdo/PdoServiceFactoryTest.php @@ -18,7 +18,7 @@ class PdoServiceFactoryTest extends TestCase { - public function setUp() + protected function setUp() : void { $this->container = $this->prophesize(ContainerInterface::class); $this->factory = new PdoServiceFactory(); diff --git a/test/Repository/Pdo/RefreshTokenRepositoryFactoryTest.php b/test/Repository/Pdo/RefreshTokenRepositoryFactoryTest.php index 30ab113..7fd6a0b 100644 --- a/test/Repository/Pdo/RefreshTokenRepositoryFactoryTest.php +++ b/test/Repository/Pdo/RefreshTokenRepositoryFactoryTest.php @@ -23,7 +23,7 @@ class RefreshTokenRepositoryFactoryTest extends TestCase */ private $container; - public function setUp() + protected function setUp() : void { $this->container = $this->prophesize(ContainerInterface::class); $this->pdo = $this->prophesize(PdoService::class); diff --git a/test/Repository/Pdo/RefreshTokenRepositoryTest.php b/test/Repository/Pdo/RefreshTokenRepositoryTest.php index a92f179..c726839 100644 --- a/test/Repository/Pdo/RefreshTokenRepositoryTest.php +++ b/test/Repository/Pdo/RefreshTokenRepositoryTest.php @@ -25,7 +25,7 @@ class RefreshTokenRepositoryTest extends TestCase { - public function setUp() + protected function setUp() : void { $this->pdo = $this->prophesize(PdoService::class); $this->repo = new RefreshTokenRepository($this->pdo->reveal()); diff --git a/test/Repository/Pdo/ScopeRepositoryFactoryTest.php b/test/Repository/Pdo/ScopeRepositoryFactoryTest.php index 3fa5c2f..55c0da3 100644 --- a/test/Repository/Pdo/ScopeRepositoryFactoryTest.php +++ b/test/Repository/Pdo/ScopeRepositoryFactoryTest.php @@ -23,7 +23,7 @@ class ScopeRepositoryFactoryTest extends TestCase */ private $container; - public function setUp() + protected function setUp() : void { $this->container = $this->prophesize(ContainerInterface::class); $this->pdo = $this->prophesize(PdoService::class); diff --git a/test/Repository/Pdo/ScopeRepositoryTest.php b/test/Repository/Pdo/ScopeRepositoryTest.php index a218416..e30f88b 100644 --- a/test/Repository/Pdo/ScopeRepositoryTest.php +++ b/test/Repository/Pdo/ScopeRepositoryTest.php @@ -20,7 +20,7 @@ class ScopeRepositoryTest extends TestCase { - public function setUp() + protected function setUp() : void { $this->pdo = $this->prophesize(PdoService::class); $this->repo = new ScopeRepository($this->pdo->reveal()); diff --git a/test/Repository/Pdo/UserRepositoryTest.php b/test/Repository/Pdo/UserRepositoryTest.php index 4c25a47..7eac734 100644 --- a/test/Repository/Pdo/UserRepositoryTest.php +++ b/test/Repository/Pdo/UserRepositoryTest.php @@ -20,7 +20,7 @@ class UserRepositoryTest extends TestCase { - public function setUp() + protected function setUp() : void { $this->pdo = $this->prophesize(PdoService::class); $this->repo = new UserRepository($this->pdo->reveal()); diff --git a/test/RepositoryTraitTest.php b/test/RepositoryTraitTest.php index 5470cb8..5facc85 100644 --- a/test/RepositoryTraitTest.php +++ b/test/RepositoryTraitTest.php @@ -18,13 +18,14 @@ use League\OAuth2\Server\Repositories\UserRepositoryInterface; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; +use Zend\Expressive\Authentication\OAuth2\Exception; use Zend\Expressive\Authentication\OAuth2\RepositoryTrait; class RepositoryTraitTest extends TestCase { - public function setUp() + protected function setUp() : void { - $this->trait = $trait = new class { + $this->trait = new class { use RepositoryTrait; public function proxy(string $name, ContainerInterface $container) @@ -35,14 +36,13 @@ public function proxy(string $name, ContainerInterface $container) $this->container = $this->prophesize(ContainerInterface::class); } - /** - * @expectedException Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException - */ public function testGetUserRepositoryWithoutService() { $this->container ->has(UserRepositoryInterface::class) ->willReturn(false); + + $this->expectException(Exception\InvalidConfigException::class); $this->trait->proxy('getUserRepository', $this->container->reveal()); } @@ -59,14 +59,13 @@ public function testGetUserRepository() $this->assertInstanceOf(UserRepositoryInterface::class, $result); } - /** - * @expectedException Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException - */ public function testGetScopeRepositoryWithoutService() { $this->container ->has(ScopeRepositoryInterface::class) ->willReturn(false); + + $this->expectException(Exception\InvalidConfigException::class); $this->trait->proxy('getScopeRepository', $this->container->reveal()); } @@ -83,14 +82,13 @@ public function testGetScopeRepository() $this->assertInstanceOf(ScopeRepositoryInterface::class, $result); } - /** - * @expectedException Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException - */ public function testGetAccessTokenRepositoryWithoutService() { $this->container ->has(AccessTokenRepositoryInterface::class) ->willReturn(false); + + $this->expectException(Exception\InvalidConfigException::class); $this->trait->proxy('getAccessTokenRepository', $this->container->reveal()); } @@ -107,14 +105,13 @@ public function testGetAccessTokenRepository() $this->assertInstanceOf(AccessTokenRepositoryInterface::class, $result); } - /** - * @expectedException Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException - */ public function testGetClientRepositoryWithoutService() { $this->container ->has(ClientRepositoryInterface::class) ->willReturn(false); + + $this->expectException(Exception\InvalidConfigException::class); $this->trait->proxy('getClientRepository', $this->container->reveal()); } @@ -131,14 +128,13 @@ public function testGetClientRepository() $this->assertInstanceOf(ClientRepositoryInterface::class, $result); } - /** - * @expectedException Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException - */ public function testGetRefreshTokenRepositoryWithoutService() { $this->container ->has(RefreshTokenRepositoryInterface::class) ->willReturn(false); + + $this->expectException(Exception\InvalidConfigException::class); $this->trait->proxy('getRefreshTokenRepository', $this->container->reveal()); } @@ -155,14 +151,13 @@ public function testGetRefreshTokenRepository() $this->assertInstanceOf(RefreshTokenRepositoryInterface::class, $result); } - /** - * @expectedException Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException - */ public function testGetAuthCodeRepositoryWithoutService() { $this->container ->has(AuthCodeRepositoryInterface::class) ->willReturn(false); + + $this->expectException(Exception\InvalidConfigException::class); $this->trait->proxy('getAuthCodeRepository', $this->container->reveal()); } diff --git a/test/ResourceServerFactoryTest.php b/test/ResourceServerFactoryTest.php index ad3be93..085c4a0 100644 --- a/test/ResourceServerFactoryTest.php +++ b/test/ResourceServerFactoryTest.php @@ -27,7 +27,7 @@ class ResourceServerFactoryTest extends TestCase 'key_permissions_check' => false, ]; - public function setUp() + protected function setUp() : void { $this->container = $this->prophesize(ContainerInterface::class); } @@ -48,9 +48,6 @@ public function testInvokeWithEmptyConfig() $factory($this->container->reveal()); } - /** - * @expectedException Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException - */ public function testInvokeWithConfigWithoutRepository() { $this->container->has('config')->willReturn(true); @@ -64,6 +61,8 @@ public function testInvokeWithConfigWithoutRepository() ->willReturn(false); $factory = new ResourceServerFactory(); + + $this->expectException(Exception\InvalidConfigException::class); $factory($this->container->reveal()); } diff --git a/test/TokenEndpointHandlerFactoryTest.php b/test/TokenEndpointHandlerFactoryTest.php index f706b21..3f05a0a 100644 --- a/test/TokenEndpointHandlerFactoryTest.php +++ b/test/TokenEndpointHandlerFactoryTest.php @@ -30,7 +30,7 @@ class TokenEndpointHandlerFactoryTest extends TestCase */ private $subject; - protected function setUp()/* The :void return type declaration that should be here would cause a BC issue */ + protected function setUp() : void { $this->subject = new TokenEndpointHandlerFactory(); parent::setUp();