From e4b403a5c4a89d6b2f23c1eb9cc223cdae0b5686 Mon Sep 17 00:00:00 2001 From: Yevgeny Tomenko Date: Sat, 22 Aug 2026 21:13:59 +0300 Subject: [PATCH 1/2] update tooling. fix tests and phpunit notices. remove deprecations. --- Docs/Documentation/Authentication.md | 15 ++- composer.json | 5 +- config/auth.php | 19 +-- phpstan.neon | 27 +++- src/AuthPlugin.php | 45 +++++++ src/Authentication/AuthenticationService.php | 4 +- .../TwoFactorProcessor/U2FProcessor.php | 121 ++++++++++++++++++ .../OneTimeTokenAuthenticator.php | 1 + src/Authenticator/SocialAuthenticator.php | 2 +- src/Authenticator/TwoFactorAuthenticator.php | 2 +- .../OneTimePasswordAuthenticatorComponent.php | 15 ++- src/Exception/InvalidProviderException.php | 2 +- src/Exception/InvalidSettingsException.php | 2 +- src/Middleware/RbacMiddleware.php | 9 +- src/Plugin.php | 2 +- src/Policy/SuperuserPolicy.php | 20 ++- src/Rbac/Rbac.php | 42 +++++- src/Rbac/Rules/Owner.php | 4 +- src/Social/Mapper/Github.php | 2 - src/Social/Service/OAuth2Service.php | 2 +- src/Test/BaseTestTrait.php | 2 +- src/Traits/IsAuthorizedTrait.php | 2 +- src/Traits/ReCaptchaTrait.php | 2 +- .../AuthenticationServiceTest.php | 74 +++++------ tests/TestCase/Authentication/FailureTest.php | 2 +- .../Authenticator/CookieAuthenticatorTest.php | 4 +- .../Authenticator/FormAuthenticatorTest.php | 54 ++++---- .../Authenticator/SocialAuthenticatorTest.php | 32 ++--- .../TwoFactorAuthenticatorTest.php | 4 +- ...TimePasswordAuthenticatorComponentTest.php | 6 +- .../Middleware/SocialAuthMiddlewareTest.php | 6 +- .../Middleware/TwoFactorMiddlewareTest.php | 30 +++-- .../TestCase/Policy/CollectionPolicyTest.php | 48 +++---- tests/TestCase/Policy/RbacPolicyTest.php | 18 +-- tests/TestCase/Policy/SuperuserPolicyTest.php | 4 +- tests/TestCase/Rbac/RbacTest.php | 2 + tests/TestCase/Social/MapUserTest.php | 14 +- .../Social/Service/OAuth1ServiceTest.php | 16 ++- .../Social/Service/OAuth2ServiceTest.php | 14 +- .../TestCase/Traits/IsAuthorizedTraitTest.php | 33 ++--- .../Traits/StubAuthorizedController.php | 36 ++++++ 41 files changed, 512 insertions(+), 232 deletions(-) create mode 100644 src/AuthPlugin.php create mode 100644 src/Authentication/TwoFactorProcessor/U2FProcessor.php create mode 100644 tests/TestCase/Traits/StubAuthorizedController.php diff --git a/Docs/Documentation/Authentication.md b/Docs/Documentation/Authentication.md index f7ddecd..100c426 100644 --- a/Docs/Documentation/Authentication.md +++ b/Docs/Documentation/Authentication.md @@ -66,16 +66,21 @@ class Application extends BaseApplication implements AuthenticationServiceProvid 'password' => 'password' ]; - // Load identifiers - $service->loadIdentifier('Authentication.Password', compact('fields')); - // Load the authenticators, you want session first $service->loadAuthenticator('Authentication.Session', [ - 'skipTwoFactorVerify' => true + 'skipTwoFactorVerify' => true, + 'identifier' => [ + 'className' => 'Authentication.Password', + 'fields' => $fields, + ], ]); $service->loadAuthenticator('Authentication.Form', [ 'fields' => $fields, - 'loginUrl' => '/users/login' + 'loginUrl' => '/users/login', + 'identifier' => [ + 'className' => 'Authentication.Password', + 'fields' => $fields, + ], ]); return $service; diff --git a/composer.json b/composer.json index ce92741..e0ed7e2 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,8 @@ "cakephp/cakephp": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^10.0", + "phpstan/phpstan": "^2.2.2,<2.2.6", + "phpunit/phpunit": "^10.5 || ^11.1 || ^12.5", "endroid/qr-code": "^6.0 || ^5.0", "league/oauth2-facebook": "@stable", "league/oauth2-instagram": "@stable", @@ -70,7 +71,7 @@ "cs-check": "phpcs -n -p --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests", "cs-fix": "phpcbf --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests", "test": "phpunit --stderr", - "stan": "phpstan analyse src/", + "stan": "vendor/bin/phpstan analyse src/", "psalm": "php vendor/psalm/phar/psalm.phar --show-info=false src/ ", "stan-setup": "cp composer.json composer.backup && composer require --dev phpstan/phpstan:^1.9.0 psalm/phar:^5.1.0 && mv composer.backup composer.json", "coverage-test": "phpunit --stderr --coverage-clover=clover.xml" diff --git a/config/auth.php b/config/auth.php index 2dafc60..cf2ec4e 100644 --- a/config/auth.php +++ b/config/auth.php @@ -23,7 +23,7 @@ 'redirectUri' => Router::fullBaseUrl() . '/auth/facebook', 'linkSocialUri' => Router::fullBaseUrl() . '/link-social/facebook', 'callbackLinkSocialUri' => Router::fullBaseUrl() . '/callback-link-social/facebook', - ] + ], ], 'twitter' => [ 'service' => 'CakeDC\Auth\Social\Service\OAuth1Service', @@ -43,7 +43,7 @@ 'redirectUri' => Router::fullBaseUrl() . '/auth/linkedIn', 'linkSocialUri' => Router::fullBaseUrl() . '/link-social/linkedIn', 'callbackLinkSocialUri' => Router::fullBaseUrl() . '/callback-link-social/linkedIn', - ] + ], ], 'instagram' => [ 'service' => 'CakeDC\Auth\Social\Service\OAuth2Service', @@ -64,7 +64,7 @@ 'redirectUri' => Router::fullBaseUrl() . '/auth/google', 'linkSocialUri' => Router::fullBaseUrl() . '/link-social/google', 'callbackLinkSocialUri' => Router::fullBaseUrl() . '/callback-link-social/google', - ] + ], ], 'amazon' => [ 'service' => 'CakeDC\Auth\Social\Service\OAuth2Service', @@ -74,7 +74,7 @@ 'redirectUri' => Router::fullBaseUrl() . '/auth/amazon', 'linkSocialUri' => Router::fullBaseUrl() . '/link-social/amazon', 'callbackLinkSocialUri' => Router::fullBaseUrl() . '/callback-link-social/amazon', - ] + ], ], 'azure' => [ 'service' => 'CakeDC\Auth\Social\Service\OAuth2Service', @@ -84,7 +84,7 @@ 'redirectUri' => Router::fullBaseUrl() . '/auth/azure', 'linkSocialUri' => Router::fullBaseUrl() . '/link-social/azure', 'callbackLinkSocialUri' => Router::fullBaseUrl() . '/callback-link-social/azure', - ] + ], ], ], 'TwoFactorProcessors' => [ @@ -110,7 +110,7 @@ // QR-code provider (more on this later) 'qrcodeprovider' => new \RobThree\Auth\Providers\Qr\EndroidQrCodeProvider(), // Random Number Generator provider (more on this later) - 'rngprovider' => null + 'rngprovider' => null, ], 'Webauthn2fa' => [ 'enabled' => false, @@ -122,6 +122,9 @@ 'controller' => 'Users', 'action' => 'webauthn2fa', 'prefix' => false, - ] - ] + ], + ], + 'CakeDC/Auth' => [ + 'emitEvents' => false, + ], ]; diff --git a/phpstan.neon b/phpstan.neon index 2d0c7d7..9438ba6 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,7 +1,30 @@ parameters: level: 6 - checkMissingIterableValueType: false - checkGenericClassInNonGenericObjectType: false treatPhpDocTypesAsCertain: false bootstrapFiles: - tests/bootstrap.php + ignoreErrors: + - + identifier: new.static + reportUnmatched: false + - + identifier: missingType.iterableValue + reportUnmatched: false + - + identifier: missingType.generics + reportUnmatched: false + - + # Public library trait + identifier: trait.unused + path: src/Traits/IsAuthorizedTrait.php + reportUnmatched: false + - + # Testsuite library trait + identifier: trait.unused + path: src/Test/BaseTestTrait.php + reportUnmatched: false + - + # U2F deprecated - classes removed but processor retained + identifier: class.notFound + path: src/Authentication/TwoFactorProcessor/U2FProcessor.php + reportUnmatched: false diff --git a/src/AuthPlugin.php b/src/AuthPlugin.php new file mode 100644 index 0000000..761a5ec --- /dev/null +++ b/src/AuthPlugin.php @@ -0,0 +1,45 @@ +scope('/auth', function ($routes) use ($oauthPath): void { + $routes->connect( + '/:provider', + $oauthPath, + ['provider' => implode('|', array_keys(Configure::read('OAuth.providers')))], + ); + }); + } + } +} diff --git a/src/Authentication/AuthenticationService.php b/src/Authentication/AuthenticationService.php index bfa4d70..53f5763 100644 --- a/src/Authentication/AuthenticationService.php +++ b/src/Authentication/AuthenticationService.php @@ -14,6 +14,7 @@ namespace CakeDC\Auth\Authentication; use Authentication\AuthenticationService as BaseService; +use Authentication\Authenticator\Result; use Authentication\Authenticator\ResultInterface; use Authentication\Authenticator\StatelessInterface; use Cake\Datasource\EntityInterface; @@ -62,7 +63,7 @@ public function authenticate(ServerRequestInterface $request): ResultInterface { if ($this->authenticators()->isEmpty()) { throw new RuntimeException( - 'No authenticators loaded. You need to load at least one authenticator.' + 'No authenticators loaded. You need to load at least one authenticator.', ); } @@ -71,6 +72,7 @@ public function authenticate(ServerRequestInterface $request): ResultInterface foreach ($this->authenticators() as $authenticator) { $result = $authenticator->authenticate($request); if ($result->isValid()) { + /** @phpstan-ignore method.notFound */ $skipTwoFactorVerify = $authenticator->getConfig('skipTwoFactorVerify'); $userData = $result->getData(); if ($userData instanceof EntityInterface) { diff --git a/src/Authentication/TwoFactorProcessor/U2FProcessor.php b/src/Authentication/TwoFactorProcessor/U2FProcessor.php new file mode 100644 index 0000000..77c95fa --- /dev/null +++ b/src/Authentication/TwoFactorProcessor/U2FProcessor.php @@ -0,0 +1,121 @@ +getU2fAuthenticationChecker()->isRequired($userData); + } + + /** + * Proceed to 2fa processor after a valid result result. + * + * @param \Psr\Http\Message\ServerRequestInterface $request Request instance. + * @param \Authentication\Authenticator\ResultInterface $result Input result object. + * @return \Authentication\Authenticator\ResultInterface + */ + public function proceed(ServerRequestInterface $request, ResultInterface $result): ResultInterface + { + /** + * @var \Cake\Http\Session $session + */ + $session = $request->getAttribute('session'); + $session->write($this->getSessionKey(), $result->getData()); + $result = new Result(null, $this->getType()); + + return $result; + } + + /** + * Generates 2fa url, if enable. + * + * @param string $type Processor type. + * @return array|null + */ + public function getUrlByType(string $type): ?array + { + if ($type == $this->getType()) { + return Configure::read('U2f.startAction'); + } + + return null; + } + + /** + * Get the configured u2f authentication checker + * + * @return \CakeDC\Auth\Authentication\U2fAuthenticationCheckerInterface + */ + protected function getU2fAuthenticationChecker(): U2fAuthenticationCheckerInterface + { + return (new U2fAuthenticationCheckerFactory())->build(); + } +} diff --git a/src/Authenticator/OneTimeTokenAuthenticator.php b/src/Authenticator/OneTimeTokenAuthenticator.php index 1813862..21c842a 100644 --- a/src/Authenticator/OneTimeTokenAuthenticator.php +++ b/src/Authenticator/OneTimeTokenAuthenticator.php @@ -30,6 +30,7 @@ public function authenticate(ServerRequestInterface $request): ResultInterface $usersTable = TableRegistry::getTableLocator()->get(Configure::read('Users.table')); + /** @phpstan-ignore method.notFound */ $user = $usersTable->loginWithToken($token); if (!$user) { diff --git a/src/Authenticator/SocialAuthenticator.php b/src/Authenticator/SocialAuthenticator.php index 42eabbf..d03ea4f 100644 --- a/src/Authenticator/SocialAuthenticator.php +++ b/src/Authenticator/SocialAuthenticator.php @@ -108,7 +108,7 @@ private function getRawData(ServerRequestInterface $request, ServiceInterface $s $message = sprintf( "Error getting an access token / retrieving the authorized user's profile data. Error message: %s %s", $exception->getMessage(), - (string)$exception + (string)$exception, ); $this->log($message); diff --git a/src/Authenticator/TwoFactorAuthenticator.php b/src/Authenticator/TwoFactorAuthenticator.php index cd8d172..eafbfb9 100644 --- a/src/Authenticator/TwoFactorAuthenticator.php +++ b/src/Authenticator/TwoFactorAuthenticator.php @@ -55,7 +55,7 @@ protected function _buildLoginUrlErrorResult(ServerRequestInterface $request): R sprintf( 'Login URL `%s` did not match `%s`.', (string)$request->getUri(), - implode('` or `', (array)$this->getConfig('loginUrl')) + implode('` or `', (array)$this->getConfig('loginUrl')), ), ]; diff --git a/src/Controller/Component/OneTimePasswordAuthenticatorComponent.php b/src/Controller/Component/OneTimePasswordAuthenticatorComponent.php index cfa5e6e..0c8c4dd 100644 --- a/src/Controller/Component/OneTimePasswordAuthenticatorComponent.php +++ b/src/Controller/Component/OneTimePasswordAuthenticatorComponent.php @@ -16,6 +16,8 @@ use Cake\Controller\Component; use Cake\Core\Configure; use RobThree\Auth\TwoFactorAuth; +use RuntimeException; +use Throwable; /** * OneTimePasswordAuthenticator Component. @@ -47,17 +49,18 @@ public function initialize(array $config): void digits: Configure::read('OneTimePasswordAuthenticator.digits'), period: Configure::read('OneTimePasswordAuthenticator.period'), algorithm: Configure::read('OneTimePasswordAuthenticator.algorithm'), - rngprovider: Configure::read('OneTimePasswordAuthenticator.rngprovider') + rngprovider: Configure::read('OneTimePasswordAuthenticator.rngprovider'), ); - } catch (\Throwable $t) { - throw new \RuntimeException( - message:__d( + } catch (Throwable $t) { + throw new RuntimeException( + message: __d( 'cake_d_c/users', 'An error has occurred configuring OneTimePasswordAuthenticator. ' . 'Please ensure you have installed robthree/twofactorauth and endroid/qr-code ' . - '(or your preferred QR provider): {0}', $t->getMessage() + '(or your preferred QR provider): {0}', + $t->getMessage(), ), - previous: $t + previous: $t, ); } } diff --git a/src/Exception/InvalidProviderException.php b/src/Exception/InvalidProviderException.php index b6d50de..ed0d1c7 100644 --- a/src/Exception/InvalidProviderException.php +++ b/src/Exception/InvalidProviderException.php @@ -32,7 +32,7 @@ class InvalidProviderException extends CakeException * @param int $code code * @param null $previous previous */ - public function __construct(array|string $message, int $code = 500, $previous = null) + public function __construct(array|string $message, int $code = 500, null $previous = null) { parent::__construct($message, $code, $previous); } diff --git a/src/Exception/InvalidSettingsException.php b/src/Exception/InvalidSettingsException.php index dab28d3..e66757d 100644 --- a/src/Exception/InvalidSettingsException.php +++ b/src/Exception/InvalidSettingsException.php @@ -32,7 +32,7 @@ class InvalidSettingsException extends CakeException * @param int $code code * @param null $previous previous */ - public function __construct(array|string $message, int $code = 500, $previous = null) + public function __construct(array|string $message, int $code = 500, null $previous = null) { parent::__construct($message, $code, $previous); } diff --git a/src/Middleware/RbacMiddleware.php b/src/Middleware/RbacMiddleware.php index 2fae0d3..56ceb6f 100644 --- a/src/Middleware/RbacMiddleware.php +++ b/src/Middleware/RbacMiddleware.php @@ -57,9 +57,6 @@ * 'password' => 'password' * ]; * - * // Load identifiers - * $service->loadIdentifier('Authentication.Password', compact('fields')); - * * // Load the authenticators, you want session first * $service->loadAuthenticator('Authentication.Session'); * $service->loadAuthenticator('Authentication.Form', [ @@ -68,7 +65,11 @@ * 'plugin' => 'CakeDC/Users', * 'controller' => 'Users', * 'action' => 'login', - * ] + * ], + * 'identifier' => [ + * 'className' => 'Authentication.Password', + * 'fields' => $fields, + * ], * ]); * * // Add it to the authentication middleware diff --git a/src/Plugin.php b/src/Plugin.php index f50660a..8210531 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -34,7 +34,7 @@ public function routes(RouteBuilder $routes): void $routes->connect( '/:provider', $oauthPath, - ['provider' => implode('|', array_keys(Configure::read('OAuth.providers')))] + ['provider' => implode('|', array_keys(Configure::read('OAuth.providers')))], ); }); } diff --git a/src/Policy/SuperuserPolicy.php b/src/Policy/SuperuserPolicy.php index 2755a64..0598ac7 100644 --- a/src/Policy/SuperuserPolicy.php +++ b/src/Policy/SuperuserPolicy.php @@ -13,7 +13,10 @@ namespace CakeDC\Auth\Policy; use Authorization\IdentityInterface; +use Cake\Core\Configure; use Cake\Core\InstanceConfigTrait; +use Cake\Event\Event; +use Cake\Event\EventManager; use Psr\Http\Message\ServerRequestInterface; /** @@ -58,7 +61,22 @@ public function canAccess(?IdentityInterface $identity, ServerRequestInterface $ $superuserField = $this->getConfig('superuser_field'); $isSuperUser = $user[$superuserField] ?? false; + $allowed = $isSuperUser === true; + $emitEvents = Configure::read('CakeDC/Auth.emitEvents', false); - return $isSuperUser === true; + if ($allowed && $emitEvents) { + $role = is_array($user) ? ($user['role'] ?? null) : null; + EventManager::instance()->dispatch(new Event('Auth.Authorization.checked', $this, [ + 'allowed' => true, + 'policy' => static::class, + 'role' => is_string($role) ? $role : null, + 'user' => $user, + 'request' => $resource, + 'permission' => null, + 'reason' => 'SuperuserPolicy allowed', + ])); + } + + return $allowed; } } diff --git a/src/Rbac/Rbac.php b/src/Rbac/Rbac.php index d65eebf..635f8ed 100644 --- a/src/Rbac/Rbac.php +++ b/src/Rbac/Rbac.php @@ -16,6 +16,8 @@ use ArrayAccess; use Cake\Core\Configure; use Cake\Core\InstanceConfigTrait; +use Cake\Event\Event; +use Cake\Event\EventManager; use Cake\Log\LogTrait; use Cake\Utility\Hash; use Cake\Utility\Inflector; @@ -61,6 +63,13 @@ class Rbac implements RbacInterface */ protected array $permissions; + /** + * Event emitor flag + * + * @var bool + */ + protected bool $emitEvents = false; + /** * Rbac constructor. * @@ -68,6 +77,7 @@ class Rbac implements RbacInterface */ public function __construct(array $config = []) { + $this->emitEvents = Configure::read('CakeDC/Auth.emitEvents', false); if (!isset($config['log'])) { $config['log'] = Configure::read('debug'); } @@ -125,10 +135,34 @@ public function checkPermissions(array|ArrayAccess $user, ServerRequestInterface $this->log($matchResult->getReason(), LogLevel::DEBUG); } + if ($this->emitEvents) { + EventManager::instance()->dispatch(new Event('Auth.Rbac.checked', $this, [ + 'allowed' => $matchResult->isAllowed(), + 'role' => $role, + 'user' => $user, + 'request' => $request, + 'permission' => $permission, + 'result' => $matchResult, + 'reason' => $matchResult->getReason(), + ])); + } + return $matchResult->isAllowed(); } } + if ($this->emitEvents) { + EventManager::instance()->dispatch(new Event('Auth.Rbac.checked', $this, [ + 'allowed' => false, + 'role' => $role, + 'user' => $user, + 'request' => $request, + 'permission' => null, + 'result' => null, + 'reason' => 'No permission rule matched', + ])); + } + return false; } @@ -209,7 +243,7 @@ protected function _matchPermission(array $permission, array|ArrayAccess $user, 'For %s --> Rule matched %s with result = %s', json_encode($reserved), json_encode($permission), - $return + $return, ); return new PermissionMatchResult($return, $reason); @@ -235,9 +269,9 @@ protected function _matchOrAsterisk(mixed $possibleValues, mixed $value, bool $a $possibleArray = (array)$possibleValues; return $possibleValues === '*' || - $value === $possibleValues || - in_array($value, $possibleArray) || - in_array(Inflector::camelize((string)$value, '-'), $possibleArray); + $value === $possibleValues || + in_array($value, $possibleArray) || + in_array(Inflector::camelize((string)$value, '-'), $possibleArray); } /** diff --git a/src/Rbac/Rules/Owner.php b/src/Rbac/Rules/Owner.php index f52006f..c5772a3 100644 --- a/src/Rbac/Rules/Owner.php +++ b/src/Rbac/Rules/Owner.php @@ -79,7 +79,7 @@ public function allowed(array|ArrayAccess $user, string $role, ServerRequestInte 'Missing column %s in table %s while checking ownership permissions for user %s', $this->getConfig('ownerForeignKey'), $table->getAlias(), - $userId + $userId, ); throw new OutOfBoundsException($msg); } @@ -88,7 +88,7 @@ public function allowed(array|ArrayAccess $user, string $role, ServerRequestInte 'Missing column %s in table %s while checking ownership permissions for user %s', $this->getConfig('ownerForeignKey'), $table->getAlias(), - $userId + $userId, ); throw new OutOfBoundsException($msg, $ex->getCode(), $ex); } diff --git a/src/Social/Mapper/Github.php b/src/Social/Mapper/Github.php index 4fe5f9a..ed27d63 100644 --- a/src/Social/Mapper/Github.php +++ b/src/Social/Mapper/Github.php @@ -13,8 +13,6 @@ namespace CakeDC\Auth\Social\Mapper; -use Cake\Utility\Hash; - /** * Github Mapper */ diff --git a/src/Social/Service/OAuth2Service.php b/src/Social/Service/OAuth2Service.php index fe07669..aa090c2 100644 --- a/src/Social/Service/OAuth2Service.php +++ b/src/Social/Service/OAuth2Service.php @@ -69,7 +69,7 @@ public function getAuthorizationUrl(ServerRequestInterface $request): string } return $this->provider->getAuthorizationUrl( - $this->getConfig('authParams', []) + $this->getConfig('authParams', []), ); } diff --git a/src/Test/BaseTestTrait.php b/src/Test/BaseTestTrait.php index db34747..1503aa3 100644 --- a/src/Test/BaseTestTrait.php +++ b/src/Test/BaseTestTrait.php @@ -64,7 +64,7 @@ protected function _testPermissions(string $url, string $username, string $metho $method, $ajax, $responseCode, - $responseContains + $responseContains, ), 0); } $this->loginAsUserName($username); diff --git a/src/Traits/IsAuthorizedTrait.php b/src/Traits/IsAuthorizedTrait.php index 3b47c47..865311d 100644 --- a/src/Traits/IsAuthorizedTrait.php +++ b/src/Traits/IsAuthorizedTrait.php @@ -81,7 +81,7 @@ protected function _createUrlRequestToCheck(string $url): ServerRequest return $targetRequest->withAttribute( 'rbac', - $this->getRequest()->getAttribute('rbac') + $this->getRequest()->getAttribute('rbac'), ); } } diff --git a/src/Traits/ReCaptchaTrait.php b/src/Traits/ReCaptchaTrait.php index be4bba8..007033b 100644 --- a/src/Traits/ReCaptchaTrait.php +++ b/src/Traits/ReCaptchaTrait.php @@ -40,7 +40,7 @@ public function validateReCaptchaFromRequest(ServerRequestInterface $request): b return $this->validateReCaptcha( $captcha, - $request->clientIp() + $request->clientIp(), ); } diff --git a/tests/TestCase/Authentication/AuthenticationServiceTest.php b/tests/TestCase/Authentication/AuthenticationServiceTest.php index 004aa50..20b187a 100644 --- a/tests/TestCase/Authentication/AuthenticationServiceTest.php +++ b/tests/TestCase/Authentication/AuthenticationServiceTest.php @@ -47,14 +47,11 @@ public function testAuthenticateEmptyAuthenticators() $request = ServerRequestFactory::fromGlobals( ['REQUEST_URI' => '/testpath'], [], - ['username' => 'user-not-found', 'password' => 'password'] + ['username' => 'user-not-found', 'password' => 'password'], ); $service = new AuthenticationService([ 'processors' => [], - 'identifiers' => [ - 'Authentication.Password', - ], 'authenticators' => [], ]); $this->expectException(RuntimeException::class); @@ -77,17 +74,18 @@ public function testAuthenticateFail() $request = ServerRequestFactory::fromGlobals( ['REQUEST_URI' => '/testpath'], [], - ['username' => 'user-not-found', 'password' => 'password'] + ['username' => 'user-not-found', 'password' => 'password'], ); $service = new AuthenticationService([ 'processors' => [], - 'identifiers' => [ - 'Authentication.Password', - ], 'authenticators' => [ - 'Authentication.Session', - 'CakeDC/Auth.Form', + 'Authentication.Session' => [ + 'identifier' => 'Authentication.Password', + ], + 'CakeDC/Auth.Form' => [ + 'identifier' => 'Authentication.Password', + ], ], ]); @@ -100,13 +98,11 @@ public function testAuthenticateFail() $sessionFailure = new Failure( $service->authenticators()->get('Session'), - new Result(null, Result::FAILURE_IDENTITY_NOT_FOUND) + new Result(null, Result::FAILURE_IDENTITY_NOT_FOUND), ); $formFailure = new Failure( $service->authenticators()->get('Form'), - new Result(null, Result::FAILURE_IDENTITY_NOT_FOUND, [ - 'Password' => [], - ]) + new Result(null, Result::FAILURE_IDENTITY_NOT_FOUND), ); $expected = [$sessionFailure, $formFailure]; $actual = $service->getFailures(); @@ -130,7 +126,7 @@ public function testAuthenticate() $request = ServerRequestFactory::fromGlobals( ['REQUEST_URI' => '/testpath'], [], - ['username' => 'user-1', 'password' => 'password'] + ['username' => 'user-1', 'password' => 'password'], ); $service = new AuthenticationService([ @@ -138,12 +134,13 @@ public function testAuthenticate() new OneTimePasswordProcessor(), new Webauthn2faProcessor(), ], - 'identifiers' => [ - 'Authentication.Password', - ], 'authenticators' => [ - 'Authentication.Session', - 'CakeDC/Auth.Form', + 'Authentication.Session' => [ + 'identifier' => 'Authentication.Password', + ], + 'CakeDC/Auth.Form' => [ + 'identifier' => 'Authentication.Password', + ], ], ]); @@ -152,14 +149,14 @@ public function testAuthenticate() $this->assertTrue($result->isValid()); $this->assertEquals( 'user-1', - $result->getData()['username'] + $result->getData()['username'], ); $provider = $service->getAuthenticationProvider(); $this->assertInstanceOf(FormAuthenticator::class, $provider); $sessionFailure = new Failure( $service->authenticators()->get('Session'), - new Result(null, Result::FAILURE_IDENTITY_NOT_FOUND) + new Result(null, Result::FAILURE_IDENTITY_NOT_FOUND), ); $expected = [$sessionFailure]; $actual = $service->getFailures(); @@ -183,7 +180,7 @@ public function testAuthenticateShouldDoGoogleVerifyEnabled() $request = ServerRequestFactory::fromGlobals( ['REQUEST_URI' => '/testpath'], [], - ['username' => 'user-1', 'password' => 'password'] + ['username' => 'user-1', 'password' => 'password'], ); $service = new AuthenticationService([ @@ -191,15 +188,14 @@ public function testAuthenticateShouldDoGoogleVerifyEnabled() new OneTimePasswordProcessor(), new Webauthn2faProcessor(), ], - 'identifiers' => [ - 'Authentication.Password' => [], - ], 'authenticators' => [ 'Authentication.Session' => [ 'skipTwoFactorVerify' => true, + 'identifier' => 'Authentication.Password', ], 'CakeDC/Auth.Form' => [ 'skipTwoFactorVerify' => false, + 'identifier' => 'Authentication.Password', ], ], ]); @@ -211,11 +207,11 @@ public function testAuthenticateShouldDoGoogleVerifyEnabled() $this->assertNull($result->getData()); $this->assertEquals( 'user-1', - $request->getAttribute('session')->read('temporarySession.username') + $request->getAttribute('session')->read('temporarySession.username'), ); $sessionFailure = new Failure( $service->authenticators()->get('Session'), - new Result(null, Result::FAILURE_IDENTITY_NOT_FOUND) + new Result(null, Result::FAILURE_IDENTITY_NOT_FOUND), ); $expected = [$sessionFailure]; $actual = $service->getFailures(); @@ -239,7 +235,7 @@ public function testAuthenticateShouldDoGoogleVerifyDisabled() $request = ServerRequestFactory::fromGlobals( ['REQUEST_URI' => '/testpath'], [], - ['username' => 'user-1', 'password' => 'password'] + ['username' => 'user-1', 'password' => 'password'], ); $service = new AuthenticationService([ @@ -247,15 +243,14 @@ public function testAuthenticateShouldDoGoogleVerifyDisabled() new OneTimePasswordProcessor(), new Webauthn2faProcessor(), ], - 'identifiers' => [ - 'Authentication.Password' => [], - ], 'authenticators' => [ 'Authentication.Session' => [ 'skipTwoFactorVerify' => true, + 'identifier' => 'Authentication.Password', ], 'CakeDC/Auth.Form' => [ 'skipTwoFactorVerify' => false, + 'identifier' => 'Authentication.Password', ], ], ]); @@ -265,14 +260,14 @@ public function testAuthenticateShouldDoGoogleVerifyDisabled() $this->assertTrue($result->isValid()); $this->assertEquals( 'user-1', - $result->getData()['username'] + $result->getData()['username'], ); $result = $service->getAuthenticationProvider(); $this->assertInstanceOf(FormAuthenticator::class, $result); $sessionFailure = new Failure( $service->authenticators()->get('Session'), - new Result(null, Result::FAILURE_IDENTITY_NOT_FOUND) + new Result(null, Result::FAILURE_IDENTITY_NOT_FOUND), ); $expected = [$sessionFailure]; $actual = $service->getFailures(); @@ -295,7 +290,7 @@ public function testAuthenticateShouldDoWebauthn2faEnabled() $request = ServerRequestFactory::fromGlobals( ['REQUEST_URI' => '/testpath'], [], - ['username' => 'user-1', 'password' => 'password'] + ['username' => 'user-1', 'password' => 'password'], ); $service = new AuthenticationService([ @@ -303,15 +298,14 @@ public function testAuthenticateShouldDoWebauthn2faEnabled() new OneTimePasswordProcessor(), new Webauthn2faProcessor(), ], - 'identifiers' => [ - 'Authentication.Password' => [], - ], 'authenticators' => [ 'Authentication.Session' => [ 'skipTwoFactorVerify' => true, + 'identifier' => 'Authentication.Password', ], 'CakeDC/Auth.Form' => [ 'skipTwoFactorVerify' => false, + 'identifier' => 'Authentication.Password', ], ], ]); @@ -323,11 +317,11 @@ public function testAuthenticateShouldDoWebauthn2faEnabled() $this->assertNull($request->getAttribute('session')->read('Auth.username')); $this->assertEquals( 'user-1', - $request->getAttribute('session')->read('Webauthn2fa.User.username') + $request->getAttribute('session')->read('Webauthn2fa.User.username'), ); $sessionFailure = new Failure( $service->authenticators()->get('Session'), - new Result(null, Result::FAILURE_IDENTITY_NOT_FOUND) + new Result(null, Result::FAILURE_IDENTITY_NOT_FOUND), ); $expected = [$sessionFailure]; $actual = $service->getFailures(); diff --git a/tests/TestCase/Authentication/FailureTest.php b/tests/TestCase/Authentication/FailureTest.php index fa99f6f..8fab781 100644 --- a/tests/TestCase/Authentication/FailureTest.php +++ b/tests/TestCase/Authentication/FailureTest.php @@ -30,7 +30,7 @@ public function testGetters() $authenticator = new FormAuthenticator(new IdentifierCollection([])); $result = new Result( ['id' => '10', 'username' => 'johndoe'], - Result::SUCCESS + Result::SUCCESS, ); $failure = new Failure($authenticator, $result); $this->assertSame($authenticator, $failure->getAuthenticator()); diff --git a/tests/TestCase/Authenticator/CookieAuthenticatorTest.php b/tests/TestCase/Authenticator/CookieAuthenticatorTest.php index e2c1b34..d0c02f7 100644 --- a/tests/TestCase/Authenticator/CookieAuthenticatorTest.php +++ b/tests/TestCase/Authenticator/CookieAuthenticatorTest.php @@ -20,6 +20,7 @@ use Cake\Utility\Security; use CakeDC\Auth\Authenticator\CookieAuthenticator; use Laminas\Diactoros\Uri; +use PHPUnit\Framework\Attributes\DataProvider; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; @@ -53,6 +54,7 @@ public static function dataProviderPersistIdentity() * @dataProvider dataProviderPersistIdentity * @return void */ + #[DataProvider('dataProviderPersistIdentity')] public function testPersistIdentity($setCookie, $field, array $post, array $session) { $identifiers = new IdentifierCollection([ @@ -81,7 +83,7 @@ public function testPersistIdentity($setCookie, $field, array $post, array $sess $this->assertInstanceOf(RequestInterface::class, $result['request']); $this->assertInstanceOf(ResponseInterface::class, $result['response']); if ($setCookie) { - $this->assertStringContainsString('CookieAuth=%5B%22johndoe%22%2C%22%242y%24', $result['response']->getHeaderLine('Set-Cookie')); + $this->assertStringContainsString('CookieAuth=%5B%22johndoe%22%2C', $result['response']->getHeaderLine('Set-Cookie')); } else { $this->assertStringNotContainsString('CookieAuth', $result['response']->getHeaderLine('Set-Cookie')); } diff --git a/tests/TestCase/Authenticator/FormAuthenticatorTest.php b/tests/TestCase/Authenticator/FormAuthenticatorTest.php index 9e30092..e6ed09c 100644 --- a/tests/TestCase/Authenticator/FormAuthenticatorTest.php +++ b/tests/TestCase/Authenticator/FormAuthenticatorTest.php @@ -42,17 +42,17 @@ public function testAuthenticateBaseFailed() $request = ServerRequestFactory::fromGlobals( ['REQUEST_URI' => '/testpath'], [], - ['username' => 'marcelo', 'password' => 'password', 'g-recaptcha-response' => 'BD-S2333-156465897897'] + ['username' => 'marcelo', 'password' => 'password', 'g-recaptcha-response' => 'BD-S2333-156465897897'], ); $baseResult = new Result( null, - Result::FAILURE_OTHER + Result::FAILURE_OTHER, ); $BaseAuthenticator->expects($this->once()) ->method('authenticate') ->with($request) - ->will($this->returnValue($baseResult)); + ->willReturn($baseResult); $Authenticator = $this->getMockBuilder(FormAuthenticator::class)->setConstructorArgs([ $identifiers, @@ -76,8 +76,8 @@ public function testAuthenticateBaseFailed() AbstractIdentifier::CREDENTIAL_PASSWORD => 'password', ], 'keyCheckEnabledRecaptcha' => 'Users.reCaptcha.login', - ]) - )->will($this->returnValue($BaseAuthenticator)); + ]), + )->willReturn($BaseAuthenticator); $Authenticator->expects($this->never()) ->method('validateReCaptcha'); @@ -106,7 +106,7 @@ public function testAuthenticate() $request = ServerRequestFactory::fromGlobals( ['REQUEST_URI' => '/testpath'], [], - ['username' => 'marcelo', 'password' => 'password', 'g-recaptcha-response' => 'BD-S2333-156465897897'] + ['username' => 'marcelo', 'password' => 'password', 'g-recaptcha-response' => 'BD-S2333-156465897897'], ); $baseResult = new Result( @@ -115,12 +115,12 @@ public function testAuthenticate() 'username' => 'marcelo', 'role' => 'user', ], - Result::SUCCESS + Result::SUCCESS, ); $BaseAuthenticator->expects($this->once()) ->method('authenticate') ->with($request) - ->will($this->returnValue($baseResult)); + ->willReturn($baseResult); $Authenticator = $this->getMockBuilder(FormAuthenticator::class)->setConstructorArgs([ $identifiers, @@ -144,15 +144,15 @@ public function testAuthenticate() AbstractIdentifier::CREDENTIAL_PASSWORD => 'password', ], 'keyCheckEnabledRecaptcha' => 'Users.reCaptcha.login', - ]) - )->will($this->returnValue($BaseAuthenticator)); + ]), + )->willReturn($BaseAuthenticator); $Authenticator->expects($this->once()) ->method('validateReCaptcha') ->with( - $this->equalTo('BD-S2333-156465897897') + $this->equalTo('BD-S2333-156465897897'), ) - ->will($this->returnValue(true)); + ->willReturn(true); $actualIdentifiers = $Authenticator->getIdentifier(); $this->assertInstanceOf(IdentifierCollection::class, $actualIdentifiers); $result = $Authenticator->authenticate($request); @@ -179,7 +179,7 @@ public function testAuthenticateNotRequiredReCaptcha() $request = ServerRequestFactory::fromGlobals( ['REQUEST_URI' => '/testpath'], [], - ['username' => 'marcelo', 'password' => 'password', 'g-recaptcha-response' => 'BD-S2333-156465897897'] + ['username' => 'marcelo', 'password' => 'password', 'g-recaptcha-response' => 'BD-S2333-156465897897'], ); $baseResult = new Result( @@ -188,12 +188,12 @@ public function testAuthenticateNotRequiredReCaptcha() 'username' => 'marcelo', 'role' => 'user', ], - Result::SUCCESS + Result::SUCCESS, ); $BaseAuthenticator->expects($this->once()) ->method('authenticate') ->with($request) - ->will($this->returnValue($baseResult)); + ->willReturn($baseResult); $Authenticator = $this->getMockBuilder(FormAuthenticator::class)->setConstructorArgs([ $identifiers, @@ -216,8 +216,8 @@ public function testAuthenticateNotRequiredReCaptcha() AbstractIdentifier::CREDENTIAL_PASSWORD => 'password', ], 'keyCheckEnabledRecaptcha' => 'Users.reCaptcha.login', - ]) - )->will($this->returnValue($BaseAuthenticator)); + ]), + )->willReturn($BaseAuthenticator); $Authenticator->expects($this->never()) ->method('validateReCaptcha'); @@ -246,7 +246,7 @@ public function testAuthenticateInvalidRecaptcha() $request = ServerRequestFactory::fromGlobals( ['REQUEST_URI' => '/testpath'], [], - ['username' => 'marcelo', 'password' => 'password', 'g-recaptcha-response' => 'BD-S2333-156465897897'] + ['username' => 'marcelo', 'password' => 'password', 'g-recaptcha-response' => 'BD-S2333-156465897897'], ); $baseResult = new Result( @@ -255,12 +255,12 @@ public function testAuthenticateInvalidRecaptcha() 'username' => 'marcelo', 'role' => 'user', ], - Result::SUCCESS + Result::SUCCESS, ); $BaseAuthenticator->expects($this->once()) ->method('authenticate') ->with($request) - ->will($this->returnValue($baseResult)); + ->willReturn($baseResult); $Authenticator = $this->getMockBuilder(FormAuthenticator::class)->setConstructorArgs([ $identifiers, @@ -283,15 +283,15 @@ public function testAuthenticateInvalidRecaptcha() AbstractIdentifier::CREDENTIAL_PASSWORD => 'password', ], 'keyCheckEnabledRecaptcha' => 'Users.reCaptcha.login', - ]) - )->will($this->returnValue($BaseAuthenticator)); + ]), + )->willReturn($BaseAuthenticator); $Authenticator->expects($this->once()) ->method('validateReCaptcha') ->with( - $this->equalTo('BD-S2333-156465897897') + $this->equalTo('BD-S2333-156465897897'), ) - ->will($this->returnValue(false)); + ->willReturn(false); $result = $Authenticator->authenticate($request); $this->assertInstanceOf(Result::class, $result); @@ -318,7 +318,7 @@ public function testGetBaseAuthenticator() AbstractIdentifier::CREDENTIAL_PASSWORD => 'password', ], 'keyCheckEnabledRecaptcha' => 'Users.reCaptcha.login', - ] + ], ); $actual = $Authenticator->getBaseAuthenticator(); $this->assertInstanceOf(CakeFormAuthenticator::class, $actual); @@ -354,7 +354,7 @@ public function testGetBaseAuthenticatorCustom() ], 'keyCheckEnabledRecaptcha' => 'Users.reCaptcha.login', 'baseClassName' => CakeFormAuthenticator::class, - ] + ], ); $actual = $Authenticator->getBaseAuthenticator(); $this->assertInstanceOf(CakeFormAuthenticator::class, $actual); @@ -390,7 +390,7 @@ public function testGetBaseAuthenticatorError() ], 'keyCheckEnabledRecaptcha' => 'Users.reCaptcha.login', 'baseClassName' => 'NotExistingAuthenticator', - ] + ], ); $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Base class for FormAuthenticator NotExistingAuthenticator does not exist'); diff --git a/tests/TestCase/Authenticator/SocialAuthenticatorTest.php b/tests/TestCase/Authenticator/SocialAuthenticatorTest.php index 51f519a..33b41f6 100644 --- a/tests/TestCase/Authenticator/SocialAuthenticatorTest.php +++ b/tests/TestCase/Authenticator/SocialAuthenticatorTest.php @@ -27,6 +27,7 @@ use League\OAuth2\Client\Provider\Facebook; use League\OAuth2\Client\Provider\FacebookUser; use League\OAuth2\Client\Token\AccessToken; +use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations; use UnexpectedValueException; /** @@ -34,6 +35,7 @@ * * @package CakeDC\Auth\Test\TestCase\Authenticator */ +#[AllowMockObjectsWithoutExpectations] class SocialAuthenticatorTest extends TestCase { public array $fixtures = [ @@ -205,16 +207,16 @@ public function testAuthenticateSuccessfullyAuthenticated() ->method('getAccessToken') ->with( $this->equalTo('authorization_code'), - $this->equalTo(['code' => 'ZPO9972j3092304230']) + $this->equalTo(['code' => 'ZPO9972j3092304230']), ) - ->will($this->returnValue($Token)); + ->willReturn($Token); $this->Provider->expects($this->any()) ->method('getResourceOwner') ->with( - $this->equalTo($Token) + $this->equalTo($Token), ) - ->will($this->returnValue($user)); + ->willReturn($user); $service = (new ServiceFactory())->createFromProvider('facebook'); $this->Request = $this->Request->withAttribute('socialService', $service); @@ -268,14 +270,14 @@ public function testAuthenticateGetRawDataNull() ->method('getAccessToken') ->with( $this->equalTo('authorization_code'), - $this->equalTo(['code' => 'ZPO9972j3092304230']) + $this->equalTo(['code' => 'ZPO9972j3092304230']), ) - ->will($this->returnValue($Token)); + ->willReturn($Token); $this->Provider->expects($this->any()) ->method('getResourceOwner') ->with( - $this->equalTo($Token) + $this->equalTo($Token), ) ->will($this->throwException(new UnexpectedValueException('User not found'))); @@ -328,17 +330,17 @@ public function testAuthenticateGetRawDataNotExpectedException() ->method('getAccessToken') ->with( $this->equalTo('authorization_code'), - $this->equalTo(['code' => 'ZPO9972j3092304230']) + $this->equalTo(['code' => 'ZPO9972j3092304230']), ) - ->will($this->returnValue($Token)); + ->willReturn($Token); $this->Provider->expects($this->any()) ->method('getResourceOwner') ->with( - $this->equalTo($Token) + $this->equalTo($Token), ) ->will($this->throwException( - new InvalidArgumentException('Invalid argument at getResourceOwner') + new InvalidArgumentException('Invalid argument at getResourceOwner'), )); $service = (new ServiceFactory())->createFromProvider('facebook'); @@ -420,16 +422,16 @@ public function testAuthenticateIdentifierReturnedNull() ->method('getAccessToken') ->with( $this->equalTo('authorization_code'), - $this->equalTo(['code' => 'ZPO9972j3092304230']) + $this->equalTo(['code' => 'ZPO9972j3092304230']), ) - ->will($this->returnValue($Token)); + ->willReturn($Token); $this->Provider->expects($this->any()) ->method('getResourceOwner') ->with( - $this->equalTo($Token) + $this->equalTo($Token), ) - ->will($this->returnValue($user)); + ->willReturn($user); $service = (new ServiceFactory())->createFromProvider('facebook'); $this->Request = $this->Request->withAttribute('socialService', $service); diff --git a/tests/TestCase/Authenticator/TwoFactorAuthenticatorTest.php b/tests/TestCase/Authenticator/TwoFactorAuthenticatorTest.php index 8f07227..ebe3507 100644 --- a/tests/TestCase/Authenticator/TwoFactorAuthenticatorTest.php +++ b/tests/TestCase/Authenticator/TwoFactorAuthenticatorTest.php @@ -61,7 +61,7 @@ public function testAuthenticateFailedInvalidUrl() 'id' => '42', 'username' => 'marcelo', 'role' => 'user', - ]) + ]), ); $identifiers = new IdentifierCollection([ 'Authentication.Password', @@ -91,7 +91,7 @@ public function testAuthenticate() 'id' => '42', 'username' => 'marcelo', 'role' => 'user', - ]) + ]), ); $identifiers = new IdentifierCollection([ 'Authentication.Password', diff --git a/tests/TestCase/Controller/Component/OneTimePasswordAuthenticatorComponentTest.php b/tests/TestCase/Controller/Component/OneTimePasswordAuthenticatorComponentTest.php index c951103..0f0990b 100644 --- a/tests/TestCase/Controller/Component/OneTimePasswordAuthenticatorComponentTest.php +++ b/tests/TestCase/Controller/Component/OneTimePasswordAuthenticatorComponentTest.php @@ -62,11 +62,7 @@ public function setUp(): void Configure::write('App.namespace', 'Users'); Configure::write('OneTimePasswordAuthenticator.login', true); - $this->request = $this->getMockBuilder(ServerRequest::class) - ->onlyMethods(['is']) - ->addMethods(['method']) - ->getMock(); - $this->request->expects($this->any())->method('is')->will($this->returnValue(true)); + $this->request = $this->createStub(ServerRequest::class); $this->Controller = new Controller($this->request); $this->Registry = $this->Controller->components(); $this->Controller->components()->set('OneTimePasswordAuthenticator', new OneTimePasswordAuthenticatorComponent($this->Registry)); diff --git a/tests/TestCase/Middleware/SocialAuthMiddlewareTest.php b/tests/TestCase/Middleware/SocialAuthMiddlewareTest.php index 5f8d378..5fb28ed 100644 --- a/tests/TestCase/Middleware/SocialAuthMiddlewareTest.php +++ b/tests/TestCase/Middleware/SocialAuthMiddlewareTest.php @@ -23,9 +23,11 @@ use CakeDC\Auth\Social\Service\OAuth2Service; use Laminas\Diactoros\Uri; use League\OAuth2\Client\Provider\Facebook; +use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +#[AllowMockObjectsWithoutExpectations] class SocialAuthMiddlewareTest extends TestCase { public array $fixtures = [ @@ -129,11 +131,11 @@ public function testProceedStepOne() $this->Provider->expects($this->any()) ->method('getState') - ->will($this->returnValue('_NEW_STATE_')); + ->willReturn('_NEW_STATE_'); $this->Provider->expects($this->any()) ->method('getAuthorizationUrl') - ->will($this->returnValue('http://facebook.com/redirect/url')); + ->willReturn('http://facebook.com/redirect/url'); $Middleware = new SocialAuthMiddleware([ 'urlChecker' => 'Authentication.Default', diff --git a/tests/TestCase/Middleware/TwoFactorMiddlewareTest.php b/tests/TestCase/Middleware/TwoFactorMiddlewareTest.php index 0052c3d..b50a300 100644 --- a/tests/TestCase/Middleware/TwoFactorMiddlewareTest.php +++ b/tests/TestCase/Middleware/TwoFactorMiddlewareTest.php @@ -23,12 +23,14 @@ use Cake\TestSuite\TestCase; use CakeDC\Auth\Authentication\AuthenticationService; use CakeDC\Auth\Middleware\TwoFactorMiddleware; +use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations; /** * Class OneTimePasswordAuthenticatorMiddlewareTest * * @package TestCase\Middleware */ +#[AllowMockObjectsWithoutExpectations] class TwoFactorMiddlewareTest extends TestCase { /** @@ -71,19 +73,20 @@ public function testInvokeNotNeeded() $service = $this->getMockBuilder(AuthenticationService::class)->setConstructorArgs([ [ - 'identifiers' => [ - 'Authentication.Password', - ], 'authenticators' => [ - 'Authentication.Session', - 'CakeDC/Auth.Form', + 'Authentication.Session' => [ + 'identifier' => 'Authentication.Password', + ], + 'CakeDC/Auth.Form' => [ + 'identifier' => 'Authentication.Password', + ], ], ], ])->onlyMethods(['getResult'])->getMock(); $result = new Result(['id' => 10, 'username' => 'johndoe'], Result::SUCCESS); $service->expects($this->any()) ->method('getResult') - ->will($this->returnValue($result)); + ->willReturn($result); $request = $request->withAttribute('authentication', $service); $middleware = $this->TwoFactorMiddleware; @@ -99,7 +102,7 @@ public function testInvokeNeedVerify() $request = ServerRequestFactory::fromGlobals( ['REQUEST_URI' => '/login'], [], - ['username' => 'user-1', 'password' => 'password', 'remember_me' => 1] + ['username' => 'user-1', 'password' => 'password', 'remember_me' => 1], ); $handler = $this->getMockBuilder(Runner::class) ->onlyMethods(['handle']) @@ -118,19 +121,20 @@ public function testInvokeNeedVerify() ]); $service = $this->getMockBuilder(AuthenticationService::class)->setConstructorArgs([ [ - 'identifiers' => [ - 'Authentication.Password', - ], 'authenticators' => [ - 'Authentication.Session', - 'CakeDC/Auth.Form', + 'Authentication.Session' => [ + 'identifier' => 'Authentication.Password', + ], + 'CakeDC/Auth.Form' => [ + 'identifier' => 'Authentication.Password', + ], ], ], ])->onlyMethods(['getResult'])->getMock(); $result = new Result(null, AuthenticationService::NEED_TWO_FACTOR_VERIFY); $service->expects($this->any()) ->method('getResult') - ->will($this->returnValue($result)); + ->willReturn($result); $request = $request->withAttribute('authentication', $service); $middleware = $this->TwoFactorMiddleware; diff --git a/tests/TestCase/Policy/CollectionPolicyTest.php b/tests/TestCase/Policy/CollectionPolicyTest.php index 6a032b7..31bc8b1 100644 --- a/tests/TestCase/Policy/CollectionPolicyTest.php +++ b/tests/TestCase/Policy/CollectionPolicyTest.php @@ -20,6 +20,7 @@ use CakeDC\Auth\Policy\CollectionPolicy; use CakeDC\Auth\Policy\RbacPolicy; use CakeDC\Auth\Policy\SuperuserPolicy; +use PHPUnit\Framework\Attributes\DataProvider; /** * Class CollectionPolicyTest @@ -33,34 +34,12 @@ class CollectionPolicyTest extends TestCase * * @return array */ - public function dataProviderCanAccess() + public static function dataProviderCanAccess() { - $rbacPolicy = function ($success) { - $Mock = $this->getMockBuilder(RbacPolicy::class) - ->onlyMethods(['canAccess']) - ->getMock(); - - $Mock->expects($this->once()) - ->method('canAccess') - ->will($this->returnValue($success)); - - return $Mock; - }; - $rbacPolicyNever = function () { - $Mock = $this->getMockBuilder(RbacPolicy::class) - ->onlyMethods(['canAccess']) - ->getMock(); - - $Mock->expects($this->never()) - ->method('canAccess'); - - return $Mock; - }; - return [ - [true, $rbacPolicyNever(), true], - [false, $rbacPolicy(false), false], - [false, $rbacPolicy(true), true], + [true, 'never', true], + [false, 'false', false], + [false, 'true', true], ]; } @@ -68,21 +47,32 @@ public function dataProviderCanAccess() * Test canAccess method * * @param bool $isSuperuser Is this a super user - * @param RbacPolicy $rbacPolicy Rbac policy instance + * @param string $rbacBehavior 'never', 'true', or 'false' * @param bool $expected The expected result; * @dataProvider dataProviderCanAccess * @return void */ - public function testCanAccess($isSuperuser, RbacPolicy $rbacPolicy, $expected) + #[DataProvider('dataProviderCanAccess')] + public function testCanAccess($isSuperuser, $rbacBehavior, $expected) { $user = new Entity([ 'id' => '00000000-0000-0000-0000-000000000001', 'is_superuser' => $isSuperuser, ]); - $service = $this->createMock(AuthorizationServiceInterface::class); + $service = $this->createStub(AuthorizationServiceInterface::class); $identity = new IdentityDecorator($service, $user); $request = ServerRequestFactory::fromGlobals(); + $rbacPolicy = $this->createMock(RbacPolicy::class); + if ($rbacBehavior === 'never') { + $rbacPolicy->expects($this->never()) + ->method('canAccess'); + } else { + $rbacPolicy->expects($this->once()) + ->method('canAccess') + ->willReturn($rbacBehavior === 'true'); + } + $policy = new CollectionPolicy([ SuperuserPolicy::class, $rbacPolicy, diff --git a/tests/TestCase/Policy/RbacPolicyTest.php b/tests/TestCase/Policy/RbacPolicyTest.php index f1bbed6..81f7297 100644 --- a/tests/TestCase/Policy/RbacPolicyTest.php +++ b/tests/TestCase/Policy/RbacPolicyTest.php @@ -34,7 +34,7 @@ public function testBeforeRbacReturnedTrue() 'id' => '00000000-0000-0000-0000-000000000001', 'password' => '12345', ]); - $service = $this->createMock(AuthorizationServiceInterface::class); + $service = $this->createStub(AuthorizationServiceInterface::class); $identity = new IdentityDecorator($service, $user); $request = ServerRequestFactory::fromGlobals(); $request = $request->withAttribute('identity', $identity); @@ -44,9 +44,9 @@ public function testBeforeRbacReturnedTrue() ->method('checkPermissions') ->with( $this->equalTo($identity->getOriginalData()), - $this->equalTo($request) + $this->equalTo($request), ) - ->will($this->returnValue(true)); + ->willReturn(true); $policy = new RbacPolicy(); $this->assertTrue($policy->canAccess($identity, $request)); } @@ -60,7 +60,7 @@ public function testBeforeRbacReturnedFalse() 'id' => '00000000-0000-0000-0000-000000000001', 'password' => '12345', ]); - $service = $this->createMock(AuthorizationServiceInterface::class); + $service = $this->createStub(AuthorizationServiceInterface::class); $identity = new IdentityDecorator($service, $user); $request = ServerRequestFactory::fromGlobals(); @@ -71,9 +71,9 @@ public function testBeforeRbacReturnedFalse() ->method('checkPermissions') ->with( $this->equalTo($identity->getOriginalData()), - $this->equalTo($request) + $this->equalTo($request), ) - ->will($this->returnValue(false)); + ->willReturn(false); $request = $request->withAttribute('rbac', $rbac); $policy = new RbacPolicy(); $this->assertFalse($policy->canAccess($request->getAttribute('identity'), $request)); @@ -85,7 +85,7 @@ public function testBeforeRbacReturnedFalse() public function testGetRbac() { $request = ServerRequestFactory::fromGlobals(); - $rbac = $this->getMockBuilder(Rbac::class)->onlyMethods(['checkPermissions'])->getMock(); + $rbac = $this->createStub(Rbac::class); $request = $request->withAttribute('rbac', $rbac); $policy = new RbacPolicy(); $actual = $policy->getRbac($request); @@ -98,7 +98,7 @@ public function testGetRbac() public function testGetRbacIgnoreConfigObject() { $request = ServerRequestFactory::fromGlobals(); - $rbac = $this->getMockBuilder(Rbac::class)->onlyMethods(['checkPermissions'])->getMock(); + $rbac = $this->createStub(Rbac::class); $request = $request->withAttribute('rbac', $rbac); $policy = new RbacPolicy([ 'adapter' => new Rbac(['role' => 'my_role']), @@ -113,7 +113,7 @@ public function testGetRbacIgnoreConfigObject() public function testGetRbacUseObject() { $request = ServerRequestFactory::fromGlobals(); - $rbac = $this->getMockBuilder(Rbac::class)->onlyMethods(['checkPermissions'])->getMock(); + $rbac = $this->createStub(Rbac::class); $policy = new RbacPolicy([ 'adapter' => $rbac, ]); diff --git a/tests/TestCase/Policy/SuperuserPolicyTest.php b/tests/TestCase/Policy/SuperuserPolicyTest.php index 1efff8c..9e32e0c 100644 --- a/tests/TestCase/Policy/SuperuserPolicyTest.php +++ b/tests/TestCase/Policy/SuperuserPolicyTest.php @@ -19,6 +19,7 @@ use Cake\ORM\Entity; use Cake\TestSuite\TestCase; use CakeDC\Auth\Policy\SuperuserPolicy; +use PHPUnit\Framework\Attributes\DataProvider; class SuperuserPolicyTest extends TestCase { @@ -50,12 +51,13 @@ public static function dataProviderCanAccess() * @dataProvider dataProviderCanAccess * @return void */ + #[DataProvider('dataProviderCanAccess')] public function testCanAccess($userData, $expected, $config = []) { $user = new Entity($userData + [ 'id' => '00000000-0000-0000-0000-000000000001', ]); - $service = $this->createMock(AuthorizationServiceInterface::class); + $service = $this->createStub(AuthorizationServiceInterface::class); $identity = new IdentityDecorator($service, $user); $request = ServerRequestFactory::fromGlobals(); $request = $request->withAttribute('identity', $identity); diff --git a/tests/TestCase/Rbac/RbacTest.php b/tests/TestCase/Rbac/RbacTest.php index 7b0814b..61a6918 100644 --- a/tests/TestCase/Rbac/RbacTest.php +++ b/tests/TestCase/Rbac/RbacTest.php @@ -175,6 +175,7 @@ protected function assertConstructorPermissions($instance, $config, $permissions /** * @dataProvider providerAuthorize */ + #[DataProvider('providerAuthorize')] public function testAuthorize($permissions, $user, $requestParams, $expected) { $this->rbac = new Rbac(['permissions' => $permissions]); @@ -1202,6 +1203,7 @@ function () { * @param array $requestParams * @param string $expectedMsg */ + #[DataProvider('badPermissionProvider')] public function testBadPermission($permissions, $user, $requestParams, $expectedMsg) { $rbac = $this->getMockBuilder(Rbac::class) diff --git a/tests/TestCase/Social/MapUserTest.php b/tests/TestCase/Social/MapUserTest.php index b4c02c9..61b1d5a 100644 --- a/tests/TestCase/Social/MapUserTest.php +++ b/tests/TestCase/Social/MapUserTest.php @@ -42,19 +42,7 @@ public function setUp(): void { parent::setUp(); - $this->Provider = $this->getMockBuilder(Facebook::class)->setConstructorArgs([ - [ - 'graphApiVersion' => 'v2.8', - 'redirectUri' => '/auth/facebook', - 'linkSocialUri' => '/link-social/facebook', - 'callbackLinkSocialUri' => '/callback-link-social/facebook', - 'clientId' => '10003030300303', - 'clientSecret' => 'secretpassword', - ], - [], - ])->onlyMethods([ - 'getAccessToken', 'getState', 'getAuthorizationUrl', 'getResourceOwner', - ])->getMock(); + $this->Provider = $this->createStub(Facebook::class); $config = [ 'service' => OAuth2Service::class, diff --git a/tests/TestCase/Social/Service/OAuth1ServiceTest.php b/tests/TestCase/Social/Service/OAuth1ServiceTest.php index 8d94590..f751a4e 100644 --- a/tests/TestCase/Social/Service/OAuth1ServiceTest.php +++ b/tests/TestCase/Social/Service/OAuth1ServiceTest.php @@ -26,7 +26,9 @@ use League\OAuth1\Client\Credentials\TokenCredentials; use League\OAuth1\Client\Server\Twitter; use League\OAuth1\Client\Server\User; +use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations; +#[AllowMockObjectsWithoutExpectations] class OAuth1ServiceTest extends TestCase { /** @@ -143,14 +145,14 @@ public function testGetAuthorizationUrl() $this->Provider->expects($this->once()) ->method('getTemporaryCredentials') - ->will($this->returnValue($Credentials)); + ->willReturn($Credentials); $this->Provider->expects($this->once()) ->method('getAuthorizationUrl') ->with( - $this->equalTo($Credentials) + $this->equalTo($Credentials), ) - ->will($this->returnValue('http://twitter.com/redirect/url')); + ->willReturn('http://twitter.com/redirect/url'); $actual = $this->Service->getAuthorizationUrl($this->Request); $expected = 'http://twitter.com/redirect/url'; @@ -326,16 +328,16 @@ public function testGetUser() ->with( $this->equalTo($Credentials), $this->equalTo('good39972j3092304230'), - $this->equalTo('77312h2312390839012') + $this->equalTo('77312h2312390839012'), ) - ->will($this->returnValue($TokenCredentials)); + ->willReturn($TokenCredentials); $this->Provider->expects($this->once()) ->method('getUserDetails') ->with( - $this->equalTo($TokenCredentials) + $this->equalTo($TokenCredentials), ) - ->will($this->returnValue($user)); + ->willReturn($user); $actual = $this->Service->getUser($this->Request); diff --git a/tests/TestCase/Social/Service/OAuth2ServiceTest.php b/tests/TestCase/Social/Service/OAuth2ServiceTest.php index a10f314..09afdfe 100644 --- a/tests/TestCase/Social/Service/OAuth2ServiceTest.php +++ b/tests/TestCase/Social/Service/OAuth2ServiceTest.php @@ -26,7 +26,9 @@ use League\OAuth2\Client\Provider\Facebook; use League\OAuth2\Client\Provider\FacebookUser; use League\OAuth2\Client\Token\AccessToken; +use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations; +#[AllowMockObjectsWithoutExpectations] class OAuth2ServiceTest extends TestCase { /** @@ -219,14 +221,14 @@ public function testGetAuthorizationUrl() { $this->Provider->expects($this->once()) ->method('getState') - ->will($this->returnValue('_NEW_STATE_')); + ->willReturn('_NEW_STATE_'); $this->Provider->expects($this->once()) ->method('getAuthorizationUrl') ->with($this->equalTo([ 'scope' => ['public_profile', 'email', 'user_birthday', 'user_gender', 'user_link'], ])) - ->will($this->returnValue('http://facebook.com/redirect/url')); + ->willReturn('http://facebook.com/redirect/url'); $actual = $this->Service->getAuthorizationUrl($this->Request); $expected = 'http://facebook.com/redirect/url'; @@ -298,16 +300,16 @@ public function testGetUser() ->method('getAccessToken') ->with( $this->equalTo('authorization_code'), - $this->equalTo(['code' => 'ZPO9972j3092304230']) + $this->equalTo(['code' => 'ZPO9972j3092304230']), ) - ->will($this->returnValue($Token)); + ->willReturn($Token); $this->Provider->expects($this->once()) ->method('getResourceOwner') ->with( - $this->equalTo($Token) + $this->equalTo($Token), ) - ->will($this->returnValue($user)); + ->willReturn($user); $actual = $this->Service->getUser($this->Request); diff --git a/tests/TestCase/Traits/IsAuthorizedTraitTest.php b/tests/TestCase/Traits/IsAuthorizedTraitTest.php index cea8504..5c0f8d0 100644 --- a/tests/TestCase/Traits/IsAuthorizedTraitTest.php +++ b/tests/TestCase/Traits/IsAuthorizedTraitTest.php @@ -26,7 +26,8 @@ use CakeDC\Auth\Policy\RbacPolicy; use CakeDC\Auth\Policy\SuperuserPolicy; use CakeDC\Auth\Rbac\Rbac; -use CakeDC\Auth\Traits\IsAuthorizedTrait; +use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations; +use PHPUnit\Framework\Attributes\DataProvider; use RuntimeException; /** @@ -34,6 +35,7 @@ * * @package CakeDC\Auth\Test\TestCase\Traits */ +#[AllowMockObjectsWithoutExpectations] class IsAuthorizedTraitTest extends TestCase { /** @@ -64,9 +66,9 @@ public static function dataProviderIsAuthorized() */ public function testIsAuthorizedEmpty() { - $Trait = $this->getMockBuilder(IsAuthorizedTrait::class) - ->addMethods(['getRequest']) - ->getMockForTrait(); + $Trait = $this->getMockBuilder(StubAuthorizedController::class) + ->onlyMethods(['getRequest']) + ->getMock(); $Trait->expects($this->never()) ->method('getRequest'); $this->assertFalse($Trait->isAuthorized(null)); @@ -82,6 +84,7 @@ public function testIsAuthorizedEmpty() * @dataProvider dataProviderIsAuthorized * @return void */ + #[DataProvider('dataProviderIsAuthorized')] public function testIsAuthorizedWithMock($url, $authorize, $invalidUrl = false) { $builder = Router::createRouteBuilder('/'); @@ -100,9 +103,9 @@ public function testIsAuthorizedWithMock($url, $authorize, $invalidUrl = false) $rbac->expects($this->once()) ->method('checkPermissions') ->with( - $this->equalTo($identity->getOriginalData()) + $this->equalTo($identity->getOriginalData()), ) - ->will($this->returnValue($authorize)); + ->willReturn($authorize); $request = $request->withAttribute('rbac', $rbac); $map = new MapResolver(); @@ -111,7 +114,7 @@ public function testIsAuthorizedWithMock($url, $authorize, $invalidUrl = false) new CollectionPolicy([ SuperuserPolicy::class, RbacPolicy::class, - ]) + ]), ); $orm = new OrmResolver(); $resolver = new ResolverCollection([ @@ -122,12 +125,12 @@ public function testIsAuthorizedWithMock($url, $authorize, $invalidUrl = false) $request = $request->withAttribute('authorization', $service); $request = $request->withAttribute('identity', new IdentityDecorator($service, $identity)); - $Trait = $this->getMockBuilder(IsAuthorizedTrait::class) - ->addMethods(['getRequest']) - ->getMockForTrait(); + $Trait = $this->getMockBuilder(StubAuthorizedController::class) + ->onlyMethods(['getRequest']) + ->getMock(); $Trait->expects($this->any()) ->method('getRequest') - ->will($this->returnValue($request)); + ->willReturn($request); $result = $Trait->isAuthorized($url); $this->assertSame($authorize, $result); @@ -152,12 +155,12 @@ public function testIsAuthorizedWithoutService() ->method('checkPermissions'); $request = $request->withAttribute('rbac', $rbac); - $Trait = $this->getMockBuilder(IsAuthorizedTrait::class) - ->addMethods(['getRequest']) - ->getMockForTrait(); + $Trait = $this->getMockBuilder(StubAuthorizedController::class) + ->onlyMethods(['getRequest']) + ->getMock(); $Trait->expects($this->any()) ->method('getRequest') - ->will($this->returnValue($request)); + ->willReturn($request); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Could not find the authorization service in the request.'); diff --git a/tests/TestCase/Traits/StubAuthorizedController.php b/tests/TestCase/Traits/StubAuthorizedController.php new file mode 100644 index 0000000..a98e91d --- /dev/null +++ b/tests/TestCase/Traits/StubAuthorizedController.php @@ -0,0 +1,36 @@ +stubRequest; + } + + public function setStubRequest(ServerRequest $request): void + { + $this->stubRequest = $request; + } +} From 5db4726a53e86048f9a56cc8a639301371935a10 Mon Sep 17 00:00:00 2001 From: Yevgeny Tomenko Date: Sat, 22 Aug 2026 21:21:08 +0300 Subject: [PATCH 2/2] fix ci issues --- .github/workflows/ci.yml | 4 ++-- composer.json | 4 ++-- phpunit.xml.dist | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 84a9678..a5241ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - php-version: ['8.1', '8.2', '8.3', '8.4'] + php-version: ['8.2', '8.3', '8.4', '8.5'] db-type: [sqlite, mysql, pgsql] prefer-lowest: [''] @@ -124,4 +124,4 @@ jobs: - name: Run phpstan if: success() || failure() run: composer stan - continue-on-error: true \ No newline at end of file + continue-on-error: true diff --git a/composer.json b/composer.json index e0ed7e2..322604a 100644 --- a/composer.json +++ b/composer.json @@ -26,8 +26,8 @@ "source": "https://github.com/CakeDC/auth" }, "require": { - "php": ">=8.1", - "cakephp/cakephp": "^5.0" + "php": ">=8.2", + "cakephp/cakephp": "^5.3" }, "require-dev": { "phpstan/phpstan": "^2.2.2,<2.2.6", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 26c90fb..d783e18 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -12,7 +12,6 @@ -