From 5637c9918e0e869edbd881f6f328e4f2696b358f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20H=C3=B6fler?= Date: Sun, 11 Oct 2026 19:15:40 +0200 Subject: [PATCH] fix: password rehash never ran for CakeDC/Auth.Form - LoginComponent::handlePasswordRehash() checked method_exists($provider, 'getIdentifier'), but CakeDC\Auth\Authenticator\FormAuthenticator proxies getIdentifier() to the base authenticator via __call, so the check was always false and the rehash was skipped silently. Use is_callable() instead. - Default Auth.PasswordRehash.authenticators.Form is now 'Password': the identifier configured as 'Authentication.Password' is registered as 'Password' in the identifier collection. - The test now uses the real FormAuthenticator and an identifier collection loaded like the default configuration instead of a mock with an added getIdentifier() method. Refs #1170 Co-Authored-By: Claude Opus 5.5 --- config/users.php | 3 ++- src/Controller/Component/LoginComponent.php | 3 ++- .../Controller/Traits/LoginTraitTest.php | 20 +++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/config/users.php b/config/users.php index 1210d07b..3241a773 100644 --- a/config/users.php +++ b/config/users.php @@ -285,7 +285,8 @@ 'RbacPolicy' => [], 'PasswordRehash' => [ 'authenticators' => [ - 'Form' => 'Authentication.Password', + // name of the identifier inside the collection of the authenticator ('Authentication.Password' is loaded as 'Password') + 'Form' => 'Password', ], ], ], diff --git a/src/Controller/Component/LoginComponent.php b/src/Controller/Component/LoginComponent.php index 3d0d5735..e5cfdfbb 100644 --- a/src/Controller/Component/LoginComponent.php +++ b/src/Controller/Component/LoginComponent.php @@ -210,7 +210,8 @@ protected function handlePasswordRehash($service, $user, \Cake\Http\ServerReques } $authenticationProvider = $service->getAuthenticationProvider(); - if (!$authenticationProvider || !method_exists($authenticationProvider, 'getIdentifier')) { + // is_callable: CakeDC\Auth\Authenticator\FormAuthenticator proxies getIdentifier() via __call + if (!$authenticationProvider || !is_callable([$authenticationProvider, 'getIdentifier'])) { return; } diff --git a/tests/TestCase/Controller/Traits/LoginTraitTest.php b/tests/TestCase/Controller/Traits/LoginTraitTest.php index 2aac76fe..4095f00d 100644 --- a/tests/TestCase/Controller/Traits/LoginTraitTest.php +++ b/tests/TestCase/Controller/Traits/LoginTraitTest.php @@ -288,9 +288,10 @@ public function testLoginRehash() public function testLoginRehashWithAuthenticatorStructure() { + // default configuration of the plugin (config/users.php) \Cake\Core\Configure::write('Auth.PasswordRehash', [ 'authenticators' => [ - 'Form' => 'Authentication.Password', + 'Form' => 'Password', ], ]); @@ -309,16 +310,13 @@ public function testLoginRehashWithAuthenticatorStructure() ->method('needsPasswordRehash') ->willReturn(true); - $identifierCollection = new IdentifierCollection([]); - $identifierCollection->set('Authentication.Password', $passwordIdentifier); + // 'Authentication.Password' configured as identifier of the Form authenticator is loaded as 'Password' + $identifierCollection = new IdentifierCollection(['Authentication.Password' => []]); + $this->assertTrue($identifierCollection->has('Password')); + $identifierCollection->set('Password', $passwordIdentifier); - $formAuthenticator = $this->getMockBuilder(FormAuthenticator::class) - ->disableOriginalConstructor() - ->addMethods(['getIdentifier']) - ->getMock(); - $formAuthenticator->expects($this->any()) - ->method('getIdentifier') - ->willReturn($identifierCollection); + // the real authenticator, it proxies getIdentifier() to the base authenticator via __call + $formAuthenticator = new FormAuthenticator($identifierCollection); $authenticatorCollection = $this->getMockBuilder(AuthenticatorCollection::class) ->disableOriginalConstructor() @@ -392,7 +390,7 @@ public function testLoginRehashWithAuthenticatorStructure() 'component' => 'CakeDC/Users.Login', 'targetAuthenticator' => FormAuthenticator::class, 'PasswordRehash' => [ - 'authenticators' => ['Form' => 'Authentication.Password'], + 'authenticators' => ['Form' => 'Password'], ], ];