From 967e2450fae2d80e4557a8d075f67d3f1086e7e0 Mon Sep 17 00:00:00 2001 From: webimpress Date: Sun, 1 Sep 2019 21:25:23 +0100 Subject: [PATCH 01/35] Bumped to next dev version (1.2.1) --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 561b775..4dfef9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,28 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. +## 1.2.1 - TBD + +### Added + +- Nothing. + +### Changed + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + ## 1.2.0 - 2019-09-01 ### Added From ff44b98bfa2524f977628812e6b9b97f80a08353 Mon Sep 17 00:00:00 2001 From: webimpress Date: Sun, 1 Sep 2019 21:27:04 +0100 Subject: [PATCH 02/35] Bumped to next dev version (1.3.0) --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dfef9b..f86d47b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,28 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. +## 1.3.0 - TBD + +### Added + +- Nothing. + +### Changed + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + ## 1.2.1 - TBD ### Added From b10e7399eff57d8517838a34a1961e07ea4055fe Mon Sep 17 00:00:00 2001 From: Michael Deutscher Date: Sun, 8 Sep 2019 14:07:29 +0200 Subject: [PATCH 03/35] Update docs with option to disable permission check for keys --- docs/book/v1/intro.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/book/v1/intro.md b/docs/book/v1/intro.md index 8df0477..3f544e5 100644 --- a/docs/book/v1/intro.md +++ b/docs/book/v1/intro.md @@ -81,6 +81,19 @@ 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 600 or 660 +is expected). 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). From 483fb118c5168631c3bc5a589a8a98f783caae1d Mon Sep 17 00:00:00 2001 From: webimpress Date: Sun, 8 Sep 2019 22:01:34 +0200 Subject: [PATCH 04/35] Updated permission check - expected and recommended --- docs/book/v1/intro.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/book/v1/intro.md b/docs/book/v1/intro.md index 3f544e5..9759f54 100644 --- a/docs/book/v1/intro.md +++ b/docs/book/v1/intro.md @@ -81,9 +81,10 @@ 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 600 or 660 -is expected). In case the environment/operating system (e.g. Windows) does not -support such a permissions, the check can be disabled: +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 // ... From 64f220c47a3db6fff135f3439b1a1c879037705c Mon Sep 17 00:00:00 2001 From: Anderson Luciano Date: Tue, 26 Nov 2019 11:55:31 -0300 Subject: [PATCH 05/35] Implemented the check of fetched row, if isn't a array, throw exception --- src/Repository/Pdo/AccessTokenRepository.php | 4 ++++ .../Repository/Pdo/AccessTokenRepositoryTest.php | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/Repository/Pdo/AccessTokenRepository.php b/src/Repository/Pdo/AccessTokenRepository.php index cac3249..630839e 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/test/Repository/Pdo/AccessTokenRepositoryTest.php b/test/Repository/Pdo/AccessTokenRepositoryTest.php index 4bb48cc..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; @@ -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); From 9af0b0063f887f896bd61d2adc84ae649ee7a03a Mon Sep 17 00:00:00 2001 From: Anderson Luciano Date: Tue, 26 Nov 2019 12:17:01 -0300 Subject: [PATCH 06/35] Fix cs-check issue --- src/Repository/Pdo/AccessTokenRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Repository/Pdo/AccessTokenRepository.php b/src/Repository/Pdo/AccessTokenRepository.php index 630839e..03aa739 100644 --- a/src/Repository/Pdo/AccessTokenRepository.php +++ b/src/Repository/Pdo/AccessTokenRepository.php @@ -115,7 +115,7 @@ public function isAccessTokenRevoked($tokenId) return false; } $row = $sth->fetch(); - if (!is_array($row)) { + if (! is_array($row)) { throw OAuthServerException::invalidRefreshToken(); } From 425e5e80d30f25a8e46ebc7da68d61d907ca1e68 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sat, 28 Dec 2019 13:28:24 -0600 Subject: [PATCH 07/35] docs: adds CHANGELOG entry for #71 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dfef9b..30c4a31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ All notable changes to this project will be documented in this file, in reverse ### Fixed -- Nothing. +- [#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. ## 1.2.0 - 2019-09-01 From 73da89822c082409d1c761798ff4465172a7eeb8 Mon Sep 17 00:00:00 2001 From: Marc Bennewitz Date: Fri, 2 Nov 2018 10:04:17 +0100 Subject: [PATCH 08/35] An OAuth2 client is not a user --- src/OAuth2Adapter.php | 9 +++++++-- test/OAuth2AdapterTest.php | 11 ++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/OAuth2Adapter.php b/src/OAuth2Adapter.php index 999bcd6..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, diff --git a/test/OAuth2AdapterTest.php b/test/OAuth2AdapterTest.php index 611e92d..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,6 +33,9 @@ class OAuth2AdapterTest extends TestCase /** @var callable */ private $responseFactory; + /** @var callable */ + private $userFactory; + protected function setUp() : void { $this->resourceServer = $this->prophesize(ResourceServer::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() From d6806e33dbc328fefdfd057487839b93a4e60a65 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sat, 28 Dec 2019 13:45:06 -0600 Subject: [PATCH 09/35] docs: adds CHANGELOG for #55 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30c4a31..60c6c81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ All notable changes to this project will be documented in this file, in reverse ### Changed -- Nothing. +- [#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 From f18115596239514d9fa04c1da1dfb9b4d39539ac Mon Sep 17 00:00:00 2001 From: Sam Sheridan Date: Tue, 10 Dec 2019 10:39:23 +0000 Subject: [PATCH 10/35] Fixed issues with incorrect mysql schema in provided example The example mysql example has missing columns and incorrect column types. --- data/oauth2.sql | 145 ++++++++++++++++++++++++++++-------------------- 1 file changed, 85 insertions(+), 60 deletions(-) diff --git a/data/oauth2.sql b/data/oauth2.sql index 508e667..984c600 100644 --- a/data/oauth2.sql +++ b/data/oauth2.sql @@ -1,66 +1,91 @@ -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 INDEX idx1_oauth_access_tokens ON oauth_access_tokens(user_id); +CREATE TABLE `oauth_access_tokens` ( + `id` varchar(100) NOT NULL, + `user_id` int(10) unsigned DEFAULT NULL, + `client_id` int(10) unsigned 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, + PRIMARY KEY (`id`), + KEY `IDX_CA42527CA76ED39519EB6921BDA26CCD` (`user_id`,`client_id`), + KEY `IDX_CA42527CA76ED395` (`user_id`), + KEY `IDX_CA42527C19EB6921` (`client_id`) +) ENGINE=InnoDB; -CREATE TABLE oauth_refresh_tokens ( - id VARCHAR(100), - access_token_id VARCHAR(100), - revoked BOOLEAN, - expires_at TIMESTAMP NULL, - PRIMARY KEY(id) -); -CREATE INDEX idx1_oauth_refresh_tokens ON oauth_refresh_tokens(access_token_id); +-- +-- Table structure for table `oauth_auth_codes` +-- -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 idx1_oauth_clients ON oauth_clients(user_id); +CREATE TABLE `oauth_auth_codes` ( + `id` varchar(100) NOT NULL, + `user_id` int(10) unsigned DEFAULT NULL, + `client_id` int(10) unsigned NOT NULL, + `scopes` text, + `revoked` tinyint(1) NOT NULL DEFAULT '0', + `expires_at` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_BB493F83A76ED395` (`user_id`), + KEY `IDX_BB493F8319EB6921` (`client_id`) +) ENGINE=InnoDB; -CREATE TABLE oauth_personal_access_clients ( - client_id INTEGER, - created_at TIMESTAMP NULL, - updated_at TIMESTAMP NULL -); -CREATE INDEX idx1_oauth_personal_access_clients ON oauth_personal_access_clients(client_id); +-- +-- Table structure for table `oauth_clients` +-- -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 TABLE `oauth_clients` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `user_id` int(10) unsigned 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, + PRIMARY KEY (`id`), + KEY `IDX_13CE81015E237E06A76ED395BDA26CCD` (`name`,`user_id`), + KEY `IDX_13CE8101A76ED395` (`user_id`) +) ENGINE=InnoDB; -CREATE TABLE oauth_scopes ( - id VARCHAR(30) NOT NULL, - PRIMARY KEY (id) -); +-- +-- Table structure for table `oauth_refresh_tokens` +-- + +CREATE TABLE `oauth_refresh_tokens` ( + `id` varchar(100) NOT NULL, + `access_token_id` varchar(100) NOT NULL, + `revoked` tinyint(1) NOT NULL DEFAULT '0', + `expires_at` datetime NOT NULL, + PRIMARY KEY (`id`), + KEY `IDX_5AB6872CCB2688BDA26CCD` (`access_token_id`) +) ENGINE=InnoDB; + +-- +-- Table structure for table `oauth_scopes` +-- + +CREATE TABLE `oauth_scopes` ( + `id` varchar(100) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB; + +-- +-- Table structure for table `oauth_users` +-- + +CREATE TABLE `oauth_users` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `username` varchar(320) NOT NULL, + `password` varchar(100) NOT NULL, + `first_name` varchar(80) DEFAULT NULL, + `last_name` varchar(80) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UNIQ_93804FF8F85E0677` (`username`) +) ENGINE=InnoDB; From 64f9d4cb903041ed2505829b6d79f9ac52c95c89 Mon Sep 17 00:00:00 2001 From: Sam Sheridan Date: Tue, 10 Dec 2019 10:39:45 +0000 Subject: [PATCH 11/35] docs: adds CHANGELOG entry for #72 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60c6c81..639cb7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,8 @@ All notable changes to this project will be documented in this file, in reverse - [#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 From e53f2bc38e29cd7c1f5570ecb468c5806cc7dd29 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sat, 28 Dec 2019 13:56:45 -0600 Subject: [PATCH 12/35] 1.2.1 readiness --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 639cb7b..8d3f141 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. -## 1.2.1 - TBD +## 1.2.1 - 2019-12-28 ### Added From 242406ed07038222504d54e94842df43921e6c77 Mon Sep 17 00:00:00 2001 From: Marc Guyer Date: Tue, 30 Jul 2019 16:54:57 -0400 Subject: [PATCH 13/35] Enable event listeners via config. Tests. Docs. --- config/oauth2.php | 19 +++++++++ docs/book/v1/intro.md | 16 +++++++ src/AuthorizationServerFactory.php | 49 +++++++++++++++++++--- src/ConfigTrait.php | 21 ++++++++++ test/AuthorizationServerFactoryTest.php | 55 +++++++++++++++++++++++++ test/ConfigTraitTest.php | 39 ++++++++++++++++++ 6 files changed, 193 insertions(+), 6 deletions(-) diff --git a/config/oauth2.php b/config/oauth2.php index 9abb090..70ef994 100644 --- a/config/oauth2.php +++ b/config/oauth2.php @@ -35,6 +35,25 @@ \League\OAuth2\Server\Grant\RefreshTokenGrant::class => \League\OAuth2\Server\Grant\RefreshTokenGrant::class ], + + // optionally add listener config + // 'listeners' => [ + // [ + // // event name + // \League\OAuth2\Server\RequestEvent::CLIENT_AUTHENTICATION_FAILED, + // // listener defined as a service key + // \My\League\Event\Listener\For\Auth\Failure::class, + // ], [ + // \League\OAuth2\Server\RequestEvent::CLIENT_AUTHENTICATION_FAILED, + // // listener defined as an anonymous function + // function (\League\OAuth2\Server\RequestEvent $event) { + // // do something + // }, + // // priority NORMAL (0) is the default but you may set an + // // int val of your choosing + // // League\Event\ListenerAcceptorInterface::P_HIGH, + // ], + // ], ]; // Conditionally include the encryption_key config setting, based on presence of file. diff --git a/docs/book/v1/intro.md b/docs/book/v1/intro.md index 9759f54..27e23d2 100644 --- a/docs/book/v1/intro.md +++ b/docs/book/v1/intro.md @@ -74,6 +74,20 @@ return [ Grant\ImplicitGrant::class => Grant\ImplicitGrant::class, Grant\RefreshTokenGrant::class => Grant\RefreshTokenGrant::class ], + + // optionally configure event listeners + // 'listeners' => [ + // [ + // \League\OAuth2\Server\RequestEvent::CLIENT_AUTHENTICATION_FAILED, + // \My\Event\Listener\Service::class, + // ], + // [ + // \League\OAuth2\Server\RequestEvent::ACCESS_TOKEN_ISSUED, + // function (\League\OAuth2\Server\RequestEvent $event) { + // // do something + // }, + // ], + // ], ]; ``` @@ -126,6 +140,8 @@ 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. +The `listeners` array is for enabling event listeners. Listeners are not required. This is an array of arrays. Each array in the list must contain at least 2 elements. The first element must be a string that corresponds to the name of the event to listen for. The second element must be either a string or an anonymous function. If it's a string it's assumed to be a container service key that points to your listener. There may be a third element of the array -- it must be an integer. The third element is the `$priority` argument when registering the listener. See the [Authorization Server Domain Events documentation](https://oauth2.thephpleague.com/authorization-server/events/). + 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): diff --git a/src/AuthorizationServerFactory.php b/src/AuthorizationServerFactory.php index 1ecbc76..9d4ded8 100644 --- a/src/AuthorizationServerFactory.php +++ b/src/AuthorizationServerFactory.php @@ -12,19 +12,27 @@ use DateInterval; 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 +54,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 +65,35 @@ public function __invoke(ContainerInterface $container) : AuthorizationServer ); } + // add listeners if configured + $this->addListeners($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); + if (null === $listeners) { + return; + } + foreach ($listeners as $listenerConfig) { + $event = $listenerConfig[0]; + $listener = $listenerConfig[1]; + $priority = $listenerConfig[2] ?? null; + if (is_string($listener)) { + $listener = $container->get($listener); + } + $authServer->getEmitter() + ->addListener($event, $listener, $priority); + } + } } diff --git a/src/ConfigTrait.php b/src/ConfigTrait.php index 163e9bf..7822406 100644 --- a/src/ConfigTrait.php +++ b/src/ConfigTrait.php @@ -103,4 +103,25 @@ protected function getGrantsConfig(ContainerInterface $container) : array return $config['grants']; } + + /** + * @param ContainerInterface $container + * + * @return array|null + */ + protected function getListenersConfig(ContainerInterface $container) : ?array + { + $config = $container->get('config')['authentication'] ?? []; + + if (empty($config['listeners'])) { + return null; + } + if (! is_array($config['listeners'])) { + throw new InvalidConfigException( + 'The listeners must be an array value' + ); + } + + return $config['listeners']; + } } diff --git a/test/AuthorizationServerFactoryTest.php b/test/AuthorizationServerFactoryTest.php index ef1df13..ae3a9bd 100644 --- a/test/AuthorizationServerFactoryTest.php +++ b/test/AuthorizationServerFactoryTest.php @@ -19,6 +19,8 @@ use PHPUnit\Framework\TestCase; 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; @@ -66,4 +68,57 @@ public function testInvoke() $this->assertInstanceOf(AuthorizationServer::class, $result); } + + public function testInvokeWithListenerConfig() + { + $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); + + $mockListener = $this->prophesize(ListenerInterface::class); + $mockContainer->get(ListenerInterface::class) + ->will([$mockListener, 'reveal']); + + $config = [ + 'authentication' => [ + 'private_key' => __DIR__ . '/TestAsset/private.key', + 'encryption_key' => 'iALlwJ1sH77dmFCJFo+pMdM6Af4bF/hCca1EDDx7MwE=', + 'access_token_expire' => 'P1D', + 'grants' => [ + ClientCredentialsGrant::class + => ClientCredentialsGrant::class, + ], + 'listeners' => [ + [ + RequestEvent::CLIENT_AUTHENTICATION_FAILED, + function (RequestEvent $event) { + // do something + } + ], [ + RequestEvent::CLIENT_AUTHENTICATION_FAILED, + ListenerInterface::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('config')->willReturn($config); + + $factory = new AuthorizationServerFactory(); + + $result = $factory($mockContainer->reveal()); + + $this->assertInstanceOf(AuthorizationServer::class, $result); + } } diff --git a/test/ConfigTraitTest.php b/test/ConfigTraitTest.php index 52c71aa..4460349 100644 --- a/test/ConfigTraitTest.php +++ b/test/ConfigTraitTest.php @@ -152,4 +152,43 @@ 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->assertNull($result); + } + + /** + * @expectedException Zend\Expressive\Authentication\OAuth2\Exception\InvalidConfigException + */ + public function testGetListenersConfigNoArrayValue() + { + $this->container + ->get('config') + ->willReturn([ + 'authentication' => [ + 'listeners' => 'xxx', + ], + ]); + + $this->trait->proxy('getListenersConfig', $this->container->reveal()); + } + + public function testGetListenersConfig() + { + $this->container->get('config') + ->willReturn([ + 'authentication' => [ + 'listeners' => $expected = [['xxx']], + ], + ]); + $result = $this->trait + ->proxy('getListenersConfig', $this->container->reveal()); + $this->assertEquals($expected, $result); + } } From 49a0cf739bca89ad355f64cf2a0960b64f1afac6 Mon Sep 17 00:00:00 2001 From: Marc Guyer Date: Sun, 11 Aug 2019 22:09:47 -0400 Subject: [PATCH 14/35] Add support for listener providers config * Remove commented out example listeners config * Rewrite docs with subheadings and better example configs * Empty config returns empty array instead of null * Add some basic error handling for missing container services * Tests --- docs/book/v1/intro.md | 58 ++++++--- src/AuthorizationServerFactory.php | 51 +++++++- src/ConfigTrait.php | 35 ++++- test/AuthorizationServerFactoryTest.php | 164 ++++++++++++++++++++++-- test/ConfigTraitTest.php | 45 ++++++- 5 files changed, 311 insertions(+), 42 deletions(-) diff --git a/docs/book/v1/intro.md b/docs/book/v1/intro.md index 27e23d2..eda3fb1 100644 --- a/docs/book/v1/intro.md +++ b/docs/book/v1/intro.md @@ -74,20 +74,6 @@ return [ Grant\ImplicitGrant::class => Grant\ImplicitGrant::class, Grant\RefreshTokenGrant::class => Grant\RefreshTokenGrant::class ], - - // optionally configure event listeners - // 'listeners' => [ - // [ - // \League\OAuth2\Server\RequestEvent::CLIENT_AUTHENTICATION_FAILED, - // \My\Event\Listener\Service::class, - // ], - // [ - // \League\OAuth2\Server\RequestEvent::ACCESS_TOKEN_ISSUED, - // function (\League\OAuth2\Server\RequestEvent $event) { - // // do something - // }, - // ], - // ], ]; ``` @@ -140,7 +126,47 @@ 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. -The `listeners` array is for enabling event listeners. Listeners are not required. This is an array of arrays. Each array in the list must contain at least 2 elements. The first element must be a string that corresponds to the name of the event to listen for. The second element must be either a string or an anonymous function. If it's a string it's assumed to be a container service key that points to your listener. There may be a third element of the array -- it must be an integer. The third element is the `$priority` argument when registering the listener. See the [Authorization Server Domain Events documentation](https://oauth2.thephpleague.com/authorization-server/events/). +### Configure Event Listeners + +_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): @@ -168,7 +194,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/src/AuthorizationServerFactory.php b/src/AuthorizationServerFactory.php index 9d4ded8..6eb1106 100644 --- a/src/AuthorizationServerFactory.php +++ b/src/AuthorizationServerFactory.php @@ -11,6 +11,8 @@ namespace Zend\Expressive\Authentication\OAuth2; use DateInterval; +use League\Event\ListenerProviderInterface; + use League\OAuth2\Server\AuthorizationServer; use Psr\Container\ContainerInterface; @@ -68,6 +70,9 @@ 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; } @@ -82,18 +87,56 @@ private function addListeners( ContainerInterface $container ): void { $listeners = $this->getListenersConfig($container); - if (null === $listeners) { - return; - } - foreach ($listeners as $listenerConfig) { + + 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/ConfigTrait.php b/src/ConfigTrait.php index 7822406..b424412 100644 --- a/src/ConfigTrait.php +++ b/src/ConfigTrait.php @@ -107,21 +107,42 @@ protected function getGrantsConfig(ContainerInterface $container) : array /** * @param ContainerInterface $container * - * @return array|null + * @return array */ - protected function getListenersConfig(ContainerInterface $container) : ?array + protected function getListenersConfig(ContainerInterface $container) : array { $config = $container->get('config')['authentication'] ?? []; - if (empty($config['listeners'])) { - return null; + if (empty($config['event-listeners'])) { + return []; } - if (! is_array($config['listeners'])) { + if (! is_array($config['event-listeners'])) { throw new InvalidConfigException( - 'The listeners must be an array value' + 'The event-listeners config must be an array value' ); } - return $config['listeners']; + 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/test/AuthorizationServerFactoryTest.php b/test/AuthorizationServerFactoryTest.php index ae3a9bd..94c5990 100644 --- a/test/AuthorizationServerFactoryTest.php +++ b/test/AuthorizationServerFactoryTest.php @@ -10,13 +10,18 @@ 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; @@ -26,8 +31,11 @@ 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); @@ -69,8 +77,10 @@ public function testInvoke() $this->assertInstanceOf(AuthorizationServer::class, $result); } - public function testInvokeWithListenerConfig() - { + /** + * @return ObjectProphecy + */ + private function getContainerMock(): ObjectProphecy { $mockContainer = $this->prophesize(ContainerInterface::class); $mockClientRepo = $this->prophesize(ClientRepositoryInterface::class); $mockAccessTokenRepo = $this->prophesize(AccessTokenRepositoryInterface::class); @@ -78,9 +88,52 @@ public function testInvokeWithListenerConfig() $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->get(ListenerInterface::class) - ->will([$mockListener, 'reveal']); + $mockContainer->has(ListenerInterface::class)->willReturn(true); + $mockContainer->get(ListenerInterface::class)->willReturn($mockListener->reveal()); $config = [ 'authentication' => [ @@ -91,7 +144,7 @@ public function testInvokeWithListenerConfig() ClientCredentialsGrant::class => ClientCredentialsGrant::class, ], - 'listeners' => [ + 'event-listeners' => [ [ RequestEvent::CLIENT_AUTHENTICATION_FAILED, function (RequestEvent $event) { @@ -105,14 +158,70 @@ function (RequestEvent $event) { ] ]; - $mockContainer->has(ClientRepositoryInterface::class)->willReturn(true); - $mockContainer->has(AccessTokenRepositoryInterface::class)->willReturn(true); - $mockContainer->has(ScopeRepositoryInterface::class)->willReturn(true); + $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(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('config')->willReturn($config); $factory = new AuthorizationServerFactory(); @@ -121,4 +230,35 @@ function (RequestEvent $event) { $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); + + $result = $factory($mockContainer->reveal()); + + } } diff --git a/test/ConfigTraitTest.php b/test/ConfigTraitTest.php index 4460349..6e615f0 100644 --- a/test/ConfigTraitTest.php +++ b/test/ConfigTraitTest.php @@ -160,7 +160,7 @@ public function testGetListenersConfigNoConfig() ->willReturn([]); $result = $this->trait ->proxy('getListenersConfig', $this->container->reveal()); - $this->assertNull($result); + $this->assertInternalType('array', $result); } /** @@ -172,7 +172,7 @@ public function testGetListenersConfigNoArrayValue() ->get('config') ->willReturn([ 'authentication' => [ - 'listeners' => 'xxx', + 'event-listeners' => 'xxx', ], ]); @@ -184,11 +184,50 @@ public function testGetListenersConfig() $this->container->get('config') ->willReturn([ 'authentication' => [ - 'listeners' => $expected = [['xxx']], + '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); + } } From 9361dd22857ebc35c06aed2b99aa3109955d84e9 Mon Sep 17 00:00:00 2001 From: Marc Guyer Date: Sun, 11 Aug 2019 22:16:39 -0400 Subject: [PATCH 15/35] Fix cs errors --- src/AuthorizationServerFactory.php | 4 ++-- test/AuthorizationServerFactoryTest.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AuthorizationServerFactory.php b/src/AuthorizationServerFactory.php index 6eb1106..e41d399 100644 --- a/src/AuthorizationServerFactory.php +++ b/src/AuthorizationServerFactory.php @@ -93,7 +93,7 @@ private function addListeners( $listener = $listenerConfig[1]; $priority = $listenerConfig[2] ?? null; if (is_string($listener)) { - if (!$container->has($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 ' . @@ -124,7 +124,7 @@ private function addListenerProviders( foreach ($providers as $idx => $provider) { if (is_string($provider)) { - if (!$container->has($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 ' . diff --git a/test/AuthorizationServerFactoryTest.php b/test/AuthorizationServerFactoryTest.php index 94c5990..9edfb49 100644 --- a/test/AuthorizationServerFactoryTest.php +++ b/test/AuthorizationServerFactoryTest.php @@ -80,7 +80,8 @@ public function testInvoke() /** * @return ObjectProphecy */ - private function getContainerMock(): ObjectProphecy { + private function getContainerMock(): ObjectProphecy + { $mockContainer = $this->prophesize(ContainerInterface::class); $mockClientRepo = $this->prophesize(ClientRepositoryInterface::class); $mockAccessTokenRepo = $this->prophesize(AccessTokenRepositoryInterface::class); @@ -259,6 +260,5 @@ public function testInvokeWithListenerProviderConfigMissingServiceThrowsExceptio $this->expectException(InvalidConfigException::class); $result = $factory($mockContainer->reveal()); - } } From 43311f018b42233de05f5091ea57a3efc6096910 Mon Sep 17 00:00:00 2001 From: Marc Guyer Date: Wed, 14 Aug 2019 13:47:24 -0400 Subject: [PATCH 16/35] Remove commented-out listener config example --- config/oauth2.php | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/config/oauth2.php b/config/oauth2.php index 70ef994..9abb090 100644 --- a/config/oauth2.php +++ b/config/oauth2.php @@ -35,25 +35,6 @@ \League\OAuth2\Server\Grant\RefreshTokenGrant::class => \League\OAuth2\Server\Grant\RefreshTokenGrant::class ], - - // optionally add listener config - // 'listeners' => [ - // [ - // // event name - // \League\OAuth2\Server\RequestEvent::CLIENT_AUTHENTICATION_FAILED, - // // listener defined as a service key - // \My\League\Event\Listener\For\Auth\Failure::class, - // ], [ - // \League\OAuth2\Server\RequestEvent::CLIENT_AUTHENTICATION_FAILED, - // // listener defined as an anonymous function - // function (\League\OAuth2\Server\RequestEvent $event) { - // // do something - // }, - // // priority NORMAL (0) is the default but you may set an - // // int val of your choosing - // // League\Event\ListenerAcceptorInterface::P_HIGH, - // ], - // ], ]; // Conditionally include the encryption_key config setting, based on presence of file. From 68c8c2c174b1ffc9607a1a1881fe073d963015bf Mon Sep 17 00:00:00 2001 From: Marc Guyer Date: Wed, 14 Aug 2019 14:48:04 -0400 Subject: [PATCH 17/35] Use underscores for config keys --- docs/book/v1/intro.md | 10 +++++----- src/AuthorizationServerFactory.php | 4 ++-- src/ConfigTrait.php | 16 ++++++++-------- test/AuthorizationServerFactoryTest.php | 8 ++++---- test/ConfigTraitTest.php | 8 ++++---- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/docs/book/v1/intro.md b/docs/book/v1/intro.md index eda3fb1..86e43e2 100644 --- a/docs/book/v1/intro.md +++ b/docs/book/v1/intro.md @@ -128,17 +128,17 @@ you can extend this array to add your own custom grants. ### Configure Event Listeners -_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). +_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. +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. +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/). @@ -146,7 +146,7 @@ Example config: ```php return [ - 'event-listeners' => [ + 'event_listeners' => [ // using a container service [ \League\OAuth2\Server\RequestEvent::CLIENT_AUTHENTICATION_FAILED, @@ -160,7 +160,7 @@ return [ }, ], ], - 'event-listener-providers' => [ + 'event_listener_providers' => [ \My\Event\ListenerProvider\Service::class, ], ]; diff --git a/src/AuthorizationServerFactory.php b/src/AuthorizationServerFactory.php index e41d399..b4ad06f 100644 --- a/src/AuthorizationServerFactory.php +++ b/src/AuthorizationServerFactory.php @@ -95,7 +95,7 @@ private function addListeners( if (is_string($listener)) { if (! $container->has($listener)) { throw new Exception\InvalidConfigException(sprintf( - 'The second element of event-listeners config at ' . + '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.', @@ -126,7 +126,7 @@ private function addListenerProviders( if (is_string($provider)) { if (! $container->has($provider)) { throw new Exception\InvalidConfigException(sprintf( - 'The event-listener-providers config at ' . + '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.', diff --git a/src/ConfigTrait.php b/src/ConfigTrait.php index b424412..1b75670 100644 --- a/src/ConfigTrait.php +++ b/src/ConfigTrait.php @@ -113,16 +113,16 @@ protected function getListenersConfig(ContainerInterface $container) : array { $config = $container->get('config')['authentication'] ?? []; - if (empty($config['event-listeners'])) { + if (empty($config['event_listeners'])) { return []; } - if (! is_array($config['event-listeners'])) { + if (! is_array($config['event_listeners'])) { throw new InvalidConfigException( - 'The event-listeners config must be an array value' + 'The event_listeners config must be an array value' ); } - return $config['event-listeners']; + return $config['event_listeners']; } /** @@ -134,15 +134,15 @@ protected function getListenerProvidersConfig(ContainerInterface $container) : a { $config = $container->get('config')['authentication'] ?? []; - if (empty($config['event-listener-providers'])) { + if (empty($config['event_listener_providers'])) { return []; } - if (! is_array($config['event-listener-providers'])) { + if (! is_array($config['event_listener_providers'])) { throw new InvalidConfigException( - 'The event-listener-providers config must be an array value' + 'The event_listener_providers config must be an array value' ); } - return $config['event-listener-providers']; + return $config['event_listener_providers']; } } diff --git a/test/AuthorizationServerFactoryTest.php b/test/AuthorizationServerFactoryTest.php index 9edfb49..e84df8e 100644 --- a/test/AuthorizationServerFactoryTest.php +++ b/test/AuthorizationServerFactoryTest.php @@ -145,7 +145,7 @@ public function testInvokeWithListenerConfig() ClientCredentialsGrant::class => ClientCredentialsGrant::class, ], - 'event-listeners' => [ + 'event_listeners' => [ [ RequestEvent::CLIENT_AUTHENTICATION_FAILED, function (RequestEvent $event) { @@ -183,7 +183,7 @@ public function testInvokeWithListenerConfigMissingServiceThrowsException() ClientCredentialsGrant::class => ClientCredentialsGrant::class, ], - 'event-listeners' => [ + 'event_listeners' => [ [ RequestEvent::CLIENT_AUTHENTICATION_FAILED, ListenerInterface::class @@ -217,7 +217,7 @@ public function testInvokeWithListenerProviderConfig() ClientCredentialsGrant::class => ClientCredentialsGrant::class, ], - 'event-listener-providers' => [ + 'event_listener_providers' => [ ListenerProviderInterface::class ] ] @@ -247,7 +247,7 @@ public function testInvokeWithListenerProviderConfigMissingServiceThrowsExceptio ClientCredentialsGrant::class => ClientCredentialsGrant::class, ], - 'event-listener-providers' => [ + 'event_listener_providers' => [ ListenerProviderInterface::class ] ] diff --git a/test/ConfigTraitTest.php b/test/ConfigTraitTest.php index 6e615f0..2808660 100644 --- a/test/ConfigTraitTest.php +++ b/test/ConfigTraitTest.php @@ -172,7 +172,7 @@ public function testGetListenersConfigNoArrayValue() ->get('config') ->willReturn([ 'authentication' => [ - 'event-listeners' => 'xxx', + 'event_listeners' => 'xxx', ], ]); @@ -184,7 +184,7 @@ public function testGetListenersConfig() $this->container->get('config') ->willReturn([ 'authentication' => [ - 'event-listeners' => $expected = [['xxx']], + 'event_listeners' => $expected = [['xxx']], ], ]); $result = $this->trait @@ -211,7 +211,7 @@ public function testGetListenerProvidersConfigNoArrayValue() ->get('config') ->willReturn([ 'authentication' => [ - 'event-listener-providers' => 'xxx', + 'event_listener_providers' => 'xxx', ], ]); @@ -223,7 +223,7 @@ public function testGetListenerProvidersConfig() $this->container->get('config') ->willReturn([ 'authentication' => [ - 'event-listener-providers' => $expected = ['xxx'], + 'event_listener_providers' => $expected = ['xxx'], ], ]); $result = $this->trait From db8cc1ba8e9f5e5e89784aef6ed54c981a71c801 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sat, 28 Dec 2019 14:01:54 -0600 Subject: [PATCH 18/35] docs: note that configurable event listeners are from 1.3.0 forward --- docs/book/v1/intro.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/book/v1/intro.md b/docs/book/v1/intro.md index 86e43e2..16dbf3b 100644 --- a/docs/book/v1/intro.md +++ b/docs/book/v1/intro.md @@ -128,6 +128,8 @@ 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 From 08e5e5f29e42ff210b9408aa1ff81820f6c61fdb Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sat, 28 Dec 2019 14:05:25 -0600 Subject: [PATCH 19/35] qa: CS fixes for AuthorizationServerFactoryTest - trailing commas in array entries - consistent whitespace --- test/AuthorizationServerFactoryTest.php | 60 +++++++++++-------------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/test/AuthorizationServerFactoryTest.php b/test/AuthorizationServerFactoryTest.php index e84df8e..05d9898 100644 --- a/test/AuthorizationServerFactoryTest.php +++ b/test/AuthorizationServerFactoryTest.php @@ -51,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, ], ] ]; @@ -112,12 +110,10 @@ public function testInvokeWithNullGrant() 'encryption_key' => 'iALlwJ1sH77dmFCJFo+pMdM6Af4bF/hCca1EDDx7MwE=', 'access_token_expire' => 'P1D', 'grants' => [ - ClientCredentialsGrant::class - => null, - PasswordGrant::class - => PasswordGrant::class, + ClientCredentialsGrant::class => null, + PasswordGrant::class => PasswordGrant::class, ], - ] + ], ]; $mockContainer->get('config')->willReturn($config); @@ -142,21 +138,21 @@ public function testInvokeWithListenerConfig() 'encryption_key' => 'iALlwJ1sH77dmFCJFo+pMdM6Af4bF/hCca1EDDx7MwE=', 'access_token_expire' => 'P1D', 'grants' => [ - ClientCredentialsGrant::class - => ClientCredentialsGrant::class, + ClientCredentialsGrant::class => ClientCredentialsGrant::class, ], 'event_listeners' => [ [ RequestEvent::CLIENT_AUTHENTICATION_FAILED, function (RequestEvent $event) { // do something - } - ], [ + }, + ], + [ RequestEvent::CLIENT_AUTHENTICATION_FAILED, - ListenerInterface::class - ] - ] - ] + ListenerInterface::class, + ], + ], + ], ]; $mockContainer->get('config')->willReturn($config); @@ -180,16 +176,15 @@ public function testInvokeWithListenerConfigMissingServiceThrowsException() 'encryption_key' => 'iALlwJ1sH77dmFCJFo+pMdM6Af4bF/hCca1EDDx7MwE=', 'access_token_expire' => 'P1D', 'grants' => [ - ClientCredentialsGrant::class - => ClientCredentialsGrant::class, + ClientCredentialsGrant::class => ClientCredentialsGrant::class, ], 'event_listeners' => [ [ RequestEvent::CLIENT_AUTHENTICATION_FAILED, - ListenerInterface::class - ] - ] - ] + ListenerInterface::class, + ], + ], + ], ]; $mockContainer->get('config')->willReturn($config); @@ -214,13 +209,12 @@ public function testInvokeWithListenerProviderConfig() 'encryption_key' => 'iALlwJ1sH77dmFCJFo+pMdM6Af4bF/hCca1EDDx7MwE=', 'access_token_expire' => 'P1D', 'grants' => [ - ClientCredentialsGrant::class - => ClientCredentialsGrant::class, + ClientCredentialsGrant::class => ClientCredentialsGrant::class, ], 'event_listener_providers' => [ ListenerProviderInterface::class - ] - ] + ], + ], ]; $mockContainer->get('config')->willReturn($config); @@ -244,13 +238,12 @@ public function testInvokeWithListenerProviderConfigMissingServiceThrowsExceptio 'encryption_key' => 'iALlwJ1sH77dmFCJFo+pMdM6Af4bF/hCca1EDDx7MwE=', 'access_token_expire' => 'P1D', 'grants' => [ - ClientCredentialsGrant::class - => ClientCredentialsGrant::class, + ClientCredentialsGrant::class => ClientCredentialsGrant::class, ], 'event_listener_providers' => [ - ListenerProviderInterface::class - ] - ] + ListenerProviderInterface::class, + ], + ], ]; $mockContainer->get('config')->willReturn($config); @@ -258,7 +251,6 @@ public function testInvokeWithListenerProviderConfigMissingServiceThrowsExceptio $factory = new AuthorizationServerFactory(); $this->expectException(InvalidConfigException::class); - - $result = $factory($mockContainer->reveal()); + $factory($mockContainer->reveal()); } } From 93ddf21e717d2563952cdaee9f5c8b2b22645cb5 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sat, 28 Dec 2019 14:08:14 -0600 Subject: [PATCH 20/35] docs: adds CHANGELOG entry for #62 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5760a6..bb34f4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ All notable changes to this project will be documented in this file, in reverse ### Added -- Nothing. +- [#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 From 0b9ed00cd1f367955459ccb732ec90cc1d8b4d82 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sat, 28 Dec 2019 14:39:37 -0600 Subject: [PATCH 21/35] fix: ensure `data/oauth2.sql` works for sqlite3 The database is used in unit tests, which use sqlite. The latest changes to the database made it unusable for anything but MySQL. The changes in this patch should work across any database that understands ANSI SQL, with the potential exception of how autoincrement is defined. --- data/oauth2.sql | 75 ++++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/data/oauth2.sql b/data/oauth2.sql index 984c600..f971783 100644 --- a/data/oauth2.sql +++ b/data/oauth2.sql @@ -3,44 +3,44 @@ -- CREATE TABLE `oauth_access_tokens` ( - `id` varchar(100) NOT NULL, - `user_id` int(10) unsigned DEFAULT NULL, - `client_id` int(10) unsigned NOT NULL, + `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, - PRIMARY KEY (`id`), - KEY `IDX_CA42527CA76ED39519EB6921BDA26CCD` (`user_id`,`client_id`), - KEY `IDX_CA42527CA76ED395` (`user_id`), - KEY `IDX_CA42527C19EB6921` (`client_id`) -) ENGINE=InnoDB; + `expires_at` datetime NOT NULL +); + +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) NOT NULL, - `user_id` int(10) unsigned DEFAULT NULL, - `client_id` int(10) unsigned NOT NULL, + `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, - PRIMARY KEY (`id`), - KEY `IDX_BB493F83A76ED395` (`user_id`), - KEY `IDX_BB493F8319EB6921` (`client_id`) -) ENGINE=InnoDB; + `expires_at` datetime DEFAULT NULL +); + +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` int(10) unsigned NOT NULL AUTO_INCREMENT, - `user_id` int(10) unsigned DEFAULT NULL, + `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, @@ -48,44 +48,43 @@ CREATE TABLE `oauth_clients` ( `password_client` tinyint(1) DEFAULT NULL, `revoked` tinyint(1) DEFAULT NULL, `created_at` datetime DEFAULT CURRENT_TIMESTAMP, - `updated_at` datetime DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `IDX_13CE81015E237E06A76ED395BDA26CCD` (`name`,`user_id`), - KEY `IDX_13CE8101A76ED395` (`user_id`) -) ENGINE=InnoDB; + `updated_at` datetime DEFAULT 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) NOT NULL, + `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, - PRIMARY KEY (`id`), - KEY `IDX_5AB6872CCB2688BDA26CCD` (`access_token_id`) -) ENGINE=InnoDB; + `expires_at` datetime NOT NULL +); + +CREATE INDEX `IDX_5AB6872CCB2688BDA26CCD` ON oauth_refresh_tokens (`access_token_id`); -- -- Table structure for table `oauth_scopes` -- CREATE TABLE `oauth_scopes` ( - `id` varchar(100) NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB; + `id` varchar(100) PRIMARY KEY NOT NULL +); -- -- Table structure for table `oauth_users` -- CREATE TABLE `oauth_users` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `username` varchar(320) NOT NULL, + `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, - PRIMARY KEY (`id`), - UNIQUE KEY `UNIQ_93804FF8F85E0677` (`username`) -) ENGINE=InnoDB; + `last_name` varchar(80) DEFAULT NULL +); + +CREATE INDEX `UNIQ_93804FF8F85E0677` ON oauth_users (`username`); From 29690bddaada7f11ef43d170cb831ca3bf6d8595 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sat, 28 Dec 2019 14:42:06 -0600 Subject: [PATCH 22/35] 1.3.0 readiness - Updates branch aliases: - dev-master => 1.3.x-dev - dev-develop => 2.0.x-dev - Updates CHANGELOG - adds date for 1.3.0 release --- CHANGELOG.md | 2 +- composer.json | 4 ++-- composer.lock | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb34f4a..3223d73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. -## 1.3.0 - TBD +## 1.3.0 - 2019-12-28 ### Added diff --git a/composer.json b/composer.json index c421e58..12eb713 100644 --- a/composer.json +++ b/composer.json @@ -53,8 +53,8 @@ }, "extra": { "branch-alias": { - "dev-master": "1.2.x-dev", - "dev-develop": "1.3.x-dev" + "dev-master": "1.3.x-dev", + "dev-develop": "2.0.x-dev" }, "zf": { "config-provider": "Zend\\Expressive\\Authentication\\OAuth2\\ConfigProvider" diff --git a/composer.lock b/composer.lock index 2de372e..63cb195 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": "bda1e7feca55a8efac26338aa133c188", + "content-hash": "3d1f67477142c8e8d333311a44340240", "packages": [ { "name": "defuse/php-encryption", From a508ed027c5056ccf2fb35065079214bff46fefb Mon Sep 17 00:00:00 2001 From: Tsvetomir Lazarov Date: Mon, 7 Oct 2019 17:33:34 +0300 Subject: [PATCH 23/35] Update league/oauth2-server to ^8.0.0 --- composer.json | 2 +- composer.lock | 309 +++++++++++++++++++++++++------------------------- 2 files changed, 157 insertions(+), 154 deletions(-) diff --git a/composer.json b/composer.json index 12eb713..733c754 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ }, "require": { "php": "^7.1", - "league/oauth2-server": "^7.3.0", + "league/oauth2-server": "^8.0.0", "psr/container": "^1.0", "psr/http-message": "^1.0.1", "psr/http-server-middleware": "^1.0", diff --git a/composer.lock b/composer.lock index 63cb195..f7a6268 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": "3d1f67477142c8e8d333311a44340240", + "content-hash": "49e1aaaedd3215a18326b2b91a354874", "packages": [ { "name": "defuse/php-encryption", @@ -113,8 +113,8 @@ "authors": [ { "name": "Luís Otávio Cobucci Oblonczyk", - "role": "Developer", - "email": "lcobucci@gmail.com" + "email": "lcobucci@gmail.com", + "role": "Developer" } ], "description": "A simple library to work with JSON Web Token and JSON Web Signature", @@ -176,24 +176,25 @@ }, { "name": "league/oauth2-server", - "version": "7.4.0", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/thephpleague/oauth2-server.git", - "reference": "2eb1cf79e59d807d89c256e7ac5e2bf8bdbd4acf" + "reference": "e1dc4d708c56fcfa205be4bb1862b6d525b4baac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/oauth2-server/zipball/2eb1cf79e59d807d89c256e7ac5e2bf8bdbd4acf", - "reference": "2eb1cf79e59d807d89c256e7ac5e2bf8bdbd4acf", + "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.2.2", - "league/event": "^2.1", - "php": ">=7.0.0", + "lcobucci/jwt": "^3.3.1", + "league/event": "^2.2", + "php": ">=7.1.0", "psr/http-message": "^1.0.1" }, "replace": { @@ -201,12 +202,11 @@ "lncd/oauth2": "*" }, "require-dev": { - "phpstan/phpstan": "^0.9.2", - "phpstan/phpstan-phpunit": "^0.9.4", - "phpstan/phpstan-strict-rules": "^0.9.0", - "phpunit/phpunit": "^6.3 || ^7.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": "^1.3.2" + "zendframework/zend-diactoros": "^2.1.2" }, "type": "library", "autoload": { @@ -249,7 +249,7 @@ "secure", "server" ], - "time": "2019-05-05T09:22:01+00:00" + "time": "2019-07-13T18:58:26+00:00" }, { "name": "paragonie/random_compat", @@ -598,20 +598,21 @@ ], "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.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "a2c590166b2133a4633738648b6b064edae0814a" + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", - "reference": "a2c590166b2133a4633738648b6b064edae0814a", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1", + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1", "shasum": "" }, "require": { @@ -654,20 +655,20 @@ "constructor", "instantiate" ], - "time": "2019-03-17T17:37:11+00:00" + "time": "2019-10-21T16:45:58+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.9.3", + "version": "1.9.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea" + "reference": "579bb7356d91f9456ccd505f24ca8b667966a0a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea", - "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/579bb7356d91f9456ccd505f24ca8b667966a0a7", + "reference": "579bb7356d91f9456ccd505f24ca8b667966a0a7", "shasum": "" }, "require": { @@ -702,7 +703,7 @@ "object", "object graph" ], - "time": "2019-08-09T12:45:53+00:00" + "time": "2019-12-15T19:12:40+00:00" }, { "name": "phar-io/manifest", @@ -742,18 +743,18 @@ "authors": [ { "name": "Arne Blankerts", - "role": "Developer", - "email": "arne@blankerts.de" + "email": "arne@blankerts.de", + "role": "Developer" }, { "name": "Sebastian Heuer", - "role": "Developer", - "email": "sebastian@phpeople.de" + "email": "sebastian@phpeople.de", + "role": "Developer" }, { "name": "Sebastian Bergmann", - "role": "Developer", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "Developer" } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", @@ -808,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/", @@ -858,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.1", + "version": "4.3.4", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c" + "reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", - "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", + "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", @@ -909,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": "2019-04-30T17:48:53+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/", @@ -956,37 +955,38 @@ "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.1", + "version": "1.10.1", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76" + "reference": "cbe1df668b3fe136bcc909126a0f529a78d4cbbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76", - "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76", + "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": { @@ -1019,20 +1019,20 @@ "spy", "stub" ], - "time": "2019-06-13T12:50:23+00:00" + "time": "2019-12-22T21:05:45+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "7.0.7", + "version": "7.0.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "7743bbcfff2a907e9ee4a25be13d0f8ec5e73800" + "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7743bbcfff2a907e9ee4a25be13d0f8ec5e73800", - "reference": "7743bbcfff2a907e9ee4a25be13d0f8ec5e73800", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f1884187926fbb755a9aaf0b3836ad3165b478bf", + "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf", "shasum": "" }, "require": { @@ -1041,7 +1041,7 @@ "php": "^7.2", "phpunit/php-file-iterator": "^2.0.2", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.1.0", + "phpunit/php-token-stream": "^3.1.1", "sebastian/code-unit-reverse-lookup": "^1.0.1", "sebastian/environment": "^4.2.2", "sebastian/version": "^2.0.1", @@ -1082,7 +1082,7 @@ "testing", "xunit" ], - "time": "2019-07-25T05:31:54+00:00" + "time": "2019-11-20T13:55:58+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1122,8 +1122,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "role": "lead", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], "description": "FilterIterator implementation that filters files based on a list of suffixes.", @@ -1213,8 +1213,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "role": "lead", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], "description": "Utility class for timing", @@ -1226,16 +1226,16 @@ }, { "name": "phpunit/php-token-stream", - "version": "3.1.0", + "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "e899757bb3df5ff6e95089132f32cd59aac2220a" + "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e899757bb3df5ff6e95089132f32cd59aac2220a", - "reference": "e899757bb3df5ff6e95089132f32cd59aac2220a", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff", + "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff", "shasum": "" }, "require": { @@ -1271,20 +1271,20 @@ "keywords": [ "tokenizer" ], - "time": "2019-07-25T05:29:42+00:00" + "time": "2019-09-17T06:23:10+00:00" }, { "name": "phpunit/phpunit", - "version": "8.3.4", + "version": "8.5.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "e31cce0cf4499c0ccdbbb211a3280d36ab341e36" + "reference": "7870c78da3c5e4883eaef36ae47853ebb3cb86f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e31cce0cf4499c0ccdbbb211a3280d36ab341e36", - "reference": "e31cce0cf4499c0ccdbbb211a3280d36ab341e36", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7870c78da3c5e4883eaef36ae47853ebb3cb86f2", + "reference": "7870c78da3c5e4883eaef36ae47853ebb3cb86f2", "shasum": "" }, "require": { @@ -1307,7 +1307,7 @@ "sebastian/comparator": "^3.0.2", "sebastian/diff": "^3.0.2", "sebastian/environment": "^4.2.2", - "sebastian/exporter": "^3.1.0", + "sebastian/exporter": "^3.1.1", "sebastian/global-state": "^3.0.0", "sebastian/object-enumerator": "^3.0.3", "sebastian/resource-operations": "^2.0.1", @@ -1328,7 +1328,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "8.3-dev" + "dev-master": "8.5-dev" } }, "autoload": { @@ -1343,8 +1343,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "role": "lead", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], "description": "The PHP Unit Testing framework.", @@ -1354,7 +1354,7 @@ "testing", "xunit" ], - "time": "2019-08-11T06:56:55+00:00" + "time": "2019-12-25T14:49:39+00:00" }, { "name": "psr/http-factory", @@ -1414,12 +1414,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "ea693fa060702164985511acc3ceb5389c9ac761" + "reference": "44a677c8e06241a66409ae6e4820dc166fc09ab2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/ea693fa060702164985511acc3ceb5389c9ac761", - "reference": "ea693fa060702164985511acc3ceb5389c9ac761", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/44a677c8e06241a66409ae6e4820dc166fc09ab2", + "reference": "44a677c8e06241a66409ae6e4820dc166fc09ab2", "shasum": "" }, "conflict": { @@ -1440,9 +1440,9 @@ "composer/composer": "<=1-alpha.11", "contao-components/mediaelement": ">=2.14.2,<2.21.1", "contao/core": ">=2,<3.5.39", - "contao/core-bundle": ">=4,<4.4.39|>=4.5,<4.7.5", + "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", @@ -1454,8 +1454,8 @@ "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.67|>=8,<8.6.16|>=8.7,<8.7.1|>8.7.3,<8.7.5", - "drupal/drupal": ">=7,<7.67|>=8,<8.6.16|>=8.7,<8.7.1|>8.7.3,<8.7.5", + "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", @@ -1485,9 +1485,9 @@ "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", "league/commonmark": "<0.18.3", - "magento/magento1ce": "<1.9.4.1", - "magento/magento1ee": ">=1.9,<1.14.4.1", - "magento/product-community-edition": ">=2,<2.2.8|>=2.3,<2.3.1", + "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", @@ -1508,8 +1508,9 @@ "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.2", + "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", @@ -1522,7 +1523,7 @@ "silverstripe/userforms": "<3", "simple-updates/phpwhois": "<=1", "simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4", - "simplesamlphp/simplesamlphp": "<1.17.3", + "simplesamlphp/simplesamlphp": "<1.17.8", "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", "slim/slim": "<2.6", "smarty/smarty": "<3.1.33", @@ -1530,18 +1531,20 @@ "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/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.26|>=4,<4.1.12|>=4.2,<4.2.7", + "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.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", - "symfony/http-kernel": ">=2,<2.3.29|>=2.4,<2.5.12|>=2.6,<2.6.8", + "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", @@ -1552,11 +1555,12 @@ "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.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "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.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "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", @@ -1566,8 +1570,8 @@ "titon/framework": ">=0,<9.9.99", "truckersmp/phpwhois": "<=4.3.1", "twig/twig": "<1.38|>=2,<2.7", - "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.27|>=9,<9.5.8", - "typo3/cms-core": ">=8,<8.7.27|>=9,<9.5.8", + "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", @@ -1621,7 +1625,7 @@ } ], "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", - "time": "2018-03-07T15:45:44+00:00" + "time": "2019-12-26T14:16:40+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -1790,16 +1794,16 @@ }, { "name": "sebastian/environment", - "version": "4.2.2", + "version": "4.2.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404" + "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/f2a2c8e1c97c11ace607a7a667d73d47c19fe404", - "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/464c90d7bdf5ad4e8a6aea15c091fec0603d4368", + "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368", "shasum": "" }, "require": { @@ -1839,20 +1843,20 @@ "environment", "hhvm" ], - "time": "2019-05-05T09:05:15+00:00" + "time": "2019-11-20T08:46:58+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.1", + "version": "3.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "06a9a5947f47b3029d76118eb5c22802e5869687" + "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/06a9a5947f47b3029d76118eb5c22802e5869687", - "reference": "06a9a5947f47b3029d76118eb5c22802e5869687", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", + "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", "shasum": "" }, "require": { @@ -1906,7 +1910,7 @@ "export", "exporter" ], - "time": "2019-08-11T12:43:14+00:00" + "time": "2019-09-14T09:02:43+00:00" }, { "name": "sebastian/global-state", @@ -2187,8 +2191,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "role": "lead", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], "description": "Collection of value objects that represent the types of the PHP type system", @@ -2318,16 +2322,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.12.0", + "version": "v1.13.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "550ebaac289296ce228a706d0867afc34687e3f4" + "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4", - "reference": "550ebaac289296ce228a706d0867afc34687e3f4", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", + "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", "shasum": "" }, "require": { @@ -2339,7 +2343,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.13-dev" } }, "autoload": { @@ -2372,7 +2376,7 @@ "polyfill", "portable" ], - "time": "2019-08-06T08:03:45+00:00" + "time": "2019-11-27T13:56:44+00:00" }, { "name": "theseer/tokenizer", @@ -2407,8 +2411,8 @@ "authors": [ { "name": "Arne Blankerts", - "role": "Developer", - "email": "arne@blankerts.de" + "email": "arne@blankerts.de", + "role": "Developer" } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", @@ -2416,31 +2420,29 @@ }, { "name": "webmozart/assert", - "version": "1.5.0", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4" + "reference": "573381c0a64f155a0d9a23f4b0c797194805b925" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/88e6d84706d09a236046d686bbea96f07b3a34f4", - "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4", + "url": "https://api.github.com/repos/webmozart/assert/zipball/573381c0a64f155a0d9a23f4b0c797194805b925", + "reference": "573381c0a64f155a0d9a23f4b0c797194805b925", "shasum": "" }, "require": { "php": "^5.3.3 || ^7.0", "symfony/polyfill-ctype": "^1.8" }, + "conflict": { + "vimeo/psalm": "<3.6.0" + }, "require-dev": { "phpunit/phpunit": "^4.8.36 || ^7.5.13" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -2462,7 +2464,7 @@ "check", "validate" ], - "time": "2019-08-24T08:43:50+00:00" + "time": "2019-11-24T13:36:37+00:00" }, { "name": "zendframework/zend-coding-standard", @@ -2495,16 +2497,16 @@ }, { "name": "zendframework/zend-diactoros", - "version": "2.1.3", + "version": "2.2.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-diactoros.git", - "reference": "279723778c40164bcf984a2df12ff2c6ec5e61c1" + "reference": "de5847b068362a88684a55b0dbb40d85986cfa52" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/279723778c40164bcf984a2df12ff2c6ec5e61c1", - "reference": "279723778c40164bcf984a2df12ff2c6ec5e61c1", + "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/de5847b068362a88684a55b0dbb40d85986cfa52", + "reference": "de5847b068362a88684a55b0dbb40d85986cfa52", "shasum": "" }, "require": { @@ -2517,6 +2519,7 @@ "psr/http-message-implementation": "1.0" }, "require-dev": { + "ext-curl": "*", "ext-dom": "*", "ext-libxml": "*", "http-interop/http-factory-tests": "^0.5.0", @@ -2557,7 +2560,7 @@ "psr", "psr-7" ], - "time": "2019-07-10T16:13:25+00:00" + "time": "2019-11-13T19:16:13+00:00" }, { "name": "zendframework/zend-servicemanager", From 376b4bc752c7f785d3cbc6355fa02b8d4767acee Mon Sep 17 00:00:00 2001 From: Tsvetomir Lazarov Date: Mon, 7 Oct 2019 17:33:52 +0300 Subject: [PATCH 24/35] Implement validateClient in the ClientRepository --- src/Repository/Pdo/ClientRepository.php | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Repository/Pdo/ClientRepository.php b/src/Repository/Pdo/ClientRepository.php index a2f2107..dc73035 100644 --- a/src/Repository/Pdo/ClientRepository.php +++ b/src/Repository/Pdo/ClientRepository.php @@ -10,6 +10,7 @@ namespace Zend\Expressive\Authentication\OAuth2\Repository\Pdo; +use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Repositories\ClientRepositoryInterface; use Zend\Expressive\Authentication\OAuth2\Entity\ClientEntity; @@ -32,17 +33,17 @@ public function getClientEntity( $sth->bindParam(':clientIdentifier', $clientIdentifier); if (false === $sth->execute()) { - return; + return null; } $row = $sth->fetch(); if (empty($row) || ! $this->isGranted($row, $grantType)) { - return; + return null; } if ($mustValidateSecret && (empty($row['secret']) || ! password_verify((string) $clientSecret, $row['secret'])) ) { - return; + return null; } return new ClientEntity($clientIdentifier, $row['name'], $row['redirect']); @@ -68,4 +69,18 @@ protected function isGranted(array $row, string $grantType = null) : bool return true; } } + + /** + * {@inheritDoc} + */ + public function validateClient($clientIdentifier, $clientSecret, $grantType) : bool + { + $client = $this->getClientEntity( + $clientIdentifier, + $grantType, + $clientSecret + ); + + return $client instanceof ClientEntityInterface; + } } From 51dc33ef4bd358fd10db7a6b8382c03c22d60499 Mon Sep 17 00:00:00 2001 From: Tsvetomir Lazarov Date: Mon, 7 Oct 2019 23:25:50 +0300 Subject: [PATCH 25/35] Change the ClientRepository::getClientEntity() implementation and re-write tests --- src/Repository/Pdo/ClientRepository.php | 80 ++++++++++++-------- test/Pdo/OAuth2PdoMiddlewareTest.php | 18 ++++- test/Repository/Pdo/ClientRepositoryTest.php | 41 +++++----- 3 files changed, 82 insertions(+), 57 deletions(-) diff --git a/src/Repository/Pdo/ClientRepository.php b/src/Repository/Pdo/ClientRepository.php index dc73035..498b3ab 100644 --- a/src/Repository/Pdo/ClientRepository.php +++ b/src/Repository/Pdo/ClientRepository.php @@ -21,39 +21,69 @@ class ClientRepository extends AbstractRepository implements ClientRepositoryInt /** * {@inheritDoc} */ - public function getClientEntity( - $clientIdentifier, - $grantType = null, - $clientSecret = null, - $mustValidateSecret = true - ) { - $sth = $this->pdo->prepare( - 'SELECT * FROM oauth_clients WHERE name = :clientIdentifier' - ); - $sth->bindParam(':clientIdentifier', $clientIdentifier); + public function getClientEntity($clientIdentifier) : ?ClientEntityInterface + { + $clientData = $this->getClientData($clientIdentifier); - if (false === $sth->execute()) { + if (empty($clientData)) { return null; } - $row = $sth->fetch(); - if (empty($row) || ! $this->isGranted($row, $grantType)) { + + return new ClientEntity( + $clientIdentifier, + $clientData['name'] ?? '', + $clientData['redirect'] ?? '', + ); + } + + /** + * {@inheritDoc} + */ + public function validateClient($clientIdentifier, $clientSecret, $grantType) : bool + { + $clientData = $this->getClientData($clientIdentifier); + + if (empty($clientData)) { + return false; + } + + if (! $this->isGranted($clientData, $grantType)) { + return false; + } + + if (empty($clientData['secret']) || ! password_verify((string) $clientSecret, $clientData['secret'])) { + return false; + } + + return true; + } + + protected 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; } - if ($mustValidateSecret - && (empty($row['secret']) || ! password_verify((string) $clientSecret, $row['secret'])) - ) { + $row = $statement->fetch(); + + if (empty($row)) { return null; } - return new ClientEntity($clientIdentifier, $row['name'], $row['redirect']); + return $row; } /** * 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 = null) : bool @@ -69,18 +99,4 @@ protected function isGranted(array $row, string $grantType = null) : bool return true; } } - - /** - * {@inheritDoc} - */ - public function validateClient($clientIdentifier, $clientSecret, $grantType) : bool - { - $client = $this->getClientEntity( - $clientIdentifier, - $grantType, - $clientSecret - ); - - return $client instanceof ClientEntityInterface; - } } diff --git a/test/Pdo/OAuth2PdoMiddlewareTest.php b/test/Pdo/OAuth2PdoMiddlewareTest.php index 5bfed5a..26d8bc1 100644 --- a/test/Pdo/OAuth2PdoMiddlewareTest.php +++ b/test/Pdo/OAuth2PdoMiddlewareTest.php @@ -12,6 +12,7 @@ use DateInterval; use League\OAuth2\Server\AuthorizationServer; +use League\OAuth2\Server\CodeChallengeVerifiers\S256Verifier; use League\OAuth2\Server\Grant\AuthCodeGrant; use League\OAuth2\Server\Grant\ClientCredentialsGrant; use League\OAuth2\Server\Grant\ImplicitGrant; @@ -64,6 +65,8 @@ class OAuth2PdoMiddlewareTest extends TestCase const PRIVATE_KEY = __DIR__ .'/../TestAsset/private.key'; const ENCRYPTION_KEY = 'T2x2+1OGrEzfS+01OUmwhOcJiGmE58UD1fllNn6CGcQ='; + const CODE_VERIFIER = 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk'; + /** @var AccessTokenRepository */ private $accessTokenRepository; @@ -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', diff --git a/test/Repository/Pdo/ClientRepositoryTest.php b/test/Repository/Pdo/ClientRepositoryTest.php index df7e137..b4e771e 100644 --- a/test/Repository/Pdo/ClientRepositoryTest.php +++ b/test/Repository/Pdo/ClientRepositoryTest.php @@ -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,7 @@ public function testGetClientEntityReturnsNullIfNoRowReturned() $client = $this->prophesize(ClientEntityInterface::class); $this->assertNull( - $this->repo ->getClientEntity( - 'client_id', - 'grant_type' - ) + $this->repo ->getClientEntity('client_id') ); } @@ -85,7 +79,7 @@ public function invalidGrants() /** * @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 +94,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 +121,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 +148,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' ) ); } From 8597adc16fba0fe79c9d4a2b405ad3e5addaf862 Mon Sep 17 00:00:00 2001 From: Tsvetomir Lazarov Date: Mon, 7 Oct 2019 23:27:04 +0300 Subject: [PATCH 26/35] Change the copyright year to 2019 in the docblock of changed files --- src/Repository/Pdo/ClientRepository.php | 2 +- test/Pdo/OAuth2PdoMiddlewareTest.php | 2 +- test/Repository/Pdo/ClientRepositoryTest.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Repository/Pdo/ClientRepository.php b/src/Repository/Pdo/ClientRepository.php index 498b3ab..0c95f8a 100644 --- a/src/Repository/Pdo/ClientRepository.php +++ b/src/Repository/Pdo/ClientRepository.php @@ -1,7 +1,7 @@ Date: Mon, 7 Oct 2019 23:38:59 +0300 Subject: [PATCH 27/35] Remove trailing comma from constructor call, causing builds to fail on PHP < 7.3 --- src/Repository/Pdo/ClientRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Repository/Pdo/ClientRepository.php b/src/Repository/Pdo/ClientRepository.php index 0c95f8a..285da6d 100644 --- a/src/Repository/Pdo/ClientRepository.php +++ b/src/Repository/Pdo/ClientRepository.php @@ -32,7 +32,7 @@ public function getClientEntity($clientIdentifier) : ?ClientEntityInterface return new ClientEntity( $clientIdentifier, $clientData['name'] ?? '', - $clientData['redirect'] ?? '', + $clientData['redirect'] ?? '' ); } From a5f631e52d479533f4b0e5e42eb3d2254fb997fd Mon Sep 17 00:00:00 2001 From: Tsvetomir Lazarov Date: Mon, 7 Oct 2019 23:49:57 +0300 Subject: [PATCH 28/35] Add tests for more complete code coverage for the ClientRepository --- test/Repository/Pdo/ClientRepositoryTest.php | 62 ++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/test/Repository/Pdo/ClientRepositoryTest.php b/test/Repository/Pdo/ClientRepositoryTest.php index 78a2659..6a12c4e 100644 --- a/test/Repository/Pdo/ClientRepositoryTest.php +++ b/test/Repository/Pdo/ClientRepositoryTest.php @@ -60,6 +60,44 @@ public function testGetClientEntityReturnsNullIfNoRowReturned() ); } + 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() + ); + } + public function invalidGrants() { return [ @@ -76,6 +114,30 @@ 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 */ From e5abc57f863aea75f94f2d816ff3521cf6fcdb4f Mon Sep 17 00:00:00 2001 From: Tsvetomir Lazarov Date: Sun, 13 Oct 2019 02:37:55 +0300 Subject: [PATCH 29/35] Change ClientRepository::getClientData() method visibility to "private" --- src/Repository/Pdo/ClientRepository.php | 40 ++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Repository/Pdo/ClientRepository.php b/src/Repository/Pdo/ClientRepository.php index 285da6d..1dcf596 100644 --- a/src/Repository/Pdo/ClientRepository.php +++ b/src/Repository/Pdo/ClientRepository.php @@ -58,26 +58,6 @@ public function validateClient($clientIdentifier, $clientSecret, $grantType) : b return true; } - protected 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; - } - /** * Check the grantType for the client value, stored in $row * @@ -99,4 +79,24 @@ protected function isGranted(array $row, string $grantType = null) : 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; + } } From 7b0fa293aa079a52f4dcfb340e56eba27e73cf14 Mon Sep 17 00:00:00 2001 From: Tsvetomir Lazarov Date: Sun, 13 Oct 2019 02:39:10 +0300 Subject: [PATCH 30/35] Change the copyright year to 2017-2019 in the docblock of changed files --- src/Repository/Pdo/ClientRepository.php | 2 +- test/Pdo/OAuth2PdoMiddlewareTest.php | 2 +- test/Repository/Pdo/ClientRepositoryTest.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Repository/Pdo/ClientRepository.php b/src/Repository/Pdo/ClientRepository.php index 1dcf596..83597a0 100644 --- a/src/Repository/Pdo/ClientRepository.php +++ b/src/Repository/Pdo/ClientRepository.php @@ -1,7 +1,7 @@ Date: Sat, 28 Dec 2019 14:51:52 -0600 Subject: [PATCH 31/35] docs: adds CHANGELOG entry for #69 --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3223d73..1235dcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,28 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. +## 2.0.0 - TBD + +### 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 From be022bd58f4817bdf4e075177c6506c25f949666 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sat, 28 Dec 2019 14:52:47 -0600 Subject: [PATCH 32/35] 2.0.0 readiness - Updates branch aliases: - dev-master => 2.0.x-dev - dev-develop => 2.1.x-dev - Updates CHANGELOG - Adds 2.0.0 section - Sets date for 2.0.0 release --- CHANGELOG.md | 2 +- composer.json | 4 ++-- composer.lock | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1235dcb..8af8100 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. -## 2.0.0 - TBD +## 2.0.0 - 2019-12-28 ### Added diff --git a/composer.json b/composer.json index 733c754..180b8d4 100644 --- a/composer.json +++ b/composer.json @@ -53,8 +53,8 @@ }, "extra": { "branch-alias": { - "dev-master": "1.3.x-dev", - "dev-develop": "2.0.x-dev" + "dev-master": "2.0.x-dev", + "dev-develop": "2.1.x-dev" }, "zf": { "config-provider": "Zend\\Expressive\\Authentication\\OAuth2\\ConfigProvider" diff --git a/composer.lock b/composer.lock index f7a6268..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": "49e1aaaedd3215a18326b2b91a354874", + "content-hash": "607e2fc1e526e325c6d9b176d6250da4", "packages": [ { "name": "defuse/php-encryption", From 2a8417a11b6c631974a3a5b99d8375c4ed164da3 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sat, 28 Dec 2019 14:54:41 -0600 Subject: [PATCH 33/35] Bumped version to 2.0.1 --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8af8100..7d0ddb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,28 @@ 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 From b087dd64dd4aaadd86b1c2faf4a57ad727a8bfae Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 31 Dec 2019 14:53:13 -0600 Subject: [PATCH 34/35] Marking package as abandoned --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4fd5d06..0f6f5fc 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. + [![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) @@ -34,4 +38,4 @@ Browse the documentation online at https://docs.zendframework.com/zend-expressiv * [Issues](https://github.com/zendframework/zend-expressive-authentication-oauth2/issues/) * [Chat](https://zendframework-slack.herokuapp.com/) -* [Forum](https://discourse.zendframework.com/) +* [Forum](https://discourse.zendframework.com/) \ No newline at end of file From 125acf0b898fe890b4c4b55541a12059980f2d89 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Mon, 20 Jan 2020 11:41:24 -0600 Subject: [PATCH 35/35] Link to new repository --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0f6f5fc..aaa8e09 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > ## Repository abandoned 2019-12-31 > -> This repository has moved to mezzio/mezzio-authentication-oauth2. +> 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) @@ -38,4 +38,4 @@ Browse the documentation online at https://docs.zendframework.com/zend-expressiv * [Issues](https://github.com/zendframework/zend-expressive-authentication-oauth2/issues/) * [Chat](https://zendframework-slack.herokuapp.com/) -* [Forum](https://discourse.zendframework.com/) \ No newline at end of file +* [Forum](https://discourse.zendframework.com/)