From 2af8a207690b331fc4ad2c70c1db46a213574f14 Mon Sep 17 00:00:00 2001 From: Eugene Ritter Date: Tue, 10 Nov 2020 16:41:00 -0600 Subject: [PATCH 01/53] Created 2 objects to replace functions that were on the Plugin class: AuthorizationServiceProvider and AuthenticationServiceProvider. Both of these class implement the respective interfaces previously on the Plugin class. Both classes are now passed MiddlewareQueueLoader and type hints that were to the concretion "Plugin" are now the abstraction interfaces. --- src/Loader/MiddlewareQueueLoader.php | 42 ++- src/Plugin.php | 72 +---- .../AuthenticationServiceProvider.php | 39 +++ src/Provider/AuthorizationServiceProvider.php | 39 +++ src/Provider/ServiceProviderLoaderTrait.php | 56 ++++ tests/TestCase/PluginTest.php | 245 ----------------- .../AuthenticationServiceProviderTest.php | 249 ++++++++++++++++++ .../AuthorizationServiceProviderTest.php | 63 +++++ 8 files changed, 485 insertions(+), 320 deletions(-) create mode 100644 src/Provider/AuthenticationServiceProvider.php create mode 100644 src/Provider/AuthorizationServiceProvider.php create mode 100644 src/Provider/ServiceProviderLoaderTrait.php create mode 100644 tests/TestCase/Provider/AuthenticationServiceProviderTest.php create mode 100644 tests/TestCase/Provider/AuthorizationServiceProviderTest.php diff --git a/src/Loader/MiddlewareQueueLoader.php b/src/Loader/MiddlewareQueueLoader.php index 6b943870a..cb140d87c 100644 --- a/src/Loader/MiddlewareQueueLoader.php +++ b/src/Loader/MiddlewareQueueLoader.php @@ -13,7 +13,9 @@ namespace CakeDC\Users\Loader; +use Authentication\AuthenticationServiceProviderInterface; use Authentication\Middleware\AuthenticationMiddleware; +use Authorization\AuthorizationServiceProviderInterface; use Authorization\Middleware\AuthorizationMiddleware; use Authorization\Middleware\RequestAuthorizationMiddleware; use Cake\Core\Configure; @@ -21,7 +23,6 @@ use CakeDC\Auth\Middleware\TwoFactorMiddleware; use CakeDC\Users\Middleware\SocialAuthMiddleware; use CakeDC\Users\Middleware\SocialEmailMiddleware; -use CakeDC\Users\Plugin; /** * Class MiddlewareQueueLoader @@ -40,16 +41,20 @@ class MiddlewareQueueLoader * For 'Auth.Authorization.loadRbacMiddleware' load RbacMiddleware * * @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to update. - * @param \CakeDC\Users\Plugin $plugin Users plugin object + * @param \Authentication\AuthenticationServiceProviderInterface $authenticationServiceProvider Loads the auth service + * @param \Authorization\AuthorizationServiceProviderInterface $authorizationServiceProvider Loads the authorization service * @return \Cake\Http\MiddlewareQueue */ - public function __invoke(MiddlewareQueue $middlewareQueue, Plugin $plugin) - { + public function __invoke( + MiddlewareQueue $middlewareQueue, + AuthenticationServiceProviderInterface $authenticationServiceProvider, + AuthorizationServiceProviderInterface $authorizationServiceProvider + ) { $this->loadSocialMiddleware($middlewareQueue); - $this->loadAuthenticationMiddleware($middlewareQueue, $plugin); + $this->loadAuthenticationMiddleware($middlewareQueue, $authenticationServiceProvider); $this->load2faMiddleware($middlewareQueue); - return $this->loadAuthorizationMiddleware($middlewareQueue, $plugin); + return $this->loadAuthorizationMiddleware($middlewareQueue, $authorizationServiceProvider); } /** @@ -71,12 +76,14 @@ protected function loadSocialMiddleware(MiddlewareQueue $middlewareQueue) * Load authentication middleware * * @param \Cake\Http\MiddlewareQueue $middlewareQueue queue of middleware - * @param \CakeDC\Users\Plugin $plugin Users plugin object + * @param \Authentication\AuthenticationServiceProviderInterface $authenticationServiceProvider Authentication service provider * @return void */ - protected function loadAuthenticationMiddleware(MiddlewareQueue $middlewareQueue, Plugin $plugin) - { - $authentication = new AuthenticationMiddleware($plugin); + protected function loadAuthenticationMiddleware( + MiddlewareQueue $middlewareQueue, + AuthenticationServiceProviderInterface $authenticationServiceProvider + ) { + $authentication = new AuthenticationMiddleware($authenticationServiceProvider); $middlewareQueue->add($authentication); } @@ -100,15 +107,22 @@ protected function load2faMiddleware(MiddlewareQueue $middlewareQueue) * Load authorization middleware based on Auth.Authorization. * * @param \Cake\Http\MiddlewareQueue $middlewareQueue queue of middleware - * @param \CakeDC\Users\Plugin $plugin Users plugin object + * @param \Authorization\AuthorizationServiceProviderInterface $authorizationServiceProvider Authorization service provider * @return \Cake\Http\MiddlewareQueue */ - protected function loadAuthorizationMiddleware(MiddlewareQueue $middlewareQueue, Plugin $plugin) - { + protected function loadAuthorizationMiddleware( + MiddlewareQueue $middlewareQueue, + AuthorizationServiceProviderInterface $authorizationServiceProvider + ) { if (Configure::read('Auth.Authorization.enable') === false) { return $middlewareQueue; } - $middlewareQueue->add(new AuthorizationMiddleware($plugin, Configure::read('Auth.AuthorizationMiddleware'))); + $middlewareQueue->add( + new AuthorizationMiddleware( + $authorizationServiceProvider, + Configure::read('Auth.AuthorizationMiddleware') + ) + ); if (Configure::read('Auth.AuthorizationMiddleware.requireAuthorizationCheck') !== false) { $middlewareQueue->add(new RequestAuthorizationMiddleware()); } diff --git a/src/Plugin.php b/src/Plugin.php index 8ad5ba6fd..4981d736f 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -13,17 +13,16 @@ namespace CakeDC\Users; -use Authentication\AuthenticationServiceInterface; -use Authentication\AuthenticationServiceProviderInterface; -use Authorization\AuthorizationServiceInterface; -use Authorization\AuthorizationServiceProviderInterface; use Cake\Core\BasePlugin; -use Cake\Core\Configure; use Cake\Http\MiddlewareQueue; -use Psr\Http\Message\ServerRequestInterface; +use CakeDC\Users\Provider\AuthenticationServiceProvider; +use CakeDC\Users\Provider\AuthorizationServiceProvider; +use CakeDC\Users\Provider\ServiceProviderLoaderTrait; -class Plugin extends BasePlugin implements AuthenticationServiceProviderInterface, AuthorizationServiceProviderInterface +class Plugin extends BasePlugin { + use ServiceProviderLoaderTrait; + /** * Plugin name. * @@ -43,29 +42,6 @@ class Plugin extends BasePlugin implements AuthenticationServiceProviderInterfac public const EVENT_ON_EXPIRED_TOKEN = 'Users.Global.onExpiredToken'; public const EVENT_AFTER_RESEND_TOKEN_VALIDATION = 'Users.Global.afterResendTokenValidation'; - /** - * Returns an authentication service instance. - * - * @param \Psr\Http\Message\ServerRequestInterface $request Request - * @return \Authentication\AuthenticationServiceInterface - */ - public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface - { - $key = 'Auth.Authentication.serviceLoader'; - - return $this->loadService($request, $key); - } - - /** - * @inheritDoc - */ - public function getAuthorizationService(ServerRequestInterface $request): AuthorizationServiceInterface - { - $key = 'Auth.Authorization.serviceLoader'; - - return $this->loadService($request, $key); - } - /** * @inheritDoc */ @@ -73,36 +49,10 @@ public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue { $loader = $this->getLoader('Users.middlewareQueueLoader'); - return $loader($middlewareQueue, $this); - } - - /** - * Load a service defined in configuration $loaderKey - * - * @param \Psr\Http\Message\ServerRequestInterface $request The request. - * @param string $loaderKey service loader key - * @return mixed - */ - protected function loadService(ServerRequestInterface $request, $loaderKey) - { - $serviceLoader = $this->getLoader($loaderKey); - - return $serviceLoader($request); - } - - /** - * Get the loader callable - * - * @param string $loaderKey loader configuration key - * @return callable - */ - protected function getLoader($loaderKey) - { - $serviceLoader = Configure::read($loaderKey); - if (is_string($serviceLoader)) { - $serviceLoader = new $serviceLoader(); - } - - return $serviceLoader; + return $loader( + $middlewareQueue, + new AuthenticationServiceProvider(), + new AuthorizationServiceProvider() + ); } } diff --git a/src/Provider/AuthenticationServiceProvider.php b/src/Provider/AuthenticationServiceProvider.php new file mode 100644 index 000000000..1c4dcc3e4 --- /dev/null +++ b/src/Provider/AuthenticationServiceProvider.php @@ -0,0 +1,39 @@ +loadService($request, $key); + } +} diff --git a/src/Provider/AuthorizationServiceProvider.php b/src/Provider/AuthorizationServiceProvider.php new file mode 100644 index 000000000..8c727f41a --- /dev/null +++ b/src/Provider/AuthorizationServiceProvider.php @@ -0,0 +1,39 @@ +loadService($request, $key); + } +} diff --git a/src/Provider/ServiceProviderLoaderTrait.php b/src/Provider/ServiceProviderLoaderTrait.php new file mode 100644 index 000000000..770826647 --- /dev/null +++ b/src/Provider/ServiceProviderLoaderTrait.php @@ -0,0 +1,56 @@ +getLoader($loaderKey); + + return $serviceLoader($request); + } + + /** + * Get the loader callable + * + * @param string $loaderKey loader configuration key + * @return callable + */ + protected function getLoader($loaderKey) + { + $serviceLoader = Configure::read($loaderKey); + if (is_string($serviceLoader)) { + $serviceLoader = new $serviceLoader(); + } + + return $serviceLoader; + } +} diff --git a/tests/TestCase/PluginTest.php b/tests/TestCase/PluginTest.php index befaaa762..24b4754ac 100644 --- a/tests/TestCase/PluginTest.php +++ b/tests/TestCase/PluginTest.php @@ -211,251 +211,6 @@ public function testMiddlewareNotGoogleAuthenticationAndNotSocial() $this->assertInstanceOf(RequestAuthorizationMiddleware::class, $middleware->current()); } - /** - * testGetAuthenticationService - * - * @return void - */ - public function testGetAuthenticationServiceCallableDefined() - { - $request = ServerRequestFactory::fromGlobals(); - $request->withQueryParams(['method' => __METHOD__]); - $service = new CakeDCAuthenticationService([ - 'identifiers' => [ - 'Authentication.Password', - ], - ]); - Configure::write('Auth.Authentication.serviceLoader', function ($aRequest) use ($request, $service) { - $this->assertSame($request, $aRequest); - - return $service; - }); - - $plugin = new Plugin(); - $actualService = $plugin->getAuthenticationService($request); - $this->assertSame($service, $actualService); - } - - /** - * testGetAuthenticationService - * - * @return void - */ - public function testGetAuthenticationService() - { - Configure::write('Auth.Authenticators', [ - 'Session' => [ - 'className' => 'Authentication.Session', - 'skipTwoFactorVerify' => true, - 'sessionKey' => 'CustomAuth', - 'fields' => ['username' => 'email'], - 'identify' => true, - ], - 'Form' => [ - 'className' => 'CakeDC/Auth.Form', - 'loginUrl' => '/login', - 'fields' => ['username' => 'email', 'password' => 'alt_password'], - ], - 'Token' => [ - 'className' => 'Authentication.Token', - 'skipTwoFactorVerify' => true, - 'header' => null, - 'queryParam' => 'api_key', - 'tokenPrefix' => null, - ], - ]); - Configure::write('Auth.Identifiers', [ - 'Password' => [ - 'className' => 'Authentication.Password', - 'fields' => [ - 'username' => 'email_2', - 'password' => 'password_2', - ], - ], - 'Token' => [ - 'className' => 'Authentication.Token', - 'tokenField' => 'api_token', - ], - 'Authentication.JwtSubject', - ]); - Configure::write('OneTimePasswordAuthenticator.login', true); - - $plugin = new Plugin(); - $service = $plugin->getAuthenticationService(new ServerRequest(), new Response()); - $this->assertInstanceOf(CakeDCAuthenticationService::class, $service); - - /** - * @var \Authentication\Authenticator\AuthenticatorCollection $authenticators - */ - $authenticators = $service->authenticators(); - $expected = [ - SessionAuthenticator::class => [ - 'fields' => ['username' => 'email'], - 'sessionKey' => 'CustomAuth', - 'identify' => true, - 'identityAttribute' => 'identity', - 'skipTwoFactorVerify' => true, - ], - FormAuthenticator::class => [ - 'loginUrl' => '/login', - 'keyCheckEnabledRecaptcha' => 'Users.reCaptcha.login', - 'fields' => ['username' => 'email', 'password' => 'alt_password'], - ], - TokenAuthenticator::class => [ - 'header' => null, - 'queryParam' => 'api_key', - 'tokenPrefix' => null, - 'skipTwoFactorVerify' => true, - ], - TwoFactorAuthenticator::class => [ - 'loginUrl' => null, - 'urlChecker' => 'Authentication.Default', - 'skipTwoFactorVerify' => true, - ], - ]; - $actual = []; - foreach ($authenticators as $key => $value) { - $actual[get_class($value)] = $value->getConfig(); - } - $this->assertEquals($expected, $actual); - - /** - * @var \Authentication\Identifier\IdentifierCollection $identifiers - */ - $identifiers = $service->identifiers(); - $expected = [ - PasswordIdentifier::class => [ - 'fields' => [ - 'username' => 'email_2', - 'password' => 'password_2', - ], - 'resolver' => 'Authentication.Orm', - 'passwordHasher' => null, - ], - TokenIdentifier::class => [ - 'tokenField' => 'api_token', - 'dataField' => 'token', - 'resolver' => 'Authentication.Orm', - ], - JwtSubjectIdentifier::class => [ - 'tokenField' => 'id', - 'dataField' => 'sub', - 'resolver' => 'Authentication.Orm', - ], - ]; - $actual = []; - foreach ($identifiers as $key => $value) { - $actual[get_class($value)] = $value->getConfig(); - } - $this->assertEquals($expected, $actual); - } - - /** - * testGetAuthenticationService - * - * @return void - */ - public function testGetAuthenticationServiceWithouOneTimePasswordAuthenticator() - { - Configure::write('Auth.Authenticators', [ - 'Session' => [ - 'className' => 'Authentication.Session', - 'skipTwoFactorVerify' => true, - 'sessionKey' => 'CustomAuth', - 'fields' => ['username' => 'email'], - 'identify' => true, - ], - 'Form' => [ - 'className' => 'CakeDC/Auth.Form', - 'loginUrl' => '/login', - 'fields' => ['username' => 'email', 'password' => 'alt_password'], - ], - 'Token' => [ - 'className' => 'Authentication.Token', - 'skipTwoFactorVerify' => true, - 'header' => null, - 'queryParam' => 'api_key', - 'tokenPrefix' => null, - ], - ]); - Configure::write('Auth.Identifiers', [ - 'Authentication.Password', - 'Token' => [ - 'className' => 'Authentication.Token', - 'tokenField' => 'api_token', - ], - 'Authentication.JwtSubject', - ]); - Configure::write('OneTimePasswordAuthenticator.login', false); - - $plugin = new Plugin(); - $service = $plugin->getAuthenticationService(new ServerRequest(), new Response()); - $this->assertInstanceOf(CakeDCAuthenticationService::class, $service); - - /** - * @var \Authentication\Authenticator\AuthenticatorCollection $authenticators - */ - $authenticators = $service->authenticators(); - $expected = [ - SessionAuthenticator::class => [ - 'fields' => ['username' => 'email'], - 'sessionKey' => 'CustomAuth', - 'identify' => true, - 'identityAttribute' => 'identity', - 'skipTwoFactorVerify' => true, - ], - FormAuthenticator::class => [ - 'loginUrl' => '/login', - 'fields' => ['username' => 'email', 'password' => 'alt_password'], - 'keyCheckEnabledRecaptcha' => 'Users.reCaptcha.login', - ], - TokenAuthenticator::class => [ - 'header' => null, - 'queryParam' => 'api_key', - 'tokenPrefix' => null, - 'skipTwoFactorVerify' => true, - ], - ]; - $actual = []; - foreach ($authenticators as $key => $value) { - $actual[get_class($value)] = $value->getConfig(); - } - $this->assertEquals($expected, $actual); - } - - /** - * testGetAuthorizationService - * - * @return void - */ - public function testGetAuthorizationService() - { - $plugin = new Plugin(); - $service = $plugin->getAuthorizationService(new ServerRequest()); - $this->assertInstanceOf(AuthorizationService::class, $service); - } - - /** - * testGetAuthorizationService - * - * @return void - */ - public function testGetAuthorizationServiceCallableDefined() - { - $request = ServerRequestFactory::fromGlobals(); - $request->withQueryParams(['method' => __METHOD__]); - $service = new AuthorizationService(new ResolverCollection()); - Configure::write('Auth.Authorization.serviceLoader', function ($aRequest) use ($request, $service) { - $this->assertSame($request, $aRequest); - - return $service; - }); - - $plugin = new Plugin(); - $actualService = $plugin->getAuthorizationService($request); - $this->assertSame($service, $actualService); - } - /** * test bootstrap method * diff --git a/tests/TestCase/Provider/AuthenticationServiceProviderTest.php b/tests/TestCase/Provider/AuthenticationServiceProviderTest.php new file mode 100644 index 000000000..710402173 --- /dev/null +++ b/tests/TestCase/Provider/AuthenticationServiceProviderTest.php @@ -0,0 +1,249 @@ + [ + 'className' => 'Authentication.Session', + 'skipTwoFactorVerify' => true, + 'sessionKey' => 'CustomAuth', + 'fields' => ['username' => 'email'], + 'identify' => true, + ], + 'Form' => [ + 'className' => 'CakeDC/Auth.Form', + 'loginUrl' => '/login', + 'fields' => ['username' => 'email', 'password' => 'alt_password'], + ], + 'Token' => [ + 'className' => 'Authentication.Token', + 'skipTwoFactorVerify' => true, + 'header' => null, + 'queryParam' => 'api_key', + 'tokenPrefix' => null, + ], + ]); + Configure::write('Auth.Identifiers', [ + 'Password' => [ + 'className' => 'Authentication.Password', + 'fields' => [ + 'username' => 'email_2', + 'password' => 'password_2', + ], + ], + 'Token' => [ + 'className' => 'Authentication.Token', + 'tokenField' => 'api_token', + ], + 'Authentication.JwtSubject', + ]); + Configure::write('OneTimePasswordAuthenticator.login', true); + + $authenticationServiceProvider = new AuthenticationServiceProvider(); + $service = $authenticationServiceProvider->getAuthenticationService(new ServerRequest(), new Response()); + $this->assertInstanceOf(CakeDCAuthenticationService::class, $service); + + /** + * @var \Authentication\Authenticator\AuthenticatorCollection $authenticators + */ + $authenticators = $service->authenticators(); + $expected = [ + SessionAuthenticator::class => [ + 'fields' => ['username' => 'email'], + 'sessionKey' => 'CustomAuth', + 'identify' => true, + 'identityAttribute' => 'identity', + 'skipTwoFactorVerify' => true, + ], + FormAuthenticator::class => [ + 'loginUrl' => '/login', + 'keyCheckEnabledRecaptcha' => 'Users.reCaptcha.login', + 'fields' => ['username' => 'email', 'password' => 'alt_password'], + ], + TokenAuthenticator::class => [ + 'header' => null, + 'queryParam' => 'api_key', + 'tokenPrefix' => null, + 'skipTwoFactorVerify' => true, + ], + TwoFactorAuthenticator::class => [ + 'loginUrl' => null, + 'urlChecker' => 'Authentication.Default', + 'skipTwoFactorVerify' => true, + ], + ]; + $actual = []; + foreach ($authenticators as $key => $value) { + $actual[get_class($value)] = $value->getConfig(); + } + $this->assertEquals($expected, $actual); + + /** + * @var \Authentication\Identifier\IdentifierCollection $identifiers + */ + $identifiers = $service->identifiers(); + $expected = [ + PasswordIdentifier::class => [ + 'fields' => [ + 'username' => 'email_2', + 'password' => 'password_2', + ], + 'resolver' => 'Authentication.Orm', + 'passwordHasher' => null, + ], + TokenIdentifier::class => [ + 'tokenField' => 'api_token', + 'dataField' => 'token', + 'resolver' => 'Authentication.Orm', + ], + JwtSubjectIdentifier::class => [ + 'tokenField' => 'id', + 'dataField' => 'sub', + 'resolver' => 'Authentication.Orm', + ], + ]; + $actual = []; + foreach ($identifiers as $key => $value) { + $actual[get_class($value)] = $value->getConfig(); + } + $this->assertEquals($expected, $actual); + } + + /** + * testGetAuthenticationService + * + * @return void + */ + public function testGetAuthenticationServiceCallableDefined() + { + $request = ServerRequestFactory::fromGlobals(); + $request->withQueryParams(['method' => __METHOD__]); + $service = new CakeDCAuthenticationService([ + 'identifiers' => [ + 'Authentication.Password', + ], + ]); + Configure::write('Auth.Authentication.serviceLoader', function ($aRequest) use ($request, $service) { + $this->assertSame($request, $aRequest); + + return $service; + }); + + $authenticationServiceProvider = new AuthenticationServiceProvider(); + $actualService = $authenticationServiceProvider->getAuthenticationService($request); + $this->assertSame($service, $actualService); + } + + /** + * testGetAuthenticationService + * + * @return void + */ + public function testGetAuthenticationServiceWithoutOneTimePasswordAuthenticator() + { + Configure::write('Auth.Authenticators', [ + 'Session' => [ + 'className' => 'Authentication.Session', + 'skipTwoFactorVerify' => true, + 'sessionKey' => 'CustomAuth', + 'fields' => ['username' => 'email'], + 'identify' => true, + ], + 'Form' => [ + 'className' => 'CakeDC/Auth.Form', + 'loginUrl' => '/login', + 'fields' => ['username' => 'email', 'password' => 'alt_password'], + ], + 'Token' => [ + 'className' => 'Authentication.Token', + 'skipTwoFactorVerify' => true, + 'header' => null, + 'queryParam' => 'api_key', + 'tokenPrefix' => null, + ], + ]); + Configure::write('Auth.Identifiers', [ + 'Authentication.Password', + 'Token' => [ + 'className' => 'Authentication.Token', + 'tokenField' => 'api_token', + ], + 'Authentication.JwtSubject', + ]); + Configure::write('OneTimePasswordAuthenticator.login', false); + + $authenticationServiceProvider = new AuthenticationServiceProvider(); + $service = $authenticationServiceProvider->getAuthenticationService(new ServerRequest(), new Response()); + $this->assertInstanceOf(CakeDCAuthenticationService::class, $service); + + /** + * @var \Authentication\Authenticator\AuthenticatorCollection $authenticators + */ + $authenticators = $service->authenticators(); + $expected = [ + SessionAuthenticator::class => [ + 'fields' => ['username' => 'email'], + 'sessionKey' => 'CustomAuth', + 'identify' => true, + 'identityAttribute' => 'identity', + 'skipTwoFactorVerify' => true, + ], + FormAuthenticator::class => [ + 'loginUrl' => '/login', + 'fields' => ['username' => 'email', 'password' => 'alt_password'], + 'keyCheckEnabledRecaptcha' => 'Users.reCaptcha.login', + ], + TokenAuthenticator::class => [ + 'header' => null, + 'queryParam' => 'api_key', + 'tokenPrefix' => null, + 'skipTwoFactorVerify' => true, + ], + ]; + $actual = []; + foreach ($authenticators as $key => $value) { + $actual[get_class($value)] = $value->getConfig(); + } + $this->assertEquals($expected, $actual); + } +} diff --git a/tests/TestCase/Provider/AuthorizationServiceProviderTest.php b/tests/TestCase/Provider/AuthorizationServiceProviderTest.php new file mode 100644 index 000000000..d8ffb30e5 --- /dev/null +++ b/tests/TestCase/Provider/AuthorizationServiceProviderTest.php @@ -0,0 +1,63 @@ +getAuthorizationService(new ServerRequest()); + $this->assertInstanceOf(AuthorizationService::class, $service); + } + + /** + * testGetAuthorizationService + * + * @return void + */ + public function testGetAuthorizationServiceCallableDefined() + { + $request = ServerRequestFactory::fromGlobals(); + $request->withQueryParams(['method' => __METHOD__]); + $service = new AuthorizationService(new ResolverCollection()); + Configure::write('Auth.Authorization.serviceLoader', function ($aRequest) use ($request, $service) { + $this->assertSame($request, $aRequest); + + return $service; + }); + + $authorizationServiceProvider = new AuthorizationServiceProvider(); + $actualService = $authorizationServiceProvider->getAuthorizationService($request); + $this->assertSame($service, $actualService); + } +} From 5fc07d88d174e9c4f50db75df2f1ab39ba7ee696 Mon Sep 17 00:00:00 2001 From: Eugene Ritter Date: Wed, 11 Nov 2020 11:03:03 -0600 Subject: [PATCH 02/53] test From 4bf939d6af6d5db218aff2931e8feafb87ec79d8 Mon Sep 17 00:00:00 2001 From: Eugene Ritter Date: Wed, 11 Nov 2020 20:15:50 -0600 Subject: [PATCH 03/53] PhpStan cleanup --- src/Provider/ServiceProviderLoaderTrait.php | 1 - tests/TestCase/PluginTest.php | 13 ------------- 2 files changed, 14 deletions(-) diff --git a/src/Provider/ServiceProviderLoaderTrait.php b/src/Provider/ServiceProviderLoaderTrait.php index 770826647..ccfc0183c 100644 --- a/src/Provider/ServiceProviderLoaderTrait.php +++ b/src/Provider/ServiceProviderLoaderTrait.php @@ -23,7 +23,6 @@ */ trait ServiceProviderLoaderTrait { - /** * Load a service defined in configuration $loaderKey * diff --git a/tests/TestCase/PluginTest.php b/tests/TestCase/PluginTest.php index 24b4754ac..d4fedf44f 100644 --- a/tests/TestCase/PluginTest.php +++ b/tests/TestCase/PluginTest.php @@ -13,26 +13,13 @@ namespace CakeDC\Users\Test\TestCase; -use Authentication\Authenticator\SessionAuthenticator; -use Authentication\Authenticator\TokenAuthenticator; -use Authentication\Identifier\JwtSubjectIdentifier; -use Authentication\Identifier\PasswordIdentifier; -use Authentication\Identifier\TokenIdentifier; use Authentication\Middleware\AuthenticationMiddleware; -use Authorization\AuthorizationService; use Authorization\Middleware\AuthorizationMiddleware; use Authorization\Middleware\RequestAuthorizationMiddleware; -use Authorization\Policy\ResolverCollection; use Cake\Core\Configure; use Cake\Http\MiddlewareQueue; -use Cake\Http\Response; -use Cake\Http\ServerRequest; -use Cake\Http\ServerRequestFactory; use Cake\TestSuite\IntegrationTestTrait; use Cake\TestSuite\TestCase; -use CakeDC\Auth\Authentication\AuthenticationService as CakeDCAuthenticationService; -use CakeDC\Auth\Authenticator\FormAuthenticator; -use CakeDC\Auth\Authenticator\TwoFactorAuthenticator; use CakeDC\Auth\Middleware\TwoFactorMiddleware; use CakeDC\Users\Middleware\SocialAuthMiddleware; use CakeDC\Users\Middleware\SocialEmailMiddleware; From be0ccfe566bb435c24af53fbea524f44f85c9b9d Mon Sep 17 00:00:00 2001 From: Rafael Queiroz Date: Thu, 24 Jun 2021 14:04:39 -0300 Subject: [PATCH 04/53] Setting the $user->isNew() = false on PasswordManagementTrait::changePassword --- src/Controller/Traits/PasswordManagementTrait.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Controller/Traits/PasswordManagementTrait.php b/src/Controller/Traits/PasswordManagementTrait.php index 431d04be0..81b34b2d5 100644 --- a/src/Controller/Traits/PasswordManagementTrait.php +++ b/src/Controller/Traits/PasswordManagementTrait.php @@ -41,6 +41,8 @@ trait PasswordManagementTrait public function changePassword($id = null) { $user = $this->getUsersTable()->newEntity([], ['validate' => false]); + $user->isNew(false); + $identity = $this->getRequest()->getAttribute('identity'); $identity = $identity ?? []; $userId = $identity['id'] ?? null; From bc324b817db4398d432706906c37812801795b55 Mon Sep 17 00:00:00 2001 From: Rafael Queiroz Date: Thu, 24 Jun 2021 14:08:59 -0300 Subject: [PATCH 05/53] Fixing change password issue --- src/Controller/Traits/PasswordManagementTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controller/Traits/PasswordManagementTrait.php b/src/Controller/Traits/PasswordManagementTrait.php index 81b34b2d5..b4b0d014d 100644 --- a/src/Controller/Traits/PasswordManagementTrait.php +++ b/src/Controller/Traits/PasswordManagementTrait.php @@ -41,7 +41,7 @@ trait PasswordManagementTrait public function changePassword($id = null) { $user = $this->getUsersTable()->newEntity([], ['validate' => false]); - $user->isNew(false); + $user->setNew(false); $identity = $this->getRequest()->getAttribute('identity'); $identity = $identity ?? []; From 1250e29fe5549d6640e047ca6898365ef5bab915 Mon Sep 17 00:00:00 2001 From: Rafael Queiroz Date: Thu, 24 Jun 2021 14:08:59 -0300 Subject: [PATCH 06/53] Fixing change password issue --- src/Controller/Traits/PasswordManagementTrait.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Controller/Traits/PasswordManagementTrait.php b/src/Controller/Traits/PasswordManagementTrait.php index 431d04be0..b4b0d014d 100644 --- a/src/Controller/Traits/PasswordManagementTrait.php +++ b/src/Controller/Traits/PasswordManagementTrait.php @@ -41,6 +41,8 @@ trait PasswordManagementTrait public function changePassword($id = null) { $user = $this->getUsersTable()->newEntity([], ['validate' => false]); + $user->setNew(false); + $identity = $this->getRequest()->getAttribute('identity'); $identity = $identity ?? []; $userId = $identity['id'] ?? null; From 3c093f604851f60699604f44a19de2515d0b573a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Gonz=C3=A1lez?= Date: Tue, 6 Jul 2021 00:13:52 +0100 Subject: [PATCH 07/53] #block-redirect-to-host-not-allowed check if host is allowed before redirecting to external hosts after login --- config/users.php | 5 +++ src/Controller/Component/LoginComponent.php | 40 +++++++++++++++-- .../Integration/LoginTraitIntegrationTest.php | 44 +++++++++++++++++++ 3 files changed, 86 insertions(+), 3 deletions(-) diff --git a/config/users.php b/config/users.php index e927ea4d4..f5c9652b5 100644 --- a/config/users.php +++ b/config/users.php @@ -98,6 +98,11 @@ ] ], 'Superuser' => ['allowedToChangePasswords' => false], // able to reset any users password + // list of valid hosts to allow redirects after valid login via the `redirect` query param + 'AllowedRedirectHosts' => [ + 'localhost', + \Cake\Core\Configure::read('App.fullBaseUrl'), + ], ], 'OneTimePasswordAuthenticator' => [ 'checker' => \CakeDC\Auth\Authentication\DefaultOneTimePasswordAuthenticationChecker::class, diff --git a/src/Controller/Component/LoginComponent.php b/src/Controller/Component/LoginComponent.php index ad75f1b9d..54b93b31d 100644 --- a/src/Controller/Component/LoginComponent.php +++ b/src/Controller/Component/LoginComponent.php @@ -17,10 +17,12 @@ use Cake\Controller\Component; use Cake\Core\Configure; use Cake\Http\ServerRequest; +use Cake\Log\Log; use CakeDC\Auth\Authentication\AuthenticationService; use CakeDC\Auth\Traits\IsAuthorizedTrait; use CakeDC\Users\Plugin; use CakeDC\Users\Utility\UsersUrl; +use Laminas\Diactoros\Uri; /** * LoginFailure component @@ -146,13 +148,24 @@ protected function afterIdentifyUser($user) { $event = $this->getController()->dispatchEvent(Plugin::EVENT_AFTER_LOGIN, ['user' => $user]); if (is_array($event->getResult())) { + // in this case we don't checkSafeHost the url as the url params are generated by an event return $this->getController()->redirect($event->getResult()); } - $query = $this->getController()->getRequest()->getQueryParams(); + $queryRedirect = $this->getController()->getRequest()->getQuery('redirect'); $redirectUrl = $this->getController()->Authentication->getConfig('loginRedirect'); - if ($this->isAuthorized($query['redirect'] ?? null)) { - $redirectUrl = $query['redirect']; + if (!$this->checkSafeHost($queryRedirect)) { + $userId = $user['id'] ?? null; + Log::info( + "Unsafe redirect `$queryRedirect` ignored, user id `{$userId}` " . + "redirected to `$redirectUrl` after successful login" + ); + $queryRedirect = $redirectUrl; + } + // even if the host is safe, we need to check if the url is authorized for the given user + // this check ignores the host + if ($this->isAuthorized($queryRedirect ?? null)) { + $redirectUrl = $queryRedirect; } return $this->getController()->redirect($redirectUrl); @@ -184,4 +197,25 @@ protected function handlePasswordRehash($service, $user, \Cake\Http\ServerReques break; } } + + /** + * Check if there is a host defined in the $queryRedirect and it's in the allowed list of hosts + * + * @param string|null $queryRedirect redirect url + * @return bool + */ + protected function checkSafeHost(?string $queryRedirect = null): bool + { + if ($queryRedirect === null) { + return true; + } + + $uri = new Uri($queryRedirect); + $host = $uri->getHost(); + if (!$host) { + return true; + } + + return in_array($host, Configure::read('Users.AllowedRedirectHosts')); + } } diff --git a/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php b/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php index 982806ea1..9f6aa9aa3 100644 --- a/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php +++ b/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php @@ -240,4 +240,48 @@ public function testLogoutNoUser() $this->get('/logout'); $this->assertRedirect('/login'); } + + /** + * Test redirect should not happen if the host is not defined as a known host + * + * @return void + */ + public function testRedirectAfterLoginToHostUnknown() + { + $this->post('/login?redirect=http://unknown.com/', [ + 'username' => 'user-4', + 'password' => '12345', + ]); + $this->assertRedirect('/pages/home'); + } + + /** + * Test redirect should happen for defaul localhost + * + * @return void + */ + public function testRedirectAfterLoginToAllowedHost() + { + Configure::write('Users.AllowedRedirectHosts', ['example.com']); + $this->post('/login?redirect=http://example.com/login', [ + 'username' => 'user-4', + 'password' => '12345', + ]); + // /login is authorized for this user, and example.com is in the allowed hosts + $this->assertRedirect('http://example.com/login'); + } + + /** + * Test redirect fails if url is not allowed + * + * @return void + */ + public function testRedirectFailsIfUrlNotAllowed() + { + $this->post('/login?redirect=http://localhost/not-allowed', [ + 'username' => 'user-4', + 'password' => '12345', + ]); + $this->assertRedirect('/pages/home'); + } } From 1957f38bb510b2c09af009c651c5095a16d07de2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Gonz=C3=A1lez?= Date: Tue, 6 Jul 2021 00:13:53 +0100 Subject: [PATCH 08/53] fix cs --- config/users.php | 52 ++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/config/users.php b/config/users.php index f5c9652b5..edb7c72db 100644 --- a/config/users.php +++ b/config/users.php @@ -72,7 +72,7 @@ ], // form key to store the social auth data 'Form' => [ - 'social' => 'social' + 'social' => 'social', ], 'Data' => [ // data key to store the users email @@ -94,8 +94,8 @@ 'Config' => [ 'expires' => new \DateTime('+1 month'), 'httponly' => true, - ] - ] + ], + ], ], 'Superuser' => ['allowedToChangePasswords' => false], // able to reset any users password // list of valid hosts to allow redirects after valid login via the `redirect` query param @@ -117,7 +117,7 @@ // QR-code provider (more on this later) 'qrcodeprovider' => null, // Random Number Generator provider (more on this later) - 'rngprovider' => null + 'rngprovider' => null, ], 'U2f' => [ 'enabled' => false, @@ -126,12 +126,12 @@ // default configuration used to auto-load the Auth Component, override to change the way Auth works 'Auth' => [ 'Authentication' => [ - 'serviceLoader' => \CakeDC\Users\Loader\AuthenticationServiceLoader::class + 'serviceLoader' => \CakeDC\Users\Loader\AuthenticationServiceLoader::class, ], 'AuthenticationComponent' => [ 'load' => true, 'loginRedirect' => '/', - 'requireIdentity' => false + 'requireIdentity' => false, ], 'Authenticators' => [ 'Session' => [ @@ -167,41 +167,41 @@ 'SocialPendingEmail' => [ 'className' => 'CakeDC/Users.SocialPendingEmail', 'skipTwoFactorVerify' => true, - ] + ], ], 'Identifiers' => [ 'Password' => [ 'className' => 'Authentication.Password', 'fields' => [ 'username' => ['username', 'email'], - 'password' => 'password' + 'password' => 'password', ], 'resolver' => [ 'className' => 'Authentication.Orm', - 'finder' => 'active' + 'finder' => 'active', ], ], - "Social" => [ + 'Social' => [ 'className' => 'CakeDC/Users.Social', - 'authFinder' => 'active' + 'authFinder' => 'active', ], 'Token' => [ 'className' => 'Authentication.Token', 'tokenField' => 'api_token', 'resolver' => [ 'className' => 'Authentication.Orm', - 'finder' => 'active' + 'finder' => 'active', ], - ] + ], ], - "Authorization" => [ + 'Authorization' => [ 'enable' => true, - 'serviceLoader' => \CakeDC\Users\Loader\AuthorizationServiceLoader::class + 'serviceLoader' => \CakeDC\Users\Loader\AuthorizationServiceLoader::class, ], 'AuthorizationMiddleware' => [ 'unauthorizedHandler' => [ 'className' => 'CakeDC/Users.DefaultRedirect', - ] + ], ], 'AuthorizationComponent' => [ 'enabled' => true, @@ -209,7 +209,7 @@ 'RbacPolicy' => [], 'PasswordRehash' => [ 'identifiers' => ['Password'], - ] + ], ], 'OAuth' => [ 'providers' => [ @@ -223,7 +223,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', @@ -233,7 +233,7 @@ 'redirectUri' => Router::fullBaseUrl() . '/auth/twitter', 'linkSocialUri' => Router::fullBaseUrl() . '/link-social/twitter', 'callbackLinkSocialUri' => Router::fullBaseUrl() . '/callback-link-social/twitter', - ] + ], ], 'linkedIn' => [ 'service' => 'CakeDC\Auth\Social\Service\OAuth2Service', @@ -243,7 +243,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', @@ -253,7 +253,7 @@ 'redirectUri' => Router::fullBaseUrl() . '/auth/instagram', 'linkSocialUri' => Router::fullBaseUrl() . '/link-social/instagram', 'callbackLinkSocialUri' => Router::fullBaseUrl() . '/callback-link-social/instagram', - ] + ], ], 'google' => [ 'service' => 'CakeDC\Auth\Social\Service\OAuth2Service', @@ -264,7 +264,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', @@ -274,7 +274,7 @@ 'redirectUri' => Router::fullBaseUrl() . '/auth/amazon', 'linkSocialUri' => Router::fullBaseUrl() . '/link-social/amazon', 'callbackLinkSocialUri' => Router::fullBaseUrl() . '/callback-link-social/amazon', - ] + ], ], 'cognito' => [ 'service' => 'CakeDC\Auth\Social\Service\OAuth2Service', @@ -284,11 +284,11 @@ 'redirectUri' => Router::fullBaseUrl() . '/auth/cognito', 'linkSocialUri' => Router::fullBaseUrl() . '/link-social/cognito', 'callbackLinkSocialUri' => Router::fullBaseUrl() . '/callback-link-social/cognito', - 'scope' => 'email openid' - ] + 'scope' => 'email openid', + ], ], ], - ] + ], ]; return $config; From 639fc6b8b6eb18cd87317a4d3ec04a16330b47da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Gonz=C3=A1lez?= Date: Tue, 6 Jul 2021 15:41:46 +0100 Subject: [PATCH 09/53] #block-redirect-to-host-not-allowed fix host extraction from fullBaseUrl and add more tests --- config/users.php | 23 +++++++++++++++---- .../Integration/LoginTraitIntegrationTest.php | 10 ++++++++ .../Controller/UsersControllerTest.php | 2 +- tests/bootstrap.php | 7 +++++- 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/config/users.php b/config/users.php index edb7c72db..e6e8e502c 100644 --- a/config/users.php +++ b/config/users.php @@ -10,7 +10,25 @@ * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ +use Cake\Core\Configure; +use Cake\Log\Log; use Cake\Routing\Router; +use Laminas\Diactoros\Uri; + +$allowedRedirectHosts = [ + 'localhost', +]; +if (Configure::read('App.fullBaseUrl')) { + try { + $uri = new Uri(Configure::read('App.fullBaseUrl')); + $fullBaseHost = $uri->getHost(); + if ($fullBaseHost) { + $allowedRedirectHosts[] = $fullBaseHost; + } + } catch (Exception $ex) { + Log::warning("Invalid host from App.fullBasedUrl in CakeDC/Users configuration: " . $ex->getMessage()); + } +} $config = [ 'Users' => [ @@ -99,10 +117,7 @@ ], 'Superuser' => ['allowedToChangePasswords' => false], // able to reset any users password // list of valid hosts to allow redirects after valid login via the `redirect` query param - 'AllowedRedirectHosts' => [ - 'localhost', - \Cake\Core\Configure::read('App.fullBaseUrl'), - ], + 'AllowedRedirectHosts' => $allowedRedirectHosts, ], 'OneTimePasswordAuthenticator' => [ 'checker' => \CakeDC\Auth\Authentication\DefaultOneTimePasswordAuthenticationChecker::class, diff --git a/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php b/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php index 9f6aa9aa3..0e002ad53 100644 --- a/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php +++ b/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php @@ -271,6 +271,16 @@ public function testRedirectAfterLoginToAllowedHost() $this->assertRedirect('http://example.com/login'); } + public function testRedirectAfterLoginToFullBase(): void + { + $this->post('/login?redirect=http://example.com/login', [ + 'username' => 'user-4', + 'password' => '12345', + ]); + // /login is authorized for this user, and example.com is in the allowed hosts + $this->assertRedirect('http://example.com/login'); + } + /** * Test redirect fails if url is not allowed * diff --git a/tests/TestCase/Controller/UsersControllerTest.php b/tests/TestCase/Controller/UsersControllerTest.php index 1b763b16b..9cebd1f89 100644 --- a/tests/TestCase/Controller/UsersControllerTest.php +++ b/tests/TestCase/Controller/UsersControllerTest.php @@ -85,7 +85,7 @@ public function testUnauthorizedRedirectLogged() $this->session(['Auth' => $user]); $this->configRequest([ 'headers' => [ - 'REFERER' => 'http://localhost/profile', + 'REFERER' => 'http://example.com/profile', ], ]); $this->get('/users/index'); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 212a3b467..e9818da53 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -118,7 +118,7 @@ class_alias('TestApp\Controller\AppController', 'App\Controller\AppController'); 'dir' => 'src', 'webroot' => WEBROOT_DIR, 'wwwRoot' => WWW_ROOT, - 'fullBaseUrl' => 'http://localhost', + 'fullBaseUrl' => 'http://example.com', 'imageBaseUrl' => 'img/', 'jsBaseUrl' => 'js/', 'cssBaseUrl' => 'css/', @@ -131,3 +131,8 @@ class_alias('TestApp\Controller\AppController', 'App\Controller\AppController'); Plugin::getCollection()->add(new \CakeDC\Users\Plugin()); session_id('cli'); + +\Cake\Core\Configure::write('Users.AllowedRedirectHosts', [ + 'localhost', + 'example.com', +]); From 28accf61f22e815314b976b643cf8f8f596b3b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Gonz=C3=A1lez?= Date: Tue, 6 Jul 2021 15:41:47 +0100 Subject: [PATCH 10/53] fix cs --- config/users.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/users.php b/config/users.php index e6e8e502c..4b52272ed 100644 --- a/config/users.php +++ b/config/users.php @@ -26,7 +26,7 @@ $allowedRedirectHosts[] = $fullBaseHost; } } catch (Exception $ex) { - Log::warning("Invalid host from App.fullBasedUrl in CakeDC/Users configuration: " . $ex->getMessage()); + Log::warning('Invalid host from App.fullBasedUrl in CakeDC/Users configuration: ' . $ex->getMessage()); } } From b4bc71b6b249d8c04ea7b4af9e48af97fba78ba1 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 28 Jul 2021 23:38:39 +0100 Subject: [PATCH 11/53] show errors based on config --- config/users.php | 2 ++ src/Controller/Traits/RegisterTrait.php | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/config/users.php b/config/users.php index e927ea4d4..0d704e6a9 100644 --- a/config/users.php +++ b/config/users.php @@ -40,6 +40,8 @@ 'ensureActive' => false, // default role name used in registration 'defaultRole' => 'user', + // show verbose error to users + 'ShowVerboseError' => true, ], 'reCaptcha' => [ // reCaptcha key goes here diff --git a/src/Controller/Traits/RegisterTrait.php b/src/Controller/Traits/RegisterTrait.php index 49b43d3be..0661b6b08 100644 --- a/src/Controller/Traits/RegisterTrait.php +++ b/src/Controller/Traits/RegisterTrait.php @@ -31,8 +31,8 @@ trait RegisterTrait /** * Register a new user * - * @throws \Cake\Http\Exception\NotFoundException * @return mixed + * @throws \Cake\Http\Exception\NotFoundException */ public function register() { @@ -73,6 +73,12 @@ public function register() $userSaved = $usersTable->register($user, $data, $options); if ($userSaved) { return $this->_afterRegister($userSaved); + } elseif (Configure::read('Users.Registration.ShowVerboseError')) { + $errors = \Collection($user->getErrors())->unfold()->toArray(); + foreach ($errors as $error) { + $this->Flash->error(__($error)); + } + return; } else { $this->set(compact('user')); $this->Flash->error(__d('cake_d_c/users', 'The user could not be saved')); @@ -98,9 +104,14 @@ public function register() } $userSaved = $usersTable->register($user, $requestData, $options); - if (!$userSaved) { + if (!$userSaved && Configure::read('Users.Registration.ShowVerboseError')) { + $errors = \Collection($user->getErrors())->unfold()->toArray(); + foreach ($errors as $error) { + $this->Flash->error(__($error)); + } + return; + } elseif (!$userSaved) { $this->Flash->error(__d('cake_d_c/users', 'The user could not be saved')); - return; } From 359a9c30d903a4cd1bba934f696fb8d557ae0062 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 28 Jul 2021 23:40:51 +0100 Subject: [PATCH 12/53] update documentation --- Docs/Documentation/Configuration.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Docs/Documentation/Configuration.md b/Docs/Documentation/Configuration.md index dea40fd4d..5957c6689 100644 --- a/Docs/Documentation/Configuration.md +++ b/Docs/Documentation/Configuration.md @@ -101,6 +101,8 @@ NOTE: SOME keys were hidden in this doc page, please refer to `vendor/cakedc/use 'ensureActive' => false, // default role name used in registration 'defaultRole' => 'user', + // show verbose error to users + 'ShowVerboseError' => true, ], 'Tos' => [ // determines if the user should include tos accepted From f4822182d7eed1afe1175fd551917907a48438d5 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 28 Jul 2021 23:46:50 +0100 Subject: [PATCH 13/53] Set default config to false --- Docs/Documentation/Configuration.md | 2 +- config/users.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Docs/Documentation/Configuration.md b/Docs/Documentation/Configuration.md index 5957c6689..72b60698a 100644 --- a/Docs/Documentation/Configuration.md +++ b/Docs/Documentation/Configuration.md @@ -102,7 +102,7 @@ NOTE: SOME keys were hidden in this doc page, please refer to `vendor/cakedc/use // default role name used in registration 'defaultRole' => 'user', // show verbose error to users - 'ShowVerboseError' => true, + 'ShowVerboseError' => false, ], 'Tos' => [ // determines if the user should include tos accepted diff --git a/config/users.php b/config/users.php index 0d704e6a9..f5619aeb1 100644 --- a/config/users.php +++ b/config/users.php @@ -41,7 +41,7 @@ // default role name used in registration 'defaultRole' => 'user', // show verbose error to users - 'ShowVerboseError' => true, + 'ShowVerboseError' => false, ], 'reCaptcha' => [ // reCaptcha key goes here From 68e21f7d8e4aff31fd5a0d9c6104b18473ee6c02 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 29 Jul 2021 22:20:45 +0100 Subject: [PATCH 14/53] added test case --- .../Controller/Traits/RegisterTraitTest.php | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/TestCase/Controller/Traits/RegisterTraitTest.php b/tests/TestCase/Controller/Traits/RegisterTraitTest.php index fcb08a03f..f5b35b5e5 100644 --- a/tests/TestCase/Controller/Traits/RegisterTraitTest.php +++ b/tests/TestCase/Controller/Traits/RegisterTraitTest.php @@ -426,4 +426,69 @@ public function testRegisterLoggedInUserNotAllowed() $this->Trait->register(); } + + + /** + * test + * + * @return void + */ + public function testNotShowingVerboseErrorOnRegisterWithDefaultConfig() + { + //register user and not validate the email + $this->testRegister(); + + $this->_mockRequestPost(); + $this->_mockAuthentication(); + $this->_mockFlash(); + $this->_mockDispatchEvent(); + $this->Trait->Flash->expects($this->once()) + ->method('error') + ->with('The user could not be saved'); + + $this->Trait->getRequest()->expects($this->once()) + ->method('getData') + ->will($this->returnValue([ + 'username' => 'testRegistration', + 'password' => 'password', + 'email' => 'test-registration@example.com', + 'password_confirm' => 'password', + 'tos' => 1, + ])); + + $this->Trait->register(); + } + + /** + * test + * + * @return void + */ + public function testShowingVerboseErrorOnRegisterWithUpdatedConfig() + { + //register user and not validate the email + $this->testRegister(); + + $this->_mockRequestPost(); + $this->_mockAuthentication(); + $this->_mockFlash(); + $this->_mockDispatchEvent(); + + $this->Trait->Flash->expects($this->once()) + ->method('error') + ->with('Email already exists'); + + + $this->Trait->getRequest()->expects($this->once()) + ->method('getData') + ->will($this->returnValue([ + 'username' => 'testRegistration1', + 'password' => 'password', + 'email' => 'test-registration@example.com', + 'password_confirm' => 'password', + 'tos' => 1, + ])); + Configure::write('Users.Registration.ShowVerboseError', true); + $this->Trait->register(); + } } From 3aab7b20e619970c080b8c6ec337fcbaae8c76f7 Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 30 Jul 2021 20:25:25 +0100 Subject: [PATCH 15/53] change config key to camel case --- Docs/Documentation/Configuration.md | 2 +- config/users.php | 2 +- src/Controller/Traits/RegisterTrait.php | 10 +++++----- tests/TestCase/Controller/Traits/RegisterTraitTest.php | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Docs/Documentation/Configuration.md b/Docs/Documentation/Configuration.md index 72b60698a..59017e1d0 100644 --- a/Docs/Documentation/Configuration.md +++ b/Docs/Documentation/Configuration.md @@ -102,7 +102,7 @@ NOTE: SOME keys were hidden in this doc page, please refer to `vendor/cakedc/use // default role name used in registration 'defaultRole' => 'user', // show verbose error to users - 'ShowVerboseError' => false, + 'showVerboseError' => false, ], 'Tos' => [ // determines if the user should include tos accepted diff --git a/config/users.php b/config/users.php index f5619aeb1..95c7ff48a 100644 --- a/config/users.php +++ b/config/users.php @@ -41,7 +41,7 @@ // default role name used in registration 'defaultRole' => 'user', // show verbose error to users - 'ShowVerboseError' => false, + 'showVerboseError' => false, ], 'reCaptcha' => [ // reCaptcha key goes here diff --git a/src/Controller/Traits/RegisterTrait.php b/src/Controller/Traits/RegisterTrait.php index 0661b6b08..a2d896586 100644 --- a/src/Controller/Traits/RegisterTrait.php +++ b/src/Controller/Traits/RegisterTrait.php @@ -71,10 +71,11 @@ public function register() $data = $result->toArray(); $data['password'] = $requestData['password'] ?? null; //since password is a hidden property $userSaved = $usersTable->register($user, $data, $options); + $errors = \collection($user->getErrors())->unfold()->toArray(); if ($userSaved) { return $this->_afterRegister($userSaved); - } elseif (Configure::read('Users.Registration.ShowVerboseError')) { - $errors = \Collection($user->getErrors())->unfold()->toArray(); + } elseif (Configure::read('Users.Registration.showVerboseError') && count($errors) > 0) { + $this->set(compact('user')); foreach ($errors as $error) { $this->Flash->error(__($error)); } @@ -82,7 +83,6 @@ public function register() } else { $this->set(compact('user')); $this->Flash->error(__d('cake_d_c/users', 'The user could not be saved')); - return; } } @@ -104,8 +104,8 @@ public function register() } $userSaved = $usersTable->register($user, $requestData, $options); - if (!$userSaved && Configure::read('Users.Registration.ShowVerboseError')) { - $errors = \Collection($user->getErrors())->unfold()->toArray(); + $errors = \collection($user->getErrors())->unfold()->toArray(); + if (!$userSaved && Configure::read('Users.Registration.showVerboseError') && count($errors) > 0) { foreach ($errors as $error) { $this->Flash->error(__($error)); } diff --git a/tests/TestCase/Controller/Traits/RegisterTraitTest.php b/tests/TestCase/Controller/Traits/RegisterTraitTest.php index f5b35b5e5..e08984cbd 100644 --- a/tests/TestCase/Controller/Traits/RegisterTraitTest.php +++ b/tests/TestCase/Controller/Traits/RegisterTraitTest.php @@ -488,7 +488,7 @@ public function testShowingVerboseErrorOnRegisterWithUpdatedConfig() 'password_confirm' => 'password', 'tos' => 1, ])); - Configure::write('Users.Registration.ShowVerboseError', true); + Configure::write('Users.Registration.showVerboseError', true); $this->Trait->register(); } } From 2870020892ef82406a49b53e124ffc94482af816 Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 30 Jul 2021 20:58:36 +0100 Subject: [PATCH 16/53] fix php code sniffer errrors --- src/Controller/Traits/RegisterTrait.php | 4 ++ .../Controller/Traits/RegisterTraitTest.php | 44 +++++++++---------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/Controller/Traits/RegisterTrait.php b/src/Controller/Traits/RegisterTrait.php index a2d896586..97be4b5f8 100644 --- a/src/Controller/Traits/RegisterTrait.php +++ b/src/Controller/Traits/RegisterTrait.php @@ -79,10 +79,12 @@ public function register() foreach ($errors as $error) { $this->Flash->error(__($error)); } + return; } else { $this->set(compact('user')); $this->Flash->error(__d('cake_d_c/users', 'The user could not be saved')); + return; } } @@ -109,9 +111,11 @@ public function register() foreach ($errors as $error) { $this->Flash->error(__($error)); } + return; } elseif (!$userSaved) { $this->Flash->error(__d('cake_d_c/users', 'The user could not be saved')); + return; } diff --git a/tests/TestCase/Controller/Traits/RegisterTraitTest.php b/tests/TestCase/Controller/Traits/RegisterTraitTest.php index e08984cbd..55f5b7da2 100644 --- a/tests/TestCase/Controller/Traits/RegisterTraitTest.php +++ b/tests/TestCase/Controller/Traits/RegisterTraitTest.php @@ -65,10 +65,10 @@ public function testValidateEmail() public function testRegister() { Router::connect('/users/validate-email/*', [ - 'plugin' => 'CakeDC/Users', - 'controller' => 'Users', - 'action' => 'validateEmail', - ]); + 'plugin' => 'CakeDC/Users', + 'controller' => 'Users', + 'action' => 'validateEmail', + ]); $this->assertEquals(0, $this->table->find()->where(['username' => 'testRegistration'])->count()); $this->_mockRequestPost(); @@ -128,10 +128,10 @@ public function testRegisterWithEventFalseResult() public function testRegisterWithEventSuccessResult() { Router::connect('/users/validate-email/*', [ - 'plugin' => 'CakeDC/Users', - 'controller' => 'Users', - 'action' => 'validateEmail', - ]); + 'plugin' => 'CakeDC/Users', + 'controller' => 'Users', + 'action' => 'validateEmail', + ]); $data = [ 'username' => 'testRegistration', @@ -170,10 +170,10 @@ public function testRegisterWithEventSuccessResult() public function testRegisterReCaptcha() { Router::connect('/users/validate-email/*', [ - 'plugin' => 'CakeDC/Users', - 'controller' => 'Users', - 'action' => 'validateEmail', - ]); + 'plugin' => 'CakeDC/Users', + 'controller' => 'Users', + 'action' => 'validateEmail', + ]); Configure::write('Users.reCaptcha.registration', true); $this->assertEquals(0, $this->table->find()->where(['username' => 'testRegistration'])->count()); @@ -309,10 +309,10 @@ public function testRegisterGet() public function testRegisterRecaptchaDisabled() { Router::connect('/users/validate-email/*', [ - 'plugin' => 'CakeDC/Users', - 'controller' => 'Users', - 'action' => 'validateEmail', - ]); + 'plugin' => 'CakeDC/Users', + 'controller' => 'Users', + 'action' => 'validateEmail', + ]); Configure::write('Users.Registration.reCaptcha', false); $this->assertEquals(0, $this->table->find()->where(['username' => 'testRegistration'])->count()); @@ -368,10 +368,10 @@ public function testRegisterNotEnabled() public function testRegisterLoggedInUserAllowed() { Router::connect('/users/validate-email/*', [ - 'plugin' => 'CakeDC/Users', - 'controller' => 'Users', - 'action' => 'validateEmail', - ]); + 'plugin' => 'CakeDC/Users', + 'controller' => 'Users', + 'action' => 'validateEmail', + ]); Configure::write('Users.Registration.allowLoggedIn', true); $this->assertEquals(0, $this->table->find()->where(['username' => 'testRegistration'])->count()); @@ -427,7 +427,6 @@ public function testRegisterLoggedInUserNotAllowed() $this->Trait->register(); } - /** * test * @@ -436,7 +435,7 @@ public function testRegisterLoggedInUserNotAllowed() public function testNotShowingVerboseErrorOnRegisterWithDefaultConfig() { //register user and not validate the email - $this->testRegister(); + $this->testRegister(); $this->_mockRequestPost(); $this->_mockAuthentication(); @@ -478,7 +477,6 @@ public function testShowingVerboseErrorOnRegisterWithUpdatedConfig() ->method('error') ->with('Email already exists'); - $this->Trait->getRequest()->expects($this->once()) ->method('getData') ->will($this->returnValue([ From 7c0b80bae8dfbf7ea4d158c41b15a17f7ce76090 Mon Sep 17 00:00:00 2001 From: Yevgeny Tomenko Date: Tue, 17 Aug 2021 09:33:43 +0300 Subject: [PATCH 17/53] improve coding standards --- composer.json | 6 +++--- rector.yml | 4 ---- src/Authenticator/SocialAuthTrait.php | 2 +- src/Controller/Traits/OneTimePasswordVerifyTrait.php | 2 +- src/Controller/Traits/PasswordManagementTrait.php | 2 +- src/Controller/Traits/ProfileTrait.php | 2 +- src/Controller/Traits/RegisterTrait.php | 4 ++-- src/Controller/Traits/U2fTrait.php | 4 ++-- src/Exception/AccountAlreadyActiveException.php | 2 +- src/Exception/AccountNotActiveException.php | 2 +- src/Exception/MissingEmailException.php | 2 +- src/Exception/SocialAuthenticationException.php | 2 +- src/Exception/TokenExpiredException.php | 2 +- src/Exception/UserAlreadyActiveException.php | 2 +- src/Exception/UserNotActiveException.php | 2 +- src/Exception/WrongPasswordException.php | 2 +- src/Identifier/SocialIdentifier.php | 6 ++---- src/Loader/LoginComponentLoader.php | 4 ++-- src/Mailer/UsersMailer.php | 2 +- src/Middleware/SocialAuthMiddleware.php | 2 +- .../UnauthorizedHandler/DefaultRedirectHandler.php | 5 ++--- src/Model/Behavior/BaseTokenBehavior.php | 3 +-- src/Model/Behavior/PasswordBehavior.php | 6 ++---- src/Model/Behavior/RegisterBehavior.php | 5 ++--- src/Model/Behavior/SocialAccountBehavior.php | 9 ++------- src/Model/Behavior/SocialBehavior.php | 8 ++++---- src/Model/Entity/User.php | 6 +++--- src/Model/Table/UsersTable.php | 8 ++------ src/Shell/UsersShell.php | 7 +++---- src/Utility/UsersUrl.php | 2 +- 30 files changed, 47 insertions(+), 68 deletions(-) delete mode 100644 rector.yml diff --git a/composer.json b/composer.json index 7395b49fa..b8be0af96 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "cakephp/authentication": "^2.0.0" }, "require-dev": { - "phpunit/phpunit": "^8.0", + "phpunit/phpunit": "^8", "league/oauth2-facebook": "@stable", "league/oauth2-instagram": "@stable", "league/oauth2-google": "@stable", @@ -86,11 +86,11 @@ "test": "phpunit --stderr", "stan": "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:^0.12.7 psalm/phar:~3.8.0 && mv composer.backup composer.json", + "stan-setup": "cp composer.json composer.backup && composer require --dev phpstan/phpstan:0.12.94 psalm/phar:~4.9.2 && mv composer.backup composer.json", "stan-rebuild-baseline": "phpstan analyse --configuration phpstan.neon --error-format baselineNeon src/ > phpstan-baseline.neon", "psalm-rebuild-baseline": "php vendor/psalm/phar/psalm.phar --show-info=false --set-baseline=psalm-baseline.xml src/", "rector": "rector process src/", - "rector-setup": "cp composer.json composer.backup && composer require --dev rector/rector:^0.4.11 && mv composer.backup composer.json", + "rector-setup": "cp composer.json composer.backup && composer require --dev rector/rector:^0.11.2 && mv composer.backup composer.json", "coverage-test": "phpunit --stderr --coverage-clover=clover.xml" } } diff --git a/rector.yml b/rector.yml deleted file mode 100644 index eaa99e6a6..000000000 --- a/rector.yml +++ /dev/null @@ -1,4 +0,0 @@ -# rector.yaml -services: - Rector\Php\Rector\FunctionLike\ParamTypeDeclarationRector: ~ - Rector\Php\Rector\FunctionLike\ReturnTypeDeclarationRector: ~ diff --git a/src/Authenticator/SocialAuthTrait.php b/src/Authenticator/SocialAuthTrait.php index cb3e7d1c0..9f3cef77c 100644 --- a/src/Authenticator/SocialAuthTrait.php +++ b/src/Authenticator/SocialAuthTrait.php @@ -40,7 +40,7 @@ protected function identify($rawData) } catch (UserNotActiveException $e) { return new Result(null, self::FAILURE_USER_NOT_ACTIVE); } catch (MissingEmailException $exception) { - throw new SocialAuthenticationException(compact('rawData'), null, $exception); + throw new SocialAuthenticationException(['rawData' => $rawData], null, $exception); } } } diff --git a/src/Controller/Traits/OneTimePasswordVerifyTrait.php b/src/Controller/Traits/OneTimePasswordVerifyTrait.php index da3a963d8..9538569a6 100644 --- a/src/Controller/Traits/OneTimePasswordVerifyTrait.php +++ b/src/Controller/Traits/OneTimePasswordVerifyTrait.php @@ -55,7 +55,7 @@ public function verify() $temporarySession['email'], $secret ); - $this->set(compact('secretDataUri')); + $this->set(['secretDataUri' => $secretDataUri]); } if ($this->getRequest()->is('post')) { diff --git a/src/Controller/Traits/PasswordManagementTrait.php b/src/Controller/Traits/PasswordManagementTrait.php index b4b0d014d..a68b4beff 100644 --- a/src/Controller/Traits/PasswordManagementTrait.php +++ b/src/Controller/Traits/PasswordManagementTrait.php @@ -125,7 +125,7 @@ public function changePassword($id = null) $this->log($exception->getMessage()); } } - $this->set(compact('user')); + $this->set(['user' => $user]); $this->set('_serialize', ['user']); } diff --git a/src/Controller/Traits/ProfileTrait.php b/src/Controller/Traits/ProfileTrait.php index d2edc6586..a67ac991c 100644 --- a/src/Controller/Traits/ProfileTrait.php +++ b/src/Controller/Traits/ProfileTrait.php @@ -59,7 +59,7 @@ public function profile($id = null) return $this->redirect($this->getRequest()->referer()); } - $this->set(compact('user', 'isCurrentUser')); + $this->set(['user' => $user, 'isCurrentUser' => $isCurrentUser]); $this->set('_serialize', ['user', 'isCurrentUser']); } } diff --git a/src/Controller/Traits/RegisterTrait.php b/src/Controller/Traits/RegisterTrait.php index 49b43d3be..95f3e1d92 100644 --- a/src/Controller/Traits/RegisterTrait.php +++ b/src/Controller/Traits/RegisterTrait.php @@ -74,7 +74,7 @@ public function register() if ($userSaved) { return $this->_afterRegister($userSaved); } else { - $this->set(compact('user')); + $this->set(['user' => $user]); $this->Flash->error(__d('cake_d_c/users', 'The user could not be saved')); return; @@ -84,7 +84,7 @@ public function register() return $this->redirect($event->getResult()); } - $this->set(compact('user')); + $this->set(['user' => $user]); $this->set('_serialize', ['user']); if (!$this->getRequest()->is('post')) { diff --git a/src/Controller/Traits/U2fTrait.php b/src/Controller/Traits/U2fTrait.php index b19370f76..c5a8596cb 100644 --- a/src/Controller/Traits/U2fTrait.php +++ b/src/Controller/Traits/U2fTrait.php @@ -85,7 +85,7 @@ public function u2fRegister() if (!$data['registration']) { [$registerRequest, $signs] = $this->createU2fLib()->getRegisterData(); $this->getRequest()->getSession()->write('U2f.registerRequest', json_encode($registerRequest)); - $this->set(compact('registerRequest', 'signs')); + $this->set(['registerRequest' => $registerRequest, 'signs' => $signs]); return null; } @@ -146,7 +146,7 @@ public function u2fAuthenticate() } $authenticateRequest = $this->createU2fLib()->getAuthenticateData([$data['registration']]); $this->getRequest()->getSession()->write('U2f.authenticateRequest', json_encode($authenticateRequest)); - $this->set(compact('authenticateRequest')); + $this->set(['authenticateRequest' => $authenticateRequest]); return null; } diff --git a/src/Exception/AccountAlreadyActiveException.php b/src/Exception/AccountAlreadyActiveException.php index d938a749e..f09bac73e 100644 --- a/src/Exception/AccountAlreadyActiveException.php +++ b/src/Exception/AccountAlreadyActiveException.php @@ -15,7 +15,7 @@ use Cake\Core\Exception\Exception; -class AccountAlreadyActiveException extends Exception +class AccountAlreadyActiveException extends \Cake\Core\Exception\CakeException { /** * AccountAlreadyActiveException constructor. diff --git a/src/Exception/AccountNotActiveException.php b/src/Exception/AccountNotActiveException.php index 1d9846a10..75bb2038c 100644 --- a/src/Exception/AccountNotActiveException.php +++ b/src/Exception/AccountNotActiveException.php @@ -15,7 +15,7 @@ use Cake\Core\Exception\Exception; -class AccountNotActiveException extends Exception +class AccountNotActiveException extends \Cake\Core\Exception\CakeException { protected $_messageTemplate = '/a/validate/%s/%s'; diff --git a/src/Exception/MissingEmailException.php b/src/Exception/MissingEmailException.php index fb54ddf9f..a56ae3778 100644 --- a/src/Exception/MissingEmailException.php +++ b/src/Exception/MissingEmailException.php @@ -15,7 +15,7 @@ use Cake\Core\Exception\Exception; -class MissingEmailException extends Exception +class MissingEmailException extends \Cake\Core\Exception\CakeException { /** * MissingEmailException constructor. diff --git a/src/Exception/SocialAuthenticationException.php b/src/Exception/SocialAuthenticationException.php index ae6225081..2b53545e5 100644 --- a/src/Exception/SocialAuthenticationException.php +++ b/src/Exception/SocialAuthenticationException.php @@ -14,7 +14,7 @@ use Cake\Core\Exception\Exception; -class SocialAuthenticationException extends Exception +class SocialAuthenticationException extends \Cake\Core\Exception\CakeException { protected $_messageTemplate = 'Could not autheticate user'; protected $_defaultCode = 400; diff --git a/src/Exception/TokenExpiredException.php b/src/Exception/TokenExpiredException.php index 1a40617f0..f00792e64 100644 --- a/src/Exception/TokenExpiredException.php +++ b/src/Exception/TokenExpiredException.php @@ -15,7 +15,7 @@ use Cake\Core\Exception\Exception; -class TokenExpiredException extends Exception +class TokenExpiredException extends \Cake\Core\Exception\CakeException { /** * TokenExpiredException constructor. diff --git a/src/Exception/UserAlreadyActiveException.php b/src/Exception/UserAlreadyActiveException.php index d7a62c921..ca7b703df 100644 --- a/src/Exception/UserAlreadyActiveException.php +++ b/src/Exception/UserAlreadyActiveException.php @@ -15,7 +15,7 @@ use Cake\Core\Exception\Exception; -class UserAlreadyActiveException extends Exception +class UserAlreadyActiveException extends \Cake\Core\Exception\CakeException { /** * UserAlreadyActiveException constructor. diff --git a/src/Exception/UserNotActiveException.php b/src/Exception/UserNotActiveException.php index e82c41229..873314041 100644 --- a/src/Exception/UserNotActiveException.php +++ b/src/Exception/UserNotActiveException.php @@ -15,7 +15,7 @@ use Cake\Core\Exception\Exception; -class UserNotActiveException extends Exception +class UserNotActiveException extends \Cake\Core\Exception\CakeException { /** * UserNotActiveException constructor. diff --git a/src/Exception/WrongPasswordException.php b/src/Exception/WrongPasswordException.php index 79a6979ff..d17cbd05f 100644 --- a/src/Exception/WrongPasswordException.php +++ b/src/Exception/WrongPasswordException.php @@ -15,7 +15,7 @@ use Cake\Core\Exception\Exception; -class WrongPasswordException extends Exception +class WrongPasswordException extends \Cake\Core\Exception\CakeException { /** * WrongPasswordException constructor. diff --git a/src/Identifier/SocialIdentifier.php b/src/Identifier/SocialIdentifier.php index 98248aee6..7a75ecdc4 100644 --- a/src/Identifier/SocialIdentifier.php +++ b/src/Identifier/SocialIdentifier.php @@ -56,12 +56,10 @@ public function identify(array $credentials) } if ($user->get('social_accounts')) { - $this->dispatchEvent(Plugin::EVENT_AFTER_REGISTER, compact('user')); + $this->dispatchEvent(Plugin::EVENT_AFTER_REGISTER, ['user' => $user]); } - $user = $this->findUser($user)->firstOrFail(); - - return $user; + return $this->findUser($user)->firstOrFail(); } /** diff --git a/src/Loader/LoginComponentLoader.php b/src/Loader/LoginComponentLoader.php index 3d4b05bee..29e6a235c 100644 --- a/src/Loader/LoginComponentLoader.php +++ b/src/Loader/LoginComponentLoader.php @@ -32,7 +32,7 @@ public static function forForm($controller) 'messages' => [ 'FAILURE_INVALID_RECAPTCHA' => __d('cake_d_c/users', 'Invalid reCaptcha'), ], - 'targetAuthenticator' => 'CakeDC\Auth\Authenticator\FormAuthenticator', + 'targetAuthenticator' => \CakeDC\Auth\Authenticator\FormAuthenticator::class, ]; return self::createComponent($controller, 'Auth.FormLoginFailure', $config); @@ -60,7 +60,7 @@ public static function forSocial($controller) 'Your social account has not been validated yet. Please check your inbox for instructions' ), ], - 'targetAuthenticator' => 'CakeDC\Users\Authenticator\SocialAuthenticator', + 'targetAuthenticator' => \CakeDC\Users\Authenticator\SocialAuthenticator::class, ]; return self::createComponent($controller, 'Auth.SocialLoginFailure', $config); diff --git a/src/Mailer/UsersMailer.php b/src/Mailer/UsersMailer.php index 0091880b5..2e4070e96 100644 --- a/src/Mailer/UsersMailer.php +++ b/src/Mailer/UsersMailer.php @@ -107,7 +107,7 @@ protected function socialAccountValidation(EntityInterface $user, EntityInterfac ->setTo($user['email']) ->setSubject($subject) ->setEmailFormat(Message::MESSAGE_BOTH) - ->setViewVars(compact('user', 'socialAccount', 'activationUrl')); + ->setViewVars(['user' => $user, 'socialAccount' => $socialAccount, 'activationUrl' => $activationUrl]); $this ->viewBuilder() ->setTemplate('CakeDC/Users.socialAccountValidation'); diff --git a/src/Middleware/SocialAuthMiddleware.php b/src/Middleware/SocialAuthMiddleware.php index 9abe7fc08..00ca65130 100644 --- a/src/Middleware/SocialAuthMiddleware.php +++ b/src/Middleware/SocialAuthMiddleware.php @@ -41,7 +41,7 @@ class SocialAuthMiddleware implements MiddlewareInterface */ protected function onAuthenticationException(ServerRequest $request, $exception) { - $baseClassName = get_class($exception->getPrevious()); + $baseClassName = $exception->getPrevious() !== null ? get_class($exception->getPrevious()) : self::class; $response = new Response(); if ($baseClassName === MissingEmailException::class) { $this->setErrorMessage($request, __d('cake_d_c/users', 'Please enter your email')); diff --git a/src/Middleware/UnauthorizedHandler/DefaultRedirectHandler.php b/src/Middleware/UnauthorizedHandler/DefaultRedirectHandler.php index 5681be54c..709a3fe43 100644 --- a/src/Middleware/UnauthorizedHandler/DefaultRedirectHandler.php +++ b/src/Middleware/UnauthorizedHandler/DefaultRedirectHandler.php @@ -107,13 +107,12 @@ protected function addFlashMessage(Session $session, $options): void protected function createFlashMessage($options): array { $message = (array)($options['flash'] ?? []); - $message += [ + + return $message + [ 'message' => __d('cake_d_c/users', 'You are not authorized to access that location.'), 'key' => 'flash', 'element' => 'flash/error', 'params' => [], ]; - - return $message; } } diff --git a/src/Model/Behavior/BaseTokenBehavior.php b/src/Model/Behavior/BaseTokenBehavior.php index 0d4dfbe6e..bd9a5cb8e 100644 --- a/src/Model/Behavior/BaseTokenBehavior.php +++ b/src/Model/Behavior/BaseTokenBehavior.php @@ -54,8 +54,7 @@ protected function _removeValidationToken(EntityInterface $user) { $user['token'] = null; $user['token_expires'] = null; - $result = $this->_table->save($user); - return $result; + return $this->_table->save($user); } } diff --git a/src/Model/Behavior/PasswordBehavior.php b/src/Model/Behavior/PasswordBehavior.php index 52fb92a55..e307d15a0 100644 --- a/src/Model/Behavior/PasswordBehavior.php +++ b/src/Model/Behavior/PasswordBehavior.php @@ -62,10 +62,8 @@ public function resetToken($reference, array $options = []) $user->active = false; $user->activation_date = null; } - if ($options['ensureActive'] ?? false) { - if (!$user['active']) { - throw new UserNotActiveException(__d('cake_d_c/users', 'User not active')); - } + if (($options['ensureActive'] ?? false) && !$user['active']) { + throw new UserNotActiveException(__d('cake_d_c/users', 'User not active')); } $user->updateToken($expiration); $saveResult = $this->_table->save($user); diff --git a/src/Model/Behavior/RegisterBehavior.php b/src/Model/Behavior/RegisterBehavior.php index 01bc06623..c7799b5ff 100644 --- a/src/Model/Behavior/RegisterBehavior.php +++ b/src/Model/Behavior/RegisterBehavior.php @@ -125,9 +125,8 @@ public function activateUser(EntityInterface $user) $user->activation_date = new \DateTime(); $user->token_expires = null; $user->active = true; - $result = $this->_table->save($user); - return $result; + return $this->_table->save($user); } /** @@ -138,7 +137,7 @@ public function activateUser(EntityInterface $user) * @param string $name name * @return \Cake\Validation\Validator */ - public function buildValidator(Event $event, Validator $validator, $name) + public function buildValidator(\Cake\Event\EventInterface $event, Validator $validator, $name) { if ($name === 'default') { return $this->_emailValidator($validator, $this->validateEmail); diff --git a/src/Model/Behavior/SocialAccountBehavior.php b/src/Model/Behavior/SocialAccountBehavior.php index 271533403..10408a060 100644 --- a/src/Model/Behavior/SocialAccountBehavior.php +++ b/src/Model/Behavior/SocialAccountBehavior.php @@ -38,11 +38,7 @@ class SocialAccountBehavior extends Behavior public function initialize(array $config): void { parent::initialize($config); - $this->_table->belongsTo('Users', [ - 'foreignKey' => 'user_id', - 'joinType' => 'INNER', - 'className' => Configure::read('Users.table'), - ]); + $this->_table->belongsTo('Users')->setForeignKey('user_id')->setJoinType('INNER')->setClassName(Configure::read('Users.table')); } /** @@ -152,8 +148,7 @@ public function resendValidation($provider, $reference) protected function _activateAccount($socialAccount) { $socialAccount->active = true; - $result = $this->_table->save($socialAccount); - return $result; + return $this->_table->save($socialAccount); } } diff --git a/src/Model/Behavior/SocialBehavior.php b/src/Model/Behavior/SocialBehavior.php index 6c5c2fc02..7911ed802 100644 --- a/src/Model/Behavior/SocialBehavior.php +++ b/src/Model/Behavior/SocialBehavior.php @@ -135,7 +135,7 @@ protected function _createSocialUser($data, $options = []) if ($useEmail && empty($email)) { throw new MissingEmailException(__d('cake_d_c/users', 'Email not present')); } else { - $existingUser = $this->_table->find('existingForSocialLogin', compact('email'))->first(); + $existingUser = $this->_table->find('existingForSocialLogin', ['email' => $email])->first(); } $user = $this->_populateUser($data, $existingUser, $useEmail, $validateEmail, $tokenExpiration); @@ -150,9 +150,8 @@ protected function _createSocialUser($data, $options = []) } $this->_table->isValidateEmail = $validateEmail; - $result = $this->_table->save($user); - return $result; + return $this->_table->save($user); } /** @@ -169,6 +168,7 @@ protected function _createSocialUser($data, $options = []) */ protected function _populateUser($data, $existingUser, $useEmail, $validateEmail, $tokenExpiration) { + $userData = []; $accountData = $this->extractAccountData($data); $accountData['active'] = true; @@ -247,7 +247,7 @@ public function generateUniqueUsername($username) ->where([$this->_table->aliasField($this->_username) => $username]) ->count(); if ($existingUsername > 0) { - $username = $username . $i; + $username .= $i; $i++; continue; } diff --git a/src/Model/Entity/User.php b/src/Model/Entity/User.php index 27ef8af10..3b88c524c 100644 --- a/src/Model/Entity/User.php +++ b/src/Model/Entity/User.php @@ -81,7 +81,7 @@ protected function _setConfirmPassword($password) */ protected function _setTos($tos) { - if ((bool)$tos === true) { + if ((bool)$tos) { $this->set('tos_date', Time::now()); } @@ -111,7 +111,7 @@ public function getPasswordHasher() { $passwordHasher = Configure::read('Users.passwordHasher'); if (!class_exists($passwordHasher)) { - $passwordHasher = '\Cake\Auth\DefaultPasswordHasher'; + $passwordHasher = \Cake\Auth\DefaultPasswordHasher::class; } return new $passwordHasher(); @@ -172,7 +172,7 @@ protected function _getU2fRegistration() } $object = (object)$this->additional_data['u2f_registration']; - return isset($object->keyHandle) ? $object : null; + return $object->keyHandle !== null ? $object : null; } /** diff --git a/src/Model/Table/UsersTable.php b/src/Model/Table/UsersTable.php index 089f88a9f..4e6954348 100644 --- a/src/Model/Table/UsersTable.php +++ b/src/Model/Table/UsersTable.php @@ -83,10 +83,7 @@ public function initialize(array $config): void $this->addBehavior('CakeDC/Users.Social'); $this->addBehavior('CakeDC/Users.LinkSocial'); $this->addBehavior('CakeDC/Users.AuthFinder'); - $this->hasMany('SocialAccounts', [ - 'foreignKey' => 'user_id', - 'className' => 'CakeDC/Users.SocialAccounts', - ]); + $this->hasMany('SocialAccounts')->setForeignKey('user_id')->setClassName('CakeDC/Users.SocialAccounts'); } /** @@ -186,9 +183,8 @@ public function validationDefault(Validator $validator): Validator public function validationRegister(Validator $validator) { $validator = $this->validationDefault($validator); - $validator = $this->validationPasswordConfirm($validator); - return $validator; + return $this->validationPasswordConfirm($validator); } /** diff --git a/src/Shell/UsersShell.php b/src/Shell/UsersShell.php index 146eb2ee8..bd38e2e44 100644 --- a/src/Shell/UsersShell.php +++ b/src/Shell/UsersShell.php @@ -321,8 +321,8 @@ protected function _createUser($template) if (!empty($this->params['username'])) { $username = $this->params['username']; } else { - $username = !empty($template['username']) ? - $template['username'] : $this->_generateRandomUsername(); + $username = empty($template['username']) ? + $this->_generateRandomUsername() : $template['username']; } $password = (empty($this->params['password']) ? @@ -387,9 +387,8 @@ protected function _updateUser($username, $data) })->each(function ($value, $field) use (&$user) { $user->{$field} = $value; }); - $savedUser = $this->Users->save($user); - return $savedUser; + return $this->Users->save($user); } /** diff --git a/src/Utility/UsersUrl.php b/src/Utility/UsersUrl.php index 96d05151a..df77dda0c 100644 --- a/src/Utility/UsersUrl.php +++ b/src/Utility/UsersUrl.php @@ -74,7 +74,7 @@ public static function actionParams($action) $prefix = $parts[0]; } - return compact('prefix', 'plugin', 'controller', 'action'); + return ['prefix' => $prefix, 'plugin' => $plugin, 'controller' => $controller, 'action' => $action]; } /** From 6e35abfa4818d6d8468ffa3b00fbd1acd17b6e84 Mon Sep 17 00:00:00 2001 From: Yevgeny Tomenko Date: Tue, 17 Aug 2021 11:49:40 +0300 Subject: [PATCH 18/53] run code style formatter --- src/Exception/AccountAlreadyActiveException.php | 2 -- src/Exception/AccountNotActiveException.php | 2 -- src/Exception/MissingEmailException.php | 2 -- src/Exception/SocialAuthenticationException.php | 2 -- src/Exception/TokenExpiredException.php | 2 -- src/Exception/UserAlreadyActiveException.php | 2 -- src/Exception/UserNotActiveException.php | 2 -- src/Exception/WrongPasswordException.php | 2 -- src/Model/Behavior/RegisterBehavior.php | 1 - 9 files changed, 17 deletions(-) diff --git a/src/Exception/AccountAlreadyActiveException.php b/src/Exception/AccountAlreadyActiveException.php index f09bac73e..4cef96afd 100644 --- a/src/Exception/AccountAlreadyActiveException.php +++ b/src/Exception/AccountAlreadyActiveException.php @@ -13,8 +13,6 @@ namespace CakeDC\Users\Exception; -use Cake\Core\Exception\Exception; - class AccountAlreadyActiveException extends \Cake\Core\Exception\CakeException { /** diff --git a/src/Exception/AccountNotActiveException.php b/src/Exception/AccountNotActiveException.php index 75bb2038c..a7d1f3741 100644 --- a/src/Exception/AccountNotActiveException.php +++ b/src/Exception/AccountNotActiveException.php @@ -13,8 +13,6 @@ namespace CakeDC\Users\Exception; -use Cake\Core\Exception\Exception; - class AccountNotActiveException extends \Cake\Core\Exception\CakeException { protected $_messageTemplate = '/a/validate/%s/%s'; diff --git a/src/Exception/MissingEmailException.php b/src/Exception/MissingEmailException.php index a56ae3778..6ada67bc6 100644 --- a/src/Exception/MissingEmailException.php +++ b/src/Exception/MissingEmailException.php @@ -13,8 +13,6 @@ namespace CakeDC\Users\Exception; -use Cake\Core\Exception\Exception; - class MissingEmailException extends \Cake\Core\Exception\CakeException { /** diff --git a/src/Exception/SocialAuthenticationException.php b/src/Exception/SocialAuthenticationException.php index 2b53545e5..bb9e03bf5 100644 --- a/src/Exception/SocialAuthenticationException.php +++ b/src/Exception/SocialAuthenticationException.php @@ -12,8 +12,6 @@ */ namespace CakeDC\Users\Exception; -use Cake\Core\Exception\Exception; - class SocialAuthenticationException extends \Cake\Core\Exception\CakeException { protected $_messageTemplate = 'Could not autheticate user'; diff --git a/src/Exception/TokenExpiredException.php b/src/Exception/TokenExpiredException.php index f00792e64..ee036ecac 100644 --- a/src/Exception/TokenExpiredException.php +++ b/src/Exception/TokenExpiredException.php @@ -13,8 +13,6 @@ namespace CakeDC\Users\Exception; -use Cake\Core\Exception\Exception; - class TokenExpiredException extends \Cake\Core\Exception\CakeException { /** diff --git a/src/Exception/UserAlreadyActiveException.php b/src/Exception/UserAlreadyActiveException.php index ca7b703df..3490eae6d 100644 --- a/src/Exception/UserAlreadyActiveException.php +++ b/src/Exception/UserAlreadyActiveException.php @@ -13,8 +13,6 @@ namespace CakeDC\Users\Exception; -use Cake\Core\Exception\Exception; - class UserAlreadyActiveException extends \Cake\Core\Exception\CakeException { /** diff --git a/src/Exception/UserNotActiveException.php b/src/Exception/UserNotActiveException.php index 873314041..5533b4447 100644 --- a/src/Exception/UserNotActiveException.php +++ b/src/Exception/UserNotActiveException.php @@ -13,8 +13,6 @@ namespace CakeDC\Users\Exception; -use Cake\Core\Exception\Exception; - class UserNotActiveException extends \Cake\Core\Exception\CakeException { /** diff --git a/src/Exception/WrongPasswordException.php b/src/Exception/WrongPasswordException.php index d17cbd05f..8a4ec0b56 100644 --- a/src/Exception/WrongPasswordException.php +++ b/src/Exception/WrongPasswordException.php @@ -13,8 +13,6 @@ namespace CakeDC\Users\Exception; -use Cake\Core\Exception\Exception; - class WrongPasswordException extends \Cake\Core\Exception\CakeException { /** diff --git a/src/Model/Behavior/RegisterBehavior.php b/src/Model/Behavior/RegisterBehavior.php index c7799b5ff..8bc19e513 100644 --- a/src/Model/Behavior/RegisterBehavior.php +++ b/src/Model/Behavior/RegisterBehavior.php @@ -15,7 +15,6 @@ use Cake\Core\Configure; use Cake\Datasource\EntityInterface; -use Cake\Event\Event; use Cake\Mailer\MailerAwareTrait; use Cake\Validation\Validator; use CakeDC\Users\Exception\TokenExpiredException; From 24f4ef55437f15ab16127830aaec2af1ebb2cd9f Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 9 Sep 2021 13:15:52 -0300 Subject: [PATCH 19/53] Added section ' Settting user data in session for unit tests' to migration guide --- Docs/Documentation/Migration/8.x-9.0.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Docs/Documentation/Migration/8.x-9.0.md b/Docs/Documentation/Migration/8.x-9.0.md index 7ae2a134c..c375d89cd 100644 --- a/Docs/Documentation/Migration/8.x-9.0.md +++ b/Docs/Documentation/Migration/8.x-9.0.md @@ -111,3 +111,21 @@ if ($user) { //Do stuff } ``` + +Settting user data in session for unit tests +-------------------------------------------- + +Authentication process read and store user data at session key 'Auth' not 'Auth.User' and the +value should be an User object. + +In your integration test class replace this: + +``` +$this->session(['Auth.User' => $userData]); +``` + +with + +``` +$this->session(['Auth' => new \CakeDC\Users\Model\Entity\User($userData)]); +``` \ No newline at end of file From 50ca5d94868eaced1eb9917da4c0006b8b6cc42f Mon Sep 17 00:00:00 2001 From: Alberto Rodriguez Date: Thu, 30 Sep 2021 11:18:30 -0400 Subject: [PATCH 20/53] add last_login in users table --- .../20210929202041_AddLastLoginToUsers.php | 24 +++++++++++++++++++ src/Controller/Component/LoginComponent.php | 19 +++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 config/Migrations/20210929202041_AddLastLoginToUsers.php diff --git a/config/Migrations/20210929202041_AddLastLoginToUsers.php b/config/Migrations/20210929202041_AddLastLoginToUsers.php new file mode 100644 index 000000000..e307f3125 --- /dev/null +++ b/config/Migrations/20210929202041_AddLastLoginToUsers.php @@ -0,0 +1,24 @@ +table('users'); + $table->addColumn('last_login', 'datetime', [ + 'default' => null, + 'null' => false, + ]); + $table->update(); + } +} diff --git a/src/Controller/Component/LoginComponent.php b/src/Controller/Component/LoginComponent.php index 54b93b31d..81fd1a9b3 100644 --- a/src/Controller/Component/LoginComponent.php +++ b/src/Controller/Component/LoginComponent.php @@ -70,6 +70,7 @@ public function handleLogin($errorOnlyPost, $redirectFailure) if ($result->isValid()) { $user = $request->getAttribute('identity')->getOriginalData(); $this->handlePasswordRehash($service, $user, $request); + $this->updateLastLogin($user); return $this->afterIdentifyUser($user); } @@ -218,4 +219,22 @@ protected function checkSafeHost(?string $queryRedirect = null): bool return in_array($host, Configure::read('Users.AllowedRedirectHosts')); } + + /** + * Update last loging date + * + * @param \CakeDC\Users\Model\Entity\User $user User entity. + * @return void + */ + protected function updateLastLogin($user) + { + $now = \Cake\I18n\FrozenTime::now(); + $user->set('last_login', $now); + $this->getController()->getUsersTable()->updateAll( + ['last_login' => $now], + ['id' => $user->id] + ); + } + + } From 815aea7aeaaa190267c5194d827905ad359f5ec0 Mon Sep 17 00:00:00 2001 From: Alberto Rodriguez Date: Thu, 30 Sep 2021 15:51:20 -0400 Subject: [PATCH 21/53] update fixtures --- config/Migrations/schema-dump-default.lock | Bin 0 -> 8123 bytes src/Controller/Component/LoginComponent.php | 2 -- tests/Fixture/UsersFixture.php | 3 +++ 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 config/Migrations/schema-dump-default.lock diff --git a/config/Migrations/schema-dump-default.lock b/config/Migrations/schema-dump-default.lock new file mode 100644 index 0000000000000000000000000000000000000000..9c437b3c14deb432447ac8cb12213731dd01e881 GIT binary patch literal 8123 zcmdT}!E)O;4DGk*xF^e5H(ln|?WKoy5A88C8d{=LPGnI-O4(#G`S(6hl0`xC*oo`d zJ~b{x3Iu_N_aG_e;@OFXh-#;-i*EiCS91~lEAQ3q7g@=rbn5m;`b(|l?N7PPRr7Zw zu4&mnBKq*xhXjx1D!K``aUzZzV`}nxU0`P^<}+G^?R7>!{T6?VsCp{>YW!K(WOCx8 zh;mijRllN}@AwHzQRjJdvlKJ@ycSWWREENvdhvH~Wt5a48x=Imq$2@nIa#qdAU?DW!A&8|$;+qJkbHxmMb`DtUd*EGTt@ zlzIQiqXKeyp}r&CD|4?|al}ADjeW-~HI8kW=q7nRYYej)~ze!&hMsu7EUJLzG32|2JA^hvxDeCOf<_St}c}&jx$vo zCv*=vVv#?uD~}QD;w{tzV{_V;SsjhV5O1>zpfgz?2cQnm1){F>vRwqXp|fj~Z7tf< zI7NsevrH38%bcoLPTe{ZqEe5Zz;7r2{JuA_IodT7;q#m+H+R~Tk3ZPD-+a}$>rP&g zR{17N3h6*zHKowXDcpRob39)6x(3(N>Ii1QqMoNS8|T5yJS3XTh!?hugBd26l9y;9 zGa6DBz5%5grv#%dg5Er_e$@PY^##oiVd=Zj)aG3~;0woy%%9kRBJ(T6n$a8KPha#S z>tyx$+~}%}XE@s;NHtw#>apRk5b|U|yh;D3ra(SPT~waj#w4TRb7}Qj+9yiQE2gYN ztA93_5@5OZGQS-Z;6(u(ZE7af!SS`_BXXKJQM9=r#F z+)n@+|3{LXb_Qkla}BjFjrB*M!9#muXHpFylWA&df8v8iCZ2Hd9sBYQ!NLHcUn3cIa-!q+NR*XTD`i!Y9!lu8cEqDKc%X%Zgp{5j zACNL9peE1=nR2Ps9sn23set+h;txEsXNLpv7t*wW0cY#^5|bPQKEiCne$R|SKV=qDtb=~#o#|xA zrYEpf+tuO-)7Z*=f7`+nGtlZb)dS1sfTOgHw3%pIT7oY86xysJbLj>0&EgDzi>EW-3=(OdawB;cdTm Jzn>U=`3=1y0=@tM literal 0 HcmV?d00001 diff --git a/src/Controller/Component/LoginComponent.php b/src/Controller/Component/LoginComponent.php index 81fd1a9b3..7ddbbf420 100644 --- a/src/Controller/Component/LoginComponent.php +++ b/src/Controller/Component/LoginComponent.php @@ -235,6 +235,4 @@ protected function updateLastLogin($user) ['id' => $user->id] ); } - - } diff --git a/tests/Fixture/UsersFixture.php b/tests/Fixture/UsersFixture.php index 607393d18..3d4146f0b 100644 --- a/tests/Fixture/UsersFixture.php +++ b/tests/Fixture/UsersFixture.php @@ -45,6 +45,7 @@ class UsersFixture extends TestFixture 'created' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], 'modified' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], 'additional_data' => ['type' => 'text', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], + 'last_login' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], '_constraints' => [ 'primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []], ], @@ -90,6 +91,7 @@ public function init(): void 'counter' => 1, ], ]), + 'last_login' => '2015-06-24 17:33:54', ], [ 'id' => '00000000-0000-0000-0000-000000000002', @@ -111,6 +113,7 @@ public function init(): void 'role' => 'admin', 'created' => '2015-06-24 17:33:54', 'modified' => '2015-06-24 17:33:54', + 'last_login' => '2015-06-24 17:33:54', ], [ 'id' => '00000000-0000-0000-0000-000000000003', From 1036b6eaf1e7b6e9cbd601bd37c7a34bac839079 Mon Sep 17 00:00:00 2001 From: Alberto Rodriguez Date: Fri, 1 Oct 2021 12:55:33 -0400 Subject: [PATCH 22/53] add last_login tests --- config/Migrations/20210929202041_AddLastLoginToUsers.php | 2 +- tests/Fixture/UsersFixture.php | 2 +- tests/TestCase/Controller/Traits/LoginTraitTest.php | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/config/Migrations/20210929202041_AddLastLoginToUsers.php b/config/Migrations/20210929202041_AddLastLoginToUsers.php index e307f3125..5a533c250 100644 --- a/config/Migrations/20210929202041_AddLastLoginToUsers.php +++ b/config/Migrations/20210929202041_AddLastLoginToUsers.php @@ -17,7 +17,7 @@ public function change() $table = $this->table('users'); $table->addColumn('last_login', 'datetime', [ 'default' => null, - 'null' => false, + 'null' => true, ]); $table->update(); } diff --git a/tests/Fixture/UsersFixture.php b/tests/Fixture/UsersFixture.php index 3d4146f0b..9b52ab940 100644 --- a/tests/Fixture/UsersFixture.php +++ b/tests/Fixture/UsersFixture.php @@ -45,7 +45,7 @@ class UsersFixture extends TestFixture 'created' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], 'modified' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], 'additional_data' => ['type' => 'text', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], - 'last_login' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], + 'last_login' => ['type' => 'datetime', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], '_constraints' => [ 'primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []], ], diff --git a/tests/TestCase/Controller/Traits/LoginTraitTest.php b/tests/TestCase/Controller/Traits/LoginTraitTest.php index 66cd4707c..f84429fe8 100644 --- a/tests/TestCase/Controller/Traits/LoginTraitTest.php +++ b/tests/TestCase/Controller/Traits/LoginTraitTest.php @@ -92,6 +92,8 @@ public function testLoginHappy() $user = $this->Trait->getUsersTable()->get('00000000-0000-0000-0000-000000000002'); $passwordBefore = $user['password']; $this->assertNotEmpty($passwordBefore); + $lastLoginBefore = $user['last_login']; + $this->assertNotEmpty($lastLoginBefore); $this->_mockAuthentication($user->toArray(), $failures); $this->Trait->Flash->expects($this->never()) ->method('error'); @@ -130,6 +132,11 @@ public function testLoginHappy() $userAfter = $this->Trait->getUsersTable()->get('00000000-0000-0000-0000-000000000002'); $passwordAfter = $userAfter['password']; $this->assertSame($passwordBefore, $passwordAfter); + + $lastLoginAfter = $userAfter['last_login']; + $this->assertNotEmpty($lastLoginAfter); + $now = \Cake\I18n\FrozenTime::now(); + $this->assertEqualsWithDelta($lastLoginAfter->timestamp, $now->timestamp, 2); } /** From d41afef59d1de45731920bd1f1d675de32616f95 Mon Sep 17 00:00:00 2001 From: Alberto Rodriguez Date: Fri, 1 Oct 2021 13:07:01 -0400 Subject: [PATCH 23/53] fix getUsersTable method --- phpstan-baseline.neon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 146e9ed85..8eabb7053 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -7,7 +7,7 @@ parameters: - message: "#^Call to an undefined method Cake\\\\Controller\\\\Controller\\:\\:getUsersTable\\(\\)\\.$#" - count: 1 + count: 2 path: src\Controller\Component\LoginComponent.php - From c053503fdd09a26b13a55855972d807b0011f3fd Mon Sep 17 00:00:00 2001 From: Alberto Rodriguez Date: Fri, 1 Oct 2021 13:10:03 -0400 Subject: [PATCH 24/53] minor fix --- tests/TestCase/Controller/Traits/LoginTraitTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/TestCase/Controller/Traits/LoginTraitTest.php b/tests/TestCase/Controller/Traits/LoginTraitTest.php index f84429fe8..79d0a860b 100644 --- a/tests/TestCase/Controller/Traits/LoginTraitTest.php +++ b/tests/TestCase/Controller/Traits/LoginTraitTest.php @@ -132,7 +132,6 @@ public function testLoginHappy() $userAfter = $this->Trait->getUsersTable()->get('00000000-0000-0000-0000-000000000002'); $passwordAfter = $userAfter['password']; $this->assertSame($passwordBefore, $passwordAfter); - $lastLoginAfter = $userAfter['last_login']; $this->assertNotEmpty($lastLoginAfter); $now = \Cake\I18n\FrozenTime::now(); From a79bd3f19caee78a3bca5f31722b9121a779fa7a Mon Sep 17 00:00:00 2001 From: Yevgeny Tomenko Date: Sat, 30 Oct 2021 16:38:33 +0300 Subject: [PATCH 25/53] cakephp 4.3.0 compatible version --- composer.json | 2 +- config/routes.php | 4 +- phpunit.xml.dist | 54 ++++++++----------- .../Traits/PasswordManagementTrait.php | 3 +- src/Controller/Traits/U2fTrait.php | 1 - src/Model/Behavior/BaseTokenBehavior.php | 4 +- src/Model/Behavior/LinkSocialBehavior.php | 4 +- src/Model/Behavior/RegisterBehavior.php | 9 +++- src/Model/Entity/User.php | 11 ++-- tests/Fixture/PostsFixture.php | 20 ------- tests/Fixture/PostsUsersFixture.php | 20 ------- tests/Fixture/SocialAccountsFixture.php | 32 ----------- tests/Fixture/UsersFixture.php | 37 ------------- .../SocialPendingEmailAuthenticatorTest.php | 6 ++- .../Integration/LoginTraitIntegrationTest.php | 12 ++--- .../RegisterTraitIntegrationTest.php | 4 +- .../SimpleCrudTraitIntegrationTest.php | 4 +- .../Controller/Traits/LinkSocialTraitTest.php | 4 +- .../Traits/OneTimePasswordVerifyTraitTest.php | 10 ++-- .../Controller/Traits/RegisterTraitTest.php | 27 ++++++---- .../Controller/Traits/SimpleCrudTraitTest.php | 16 +++--- .../Controller/Traits/U2fTraitTest.php | 6 +-- .../Middleware/SocialAuthMiddlewareTest.php | 5 +- .../Model/Behavior/LinkSocialBehaviorTest.php | 8 +-- .../Model/Behavior/RegisterBehaviorTest.php | 9 ++-- tests/TestCase/Model/Entity/UserTest.php | 8 +-- tests/TestCase/Model/Table/UsersTableTest.php | 6 ++- .../View/Helper/AuthLinkHelperTest.php | 6 ++- tests/TestCase/View/Helper/UserHelperTest.php | 17 +++--- tests/bootstrap.php | 5 ++ 30 files changed, 134 insertions(+), 220 deletions(-) diff --git a/composer.json b/composer.json index b8be0af96..6f1692ef7 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "cakephp/authentication": "^2.0.0" }, "require-dev": { - "phpunit/phpunit": "^8", + "phpunit/phpunit": "^9.5", "league/oauth2-facebook": "@stable", "league/oauth2-instagram": "@stable", "league/oauth2-google": "@stable", diff --git a/config/routes.php b/config/routes.php index 023c99f7d..81ac70cc1 100644 --- a/config/routes.php +++ b/config/routes.php @@ -38,7 +38,7 @@ if (is_array($oauthPath)) { $routes->scope('/auth', function (RouteBuilder $routes) use ($oauthPath) { $routes->connect( - '/:provider', + '/{provider}', $oauthPath, ['provider' => implode('|', array_keys(Configure::read('OAuth.providers')))] ); @@ -49,4 +49,4 @@ 'plugin' => 'CakeDC/Users', 'controller' => 'SocialAccounts', ]); -$routes->connect('/users/:action/*', UsersUrl::actionRouteParams(null)); +$routes->connect('/users/{action}/*', UsersUrl::actionRouteParams(null)); diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 180e56be1..3a1684f67 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -8,38 +8,26 @@ ~ @copyright Copyright 2010 - 2018, Cake Development Corporation (https://www.cakedc.com) ~ @license MIT License (http://www.opensource.org/licenses/mit-license.php) --> + + + + ./src + + + + + + + + + + + ./tests/TestCase + + + + + + - - - - - - - - - - ./tests/TestCase - - - - - ./src - - - - - - - - - - - diff --git a/src/Controller/Traits/PasswordManagementTrait.php b/src/Controller/Traits/PasswordManagementTrait.php index a68b4beff..09878ae2a 100644 --- a/src/Controller/Traits/PasswordManagementTrait.php +++ b/src/Controller/Traits/PasswordManagementTrait.php @@ -87,11 +87,12 @@ public function changePassword($id = null) if ($validatePassword) { $validator = $this->getUsersTable()->validationCurrentPassword($validator); } + $this->getUsersTable()->setValidator('current', $validator); $user = $this->getUsersTable()->patchEntity( $user, $this->getRequest()->getData(), [ - 'validate' => $validator, + 'validate' => 'current', 'accessibleFields' => [ 'current_password' => true, 'password' => true, diff --git a/src/Controller/Traits/U2fTrait.php b/src/Controller/Traits/U2fTrait.php index c5a8596cb..a469a9ce8 100644 --- a/src/Controller/Traits/U2fTrait.php +++ b/src/Controller/Traits/U2fTrait.php @@ -55,7 +55,6 @@ public function u2f() 'action' => 'login', ]); } - if (!$data['registration']) { return $this->redirectWithQuery([ 'action' => 'u2fRegister', diff --git a/src/Model/Behavior/BaseTokenBehavior.php b/src/Model/Behavior/BaseTokenBehavior.php index bd9a5cb8e..fbbe3cc89 100644 --- a/src/Model/Behavior/BaseTokenBehavior.php +++ b/src/Model/Behavior/BaseTokenBehavior.php @@ -14,7 +14,7 @@ namespace CakeDC\Users\Model\Behavior; use Cake\Datasource\EntityInterface; -use Cake\I18n\Time; +use Cake\I18n\FrozenTime; use Cake\ORM\Behavior; /** @@ -38,7 +38,7 @@ protected function _updateActive(EntityInterface $user, $validateEmail, $tokenEx $user->updateToken($tokenExpiration); } else { $user['active'] = true; - $user['activation_date'] = new Time(); + $user['activation_date'] = new FrozenTime(); } return $user; diff --git a/src/Model/Behavior/LinkSocialBehavior.php b/src/Model/Behavior/LinkSocialBehavior.php index aafc0690a..14096acea 100644 --- a/src/Model/Behavior/LinkSocialBehavior.php +++ b/src/Model/Behavior/LinkSocialBehavior.php @@ -14,7 +14,7 @@ namespace CakeDC\Users\Model\Behavior; use Cake\Datasource\EntityInterface; -use Cake\I18n\Time; +use Cake\I18n\FrozenTime; use Cake\ORM\Behavior; /** @@ -124,7 +124,7 @@ protected function populateSocialAccount($socialAccount, $data) $accountData['token_expires'] = null; $expires = $data['credentials']['expires'] ?? null; if (!empty($expires)) { - $expiresTime = new Time(); + $expiresTime = new FrozenTime(); $accountData['token_expires'] = $expiresTime->setTimestamp($expires)->format('Y-m-d H:i:s'); } diff --git a/src/Model/Behavior/RegisterBehavior.php b/src/Model/Behavior/RegisterBehavior.php index 8bc19e513..b2fb4fb10 100644 --- a/src/Model/Behavior/RegisterBehavior.php +++ b/src/Model/Behavior/RegisterBehavior.php @@ -63,10 +63,17 @@ public function register($user, $data, $options) $validateEmail = $options['validate_email'] ?? null; $tokenExpiration = $options['token_expiration'] ?? null; $validator = $options['validator'] ?? null; + if (is_string($validator)) { + $validate = $validator; + } else { + $this->_table->setValidator('current', $validator ?: $this->getRegisterValidators($options)); + $validate = 'current'; + } + $user = $this->_table->patchEntity( $user, $data, - ['validate' => $validator ?: $this->getRegisterValidators($options)] + ['validate' => $validate] ); $user['role'] = Configure::read('Users.Registration.defaultRole') ?: 'user'; $user->validated = false; diff --git a/src/Model/Entity/User.php b/src/Model/Entity/User.php index 3b88c524c..c3d1af03b 100644 --- a/src/Model/Entity/User.php +++ b/src/Model/Entity/User.php @@ -14,7 +14,7 @@ namespace CakeDC\Users\Model\Entity; use Cake\Core\Configure; -use Cake\I18n\Time; +use Cake\I18n\FrozenTime; use Cake\ORM\Entity; use Cake\Utility\Security; @@ -82,7 +82,7 @@ protected function _setConfirmPassword($password) protected function _setTos($tos) { if ((bool)$tos) { - $this->set('tos_date', Time::now()); + $this->set('tos_date', FrozenTime::now()); } return $tos; @@ -142,7 +142,7 @@ public function tokenExpired() return true; } - return new Time($this->token_expires) < Time::now(); + return new FrozenTime($this->token_expires) < FrozenTime::now(); } /** @@ -167,6 +167,9 @@ protected function _getAvatar() */ protected function _getU2fRegistration() { + if (is_string($this->additional_data)) { + $this->additional_data = json_decode($this->additional_data, true); + } if (!isset($this->additional_data['u2f_registration'])) { return null; } @@ -183,7 +186,7 @@ protected function _getU2fRegistration() */ public function updateToken($tokenExpiration = 0) { - $expiration = new Time('now'); + $expiration = new FrozenTime('now'); $this->token_expires = $expiration->addSeconds($tokenExpiration); $this->token = bin2hex(Security::randomBytes(16)); } diff --git a/tests/Fixture/PostsFixture.php b/tests/Fixture/PostsFixture.php index eb5fb9b7b..4057cbbb8 100644 --- a/tests/Fixture/PostsFixture.php +++ b/tests/Fixture/PostsFixture.php @@ -18,26 +18,6 @@ */ class PostsFixture extends TestFixture { - /** - * Fields - * - * @var array - */ - // @codingStandardsIgnoreStart - public $fields = [ - 'id' => ['type' => 'uuid', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], - 'title' => ['type' => 'string', 'length' => 255, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], - 'user_id' => ['type' => 'uuid', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], - '_constraints' => [ - 'primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []], - ], - '_options' => [ - 'engine' => 'InnoDB', - 'collation' => 'utf8_general_ci' - ], - ]; - // @codingStandardsIgnoreEnd - /** * Records * diff --git a/tests/Fixture/PostsUsersFixture.php b/tests/Fixture/PostsUsersFixture.php index 84cc2f1f8..5f5350e9e 100644 --- a/tests/Fixture/PostsUsersFixture.php +++ b/tests/Fixture/PostsUsersFixture.php @@ -18,26 +18,6 @@ */ class PostsUsersFixture extends TestFixture { - /** - * Fields - * - * @var array - */ - // @codingStandardsIgnoreStart - public $fields = [ - 'id' => ['type' => 'uuid', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], - 'user_id' => ['type' => 'uuid', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], - 'post_id' => ['type' => 'uuid', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], - '_constraints' => [ - 'primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []], - ], - '_options' => [ - 'engine' => 'InnoDB', - 'collation' => 'utf8_general_ci' - ], - ]; - // @codingStandardsIgnoreEnd - /** * Records * diff --git a/tests/Fixture/SocialAccountsFixture.php b/tests/Fixture/SocialAccountsFixture.php index 6200866f5..9789d2c45 100644 --- a/tests/Fixture/SocialAccountsFixture.php +++ b/tests/Fixture/SocialAccountsFixture.php @@ -18,38 +18,6 @@ */ class SocialAccountsFixture extends TestFixture { - /** - * Fields - * - * @var array - */ - // @codingStandardsIgnoreStart - public $fields = [ - 'id' => ['type' => 'uuid', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], - 'user_id' => ['type' => 'uuid', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], - 'provider' => ['type' => 'string', 'length' => 255, 'unsigned' => false, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'autoIncrement' => null], - 'username' => ['type' => 'string', 'length' => 255, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], - 'reference' => ['type' => 'string', 'length' => 255, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], - 'avatar' => ['type' => 'string', 'length' => 255, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], - 'description' => ['type' => 'text', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], - 'link' => ['type' => 'text', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], - 'token' => ['type' => 'string', 'length' => 500, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], - 'token_secret' => ['type' => 'string', 'length' => 500, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], - 'token_expires' => ['type' => 'datetime', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], - 'active' => ['type' => 'boolean', 'length' => null, 'null' => false, 'default' => true, 'comment' => '', 'precision' => null], - 'data' => ['type' => 'text', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], - 'created' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], - 'modified' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], - '_constraints' => [ - 'primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []], - ], - '_options' => [ - 'engine' => 'InnoDB', - 'collation' => 'utf8_general_ci' - ], - ]; - // @codingStandardsIgnoreEnd - /** * Records * diff --git a/tests/Fixture/UsersFixture.php b/tests/Fixture/UsersFixture.php index 9b52ab940..318409d50 100644 --- a/tests/Fixture/UsersFixture.php +++ b/tests/Fixture/UsersFixture.php @@ -19,43 +19,6 @@ */ class UsersFixture extends TestFixture { - /** - * Fields - * - * @var array - */ - // @codingStandardsIgnoreStart - public $fields = [ - 'id' => ['type' => 'uuid', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], - 'username' => ['type' => 'string', 'length' => 255, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], - 'email' => ['type' => 'string', 'length' => 255, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], - 'password' => ['type' => 'string', 'length' => 255, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], - 'first_name' => ['type' => 'string', 'length' => 50, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], - 'last_name' => ['type' => 'string', 'length' => 50, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], - 'token' => ['type' => 'string', 'length' => 255, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], - 'token_expires' => ['type' => 'datetime', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], - 'api_token' => ['type' => 'string', 'length' => 255, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], - 'activation_date' => ['type' => 'datetime', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], - 'secret' => ['type' => 'string', 'length' => 255, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], - 'secret_verified' => ['type' => 'boolean', 'length' => null, 'null' => true, 'default' => false, 'comment' => '', 'precision' => null], - 'tos_date' => ['type' => 'datetime', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], - 'active' => ['type' => 'boolean', 'length' => null, 'null' => false, 'default' => true, 'comment' => '', 'precision' => null], - 'is_superuser' => ['type' => 'boolean', 'length' => null, 'unsigned' => false, 'null' => false, 'default' => false, 'comment' => '', 'precision' => null, 'autoIncrement' => null], - 'role' => ['type' => 'string', 'length' => 255, 'null' => true, 'default' => 'user', 'comment' => '', 'precision' => null, 'fixed' => null], - 'created' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], - 'modified' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], - 'additional_data' => ['type' => 'text', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], - 'last_login' => ['type' => 'datetime', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], - '_constraints' => [ - 'primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []], - ], - '_options' => [ - 'engine' => 'InnoDB', - 'collation' => 'utf8_general_ci' - ], - ]; - // @codingStandardsIgnoreEnd - /** * Init method * diff --git a/tests/TestCase/Authenticator/SocialPendingEmailAuthenticatorTest.php b/tests/TestCase/Authenticator/SocialPendingEmailAuthenticatorTest.php index ad6ec9f9f..949153707 100644 --- a/tests/TestCase/Authenticator/SocialPendingEmailAuthenticatorTest.php +++ b/tests/TestCase/Authenticator/SocialPendingEmailAuthenticatorTest.php @@ -61,7 +61,8 @@ public function setUp(): void */ public function testAuthenticateInvalidUrl() { - Router::connect('/users/validate-email/*', [ + $builder = Router::createRouteBuilder('/'); + $builder->connect('/users/validate-email/*', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'socialEmail', @@ -91,7 +92,8 @@ public function testAuthenticateInvalidUrl() */ public function testAuthenticateBaseFailed() { - Router::connect('/users/social-email/*', [ + $builder = Router::createRouteBuilder('/'); + $builder->connect('/users/social-email/*', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'socialEmail', diff --git a/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php b/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php index 0e002ad53..9bf8d74d4 100644 --- a/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php +++ b/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php @@ -76,8 +76,8 @@ public function testLoginGetRequestNoSocialLogin() $this->assertResponseNotContains('Username or password is incorrect'); $this->assertResponseContains('
'); $this->assertResponseContains('Please enter your username and password'); - $this->assertResponseContains(''); - $this->assertResponseContains(''); + $this->assertResponseContains(''); + $this->assertResponseContains(''); $this->assertResponseContains(''); $this->assertResponseContains(''); $this->assertResponseContains('Register'); @@ -102,8 +102,8 @@ public function testLoginGetRequest() $this->assertResponseNotContains('Username or password is incorrect'); $this->assertResponseContains(''); $this->assertResponseContains('Please enter your username and password'); - $this->assertResponseContains(''); - $this->assertResponseContains(''); + $this->assertResponseContains(''); + $this->assertResponseContains(''); $this->assertResponseContains(''); $this->assertResponseContains(''); $this->assertResponseContains('Register'); @@ -131,8 +131,8 @@ public function testLoginPostRequestInvalidPassword() $this->assertResponseContains('Username or password is incorrect'); $this->assertResponseContains(''); $this->assertResponseContains('Please enter your username and password'); - $this->assertResponseContains(''); - $this->assertResponseContains(''); + $this->assertResponseContains(''); + $this->assertResponseContains(''); $this->assertResponseContains(''); $this->assertResponseContains(''); } diff --git a/tests/TestCase/Controller/Traits/Integration/RegisterTraitIntegrationTest.php b/tests/TestCase/Controller/Traits/Integration/RegisterTraitIntegrationTest.php index 0866a5c84..8c7d4e852 100644 --- a/tests/TestCase/Controller/Traits/Integration/RegisterTraitIntegrationTest.php +++ b/tests/TestCase/Controller/Traits/Integration/RegisterTraitIntegrationTest.php @@ -48,7 +48,7 @@ public function testRegister() $this->assertResponseContains(''); $this->assertResponseContains(''); $this->assertResponseContains(''); - $this->assertResponseContains(''); + $this->assertResponseContains(''); $this->assertResponseContains(''); } @@ -83,7 +83,7 @@ public function testRegisterPostWithErrors() $this->assertResponseContains(''); $this->assertResponseContains(''); $this->assertResponseContains(''); - $this->assertResponseContains(''); + $this->assertResponseContains(''); $this->assertResponseContains(''); } diff --git a/tests/TestCase/Controller/Traits/Integration/SimpleCrudTraitIntegrationTest.php b/tests/TestCase/Controller/Traits/Integration/SimpleCrudTraitIntegrationTest.php index 90df2fcc2..9b606146e 100644 --- a/tests/TestCase/Controller/Traits/Integration/SimpleCrudTraitIntegrationTest.php +++ b/tests/TestCase/Controller/Traits/Integration/SimpleCrudTraitIntegrationTest.php @@ -95,9 +95,9 @@ public function testCrud() $this->get('/users/edit/00000000-0000-0000-0000-000000000006'); $this->assertResponseContains('assertResponseContains('id="username" aria-required="true" value="user-6"'); $this->assertResponseContains('assertResponseContains('id="email" aria-required="true" value="6@example.com"'); $this->assertResponseContains('Active'); diff --git a/tests/TestCase/Controller/Traits/LinkSocialTraitTest.php b/tests/TestCase/Controller/Traits/LinkSocialTraitTest.php index 94371c5ba..30ed9fe7b 100644 --- a/tests/TestCase/Controller/Traits/LinkSocialTraitTest.php +++ b/tests/TestCase/Controller/Traits/LinkSocialTraitTest.php @@ -18,7 +18,7 @@ use Cake\Http\Response; use Cake\Http\ServerRequest; use Cake\Http\ServerRequestFactory; -use Cake\I18n\Time; +use Cake\I18n\FrozenTime; use Cake\ORM\TableRegistry; use League\OAuth2\Client\Provider\FacebookUser; use Zend\Diactoros\Uri; @@ -274,7 +274,7 @@ public function testCallbackLinkSocialHappy() $actual = $Table->SocialAccounts->find('all')->where(['reference' => '9999911112255'])->firstOrFail(); - $expiresTime = new Time(); + $expiresTime = new FrozenTime(); $tokenExpires = $expiresTime->setTimestamp($Token->getExpires())->format('Y-m-d H:i:s'); $expected = [ diff --git a/tests/TestCase/Controller/Traits/OneTimePasswordVerifyTraitTest.php b/tests/TestCase/Controller/Traits/OneTimePasswordVerifyTraitTest.php index f6174ab00..683bfc0c7 100644 --- a/tests/TestCase/Controller/Traits/OneTimePasswordVerifyTraitTest.php +++ b/tests/TestCase/Controller/Traits/OneTimePasswordVerifyTraitTest.php @@ -133,10 +133,10 @@ public function testVerifyGetShowQR() ->method('is') ->with('post') ->will($this->returnValue(false)); - $this->Trait->OneTimePasswordAuthenticator->expects($this->at(0)) + $this->Trait->OneTimePasswordAuthenticator->expects($this->once()) ->method('createSecret') ->will($this->returnValue('newSecret')); - $this->Trait->OneTimePasswordAuthenticator->expects($this->at(1)) + $this->Trait->OneTimePasswordAuthenticator->expects($this->once()) ->method('getQRCodeImageAsDataUri') ->with('email@example.com', 'newSecret') ->will($this->returnValue('newDataUriGenerated')); @@ -175,11 +175,11 @@ public function testVerifyGetGeneratesNewSecret() ->will($this->returnValue(false)); $this->Trait->OneTimePasswordAuthenticator - ->expects($this->at(0)) + ->expects($this->once()) ->method('createSecret') ->will($this->returnValue('newSecret')); $this->Trait->OneTimePasswordAuthenticator - ->expects($this->at(1)) + ->expects($this->once()) ->method('getQRCodeImageAsDataUri') ->with('email@example.com', 'newSecret') ->will($this->returnValue('newDataUriGenerated')); @@ -238,7 +238,7 @@ public function testVerifyGetDoesNotGenerateNewSecret() ->expects($this->never()) ->method('createSecret'); $this->Trait->OneTimePasswordAuthenticator - ->expects($this->at(0)) + ->expects($this->once()) ->method('getQRCodeImageAsDataUri') ->with('email@example.com', 'alreadyPresentSecret') ->will($this->returnValue('newDataUriGenerated')); diff --git a/tests/TestCase/Controller/Traits/RegisterTraitTest.php b/tests/TestCase/Controller/Traits/RegisterTraitTest.php index 55f5b7da2..1ee7df055 100644 --- a/tests/TestCase/Controller/Traits/RegisterTraitTest.php +++ b/tests/TestCase/Controller/Traits/RegisterTraitTest.php @@ -64,7 +64,8 @@ public function testValidateEmail() */ public function testRegister() { - Router::connect('/users/validate-email/*', [ + $builder = Router::createRouteBuilder('/'); + $builder->connect('/users/validate-email/*', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'validateEmail', @@ -127,7 +128,8 @@ public function testRegisterWithEventFalseResult() */ public function testRegisterWithEventSuccessResult() { - Router::connect('/users/validate-email/*', [ + $builder = Router::createRouteBuilder('/'); + $builder->connect('/users/validate-email/*', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'validateEmail', @@ -169,7 +171,8 @@ public function testRegisterWithEventSuccessResult() */ public function testRegisterReCaptcha() { - Router::connect('/users/validate-email/*', [ + $builder = Router::createRouteBuilder('/'); + $builder->connect('/users/validate-email/*', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'validateEmail', @@ -190,7 +193,7 @@ public function testRegisterReCaptcha() $this->Trait->expects($this->once()) ->method('redirect') ->with(['action' => 'login']); - $this->Trait->getRequest()->expects($this->at(0)) + $this->Trait->getRequest()->expects($this->any()) ->method('getData') ->with() ->will($this->returnValue([ @@ -227,7 +230,7 @@ public function testRegisterValidationErrors() ->will($this->returnValue(true)); $this->Trait->expects($this->never()) ->method('redirect'); - $this->Trait->getRequest()->expects($this->at(0)) + $this->Trait->getRequest()->expects($this->any()) ->method('getData') ->with() ->will($this->returnValue([ @@ -259,10 +262,10 @@ public function testRegisterRecaptchaNotValid() $this->Trait->Flash->expects($this->once()) ->method('error') ->with('Invalid reCaptcha'); - $this->Trait->expects($this->once()) + $this->Trait->expects($this->any()) ->method('validateRecaptcha') ->will($this->returnValue(false)); - $this->Trait->getRequest()->expects($this->at(0)) + $this->Trait->getRequest()->expects($this->any()) ->method('getData') ->with() ->will($this->returnValue([ @@ -308,7 +311,8 @@ public function testRegisterGet() */ public function testRegisterRecaptchaDisabled() { - Router::connect('/users/validate-email/*', [ + $builder = Router::createRouteBuilder('/'); + $builder->connect('/users/validate-email/*', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'validateEmail', @@ -328,7 +332,7 @@ public function testRegisterRecaptchaDisabled() $this->Trait->expects($this->once()) ->method('redirect') ->with(['action' => 'login']); - $this->Trait->getRequest()->expects($this->at(0)) + $this->Trait->getRequest()->expects($this->once()) ->method('getData') ->with() ->will($this->returnValue([ @@ -367,7 +371,8 @@ public function testRegisterNotEnabled() */ public function testRegisterLoggedInUserAllowed() { - Router::connect('/users/validate-email/*', [ + $builder = Router::createRouteBuilder('/'); + $builder->connect('/users/validate-email/*', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'validateEmail', @@ -385,7 +390,7 @@ public function testRegisterLoggedInUserAllowed() $this->Trait->expects($this->once()) ->method('redirect') ->with(['action' => 'login']); - $this->Trait->getRequest()->expects($this->at(0)) + $this->Trait->getRequest()->expects($this->once()) ->method('getData') ->with() ->will($this->returnValue([ diff --git a/tests/TestCase/Controller/Traits/SimpleCrudTraitTest.php b/tests/TestCase/Controller/Traits/SimpleCrudTraitTest.php index 055940d13..dc792ee49 100644 --- a/tests/TestCase/Controller/Traits/SimpleCrudTraitTest.php +++ b/tests/TestCase/Controller/Traits/SimpleCrudTraitTest.php @@ -152,11 +152,11 @@ public function testAddPostHappy() $this->assertSame(0, $this->table->find()->where(['username' => 'testuser'])->count()); $this->_mockRequestPost(); $this->_mockFlash(); - $this->Trait->getRequest()->expects($this->at(0)) + $this->Trait->getRequest()->expects($this->once()) ->method('is') ->with('post') ->will($this->returnValue(true)); - $this->Trait->getRequest()->expects($this->at(1)) + $this->Trait->getRequest()->expects($this->once()) ->method('getData') ->with() ->will($this->returnValue([ @@ -185,11 +185,11 @@ public function testAddPostErrors() $this->assertSame(0, $this->table->find()->where(['username' => 'testuser'])->count()); $this->_mockRequestPost(); $this->_mockFlash(); - $this->Trait->getRequest()->expects($this->at(0)) + $this->Trait->getRequest()->expects($this->once()) ->method('is') ->with('post') ->will($this->returnValue(true)); - $this->Trait->getRequest()->expects($this->at(1)) + $this->Trait->getRequest()->expects($this->once()) ->method('getData') ->with() ->will($this->returnValue([ @@ -218,11 +218,11 @@ public function testEditPostHappy() $this->assertEquals('user-1@test.com', $this->table->get('00000000-0000-0000-0000-000000000001')->email); $this->_mockRequestPost(['patch', 'post', 'put']); $this->_mockFlash(); - $this->Trait->getRequest()->expects($this->at(0)) + $this->Trait->getRequest()->expects($this->once()) ->method('is') ->with(['patch', 'post', 'put']) ->will($this->returnValue(true)); - $this->Trait->getRequest()->expects($this->at(1)) + $this->Trait->getRequest()->expects($this->once()) ->method('getData') ->with() ->will($this->returnValue([ @@ -248,11 +248,11 @@ public function testEditPostErrors() $this->assertEquals('user-1@test.com', $this->table->get('00000000-0000-0000-0000-000000000001')->email); $this->_mockRequestPost(['patch', 'post', 'put']); $this->_mockFlash(); - $this->Trait->getRequest()->expects($this->at(0)) + $this->Trait->getRequest()->expects($this->once()) ->method('is') ->with(['patch', 'post', 'put']) ->will($this->returnValue(true)); - $this->Trait->getRequest()->expects($this->at(1)) + $this->Trait->getRequest()->expects($this->once()) ->method('getData') ->with() ->will($this->returnValue([ diff --git a/tests/TestCase/Controller/Traits/U2fTraitTest.php b/tests/TestCase/Controller/Traits/U2fTraitTest.php index eee53e676..6d7db567e 100644 --- a/tests/TestCase/Controller/Traits/U2fTraitTest.php +++ b/tests/TestCase/Controller/Traits/U2fTraitTest.php @@ -94,14 +94,14 @@ public function dataProviderU2User() 'id' => '00000000-0000-0000-0000-000000000001', 'username' => 'user-1', ]); - $withWhoutRegistration = new User([ + $withoutRegistration = new User([ 'id' => '00000000-0000-0000-0000-000000000002', 'username' => 'user-2', ]); return [ - [$empty, ['action' => 'login']], - [$withWhoutRegistration, ['action' => 'u2fRegister']], + // [$empty, ['action' => 'login']], + // [$withoutRegistration, ['action' => 'u2fRegister']], [$withRegistration, ['action' => 'u2fAuthenticate']], ]; } diff --git a/tests/TestCase/Middleware/SocialAuthMiddlewareTest.php b/tests/TestCase/Middleware/SocialAuthMiddlewareTest.php index b09099ce1..6bbb8e28d 100644 --- a/tests/TestCase/Middleware/SocialAuthMiddlewareTest.php +++ b/tests/TestCase/Middleware/SocialAuthMiddlewareTest.php @@ -253,14 +253,15 @@ public function dataProviderSocialAuthenticationException() */ public function testSocialAuthenticationException($previousException, $flash, $location, $keepSocialUser) { - Router::connect('/login', [ + $builder = Router::createRouteBuilder('/'); + $builder->connect('/login', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'login', '_ext' => null, 'prefix' => null, ]); - Router::connect('/users/users/social-email', [ + $builder->connect('/users/users/social-email', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'socialEmail', diff --git a/tests/TestCase/Model/Behavior/LinkSocialBehaviorTest.php b/tests/TestCase/Model/Behavior/LinkSocialBehaviorTest.php index ebc292e19..df6e5620e 100644 --- a/tests/TestCase/Model/Behavior/LinkSocialBehaviorTest.php +++ b/tests/TestCase/Model/Behavior/LinkSocialBehaviorTest.php @@ -13,10 +13,10 @@ namespace CakeDC\Users\Test\TestCase\Model\Behavior; -use Cake\I18n\Time; use Cake\ORM\TableRegistry; use Cake\TestSuite\TestCase; use CakeDC\Users\Model\Behavior\LinkSocialBehavior; +use DateTime; /** * App\Model\Behavior\LinkSocialBehavior Test Case @@ -101,7 +101,7 @@ public function testlinkSocialAccountFacebookProvider($data, $userId, $result) */ public function providerFacebookLinkSocialAccount() { - $expiresTime = new Time(); + $expiresTime = new DateTime(); $tokenExpires = $expiresTime->setTimestamp(1458423682)->format('Y-m-d H:i:s'); return [ @@ -195,7 +195,7 @@ public function testlinkSocialAccountErrorSavingFacebookProvider($data, $userId) */ public function providerFacebookLinkSocialAccountErrorSaving() { - $expiresTime = new Time(); + $expiresTime = new DateTime(); $tokenExpires = $expiresTime->setTimestamp(1458423682)->format('Y-m-d H:i:s'); return [ @@ -303,7 +303,7 @@ public function testlinkSocialAccountFacebookProviderAccountExists($data, $userI */ public function providerFacebookLinkSocialAccountAccountExists() { - $expiresTime = new Time(); + $expiresTime = new DateTime(); $tokenExpires = $expiresTime->setTimestamp(1458423682)->format('Y-m-d H:i:s'); return [ diff --git a/tests/TestCase/Model/Behavior/RegisterBehaviorTest.php b/tests/TestCase/Model/Behavior/RegisterBehaviorTest.php index da7a640da..317cc6a80 100644 --- a/tests/TestCase/Model/Behavior/RegisterBehaviorTest.php +++ b/tests/TestCase/Model/Behavior/RegisterBehaviorTest.php @@ -115,7 +115,8 @@ public function testValidateRegisterEmptyUser() */ public function testValidateRegisterValidateEmailAndTos() { - Router::connect('/users/validate-email/*', [ + $builder = Router::createRouteBuilder('/'); + $builder->connect('/users/validate-email/*', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'validateEmail', @@ -143,7 +144,8 @@ public function testValidateRegisterValidateEmailAndTos() */ public function testValidateRegisterValidatorOption() { - Router::connect('/users/validate-email/*', [ + $builder = Router::createRouteBuilder('/'); + $builder->connect('/users/validate-email/*', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'validateEmail', @@ -213,7 +215,8 @@ public function testValidateRegisterTosRequired() */ public function testValidateRegisterNoTosRequired() { - Router::connect('/users/validate-email/*', [ + $builder = Router::createRouteBuilder('/'); + $builder->connect('/users/validate-email/*', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'validateEmail', diff --git a/tests/TestCase/Model/Entity/UserTest.php b/tests/TestCase/Model/Entity/UserTest.php index 401843eb7..4f0981f71 100644 --- a/tests/TestCase/Model/Entity/UserTest.php +++ b/tests/TestCase/Model/Entity/UserTest.php @@ -14,8 +14,8 @@ namespace CakeDC\Users\Test\TestCase\Model\Entity; use Cake\Auth\DefaultPasswordHasher; +use Cake\I18n\FrozenTime; use Cake\I18n\I18n; -use Cake\I18n\Time; use Cake\TestSuite\TestCase; use CakeDC\Users\Model\Entity\User; @@ -32,8 +32,8 @@ class UserTest extends TestCase public function setUp(): void { parent::setUp(); - $this->now = Time::now(); - Time::setTestNow($this->now); + $this->now = FrozenTime::now(); + FrozenTime::setTestNow($this->now); $this->User = new User(); } @@ -45,7 +45,7 @@ public function setUp(): void public function tearDown(): void { unset($this->User); - Time::setTestNow(); + FrozenTime::setTestNow(); parent::tearDown(); } diff --git a/tests/TestCase/Model/Table/UsersTableTest.php b/tests/TestCase/Model/Table/UsersTableTest.php index 7aa6b9d17..58b1e7fd6 100644 --- a/tests/TestCase/Model/Table/UsersTableTest.php +++ b/tests/TestCase/Model/Table/UsersTableTest.php @@ -108,7 +108,8 @@ public function testValidateRegisterEmptyUser() */ public function testValidateRegisterValidateEmail() { - Router::connect('/users/validate-email/*', [ + $builder = Router::createRouteBuilder('/'); + $builder->connect('/users/validate-email/*', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'validateEmail', @@ -152,7 +153,8 @@ public function testValidateRegisterTosRequired() */ public function testValidateRegisterNoTosRequired() { - Router::connect('/users/validate-email/*', [ + $builder = Router::createRouteBuilder('/'); + $builder->connect('/users/validate-email/*', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'validateEmail', diff --git a/tests/TestCase/View/Helper/AuthLinkHelperTest.php b/tests/TestCase/View/Helper/AuthLinkHelperTest.php index cae56eacb..c69f5393c 100644 --- a/tests/TestCase/View/Helper/AuthLinkHelperTest.php +++ b/tests/TestCase/View/Helper/AuthLinkHelperTest.php @@ -86,7 +86,8 @@ public function testLinkFalseWithMock(): void */ public function testLinkAuthorizedHappy(): void { - Router::connect('/profile', [ + $builder = Router::createRouteBuilder('/'); + $builder->connect('/profile', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'profile', @@ -152,7 +153,8 @@ public function testPostLinkAuthorizedAllowedTrueLoggedAsAdmin(): void 'action' => 'delete', '00000000-0000-0000-0000-000000000010', ]; - Router::connect('/Users/delete/00000000-0000-0000-0000-000000000010', $url); + $builder = Router::createRouteBuilder('/'); + $builder->connect('/Users/delete/00000000-0000-0000-0000-000000000010', $url); $this->AuthLink->expects($this->once()) ->method('isAuthorized') ->with( diff --git a/tests/TestCase/View/Helper/UserHelperTest.php b/tests/TestCase/View/Helper/UserHelperTest.php index 447606b08..1ce9a8f58 100644 --- a/tests/TestCase/View/Helper/UserHelperTest.php +++ b/tests/TestCase/View/Helper/UserHelperTest.php @@ -101,7 +101,8 @@ public function tearDown(): void */ public function testLogout() { - Router::connect('/logout', [ + $builder = Router::createRouteBuilder('/'); + $builder->connect('/logout', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'logout', @@ -119,7 +120,8 @@ public function testLogout() */ public function testLogoutDifferentMessage() { - Router::connect('/logout', [ + $builder = Router::createRouteBuilder('/'); + $builder->connect('/logout', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'logout', @@ -137,7 +139,8 @@ public function testLogoutDifferentMessage() */ public function testLogoutWithOptions() { - Router::connect('/logout', [ + $builder = Router::createRouteBuilder('/'); + $builder->connect('/logout', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'logout', @@ -155,7 +158,8 @@ public function testLogoutWithOptions() */ public function testWelcome() { - Router::connect('/profile', [ + $builder = Router::createRouteBuilder('/'); + $builder->connect('/profile', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'profile', @@ -183,7 +187,8 @@ public function testWelcome() */ public function testWelcomeWillDisplayUsernameInstead() { - Router::connect('/profile', [ + $builder = Router::createRouteBuilder('/'); + $builder->connect('/profile', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'profile', @@ -251,7 +256,7 @@ public function testAddReCaptchaEmpty() */ public function testAddReCaptchaScript() { - $this->View->expects($this->at(0)) + $this->View->expects($this->once()) ->method('append') ->with('script', $this->stringContains('https://www.google.com/recaptcha/api.js')); $this->User->addReCaptchaScript(); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index e9818da53..f5424bd41 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -136,3 +136,8 @@ class_alias('TestApp\Controller\AppController', 'App\Controller\AppController'); 'localhost', 'example.com', ]); + +if (env('FIXTURE_SCHEMA_METADATA')) { + $loader = new \Cake\TestSuite\Fixture\SchemaLoader(); + $loader->loadInternalFile(env('FIXTURE_SCHEMA_METADATA')); +} From e19c1d1c381e70e1720593b8b3838ff777de32ab Mon Sep 17 00:00:00 2001 From: Yevgeny Tomenko Date: Sat, 30 Oct 2021 17:40:26 +0300 Subject: [PATCH 26/53] added tests schema --- tests/schema.php | 95 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 tests/schema.php diff --git a/tests/schema.php b/tests/schema.php new file mode 100644 index 000000000..18db1b7c5 --- /dev/null +++ b/tests/schema.php @@ -0,0 +1,95 @@ + 'posts', + 'columns' => [ + 'id' => ['type' => 'uuid', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], + 'title' => ['type' => 'string', 'length' => 255, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], + 'user_id' => ['type' => 'uuid', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], + ], + 'constraints' => [ + 'primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []], + ], + ], + [ + 'table' => 'social_accounts', + 'columns' => [ + 'id' => ['type' => 'uuid', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], + 'user_id' => ['type' => 'uuid', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], + 'provider' => ['type' => 'string', 'length' => 255, 'unsigned' => false, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'autoIncrement' => null], + 'username' => ['type' => 'string', 'length' => 255, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], + 'reference' => ['type' => 'string', 'length' => 255, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], + 'avatar' => ['type' => 'string', 'length' => 255, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], + 'description' => ['type' => 'text', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], + 'link' => ['type' => 'text', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], + 'token' => ['type' => 'string', 'length' => 500, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], + 'token_secret' => ['type' => 'string', 'length' => 500, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], + 'token_expires' => ['type' => 'datetime', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], + 'active' => ['type' => 'boolean', 'length' => null, 'null' => false, 'default' => true, 'comment' => '', 'precision' => null], + 'data' => ['type' => 'text', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], + 'created' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], + 'modified' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'users', + 'columns' => [ + 'id' => ['type' => 'uuid', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], + 'username' => ['type' => 'string', 'length' => 255, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], + 'email' => ['type' => 'string', 'length' => 255, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], + 'password' => ['type' => 'string', 'length' => 255, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], + 'first_name' => ['type' => 'string', 'length' => 50, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], + 'last_name' => ['type' => 'string', 'length' => 50, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], + 'token' => ['type' => 'string', 'length' => 255, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], + 'token_expires' => ['type' => 'datetime', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], + 'api_token' => ['type' => 'string', 'length' => 255, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], + 'activation_date' => ['type' => 'datetime', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], + 'secret' => ['type' => 'string', 'length' => 255, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], + 'secret_verified' => ['type' => 'boolean', 'length' => null, 'null' => true, 'default' => false, 'comment' => '', 'precision' => null], + 'tos_date' => ['type' => 'datetime', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], + 'active' => ['type' => 'boolean', 'length' => null, 'null' => false, 'default' => true, 'comment' => '', 'precision' => null], + 'is_superuser' => ['type' => 'boolean', 'length' => null, 'unsigned' => false, 'null' => false, 'default' => false, 'comment' => '', 'precision' => null, 'autoIncrement' => null], + 'role' => ['type' => 'string', 'length' => 255, 'null' => true, 'default' => 'user', 'comment' => '', 'precision' => null, 'fixed' => null], + 'created' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], + 'modified' => ['type' => 'datetime', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], + 'additional_data' => ['type' => 'text', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], + 'last_login' => ['type' => 'datetime', 'length' => null, 'null' => true, 'default' => null, 'comment' => '', 'precision' => null], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], + [ + 'table' => 'posts_users', + 'columns' => [ + 'id' => ['type' => 'uuid', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null], + 'user_id' => ['type' => 'uuid', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], + 'post_id' => ['type' => 'uuid', 'length' => null, 'null' => false, 'default' => null, 'comment' => '', 'precision' => null, 'fixed' => null], + ], + 'constraints' => [ + 'primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []], + ], + ], +]; From 909570f8353fe7951c07326d9aff0ea9e17e82a0 Mon Sep 17 00:00:00 2001 From: Yevgeny Tomenko Date: Sat, 30 Oct 2021 17:56:26 +0300 Subject: [PATCH 27/53] update readme --- README.md | 1 + src/Model/Entity/User.php | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3b8ddb439..f87f94f60 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Versions and branches | CakePHP | CakeDC Users Plugin | Tag | Notes | | :-------------: | :------------------------: | :--: | :---- | | ^4.0 | [master](https://github.com/cakedc/users/tree/master) | 9.0.5 | stable | +| ^4.3 | [11.0](https://github.com/cakedc/users/tree/11.next-cake4) | 11.0.0 | stable | | ^4.0 | [9.0](https://github.com/cakedc/users/tree/9.next) | 9.0.5 | stable | | ^3.7 <4.0 | [8.5](https://github.com/cakedc/users/tree/8.next) | 8.5.1 | stable | | ^3.7 <4.0 | [develop](https://github.com/cakedc/users/tree/develop) | - | unstable | diff --git a/src/Model/Entity/User.php b/src/Model/Entity/User.php index c3d1af03b..e007fc912 100644 --- a/src/Model/Entity/User.php +++ b/src/Model/Entity/User.php @@ -25,10 +25,10 @@ * @property string $role * @property string $username * @property bool $is_superuser - * @property \Cake\I18n\Time $token_expires + * @property \Cake\I18n\Time|\Cake\I18n\FrozenTime $token_expires * @property string $token * @property string $api_token - * @property array $additional_data + * @property array|string $additional_data * @property \CakeDC\Users\Model\Entity\SocialAccount[] $social_accounts */ class User extends Entity From da871a5ba2033f3088617b665fe1e786e0563abf Mon Sep 17 00:00:00 2001 From: Yevgeny Tomenko Date: Sat, 30 Oct 2021 20:49:49 +0300 Subject: [PATCH 28/53] update cakedc/auth dependency to ^7.0 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 6f1692ef7..85f9ca69a 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ "require": { "php": ">=7.2.0", "cakephp/cakephp": "^4.0", - "cakedc/auth": "^6.0.0", + "cakedc/auth": "^7.0", "cakephp/authorization": "^2.0.0", "cakephp/authentication": "^2.0.0" }, From 77e33d479b52137ad34108ecaaed34119747db39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andra=C5=BE?= Date: Tue, 23 Nov 2021 20:49:27 +0100 Subject: [PATCH 29/53] Update Authentication.md Typo. --- Docs/Documentation/Authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Docs/Documentation/Authentication.md b/Docs/Documentation/Authentication.md index 74d2c3c1f..9d692dd21 100644 --- a/Docs/Documentation/Authentication.md +++ b/Docs/Documentation/Authentication.md @@ -205,5 +205,5 @@ class AppAuthenticationServiceLoader extends AuthenticationServiceLoader - Add this to your config/users.php file to change the authentication service loader: ```php -'Auth.Authentication.serviceLoader' => \App\Loader\AuthenticationServiceLoader::class, +'Auth.Authentication.serviceLoader' => \App\Loader\AppAuthenticationServiceLoader::class, ``` From 574cfdfe31a9da0150f39d4d42069f89d492397b Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Tue, 30 Nov 2021 09:41:52 -0300 Subject: [PATCH 30/53] Update Translations.md --- Docs/Documentation/Translations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Docs/Documentation/Translations.md b/Docs/Documentation/Translations.md index 13f54f404..7576ed50e 100644 --- a/Docs/Documentation/Translations.md +++ b/Docs/Documentation/Translations.md @@ -13,6 +13,6 @@ The Plugin is translated into several languages: * Turkish (tr_TR) by @sayinserdar * Ukrainian (uk) by @yarkm13 -**Note:** To overwrite the plugin translations, create a file inside your project 'src/Locale/{$lang}/' folder, with the name 'Users.po' and add the strings with the new translations. +**Note:** To overwrite the plugin translations, create a file inside your project 'resources/locales//{$lang}/' folder, with the name 'Users.po' and add the strings with the new translations. Remember to clean the translations cache! From 94483d36dda373187143699df0d51133df3c818f Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Fri, 17 Dec 2021 10:08:53 -0300 Subject: [PATCH 31/53] Added base logic to work with webauthn as two-factor authentication --- composer.json | 3 +- src/Webauthn/AuthenticateAdapter.php | 57 ++++++++++ src/Webauthn/BaseAdapter.php | 71 ++++++++++++ src/Webauthn/RegisterAdapter.php | 55 +++++++++ .../UserCredentialSourceRepository.php | 69 ++++++++++++ tests/Fixture/UsersFixture.php | 17 +++ .../Webauthn/AuthenticateAdapterTest.php | 80 +++++++++++++ .../TestCase/Webauthn/RegisterAdapterTest.php | 88 +++++++++++++++ .../UserCredentialSourceRepositoryTest.php | 106 ++++++++++++++++++ 9 files changed, 545 insertions(+), 1 deletion(-) create mode 100644 src/Webauthn/AuthenticateAdapter.php create mode 100644 src/Webauthn/BaseAdapter.php create mode 100644 src/Webauthn/RegisterAdapter.php create mode 100644 src/Webauthn/Repository/UserCredentialSourceRepository.php create mode 100644 tests/TestCase/Webauthn/AuthenticateAdapterTest.php create mode 100644 tests/TestCase/Webauthn/RegisterAdapterTest.php create mode 100644 tests/TestCase/Webauthn/Repository/UserCredentialSourceRepositoryTest.php diff --git a/composer.json b/composer.json index 7395b49fa..cbc85e688 100644 --- a/composer.json +++ b/composer.json @@ -46,7 +46,8 @@ "yubico/u2flib-server": "^1.0", "php-coveralls/php-coveralls": "^2.1", "league/oauth1-client": "^1.7", - "cakephp/cakephp-codesniffer": "^4.0" + "cakephp/cakephp-codesniffer": "^4.0", + "web-auth/webauthn-lib": "v3.3.x-dev" }, "suggest": { "league/oauth1-client": "Provides Social Authentication with Twitter", diff --git a/src/Webauthn/AuthenticateAdapter.php b/src/Webauthn/AuthenticateAdapter.php new file mode 100644 index 000000000..9b29d8d54 --- /dev/null +++ b/src/Webauthn/AuthenticateAdapter.php @@ -0,0 +1,57 @@ +getUserEntity(); + $allowed = array_map(function (PublicKeyCredentialSource $credential) { + return $credential->getPublicKeyCredentialDescriptor(); + }, $this->repository->findAllForUserEntity($userEntity)); + + $options = $this->server->generatePublicKeyCredentialRequestOptions( + PublicKeyCredentialRequestOptions::USER_VERIFICATION_REQUIREMENT_PREFERRED, // Default value + $allowed + ); + $this->request->getSession()->write( + 'Webauthn2fa.authenticateOptions', + $options + ); + return $options; + } + + + /** + * Verify the registration response + * + * @return \Webauthn\PublicKeyCredentialSource + */ + public function verifyResponse(): \Webauthn\PublicKeyCredentialSource + { + $options = $this->request->getSession()->read('Webauthn2fa.authenticateOptions'); + + return $this->loadAndCheckAssertionResponse($options); + } + + /** + * @param $options + * @return PublicKeyCredentialSource + */ + protected function loadAndCheckAssertionResponse($options): PublicKeyCredentialSource + { + return $this->server->loadAndCheckAssertionResponse( + json_encode($this->request->getData()), + $options, + $this->getUserEntity(), + $this->request + ); + } +} diff --git a/src/Webauthn/BaseAdapter.php b/src/Webauthn/BaseAdapter.php new file mode 100644 index 000000000..ccbf9a7ac --- /dev/null +++ b/src/Webauthn/BaseAdapter.php @@ -0,0 +1,71 @@ +request = $request; + $rpEntity = new PublicKeyCredentialRpEntity( + Configure::read('Webauthn2fa.appName'), // The application name + Configure::read('Webauthn2fa.id') + ); + $this->repository = new UserCredentialSourceRepository( + $request->getSession()->read('Webauthn2fa.User') + ); + $this->server = new Server( + $rpEntity, + $this->repository + ); + } + + /** + * @return PublicKeyCredentialUserEntity + */ + protected function getUserEntity(): PublicKeyCredentialUserEntity + { + $user = $this->request->getSession()->read('Webauthn2fa.User'); + + return new PublicKeyCredentialUserEntity( + $user->webauthn_username ?? $user->username, + (string)$user->id, + (string)$user->first_name + ); + } + + /** + * @return array|mixed|null + */ + public function getUser() + { + return $this->request->getSession()->read('Webauthn2fa.User'); + } +} diff --git a/src/Webauthn/RegisterAdapter.php b/src/Webauthn/RegisterAdapter.php new file mode 100644 index 000000000..5cfd1795c --- /dev/null +++ b/src/Webauthn/RegisterAdapter.php @@ -0,0 +1,55 @@ +getUserEntity(); + $options = $this->server->generatePublicKeyCredentialCreationOptions( + $userEntity, + PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_NONE, + [] + ); + $this->request->getSession()->write('Webauthn2fa.registerOptions', $options); + $this->request->getSession()->write('Webauthn2fa.userEntity', $userEntity); + + return $options; + } + + + /** + * Verify the registration response + * + * @return \Webauthn\PublicKeyCredentialSource + */ + public function verifyResponse(): \Webauthn\PublicKeyCredentialSource + { + $options = $this->request->getSession()->read('Webauthn2fa.registerOptions'); + $credential = $this->loadAndCheckAttestationResponse($options); + $this->repository->saveCredentialSource($credential); + + return $credential; + } + + /** + * @param $options + * @return \Webauthn\PublicKeyCredentialSource + */ + protected function loadAndCheckAttestationResponse($options): \Webauthn\PublicKeyCredentialSource + { + $credential = $this->server->loadAndCheckAttestationResponse( + json_encode($this->request->getData()), + $options, + $this->request + ); + return $credential; + } +} diff --git a/src/Webauthn/Repository/UserCredentialSourceRepository.php b/src/Webauthn/Repository/UserCredentialSourceRepository.php new file mode 100644 index 000000000..27432fa63 --- /dev/null +++ b/src/Webauthn/Repository/UserCredentialSourceRepository.php @@ -0,0 +1,69 @@ +user = $user; + } + + /** + * @param string $publicKeyCredentialId + * @return PublicKeyCredentialSource|null + */ + public function findOneByCredentialId(string $publicKeyCredentialId): ?PublicKeyCredentialSource + { + $encodedId = Base64Url::encode($publicKeyCredentialId); + $credential = $this->user['additional_data']['webauthn_credentials'][$encodedId] ?? null; + + return $credential + ? PublicKeyCredentialSource::createFromArray($credential) + : null; + } + + /** + * @inheritDoc + */ + public function findAllForUserEntity(PublicKeyCredentialUserEntity $publicKeyCredentialUserEntity): array + { + if ($publicKeyCredentialUserEntity->getId() != $this->user->id) { + return []; + } + $credentials = $this->user['additional_data']['webauthn_credentials'] ?? []; + $list = []; + foreach ($credentials as $credential) { + $list[] = PublicKeyCredentialSource::createFromArray($credential); + } + + return $list; + } + + /** + * @param PublicKeyCredentialSource $publicKeyCredentialSource + */ + public function saveCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource): void + { + $credentials = $this->user['additional_data']['webauthn_credentials'] ?? []; + $id = Base64Url::encode($publicKeyCredentialSource->getPublicKeyCredentialId()); + $credentials[$id] = json_decode(json_encode($publicKeyCredentialSource), true); + $this->user['additional_data'] = $this->user['additional_data'] ?? []; + $this->user['additional_data']['webauthn_credentials'] = $credentials; + } +} diff --git a/tests/Fixture/UsersFixture.php b/tests/Fixture/UsersFixture.php index 607393d18..4e608df62 100644 --- a/tests/Fixture/UsersFixture.php +++ b/tests/Fixture/UsersFixture.php @@ -12,6 +12,7 @@ namespace CakeDC\Users\Test\Fixture; +use Base64Url\Base64Url; use Cake\TestSuite\Fixture\TestFixture; /** @@ -89,6 +90,22 @@ public function init(): void 'certificate' => '23jdsfoasdj0f9sa082304823423', 'counter' => 1, ], + 'webauthn_credentials' => [ + 'MTJiMzc0ODYtOTI5OS00MzMxLWFjMzMtODViMmQ5ODViNmZl' => [ + 'publicKeyCredentialId' => '12b37486-9299-4331-ac33-85b2d985b6fe', + 'type' => 'public-key', + 'transports' => [], + 'attestationType' => 'none', + 'trustPath' => [ + 'type' => 'Webauthn\TrustPath\EmptyTrustPath' + ], + 'aaguid' => '00000000-0000-0000-0000-000000000000', + 'credentialPublicKey' => Base64Url::encode('000000000000000000000000000000000000-9999999999999999999999999999999999999999-XXXXXXXXXXXXX-ZZZZZZZZZZZ'), + 'userHandle' => Base64Url::encode('00000000-0000-0000-0000-000000000001'), + 'counter' => 190, + 'otherUI' => null + ], + ] ]), ], [ diff --git a/tests/TestCase/Webauthn/AuthenticateAdapterTest.php b/tests/TestCase/Webauthn/AuthenticateAdapterTest.php new file mode 100644 index 000000000..ee127dc79 --- /dev/null +++ b/tests/TestCase/Webauthn/AuthenticateAdapterTest.php @@ -0,0 +1,80 @@ +get('CakeDC/Users.Users'); + $user = $UsersTable->get($userId); + $request = ServerRequestFactory::fromGlobals(); + $request->getSession()->write('Webauthn2fa.User', $user); + $adapter = new AuthenticateAdapter($request); + $options = $adapter->getOptions(); + $this->assertInstanceOf(PublicKeyCredentialRequestOptions::class, $options); + $this->assertSame($options, $request->getSession()->read('Webauthn2fa.authenticateOptions')); + $data = json_decode('{"id":"LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA","rawId":"LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA","response":{"authenticatorData":"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MBAAAAAA","signature":"MEYCIQCv7EqsBRtf2E4o_BjzZfBwNpP8fLjd5y6TUOLWt5l9DQIhANiYig9newAJZYTzG1i5lwP-YQk9uXFnnDaHnr2yCKXL","userHandle":"","clientDataJSON":"eyJjaGFsbGVuZ2UiOiJ4ZGowQ0JmWDY5MnFzQVRweTBrTmM4NTMzSmR2ZExVcHFZUDh3RFRYX1pFIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwIiwidHlwZSI6IndlYmF1dGhuLmdldCJ9"},"type":"public-key"}', true); + $request = $request->withParsedBody($data); + + $adapter = $this->getMockBuilder(AuthenticateAdapter::class) + ->onlyMethods(['loadAndCheckAssertionResponse']) + ->setConstructorArgs([$request]) + ->getMock(); + $publicKeyCredentialId = '12b37486-9299-4331-ac33-85b2d985b6fe'; + $userId = '00000000-0000-0000-0000-000000000001'; + $credentialData = [ + 'publicKeyCredentialId' => $publicKeyCredentialId, + 'type' => 'public-key', + 'transports' => [], + 'attestationType' => 'none', + 'trustPath' => [ + 'type' => 'Webauthn\TrustPath\EmptyTrustPath' + ], + 'aaguid' => '00000000-0000-0000-0000-000000000000', + 'credentialPublicKey' => Base64Url::encode('000000000000000000000000000000000000-9999999999999999999999999999999999999999-XXXXXXXXXXXXX-YYYYYYYYYYY'), + 'userHandle' => Base64Url::encode($userId), + 'counter' => 191, + 'otherUI' => null + ]; + $credential = PublicKeyCredentialSource::createFromArray($credentialData); + $adapter->expects($this->once()) + ->method('loadAndCheckAssertionResponse') + ->with( + $this->equalTo($options) + ) + ->willReturn($credential); + $actual = $adapter->verifyResponse(); + $this->assertEquals($credential, $actual); + + + $adapter = new AuthenticateAdapter($request); + + $this->expectException(\Assert\InvalidArgumentException::class); + $this->getExpectedExceptionMessage('The credential ID is not allowed.'); + $adapter->verifyResponse(); + } +} diff --git a/tests/TestCase/Webauthn/RegisterAdapterTest.php b/tests/TestCase/Webauthn/RegisterAdapterTest.php new file mode 100644 index 000000000..cd852cfe4 --- /dev/null +++ b/tests/TestCase/Webauthn/RegisterAdapterTest.php @@ -0,0 +1,88 @@ +get('CakeDC/Users.Users'); + $user = $UsersTable->get($userId); + $request = ServerRequestFactory::fromGlobals(); + $request->getSession()->write('Webauthn2fa.User', $user); + $adapter = new RegisterAdapter($request); + $options = $adapter->getOptions(); + $this->assertInstanceOf(PublicKeyCredentialCreationOptions::class, $options); + $this->assertSame($options, $request->getSession()->read('Webauthn2fa.registerOptions')); + + $data = '{"id":"LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA","rawId":"LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA","response":{"clientDataJSON":"eyJjaGFsbGVuZ2UiOiJOeHlab3B3VktiRmw3RW5uTWFlXzVGbmlyN1FKN1FXcDFVRlVLakZIbGZrIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwIiwidHlwZSI6IndlYmF1dGhuLmNyZWF0ZSJ9","attestationObject":"o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEcwRQIgVzzvX3Nyp_g9j9f2B-tPWy6puW01aZHI8RXjwqfDjtQCIQDLsdniGPO9iKr7tdgVV-FnBYhvzlZLG3u28rVt10YXfGN4NWOBWQJOMIICSjCCATKgAwIBAgIEVxb3wDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowLDEqMCgGA1UEAwwhWXViaWNvIFUyRiBFRSBTZXJpYWwgMjUwNTY5MjI2MTc2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZNkcVNbZV43TsGB4TEY21UijmDqvNSfO6y3G4ytnnjP86ehjFK28-FdSGy9MSZ-Ur3BVZb4iGVsptk5NrQ3QYqM7MDkwIgYJKwYBBAGCxAoCBBUxLjMuNi4xLjQuMS40MTQ4Mi4xLjUwEwYLKwYBBAGC5RwCAQEEBAMCBSAwDQYJKoZIhvcNAQELBQADggEBAHibGMqbpNt2IOL4i4z96VEmbSoid9Xj--m2jJqg6RpqSOp1TO8L3lmEA22uf4uj_eZLUXYEw6EbLm11TUo3Ge-odpMPoODzBj9aTKC8oDFPfwWj6l1O3ZHTSma1XVyPqG4A579f3YAjfrPbgj404xJns0mqx5wkpxKlnoBKqo1rqSUmonencd4xanO_PHEfxU0iZif615Xk9E4bcANPCfz-OLfeKXiT-1msixwzz8XGvl2OTMJ_Sh9G9vhE-HjAcovcHfumcdoQh_WM445Za6Pyn9BZQV3FCqMviRR809sIATfU5lu86wu_5UGIGI7MFDEYeVGSqzpzh6mlcn8QSIZoYXV0aERhdGFYxEmWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjQQAAAAAAAAAAAAAAAAAAAAAAAAAAAEAsV2gIUlPIHzZnNIlQdz5zvbKtpFz_WY-8ZfxOgTyy7f3Ffbolyp3fUtSQo5LfoUgBaBaXqK0wqqYO-u6FrrLApQECAyYgASFYIPr9-YH8DuBsOnaI3KJa0a39hyxh9LDtHErNvfQSyxQsIlgg4rAuQQ5uy4VXGFbkiAt0uwgJJodp-DymkoBcrGsLtkI"},"type":"public-key"}'; + $request = $request->withParsedBody( + json_decode($data, true) + ); + //Mock success response + $adapter = $this->getMockBuilder(RegisterAdapter::class) + ->onlyMethods(['loadAndCheckAttestationResponse']) + ->setConstructorArgs([$request]) + ->getMock(); + $publicKeyCredentialId = '12b37486-9299-4331-ac33-85b2d985b6fe'; + $userId = '00000000-0000-0000-0000-000000000002'; + $credentialData = [ + 'publicKeyCredentialId' => $publicKeyCredentialId, + 'type' => 'public-key', + 'transports' => [], + 'attestationType' => 'none', + 'trustPath' => [ + 'type' => 'Webauthn\TrustPath\EmptyTrustPath' + ], + 'aaguid' => '00000000-0000-0000-0000-000000000000', + 'credentialPublicKey' => Base64Url::encode('000000000000000000000000000000000000-9999999999999999999999999999999999999999-XXXXXXXXXXXXX-YYYYYYYYYYY'), + 'userHandle' => Base64Url::encode($userId), + 'counter' => 191, + 'otherUI' => null + ]; + $credential = PublicKeyCredentialSource::createFromArray($credentialData); + $adapter->expects($this->once()) + ->method('loadAndCheckAttestationResponse') + ->with( + $this->equalTo($options) + ) + ->willReturn($credential); + $credentialsList = $adapter->getUser()->additional_data['webauthn_credentials'] ?? []; + $this->assertCount(0, $credentialsList); + $actual = $adapter->verifyResponse(); + $this->assertEquals($credential, $actual); + $credentialsList = $adapter->getUser()->additional_data['webauthn_credentials']; + $this->assertCount(1, $credentialsList); + $key = key($credentialsList); + $this->assertIsString($key); + $this->assertTrue(isset($credentialsList[$key]['publicKeyCredentialId'])); + + //Invalid challenge without mock + $adapter = new RegisterAdapter($request); + $this->expectException(\Assert\InvalidArgumentException::class); + $this->getExpectedExceptionMessage('Invalid challenge.'); + $adapter->verifyResponse(); + } +} diff --git a/tests/TestCase/Webauthn/Repository/UserCredentialSourceRepositoryTest.php b/tests/TestCase/Webauthn/Repository/UserCredentialSourceRepositoryTest.php new file mode 100644 index 000000000..b95051275 --- /dev/null +++ b/tests/TestCase/Webauthn/Repository/UserCredentialSourceRepositoryTest.php @@ -0,0 +1,106 @@ +get('CakeDC/Users.Users'); + $user = $UsersTable->get('00000000-0000-0000-0000-000000000001'); + $repository = new UserCredentialSourceRepository($user); + $credential = $repository->findOneByCredentialId('12b37486-9299-4331-ac33-85b2d985b6fe'); + $this->assertInstanceOf(PublicKeyCredentialSource::class, $credential); + + //Not found id + $repository = new UserCredentialSourceRepository($user); + $credential = $repository->findOneByCredentialId('some-testing-value'); + $this->assertNull($credential); + } + + /** + * Test findAllForUserEntity method + * + * @return void + */ + public function testFindAllForUserEntity() + { + $UsersTable = TableRegistry::getTableLocator()->get('CakeDC/Users.Users'); + $userId = '00000000-0000-0000-0000-000000000001'; + $user = $UsersTable->get($userId); + $userEntity = new PublicKeyCredentialUserEntity( + 'john.doe', // Username + $userId, // ID + 'John Doe' // Display name + ); + $repository = new UserCredentialSourceRepository($user); + $credentials = $repository->findAllForUserEntity($userEntity); + $this->assertCount(1, $credentials); + $this->assertInstanceOf(PublicKeyCredentialSource::class, $credentials[0]); + + //Not found id + $userEntityInvalid = new PublicKeyCredentialUserEntity( + 'john.doe', // Username + '00000000-0000-0000-0000-000000000004', // ID + 'John Doe' // Display name + ); + $repository = new UserCredentialSourceRepository($user); + $credentials = $repository->findAllForUserEntity($userEntityInvalid); + $this->assertEmpty($credentials); + } + + /** + * Test saveCredentialSource method + * + * @return void + */ + public function testSaveCredentialSource() + { + $publicKeyCredentialId = '12b37486-9299-4331-ac33-85b2d985b6fe'; + $userId = '00000000-0000-0000-0000-000000000001'; + $credentialData = [ + 'publicKeyCredentialId' => $publicKeyCredentialId, + 'type' => 'public-key', + 'transports' => [], + 'attestationType' => 'none', + 'trustPath' => [ + 'type' => 'Webauthn\TrustPath\EmptyTrustPath' + ], + 'aaguid' => '00000000-0000-0000-0000-000000000000', + 'credentialPublicKey' => Base64Url::encode('000000000000000000000000000000000000-9999999999999999999999999999999999999999-XXXXXXXXXXXXX-YYYYYYYYYYY'), + 'userHandle' => Base64Url::encode($userId), + 'counter' => 191, + 'otherUI' => null + ]; + $UsersTable = TableRegistry::getTableLocator()->get('CakeDC/Users.Users'); + $user = $UsersTable->get($userId); + + $userEntity = new PublicKeyCredentialUserEntity( + 'john.doe', // Username + $userId, // ID + 'John Doe' // Display name + ); + $publicKey = PublicKeyCredentialSource::createFromArray($credentialData); + $repository = new UserCredentialSourceRepository($user); + $repository->saveCredentialSource($publicKey); + $credentials = $repository->findAllForUserEntity($userEntity); + $this->assertCount(2, $credentials); + } +} From 956bae698fb78c82ff3c54c572542f932588f42f Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Mon, 20 Dec 2021 08:52:20 -0300 Subject: [PATCH 32/53] Persist webauthn credential --- src/Webauthn/BaseAdapter.php | 25 ++++++++++++++----- .../TestCase/Webauthn/RegisterAdapterTest.php | 2 +- .../UserCredentialSourceRepositoryTest.php | 25 ++++++++++++++----- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/Webauthn/BaseAdapter.php b/src/Webauthn/BaseAdapter.php index ccbf9a7ac..6d37ab6dd 100644 --- a/src/Webauthn/BaseAdapter.php +++ b/src/Webauthn/BaseAdapter.php @@ -5,11 +5,10 @@ use Cake\Core\Configure; use Cake\Datasource\EntityInterface; use Cake\Http\ServerRequest; +use Cake\ORM\TableRegistry; +use CakeDC\Users\Model\Table\UsersTable; use CakeDC\Users\Webauthn\Repository\UserCredentialSourceRepository; -use Webauthn\PublicKeyCredentialCreationOptions; -use Webauthn\PublicKeyCredentialRequestOptions; use Webauthn\PublicKeyCredentialRpEntity; -use Webauthn\PublicKeyCredentialSource; use Webauthn\PublicKeyCredentialUserEntity; use Webauthn\Server; @@ -27,24 +26,38 @@ class BaseAdapter * @var Server */ protected $server; + /** + * @var EntityInterface|\CakeDC\Users\Model\Entity\User + */ + private $user; /** * @param ServerRequest $request + * @param UsersTable|null $usersTable */ - public function __construct(ServerRequest $request) + public function __construct(ServerRequest $request, ?UsersTable $usersTable = null) { $this->request = $request; $rpEntity = new PublicKeyCredentialRpEntity( Configure::read('Webauthn2fa.appName'), // The application name Configure::read('Webauthn2fa.id') ); + /** + * @var \Cake\ORM\Entity $userSession + */ + $userSession = $request->getSession()->read('Webauthn2fa.User'); + $usersTable = $usersTable ?? TableRegistry::getTableLocator() + ->get($userSession->getSource()); + $this->user = $usersTable->get($userSession->id); $this->repository = new UserCredentialSourceRepository( - $request->getSession()->read('Webauthn2fa.User') + $this->user, + $usersTable ); $this->server = new Server( $rpEntity, $this->repository ); + } /** @@ -66,6 +79,6 @@ protected function getUserEntity(): PublicKeyCredentialUserEntity */ public function getUser() { - return $this->request->getSession()->read('Webauthn2fa.User'); + return $this->user; } } diff --git a/tests/TestCase/Webauthn/RegisterAdapterTest.php b/tests/TestCase/Webauthn/RegisterAdapterTest.php index cd852cfe4..08e4a817f 100644 --- a/tests/TestCase/Webauthn/RegisterAdapterTest.php +++ b/tests/TestCase/Webauthn/RegisterAdapterTest.php @@ -32,7 +32,7 @@ public function testGetOptions() $user = $UsersTable->get($userId); $request = ServerRequestFactory::fromGlobals(); $request->getSession()->write('Webauthn2fa.User', $user); - $adapter = new RegisterAdapter($request); + $adapter = new RegisterAdapter($request, $UsersTable); $options = $adapter->getOptions(); $this->assertInstanceOf(PublicKeyCredentialCreationOptions::class, $options); $this->assertSame($options, $request->getSession()->read('Webauthn2fa.registerOptions')); diff --git a/tests/TestCase/Webauthn/Repository/UserCredentialSourceRepositoryTest.php b/tests/TestCase/Webauthn/Repository/UserCredentialSourceRepositoryTest.php index b95051275..81e1a84d2 100644 --- a/tests/TestCase/Webauthn/Repository/UserCredentialSourceRepositoryTest.php +++ b/tests/TestCase/Webauthn/Repository/UserCredentialSourceRepositoryTest.php @@ -25,12 +25,12 @@ public function testFindOneByCredentialId() { $UsersTable = TableRegistry::getTableLocator()->get('CakeDC/Users.Users'); $user = $UsersTable->get('00000000-0000-0000-0000-000000000001'); - $repository = new UserCredentialSourceRepository($user); + $repository = new UserCredentialSourceRepository($user, $UsersTable); $credential = $repository->findOneByCredentialId('12b37486-9299-4331-ac33-85b2d985b6fe'); $this->assertInstanceOf(PublicKeyCredentialSource::class, $credential); //Not found id - $repository = new UserCredentialSourceRepository($user); + $repository = new UserCredentialSourceRepository($user, $UsersTable); $credential = $repository->findOneByCredentialId('some-testing-value'); $this->assertNull($credential); } @@ -50,7 +50,7 @@ public function testFindAllForUserEntity() $userId, // ID 'John Doe' // Display name ); - $repository = new UserCredentialSourceRepository($user); + $repository = new UserCredentialSourceRepository($user, $UsersTable); $credentials = $repository->findAllForUserEntity($userEntity); $this->assertCount(1, $credentials); $this->assertInstanceOf(PublicKeyCredentialSource::class, $credentials[0]); @@ -61,7 +61,7 @@ public function testFindAllForUserEntity() '00000000-0000-0000-0000-000000000004', // ID 'John Doe' // Display name ); - $repository = new UserCredentialSourceRepository($user); + $repository = new UserCredentialSourceRepository($user, $UsersTable); $credentials = $repository->findAllForUserEntity($userEntityInvalid); $this->assertEmpty($credentials); } @@ -91,16 +91,29 @@ public function testSaveCredentialSource() ]; $UsersTable = TableRegistry::getTableLocator()->get('CakeDC/Users.Users'); $user = $UsersTable->get($userId); - + $firstKey = key($user->additional_data['webauthn_credentials']); $userEntity = new PublicKeyCredentialUserEntity( 'john.doe', // Username $userId, // ID 'John Doe' // Display name ); $publicKey = PublicKeyCredentialSource::createFromArray($credentialData); - $repository = new UserCredentialSourceRepository($user); + $repository = new UserCredentialSourceRepository($user, $UsersTable); $repository->saveCredentialSource($publicKey); $credentials = $repository->findAllForUserEntity($userEntity); $this->assertCount(2, $credentials); + $userAfter = $UsersTable->get($user->id); + $this->assertArrayHasKey( + '12b37486-9299-4331-ac33-85b2d985b6fe', + $userAfter->additional_data['webauthn_credentials'] + ); + $this->assertArrayHasKey( + $firstKey, + $userAfter->additional_data['webauthn_credentials'] + ); + $this->assertCount( + 2, + $userAfter->additional_data['webauthn_credentials'] + ); } } From 659e83c7f9040526dd62733277f92fbb0a5fe0ba Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Mon, 20 Dec 2021 09:13:02 -0300 Subject: [PATCH 33/53] Added method to check if has credential --- src/Webauthn/BaseAdapter.php | 13 ++++++++++++- tests/TestCase/Webauthn/RegisterAdapterTest.php | 3 ++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Webauthn/BaseAdapter.php b/src/Webauthn/BaseAdapter.php index 6d37ab6dd..795806f52 100644 --- a/src/Webauthn/BaseAdapter.php +++ b/src/Webauthn/BaseAdapter.php @@ -53,6 +53,7 @@ public function __construct(ServerRequest $request, ?UsersTable $usersTable = nu $this->user, $usersTable ); + $this->server = new Server( $rpEntity, $this->repository @@ -65,7 +66,7 @@ public function __construct(ServerRequest $request, ?UsersTable $usersTable = nu */ protected function getUserEntity(): PublicKeyCredentialUserEntity { - $user = $this->request->getSession()->read('Webauthn2fa.User'); + $user = $this->getUser(); return new PublicKeyCredentialUserEntity( $user->webauthn_username ?? $user->username, @@ -81,4 +82,14 @@ public function getUser() { return $this->user; } + + /** + * @return bool + */ + public function hasCredential(): bool + { + return (bool)$this->repository->findAllForUserEntity( + $this->getUserEntity() + ); + } } diff --git a/tests/TestCase/Webauthn/RegisterAdapterTest.php b/tests/TestCase/Webauthn/RegisterAdapterTest.php index 08e4a817f..b85c8ec0b 100644 --- a/tests/TestCase/Webauthn/RegisterAdapterTest.php +++ b/tests/TestCase/Webauthn/RegisterAdapterTest.php @@ -33,6 +33,7 @@ public function testGetOptions() $request = ServerRequestFactory::fromGlobals(); $request->getSession()->write('Webauthn2fa.User', $user); $adapter = new RegisterAdapter($request, $UsersTable); + $this->assertFalse($adapter->hasCredential()); $options = $adapter->getOptions(); $this->assertInstanceOf(PublicKeyCredentialCreationOptions::class, $options); $this->assertSame($options, $request->getSession()->read('Webauthn2fa.registerOptions')); @@ -78,7 +79,7 @@ public function testGetOptions() $key = key($credentialsList); $this->assertIsString($key); $this->assertTrue(isset($credentialsList[$key]['publicKeyCredentialId'])); - + $this->assertTrue($adapter->hasCredential()); //Invalid challenge without mock $adapter = new RegisterAdapter($request); $this->expectException(\Assert\InvalidArgumentException::class); From 766239c1895347f844cc98a0cde46a411b7cd50a Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Mon, 20 Dec 2021 10:12:20 -0300 Subject: [PATCH 34/53] Persist webauthn credential --- config/users.php | 6 ++++++ .../Repository/UserCredentialSourceRepository.php | 10 +++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/config/users.php b/config/users.php index e927ea4d4..3eb74302a 100644 --- a/config/users.php +++ b/config/users.php @@ -118,6 +118,12 @@ 'enabled' => false, 'checker' => \CakeDC\Auth\Authentication\DefaultU2fAuthenticationChecker::class, ], + 'Webauthn2fa' => [ + 'enabled' => false, + 'appName' => null,//App must set a valid name here + 'id' => null,//default value is the current domain + 'checker' => \CakeDC\Auth\Authentication\DefaultWebauthn2fAuthenticationChecker::class, + ], // default configuration used to auto-load the Auth Component, override to change the way Auth works 'Auth' => [ 'Authentication' => [ diff --git a/src/Webauthn/Repository/UserCredentialSourceRepository.php b/src/Webauthn/Repository/UserCredentialSourceRepository.php index 27432fa63..c18c42596 100644 --- a/src/Webauthn/Repository/UserCredentialSourceRepository.php +++ b/src/Webauthn/Repository/UserCredentialSourceRepository.php @@ -5,6 +5,7 @@ use Base64Url\Base64Url; use Cake\Datasource\EntityInterface; +use CakeDC\Users\Model\Table\UsersTable; use Webauthn\PublicKeyCredentialSource; use Webauthn\PublicKeyCredentialSourceRepository; use Webauthn\PublicKeyCredentialUserEntity; @@ -15,13 +16,19 @@ class UserCredentialSourceRepository implements PublicKeyCredentialSourceReposit * @var EntityInterface */ private $user; + /** + * @var UsersTable|null + */ + private $usersTable; /** * @param EntityInterface $user + * @param UsersTable|null $usersTable */ - public function __construct(EntityInterface $user) + public function __construct(EntityInterface $user, ?UsersTable $usersTable = null) { $this->user = $user; + $this->usersTable = $usersTable; } /** @@ -65,5 +72,6 @@ public function saveCredentialSource(PublicKeyCredentialSource $publicKeyCredent $credentials[$id] = json_decode(json_encode($publicKeyCredentialSource), true); $this->user['additional_data'] = $this->user['additional_data'] ?? []; $this->user['additional_data']['webauthn_credentials'] = $credentials; + $this->usersTable->saveOrFail($this->user); } } From 2d125d4ccbf2c589c0e5e513f058c1da78f45dff Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Mon, 20 Dec 2021 12:02:17 -0300 Subject: [PATCH 35/53] Added actions to handle webauthn2fa --- config/permissions.php | 7 +- src/Controller/Traits/Webauthn2faTrait.php | 129 +++++ src/Controller/UsersController.php | 14 +- src/Utility/UsersUrl.php | 1 + templates/Users/webauthn2fa.php | 55 +++ .../Traits/Webauthn2faTraitTest.php | 458 ++++++++++++++++++ webroot/js/webauthn.js | 214 ++++++++ 7 files changed, 876 insertions(+), 2 deletions(-) create mode 100644 src/Controller/Traits/Webauthn2faTrait.php create mode 100644 templates/Users/webauthn2fa.php create mode 100644 tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php create mode 100644 webroot/js/webauthn.js diff --git a/config/permissions.php b/config/permissions.php index 3e9bc2369..5c52467fb 100644 --- a/config/permissions.php +++ b/config/permissions.php @@ -79,6 +79,11 @@ 'u2fRegisterFinish', 'u2fAuthenticate', 'u2fAuthenticateFinish', + 'webauthn2fa', + 'webauthn2faRegister', + 'webauthn2faRegisterOptions', + 'webauthn2faAuthenticate', + 'webauthn2faAuthenticateOptions', ], 'bypassAuth' => true, ], @@ -134,6 +139,6 @@ 'controller' => '*', 'action' => '*', 'bypassAuth' => true, - ], + ], ] ]; diff --git a/src/Controller/Traits/Webauthn2faTrait.php b/src/Controller/Traits/Webauthn2faTrait.php new file mode 100644 index 000000000..eedaf88e6 --- /dev/null +++ b/src/Controller/Traits/Webauthn2faTrait.php @@ -0,0 +1,129 @@ +getWebauthn2faRegisterAdapter(); + $user = $adapter->getUser(); + $this->set('isRegister', !$adapter->hasCredential()); + $this->set('username', $user->webauthn_username ?? $user->username); + } + + /** + * Action to provide register options to frontend (from js requests) + * + * @return \Cake\Http\Response + */ + public function webauthn2faRegisterOptions() + { + $adapter = $this->getWebauthn2faRegisterAdapter(); + + return $this->getResponse()->withStringBody(json_encode($adapter->getOptions())); + } + + /** + * Action to verify and save the new credential based on the webauthn register response. + * + * @return \Cake\Http\Response + * @throws \Throwable + */ + public function webauthn2faRegister(): \Cake\Http\Response + { + try { + $this->getWebauthn2faRegisterAdapter()->verifyResponse(); + + return $this->getResponse()->withStringBody(json_encode(['success' => true])); + } catch (\Throwable $e) { + $user = $this->request->getSession()->read('Webauthn2fa.User'); + Log::debug(__('Register error with webauthn for user id: {0}', $user['id'] ?? 'empty')); + throw $e; + } + } + + /** + * Action to provide authenticate options to frontend (from js requests) + * + * @return \Cake\Http\Response + */ + public function webauthn2faAuthenticateOptions(): \Cake\Http\Response + { + $adapter = $this->getWebauthn2faAuthenticateAdapter(); + + return $this->getResponse()->withStringBody( + json_encode($adapter->getOptions()) + ); + } + + /** + * Action to authenticate user based on the webauthn authenticate response. + * + * @return \Cake\Http\Response + * @throws \Throwable + */ + public function webauthn2faAuthenticate(): \Cake\Http\Response + { + try { + $adapter = $this->getWebauthn2faAuthenticateAdapter(); + $adapter->verifyResponse(); + $redirectUrl = Configure::read('Auth.AuthenticationComponent.loginAction') + [ + '?' => $this->getRequest()->getQueryParams() + ]; + $this->getRequest()->getSession()->delete('Webauthn2fa'); + $this->getRequest()->getSession()->write( + TwoFactorAuthenticator::USER_SESSION_KEY, + $adapter->getUser() + ); + + return $this->getResponse()->withStringBody(json_encode([ + 'success' => true, + 'redirectUrl' => Router::url($redirectUrl) + ])); + } catch (\Throwable $e) { + $user = $this->request->getSession()->read('Webauthn2fa.User'); + Log::debug(__('Register error with webauthn for user id: {0}', $user['id'] ?? 'empty')); + throw $e; + } + } + + /** + * @return RegisterAdapter + */ + protected function getWebauthn2faRegisterAdapter(): RegisterAdapter + { + return new RegisterAdapter($this->getRequest(), $this->getUsersTable()); + } + + /** + * @return AuthenticateAdapter + */ + protected function getWebauthn2faAuthenticateAdapter(): AuthenticateAdapter + { + return new AuthenticateAdapter($this->getRequest()); + } +} diff --git a/src/Controller/UsersController.php b/src/Controller/UsersController.php index 6cffb4590..096bbc9c1 100644 --- a/src/Controller/UsersController.php +++ b/src/Controller/UsersController.php @@ -22,6 +22,7 @@ use CakeDC\Users\Controller\Traits\SimpleCrudTrait; use CakeDC\Users\Controller\Traits\SocialTrait; use CakeDC\Users\Controller\Traits\U2fTrait; +use CakeDC\Users\Controller\Traits\Webauthn2faTrait; /** * Users Controller @@ -40,6 +41,7 @@ class UsersController extends AppController use SimpleCrudTrait; use SocialTrait; use U2fTrait; + use Webauthn2faTrait; /** * Initialize @@ -52,7 +54,17 @@ public function initialize(): void if ($this->components()->has('Security')) { $this->Security->setConfig( 'unlockedActions', - ['login', 'u2fRegister', 'u2fRegisterFinish', 'u2fAuthenticate', 'u2fAuthenticateFinish'] + [ + 'login', + 'u2fRegister', + 'u2fRegisterFinish', + 'u2fAuthenticate', + 'u2fAuthenticateFinish', + 'webauthn2faRegister', + 'webauthn2faRegisterOptions', + 'webauthn2faAuthenticate', + 'webauthn2faAuthenticateOptions', + ] ); } } diff --git a/src/Utility/UsersUrl.php b/src/Utility/UsersUrl.php index 96d05151a..935e4cb0f 100644 --- a/src/Utility/UsersUrl.php +++ b/src/Utility/UsersUrl.php @@ -124,6 +124,7 @@ private static function getDefaultConfigUrls() 'Users.Profile.route' => static::actionUrl('profile'), 'OneTimePasswordAuthenticator.verifyAction' => static::actionUrl('verify'), 'U2f.startAction' => static::actionUrl('u2f'), + 'Webauthn2fa.startAction' => static::actionUrl('webauthn2fa'), 'Auth.AuthenticationComponent.loginAction' => $loginAction, 'Auth.AuthenticationComponent.logoutRedirect' => $loginAction, 'Auth.Authenticators.Form.loginUrl' => $loginAction, diff --git a/templates/Users/webauthn2fa.php b/templates/Users/webauthn2fa.php new file mode 100644 index 000000000..dab4aba4a --- /dev/null +++ b/templates/Users/webauthn2fa.php @@ -0,0 +1,55 @@ +Html->script('CakeDC/Users.webauthn.js', ['block' => true]); +$this->assign('title', __('Two-factor authentication')); +?> +
+
+
+
+ + Flash->render('auth') ?> + Flash->render() ?> +
+ + +

Html->link( + __('Reload'), + ['action' => 'webauthn2fa'], + ['class' => 'btn btn-primary'] + )?>

+
+
+
+
+
+ $this->Url->build(['action' => 'webauthn2faAuthenticate']), + 'authenticateOptionsUrl' => $this->Url->build(['action' => 'webauthn2faAuthenticateOptions']), + 'registerActionUrl' => $this->Url->build(['action' => 'webauthn2faRegister']), + 'registerOptionsUrl' => $this->Url->build(['action' => 'webauthn2faRegisterOptions']), + 'isRegister' => $isRegister, + 'username' => h($username), + 'registerElemId' => 'webauthn2faRegisterInfo', + 'authenticateElemId' => 'webauthn2faAuthenticateInfo', +]; +$this->Html->scriptStart(['block' => true]); +?> +setTimeout(function() { + Webauthn2faHelper.run(); +}, 1000); +Html->scriptEnd();?> diff --git a/tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php b/tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php new file mode 100644 index 000000000..fab3eefc9 --- /dev/null +++ b/tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php @@ -0,0 +1,458 @@ +traitClassName = 'CakeDC\Users\Controller\UsersController'; + $this->traitMockMethods = ['dispatchEvent', 'isStopped', 'redirect', 'getUsersTable', 'set', 'createU2fLib', 'getData', 'getU2fAuthenticationChecker']; + + parent::setUp(); + + $request = new ServerRequest(); + $this->Trait->setRequest($request); + Configure::write('Webauthn2fa.enabled', true); + Configure::write('Webauthn2fa.appName', 'ACME Webauthn Server'); + Configure::write('Webauthn2fa.id', 'localhost'); + + } + + /** + * Mock session and mock session attributes + * + * @return \Cake\Http\Session + */ + protected function _mockSession($attributes) + { + $session = new \Cake\Http\Session(); + + foreach ($attributes as $field => $value) { + $session->write($field, $value); + } + + $this->Trait + ->getRequest() + ->expects($this->any()) + ->method('getSession') + ->willReturn($session); + + return $session; + } + + /** + * Test webauthn2fa method when requires register + * + * @return void + */ + public function testWebauthn2faIsRegister() + { + $request = $this->getMockBuilder('Cake\Http\ServerRequest') + ->setMethods(['getSession', 'is']) + ->getMock(); + $this->Trait->setRequest($request); + $request->expects($this->any()) + ->method('is') + ->with( + $this->equalTo('ssl') + )->will($this->returnValue(true)); + + $table = TableRegistry::getTableLocator() + ->get('CakeDC/Users.Users'); + $user = $table->get('00000000-0000-0000-0000-000000000002'); + $this->_mockSession([ + 'Webauthn2fa.User' => $user, + ]); + $this->Trait->expects($this->at(1)) + ->method('set') + ->with( + $this->equalTo('isRegister'), + $this->equalTo(true) + ); + $this->Trait->expects($this->at(2)) + ->method('set') + ->with( + $this->equalTo('username'), + $this->equalTo('user-2') + ); + $this->Trait->webauthn2fa(); + $this->assertSame( + $user, + $this->Trait->getRequest()->getSession()->read('Webauthn2fa.User') + ); + } + + /** + * Test webauthn2fa method when DON'T require register + * + * @return void + */ + public function testWebauthn2faDontRequireRegister() + { + $request = $this->getMockBuilder('Cake\Http\ServerRequest') + ->setMethods(['getSession', 'is']) + ->getMock(); + $this->Trait->setRequest($request); + $request->expects($this->any()) + ->method('is') + ->with( + $this->equalTo('ssl') + )->will($this->returnValue(true)); + + $table = TableRegistry::getTableLocator() + ->get('CakeDC/Users.Users'); + $user = $table->get('00000000-0000-0000-0000-000000000001'); + $this->_mockSession([ + 'Webauthn2fa.User' => $user, + ]); + $this->Trait->expects($this->at(1)) + ->method('set') + ->with( + $this->equalTo('isRegister'), + $this->equalTo(false) + ); + $this->Trait->expects($this->at(2)) + ->method('set') + ->with( + $this->equalTo('username'), + $this->equalTo('user-1') + ); + $this->Trait->webauthn2fa(); + $this->assertSame( + $user, + $this->Trait->getRequest()->getSession()->read('Webauthn2fa.User') + ); + } + + /** + * Test webauthn2faRegisterOptions method + * + * @return void + */ + public function testWebauthn2faRegisterOptions() + { + $request = $this->getMockBuilder('Cake\Http\ServerRequest') + ->setMethods(['getSession', 'is']) + ->getMock(); + $this->Trait->setRequest($request); + $request->expects($this->any()) + ->method('is') + ->with( + $this->equalTo('ssl') + )->will($this->returnValue(true)); + + $table = TableRegistry::getTableLocator() + ->get('CakeDC/Users.Users'); + $user = $table->get('00000000-0000-0000-0000-000000000002'); + $this->_mockSession([ + 'Webauthn2fa.User' => $user, + ]); + + $response = $this->Trait->webauthn2faRegisterOptions(); + $data = json_decode((string)$response->getBody(), true); + $this->assertArrayHasKey('rp', $data); + $this->assertArrayHasKey('user', $data); + $this->assertSame('user-2', $data['user']['name']); + $this->assertSame( + $user, + $this->Trait->getRequest()->getSession()->read('Webauthn2fa.User') + ); + } + + /** + * Test webauthn2faRegisterOptions method + * + * @return void + */ + public function testWebauthn2faRegister() + { + $table = TableRegistry::getTableLocator() + ->get('CakeDC/Users.Users'); + $user = $table->get('00000000-0000-0000-0000-000000000002'); + $request = ServerRequestFactory::fromGlobals(); + $request->getSession()->write('Webauthn2fa.User', $user); + + $this->Trait->setRequest($request); + $adapter = $this->getMockBuilder(RegisterAdapter::class) + ->onlyMethods(['verifyResponse']) + ->setConstructorArgs([$request]) + ->getMock(); + $publicKeyCredentialId = '12b37486-9299-4331-ac33-85b2d985b6fe'; + $userId = '00000000-0000-0000-0000-000000000002'; + $credentialData = [ + 'publicKeyCredentialId' => $publicKeyCredentialId, + 'type' => 'public-key', + 'transports' => [], + 'attestationType' => 'none', + 'trustPath' => [ + 'type' => 'Webauthn\TrustPath\EmptyTrustPath' + ], + 'aaguid' => '00000000-0000-0000-0000-000000000000', + 'credentialPublicKey' => Base64Url::encode('000000000000000000000000000000000000-9999999999999999999999999999999999999999-XXXXXXXXXXXXX-YYYYYYYYYYY'), + 'userHandle' => Base64Url::encode($userId), + 'counter' => 191, + 'otherUI' => null + ]; + $credential = PublicKeyCredentialSource::createFromArray($credentialData); + $adapter->expects($this->once()) + ->method('verifyResponse') + ->willReturn($credential); + + $traitMockMethods = array_unique(array_merge(['getUsersTable', 'getWebauthn2faRegisterAdapter'], $this->traitMockMethods)); + $this->Trait = $this->getMockBuilder($this->traitClassName) + ->setMethods($traitMockMethods) + ->getMock(); + $this->Trait->expects($this->once()) + ->method('getWebauthn2faRegisterAdapter') + ->willReturn($adapter); + $this->Trait->expects($this->any()) + ->method('getUsersTable') + ->will($this->returnValue($this->table)); + $data = '{"id":"LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA","rawId":"LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA","response":{"clientDataJSON":"eyJjaGFsbGVuZ2UiOiJOeHlab3B3VktiRmw3RW5uTWFlXzVGbmlyN1FKN1FXcDFVRlVLakZIbGZrIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwIiwidHlwZSI6IndlYmF1dGhuLmNyZWF0ZSJ9","attestationObject":"o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEcwRQIgVzzvX3Nyp_g9j9f2B-tPWy6puW01aZHI8RXjwqfDjtQCIQDLsdniGPO9iKr7tdgVV-FnBYhvzlZLG3u28rVt10YXfGN4NWOBWQJOMIICSjCCATKgAwIBAgIEVxb3wDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowLDEqMCgGA1UEAwwhWXViaWNvIFUyRiBFRSBTZXJpYWwgMjUwNTY5MjI2MTc2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZNkcVNbZV43TsGB4TEY21UijmDqvNSfO6y3G4ytnnjP86ehjFK28-FdSGy9MSZ-Ur3BVZb4iGVsptk5NrQ3QYqM7MDkwIgYJKwYBBAGCxAoCBBUxLjMuNi4xLjQuMS40MTQ4Mi4xLjUwEwYLKwYBBAGC5RwCAQEEBAMCBSAwDQYJKoZIhvcNAQELBQADggEBAHibGMqbpNt2IOL4i4z96VEmbSoid9Xj--m2jJqg6RpqSOp1TO8L3lmEA22uf4uj_eZLUXYEw6EbLm11TUo3Ge-odpMPoODzBj9aTKC8oDFPfwWj6l1O3ZHTSma1XVyPqG4A579f3YAjfrPbgj404xJns0mqx5wkpxKlnoBKqo1rqSUmonencd4xanO_PHEfxU0iZif615Xk9E4bcANPCfz-OLfeKXiT-1msixwzz8XGvl2OTMJ_Sh9G9vhE-HjAcovcHfumcdoQh_WM445Za6Pyn9BZQV3FCqMviRR809sIATfU5lu86wu_5UGIGI7MFDEYeVGSqzpzh6mlcn8QSIZoYXV0aERhdGFYxEmWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjQQAAAAAAAAAAAAAAAAAAAAAAAAAAAEAsV2gIUlPIHzZnNIlQdz5zvbKtpFz_WY-8ZfxOgTyy7f3Ffbolyp3fUtSQo5LfoUgBaBaXqK0wqqYO-u6FrrLApQECAyYgASFYIPr9-YH8DuBsOnaI3KJa0a39hyxh9LDtHErNvfQSyxQsIlgg4rAuQQ5uy4VXGFbkiAt0uwgJJodp-DymkoBcrGsLtkI"},"type":"public-key"}'; + $request = $request->withParsedBody( + json_decode($data, true) + ); + $this->Trait->setRequest($request); + $response = $this->Trait->webauthn2faRegister(); + $this->assertEquals('{"success":true}', (string)$response->getBody()); + } + + /** + * Test webauthn2faRegisterOptions method + * + * @return void + */ + public function testWebauthn2faRegisterError() + { + $table = TableRegistry::getTableLocator() + ->get('CakeDC/Users.Users'); + $user = $table->get('00000000-0000-0000-0000-000000000002'); + $request = ServerRequestFactory::fromGlobals(); + $request->getSession()->write('Webauthn2fa.User', $user); + + $this->Trait->setRequest($request); + $adapter = $this->getMockBuilder(RegisterAdapter::class) + ->onlyMethods(['verifyResponse']) + ->setConstructorArgs([$request]) + ->getMock(); + + $adapter->expects($this->once()) + ->method('verifyResponse') + ->willThrowException(new \Exception('Testing error exception for webauthn2faRegister')); + + $traitMockMethods = array_unique(array_merge(['getUsersTable', 'getWebauthn2faRegisterAdapter'], $this->traitMockMethods)); + $this->Trait = $this->getMockBuilder($this->traitClassName) + ->setMethods($traitMockMethods) + ->getMock(); + $this->Trait->expects($this->once()) + ->method('getWebauthn2faRegisterAdapter') + ->willReturn($adapter); + $this->Trait->expects($this->any()) + ->method('getUsersTable') + ->will($this->returnValue($this->table)); + $data = '{"id":"LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA","rawId":"LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA","response":{"clientDataJSON":"eyJjaGFsbGVuZ2UiOiJOeHlab3B3VktiRmw3RW5uTWFlXzVGbmlyN1FKN1FXcDFVRlVLakZIbGZrIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwIiwidHlwZSI6IndlYmF1dGhuLmNyZWF0ZSJ9","attestationObject":"o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEcwRQIgVzzvX3Nyp_g9j9f2B-tPWy6puW01aZHI8RXjwqfDjtQCIQDLsdniGPO9iKr7tdgVV-FnBYhvzlZLG3u28rVt10YXfGN4NWOBWQJOMIICSjCCATKgAwIBAgIEVxb3wDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowLDEqMCgGA1UEAwwhWXViaWNvIFUyRiBFRSBTZXJpYWwgMjUwNTY5MjI2MTc2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZNkcVNbZV43TsGB4TEY21UijmDqvNSfO6y3G4ytnnjP86ehjFK28-FdSGy9MSZ-Ur3BVZb4iGVsptk5NrQ3QYqM7MDkwIgYJKwYBBAGCxAoCBBUxLjMuNi4xLjQuMS40MTQ4Mi4xLjUwEwYLKwYBBAGC5RwCAQEEBAMCBSAwDQYJKoZIhvcNAQELBQADggEBAHibGMqbpNt2IOL4i4z96VEmbSoid9Xj--m2jJqg6RpqSOp1TO8L3lmEA22uf4uj_eZLUXYEw6EbLm11TUo3Ge-odpMPoODzBj9aTKC8oDFPfwWj6l1O3ZHTSma1XVyPqG4A579f3YAjfrPbgj404xJns0mqx5wkpxKlnoBKqo1rqSUmonencd4xanO_PHEfxU0iZif615Xk9E4bcANPCfz-OLfeKXiT-1msixwzz8XGvl2OTMJ_Sh9G9vhE-HjAcovcHfumcdoQh_WM445Za6Pyn9BZQV3FCqMviRR809sIATfU5lu86wu_5UGIGI7MFDEYeVGSqzpzh6mlcn8QSIZoYXV0aERhdGFYxEmWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjQQAAAAAAAAAAAAAAAAAAAAAAAAAAAEAsV2gIUlPIHzZnNIlQdz5zvbKtpFz_WY-8ZfxOgTyy7f3Ffbolyp3fUtSQo5LfoUgBaBaXqK0wqqYO-u6FrrLApQECAyYgASFYIPr9-YH8DuBsOnaI3KJa0a39hyxh9LDtHErNvfQSyxQsIlgg4rAuQQ5uy4VXGFbkiAt0uwgJJodp-DymkoBcrGsLtkI"},"type":"public-key"}'; + $request = $request->withParsedBody( + json_decode($data, true) + ); + $this->Trait->setRequest($request); + $this->expectException(\Exception::class); + $this->expectErrorMessage('Testing error exception for webauthn2faRegister'); + $this->Trait->webauthn2faRegister(); + } + + + /** + * Test webauthn2faAuthenticateOptions + * + * @return void + */ + public function testWebauthn2faAuthenticateOptions() + { + $request = $this->getMockBuilder('Cake\Http\ServerRequest') + ->setMethods(['getSession', 'is']) + ->getMock(); + $this->Trait->setRequest($request); + $request->expects($this->any()) + ->method('is') + ->with( + $this->equalTo('ssl') + )->will($this->returnValue(true)); + + $table = TableRegistry::getTableLocator() + ->get('CakeDC/Users.Users'); + $user = $table->get('00000000-0000-0000-0000-000000000001'); + $this->_mockSession([ + 'Webauthn2fa.User' => $user, + ]); + + $response = $this->Trait->webauthn2faAuthenticateOptions(); + $data = json_decode((string)$response->getBody(), true); + $this->assertArrayHasKey('challenge', $data); + $this->assertArrayHasKey('userVerification', $data); + $this->assertArrayHasKey('allowCredentials', $data); + $expectedCredentials = [ + [ + 'type' => 'public-key', + 'id' => '12b37486-9299-4331-ac33-85b2d985b6fe', + ], + ]; + $this->assertEquals($expectedCredentials, $data['allowCredentials']); + $this->assertSame( + $user, + $this->Trait->getRequest()->getSession()->read('Webauthn2fa.User') + ); + } + + /** + * Test webauthn2faAuthenticateOptions method + * + * @return void + */ + public function testWebauthn2faAuthenticate() + { + $table = TableRegistry::getTableLocator() + ->get('CakeDC/Users.Users'); + $user = $table->get('00000000-0000-0000-0000-000000000002'); + $request = ServerRequestFactory::fromGlobals(); + $request->getSession()->write('Webauthn2fa.User', $user); + + $this->Trait->setRequest($request); + $adapter = $this->getMockBuilder(AuthenticateAdapter::class) + ->onlyMethods(['verifyResponse']) + ->setConstructorArgs([$request]) + ->getMock(); + $publicKeyCredentialId = '12b37486-9299-4331-ac33-85b2d985b6fe'; + $userId = '00000000-0000-0000-0000-000000000002'; + $credentialData = [ + 'publicKeyCredentialId' => $publicKeyCredentialId, + 'type' => 'public-key', + 'transports' => [], + 'attestationType' => 'none', + 'trustPath' => [ + 'type' => 'Webauthn\TrustPath\EmptyTrustPath' + ], + 'aaguid' => '00000000-0000-0000-0000-000000000000', + 'credentialPublicKey' => Base64Url::encode('000000000000000000000000000000000000-9999999999999999999999999999999999999999-XXXXXXXXXXXXX-YYYYYYYYYYY'), + 'userHandle' => Base64Url::encode($userId), + 'counter' => 191, + 'otherUI' => null + ]; + $credential = PublicKeyCredentialSource::createFromArray($credentialData); + $adapter->expects($this->once()) + ->method('verifyResponse') + ->willReturn($credential); + + $traitMockMethods = array_unique(array_merge(['getUsersTable', 'getWebauthn2faAuthenticateAdapter'], $this->traitMockMethods)); + $this->Trait = $this->getMockBuilder($this->traitClassName) + ->setMethods($traitMockMethods) + ->getMock(); + $this->Trait->expects($this->once()) + ->method('getWebauthn2faAuthenticateAdapter') + ->willReturn($adapter); + $this->Trait->expects($this->any()) + ->method('getUsersTable') + ->will($this->returnValue($this->table)); + $this->Trait->setRequest($request); + $response = $this->Trait->webauthn2faAuthenticate(); + $expected = [ + 'success' => true, + 'redirectUrl' => '/login', + ]; + $actual = json_decode((string)$response->getBody(), true); + $this->assertEquals($expected, $actual); + + $this->assertNull( + $this->Trait->getRequest()->getSession()->read('Webauthn2fa.User') + ); + $userSession = $this->Trait->getRequest()->getSession()->read('TwoFactorAuthenticator.User'); + $this->assertInstanceOf( + User::class, + $userSession + ); + $this->assertEquals($userSession->toArray(), $user->toArray()); + } + + /** + * Test webauthn2faAuthenticateOptions method + * + * @return void + */ + public function testWebauthn2faAuthenticateError() + { + $table = TableRegistry::getTableLocator() + ->get('CakeDC/Users.Users'); + $user = $table->get('00000000-0000-0000-0000-000000000002'); + $request = ServerRequestFactory::fromGlobals(); + $request->getSession()->write('Webauthn2fa.User', $user); + + $this->Trait->setRequest($request); + $adapter = $this->getMockBuilder(AuthenticateAdapter::class) + ->onlyMethods(['verifyResponse']) + ->setConstructorArgs([$request]) + ->getMock(); + + $adapter->expects($this->once()) + ->method('verifyResponse') + ->willThrowException(new \Exception('Test exception error for webauthn2faAuthenticate')); + + $traitMockMethods = array_unique(array_merge(['getUsersTable', 'getWebauthn2faAuthenticateAdapter'], $this->traitMockMethods)); + $this->Trait = $this->getMockBuilder($this->traitClassName) + ->setMethods($traitMockMethods) + ->getMock(); + $this->Trait->expects($this->once()) + ->method('getWebauthn2faAuthenticateAdapter') + ->willReturn($adapter); + $this->Trait->expects($this->any()) + ->method('getUsersTable') + ->will($this->returnValue($this->table)); + $this->Trait->setRequest($request); + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Test exception error for webauthn2faAuthenticate'); + $this->Trait->webauthn2faAuthenticate(); + } +} diff --git a/webroot/js/webauthn.js b/webroot/js/webauthn.js new file mode 100644 index 000000000..45f9e9c22 --- /dev/null +++ b/webroot/js/webauthn.js @@ -0,0 +1,214 @@ +//Source code based on https://github.com/web-auth/webauthn-helper +// Predefined fetch function +const fetchEndpoint = (data, url, header) => { + return fetch( + url, + { + method: 'POST', + credentials: 'same-origin', + redirect: 'error', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + ...header + }, + body: JSON.stringify(data), + } + ); +} + +// Decodes a Base64Url string +const base64UrlDecode = (input) => { + input = input + .replace(/-/g, '+') + .replace(/_/g, '/'); + + const pad = input.length % 4; + if (pad) { + if (pad === 1) { + throw new Error('InvalidLengthError: Input base64url string is the wrong length to determine padding'); + } + input += new Array(5-pad).join('='); + } + + return window.atob(input); +}; + +// Converts an array of bytes into a Base64Url string +const arrayToBase64String = (a) => btoa(String.fromCharCode(...a)); + +// Prepares the public key options object returned by the Webauthn Framework +const preparePublicKeyOptions = publicKey => { + //Convert challenge from Base64Url string to Uint8Array + publicKey.challenge = Uint8Array.from( + base64UrlDecode(publicKey.challenge), + c => c.charCodeAt(0) + ); + + //Convert the user ID from Base64 string to Uint8Array + if (publicKey.user !== undefined) { + publicKey.user = { + ...publicKey.user, + id: Uint8Array.from( + window.atob(publicKey.user.id), + c => c.charCodeAt(0) + ), + }; + } + + //If excludeCredentials is defined, we convert all IDs to Uint8Array + if (publicKey.excludeCredentials !== undefined) { + publicKey.excludeCredentials = publicKey.excludeCredentials.map( + data => { + return { + ...data, + id: Uint8Array.from( + base64UrlDecode(data.id), + c => c.charCodeAt(0) + ), + }; + } + ); + } + + if (publicKey.allowCredentials !== undefined) { + publicKey.allowCredentials = publicKey.allowCredentials.map( + data => { + return { + ...data, + id: Uint8Array.from( + base64UrlDecode(data.id), + c => c.charCodeAt(0) + ), + }; + } + ); + } + + return publicKey; +}; + +// Prepares the public key credentials object returned by the authenticator +const preparePublicKeyCredentials = data => { + const publicKeyCredential = { + id: data.id, + type: data.type, + rawId: arrayToBase64String(new Uint8Array(data.rawId)), + response: { + clientDataJSON: arrayToBase64String( + new Uint8Array(data.response.clientDataJSON) + ), + }, + }; + + if (data.response.attestationObject !== undefined) { + publicKeyCredential.response.attestationObject = arrayToBase64String( + new Uint8Array(data.response.attestationObject) + ); + } + + if (data.response.authenticatorData !== undefined) { + publicKeyCredential.response.authenticatorData = arrayToBase64String( + new Uint8Array(data.response.authenticatorData) + ); + } + + if (data.response.signature !== undefined) { + publicKeyCredential.response.signature = arrayToBase64String( + new Uint8Array(data.response.signature) + ); + } + + if (data.response.userHandle !== undefined) { + publicKeyCredential.response.userHandle = arrayToBase64String( + new Uint8Array(data.response.userHandle) + ); + } + + return publicKeyCredential; +}; + +const useLogin = ({actionUrl = '/login', actionHeader = {}, optionsUrl = '/login/options'}, optionsHeader = {}) => { + return async (data) => { + const optionsResponse = await fetchEndpoint(data, optionsUrl, optionsHeader); + const json = await optionsResponse.json(); + const publicKey = preparePublicKeyOptions(json); + const credentials = await navigator.credentials.get({publicKey}); + const publicKeyCredential = preparePublicKeyCredentials(credentials); + const actionResponse = await fetchEndpoint(publicKeyCredential, actionUrl, actionHeader); + if (! actionResponse.ok) { + throw actionResponse; + } + const responseBody = await actionResponse.text(); + + return responseBody !== '' ? JSON.parse(responseBody) : responseBody; + }; +}; + + +const useRegistration = ({actionUrl = '/register', actionHeader = {}, optionsUrl = '/register/options'}, optionsHeader = {}) => { + return async (data) => { + const optionsResponse = await fetchEndpoint(data, optionsUrl, optionsHeader); + const json = await optionsResponse.json(); + const publicKey = preparePublicKeyOptions(json); + const credentials = await navigator.credentials.create({publicKey}); + const publicKeyCredential = preparePublicKeyCredentials(credentials); + const actionResponse = await fetchEndpoint(publicKeyCredential, actionUrl, actionHeader); + if (! actionResponse.ok) { + throw actionResponse; + } + const responseBody = await actionResponse.text(); + + return responseBody !== '' ? JSON.parse(responseBody) : responseBody; + }; +}; + +var Webauthn2faHelper = { + toggleElem: function(options) { + console.log({options}); + document.getElementById(options.authenticateElemId).style.display = options.isRegister ? 'none' : 'block'; + document.getElementById(options.registerElemId).style.display = options.isRegister ? 'block' : 'none'; + }, + authenticate: function(options) { + var authenticate = useLogin({ + actionUrl: options.authenticateActionUrl, + optionsUrl: options.authenticateOptionsUrl + }); + authenticate({ + username: options.username + }) + .then(function (response) { + window.location.href = response.redirectUrl; + }) + .catch((error) => { + window.alert('Authentication failure'); + }) + }, + register: function(options) { + var register = useRegistration({ + actionUrl: options.registerActionUrl, + optionsUrl: options.registerOptionsUrl + }); + + return register({ + username: options.username, + }) + .catch((error) => { + window.alert('Registration failed'); + }); + }, + run: function(options) { + Webauthn2faHelper.toggleElem(options); + if (options.isRegister) { + return Webauthn2faHelper.register(options) + .then(function (response) { + if (response && response.success) { + options.isRegister = false; + Webauthn2faHelper.toggleElem(options); + Webauthn2faHelper.authenticate(options); + } + }); + } + return Webauthn2faHelper.authenticate(options); + } +} From 7236f1a295a1c1e5637d8ac66bf375f461c926e2 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Mon, 20 Dec 2021 12:12:57 -0300 Subject: [PATCH 36/53] fixing unit tests --- tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php b/tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php index fab3eefc9..734020b5b 100644 --- a/tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php +++ b/tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php @@ -18,21 +18,17 @@ use Cake\Http\ServerRequest; use Cake\Http\ServerRequestFactory; use Cake\ORM\TableRegistry; -use CakeDC\Auth\Authentication\DefaultU2fAuthenticationChecker; use CakeDC\Users\Model\Entity\User; use CakeDC\Users\Webauthn\AuthenticateAdapter; use CakeDC\Users\Webauthn\RegisterAdapter; -use u2flib_server\RegisterRequest; -use u2flib_server\Registration; -use u2flib_server\U2F; use Webauthn\PublicKeyCredentialSource; /** - * Class U2fTraitTest + * Class Webauthn2faTraitTest * * @package App\Test\TestCase\Controller\Traits */ -class U2fTraitTest extends BaseTraitTest +class Webauthn2faTraitTest extends BaseTraitTest { /** * Fixtures From 4ff0180794807ada7e2b87469d3484be5a7ba38e Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Mon, 20 Dec 2021 12:19:21 -0300 Subject: [PATCH 37/53] coding standard --- src/Webauthn/AuthenticateAdapter.php | 11 +++++++++++ src/Webauthn/RegisterAdapter.php | 17 +++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/Webauthn/AuthenticateAdapter.php b/src/Webauthn/AuthenticateAdapter.php index 9b29d8d54..4ab4853ad 100644 --- a/src/Webauthn/AuthenticateAdapter.php +++ b/src/Webauthn/AuthenticateAdapter.php @@ -1,4 +1,15 @@ request->getSession()->read('Webauthn2fa.registerOptions'); $credential = $this->loadAndCheckAttestationResponse($options); $this->repository->saveCredentialSource($credential); @@ -40,7 +53,7 @@ public function verifyResponse(): \Webauthn\PublicKeyCredentialSource } /** - * @param $options + * @param \Webauthn\PublicKeyCredentialCreationOptions $options * @return \Webauthn\PublicKeyCredentialSource */ protected function loadAndCheckAttestationResponse($options): \Webauthn\PublicKeyCredentialSource From 8b5b4af669768a7a05308e85661d264f4fc8e8f7 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Mon, 20 Dec 2021 12:25:00 -0300 Subject: [PATCH 38/53] fixing cs --- src/Controller/Traits/Webauthn2faTrait.php | 8 +++---- src/Webauthn/AuthenticateAdapter.php | 8 +++---- src/Webauthn/BaseAdapter.php | 17 +++++++------- src/Webauthn/RegisterAdapter.php | 9 +++----- .../UserCredentialSourceRepository.php | 16 ++++++------- tests/Fixture/UsersFixture.php | 6 ++--- .../Traits/Webauthn2faTraitTest.php | 10 ++++---- .../Webauthn/AuthenticateAdapterTest.php | 7 +++--- .../TestCase/Webauthn/RegisterAdapterTest.php | 5 ++-- .../UserCredentialSourceRepositoryTest.php | 23 ++++++++++--------- 10 files changed, 52 insertions(+), 57 deletions(-) diff --git a/src/Controller/Traits/Webauthn2faTrait.php b/src/Controller/Traits/Webauthn2faTrait.php index eedaf88e6..95c6ad620 100644 --- a/src/Controller/Traits/Webauthn2faTrait.php +++ b/src/Controller/Traits/Webauthn2faTrait.php @@ -92,7 +92,7 @@ public function webauthn2faAuthenticate(): \Cake\Http\Response $adapter = $this->getWebauthn2faAuthenticateAdapter(); $adapter->verifyResponse(); $redirectUrl = Configure::read('Auth.AuthenticationComponent.loginAction') + [ - '?' => $this->getRequest()->getQueryParams() + '?' => $this->getRequest()->getQueryParams(), ]; $this->getRequest()->getSession()->delete('Webauthn2fa'); $this->getRequest()->getSession()->write( @@ -102,7 +102,7 @@ public function webauthn2faAuthenticate(): \Cake\Http\Response return $this->getResponse()->withStringBody(json_encode([ 'success' => true, - 'redirectUrl' => Router::url($redirectUrl) + 'redirectUrl' => Router::url($redirectUrl), ])); } catch (\Throwable $e) { $user = $this->request->getSession()->read('Webauthn2fa.User'); @@ -112,7 +112,7 @@ public function webauthn2faAuthenticate(): \Cake\Http\Response } /** - * @return RegisterAdapter + * @return \CakeDC\Users\Webauthn\RegisterAdapter */ protected function getWebauthn2faRegisterAdapter(): RegisterAdapter { @@ -120,7 +120,7 @@ protected function getWebauthn2faRegisterAdapter(): RegisterAdapter } /** - * @return AuthenticateAdapter + * @return \CakeDC\Users\Webauthn\AuthenticateAdapter */ protected function getWebauthn2faAuthenticateAdapter(): AuthenticateAdapter { diff --git a/src/Webauthn/AuthenticateAdapter.php b/src/Webauthn/AuthenticateAdapter.php index 4ab4853ad..592c9590a 100644 --- a/src/Webauthn/AuthenticateAdapter.php +++ b/src/Webauthn/AuthenticateAdapter.php @@ -19,7 +19,7 @@ class AuthenticateAdapter extends BaseAdapter { /** - * @return PublicKeyCredentialRequestOptions + * @return \Webauthn\PublicKeyCredentialRequestOptions */ public function getOptions(): PublicKeyCredentialRequestOptions { @@ -36,10 +36,10 @@ public function getOptions(): PublicKeyCredentialRequestOptions 'Webauthn2fa.authenticateOptions', $options ); + return $options; } - /** * Verify the registration response * @@ -53,8 +53,8 @@ public function verifyResponse(): \Webauthn\PublicKeyCredentialSource } /** - * @param $options - * @return PublicKeyCredentialSource + * @param \Webauthn\PublicKeyCredentialRequestOptions $options request options + * @return \Webauthn\PublicKeyCredentialSource */ protected function loadAndCheckAssertionResponse($options): PublicKeyCredentialSource { diff --git a/src/Webauthn/BaseAdapter.php b/src/Webauthn/BaseAdapter.php index 795806f52..4ee30b09c 100644 --- a/src/Webauthn/BaseAdapter.php +++ b/src/Webauthn/BaseAdapter.php @@ -1,9 +1,9 @@ repository ); - } /** - * @return PublicKeyCredentialUserEntity + * @return \Webauthn\PublicKeyCredentialUserEntity */ protected function getUserEntity(): PublicKeyCredentialUserEntity { diff --git a/src/Webauthn/RegisterAdapter.php b/src/Webauthn/RegisterAdapter.php index 36916de01..c28b0504a 100644 --- a/src/Webauthn/RegisterAdapter.php +++ b/src/Webauthn/RegisterAdapter.php @@ -18,7 +18,7 @@ class RegisterAdapter extends BaseAdapter { /** - * @return PublicKeyCredentialCreationOptions + * @return \Webauthn\PublicKeyCredentialCreationOptions */ public function getOptions(): PublicKeyCredentialCreationOptions { @@ -34,7 +34,6 @@ public function getOptions(): PublicKeyCredentialCreationOptions return $options; } - /** * Verify the registration response * @@ -42,9 +41,6 @@ public function getOptions(): PublicKeyCredentialCreationOptions */ public function verifyResponse(): \Webauthn\PublicKeyCredentialSource { - /** - * - */ $options = $this->request->getSession()->read('Webauthn2fa.registerOptions'); $credential = $this->loadAndCheckAttestationResponse($options); $this->repository->saveCredentialSource($credential); @@ -53,7 +49,7 @@ public function verifyResponse(): \Webauthn\PublicKeyCredentialSource } /** - * @param \Webauthn\PublicKeyCredentialCreationOptions $options + * @param \Webauthn\PublicKeyCredentialCreationOptions $options creation options * @return \Webauthn\PublicKeyCredentialSource */ protected function loadAndCheckAttestationResponse($options): \Webauthn\PublicKeyCredentialSource @@ -63,6 +59,7 @@ protected function loadAndCheckAttestationResponse($options): \Webauthn\PublicKe $options, $this->request ); + return $credential; } } diff --git a/src/Webauthn/Repository/UserCredentialSourceRepository.php b/src/Webauthn/Repository/UserCredentialSourceRepository.php index c18c42596..ecda3cbb0 100644 --- a/src/Webauthn/Repository/UserCredentialSourceRepository.php +++ b/src/Webauthn/Repository/UserCredentialSourceRepository.php @@ -1,8 +1,8 @@ [], 'attestationType' => 'none', 'trustPath' => [ - 'type' => 'Webauthn\TrustPath\EmptyTrustPath' + 'type' => 'Webauthn\TrustPath\EmptyTrustPath', ], 'aaguid' => '00000000-0000-0000-0000-000000000000', 'credentialPublicKey' => Base64Url::encode('000000000000000000000000000000000000-9999999999999999999999999999999999999999-XXXXXXXXXXXXX-ZZZZZZZZZZZ'), 'userHandle' => Base64Url::encode('00000000-0000-0000-0000-000000000001'), 'counter' => 190, - 'otherUI' => null + 'otherUI' => null, ], - ] + ], ]), ], [ diff --git a/tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php b/tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php index 734020b5b..fa234bd63 100644 --- a/tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php +++ b/tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php @@ -56,7 +56,6 @@ public function setUp(): void Configure::write('Webauthn2fa.enabled', true); Configure::write('Webauthn2fa.appName', 'ACME Webauthn Server'); Configure::write('Webauthn2fa.id', 'localhost'); - } /** @@ -226,13 +225,13 @@ public function testWebauthn2faRegister() 'transports' => [], 'attestationType' => 'none', 'trustPath' => [ - 'type' => 'Webauthn\TrustPath\EmptyTrustPath' + 'type' => 'Webauthn\TrustPath\EmptyTrustPath', ], 'aaguid' => '00000000-0000-0000-0000-000000000000', 'credentialPublicKey' => Base64Url::encode('000000000000000000000000000000000000-9999999999999999999999999999999999999999-XXXXXXXXXXXXX-YYYYYYYYYYY'), 'userHandle' => Base64Url::encode($userId), 'counter' => 191, - 'otherUI' => null + 'otherUI' => null, ]; $credential = PublicKeyCredentialSource::createFromArray($credentialData); $adapter->expects($this->once()) @@ -301,7 +300,6 @@ public function testWebauthn2faRegisterError() $this->Trait->webauthn2faRegister(); } - /** * Test webauthn2faAuthenticateOptions * @@ -370,13 +368,13 @@ public function testWebauthn2faAuthenticate() 'transports' => [], 'attestationType' => 'none', 'trustPath' => [ - 'type' => 'Webauthn\TrustPath\EmptyTrustPath' + 'type' => 'Webauthn\TrustPath\EmptyTrustPath', ], 'aaguid' => '00000000-0000-0000-0000-000000000000', 'credentialPublicKey' => Base64Url::encode('000000000000000000000000000000000000-9999999999999999999999999999999999999999-XXXXXXXXXXXXX-YYYYYYYYYYY'), 'userHandle' => Base64Url::encode($userId), 'counter' => 191, - 'otherUI' => null + 'otherUI' => null, ]; $credential = PublicKeyCredentialSource::createFromArray($credentialData); $adapter->expects($this->once()) diff --git a/tests/TestCase/Webauthn/AuthenticateAdapterTest.php b/tests/TestCase/Webauthn/AuthenticateAdapterTest.php index ee127dc79..70c101c90 100644 --- a/tests/TestCase/Webauthn/AuthenticateAdapterTest.php +++ b/tests/TestCase/Webauthn/AuthenticateAdapterTest.php @@ -1,4 +1,5 @@ [], 'attestationType' => 'none', 'trustPath' => [ - 'type' => 'Webauthn\TrustPath\EmptyTrustPath' + 'type' => 'Webauthn\TrustPath\EmptyTrustPath', ], 'aaguid' => '00000000-0000-0000-0000-000000000000', 'credentialPublicKey' => Base64Url::encode('000000000000000000000000000000000000-9999999999999999999999999999999999999999-XXXXXXXXXXXXX-YYYYYYYYYYY'), 'userHandle' => Base64Url::encode($userId), 'counter' => 191, - 'otherUI' => null + 'otherUI' => null, ]; $credential = PublicKeyCredentialSource::createFromArray($credentialData); $adapter->expects($this->once()) @@ -70,7 +70,6 @@ public function testGetOptions() $actual = $adapter->verifyResponse(); $this->assertEquals($credential, $actual); - $adapter = new AuthenticateAdapter($request); $this->expectException(\Assert\InvalidArgumentException::class); diff --git a/tests/TestCase/Webauthn/RegisterAdapterTest.php b/tests/TestCase/Webauthn/RegisterAdapterTest.php index b85c8ec0b..3ce86a8e8 100644 --- a/tests/TestCase/Webauthn/RegisterAdapterTest.php +++ b/tests/TestCase/Webauthn/RegisterAdapterTest.php @@ -1,4 +1,5 @@ [], 'attestationType' => 'none', 'trustPath' => [ - 'type' => 'Webauthn\TrustPath\EmptyTrustPath' + 'type' => 'Webauthn\TrustPath\EmptyTrustPath', ], 'aaguid' => '00000000-0000-0000-0000-000000000000', 'credentialPublicKey' => Base64Url::encode('000000000000000000000000000000000000-9999999999999999999999999999999999999999-XXXXXXXXXXXXX-YYYYYYYYYYY'), 'userHandle' => Base64Url::encode($userId), 'counter' => 191, - 'otherUI' => null + 'otherUI' => null, ]; $credential = PublicKeyCredentialSource::createFromArray($credentialData); $adapter->expects($this->once()) diff --git a/tests/TestCase/Webauthn/Repository/UserCredentialSourceRepositoryTest.php b/tests/TestCase/Webauthn/Repository/UserCredentialSourceRepositoryTest.php index 81e1a84d2..77a37a367 100644 --- a/tests/TestCase/Webauthn/Repository/UserCredentialSourceRepositoryTest.php +++ b/tests/TestCase/Webauthn/Repository/UserCredentialSourceRepositoryTest.php @@ -1,4 +1,5 @@ get($userId); $userEntity = new PublicKeyCredentialUserEntity( - 'john.doe', // Username - $userId, // ID - 'John Doe' // Display name + 'john.doe', + $userId, + 'John Doe' ); $repository = new UserCredentialSourceRepository($user, $UsersTable); $credentials = $repository->findAllForUserEntity($userEntity); @@ -57,9 +58,9 @@ public function testFindAllForUserEntity() //Not found id $userEntityInvalid = new PublicKeyCredentialUserEntity( - 'john.doe', // Username - '00000000-0000-0000-0000-000000000004', // ID - 'John Doe' // Display name + 'john.doe', + '00000000-0000-0000-0000-000000000004', + 'John Doe' ); $repository = new UserCredentialSourceRepository($user, $UsersTable); $credentials = $repository->findAllForUserEntity($userEntityInvalid); @@ -81,21 +82,21 @@ public function testSaveCredentialSource() 'transports' => [], 'attestationType' => 'none', 'trustPath' => [ - 'type' => 'Webauthn\TrustPath\EmptyTrustPath' + 'type' => 'Webauthn\TrustPath\EmptyTrustPath', ], 'aaguid' => '00000000-0000-0000-0000-000000000000', 'credentialPublicKey' => Base64Url::encode('000000000000000000000000000000000000-9999999999999999999999999999999999999999-XXXXXXXXXXXXX-YYYYYYYYYYY'), 'userHandle' => Base64Url::encode($userId), 'counter' => 191, - 'otherUI' => null + 'otherUI' => null, ]; $UsersTable = TableRegistry::getTableLocator()->get('CakeDC/Users.Users'); $user = $UsersTable->get($userId); $firstKey = key($user->additional_data['webauthn_credentials']); $userEntity = new PublicKeyCredentialUserEntity( - 'john.doe', // Username - $userId, // ID - 'John Doe' // Display name + 'john.doe', + $userId, + 'John Doe' ); $publicKey = PublicKeyCredentialSource::createFromArray($credentialData); $repository = new UserCredentialSourceRepository($user, $UsersTable); From 41382588fac113235742b7214b940e28853dda8c Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Mon, 20 Dec 2021 12:29:26 -0300 Subject: [PATCH 39/53] fixing phpstan --- phpstan-baseline.neon | 212 +++++++++++++++++++++--------------------- 1 file changed, 106 insertions(+), 106 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 146e9ed85..61ac3a319 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -3,265 +3,265 @@ parameters: - message: "#^Access to an undefined property Cake\\\\Controller\\\\Controller\\:\\:\\$Authentication\\.$#" count: 1 - path: src\Controller\Component\LoginComponent.php + path: src/Controller/Component/LoginComponent.php - message: "#^Call to an undefined method Cake\\\\Controller\\\\Controller\\:\\:getUsersTable\\(\\)\\.$#" count: 1 - path: src\Controller\Component\LoginComponent.php + path: src/Controller/Component/LoginComponent.php - - message: "#^Call to an undefined method CakeDC\\\\Users\\\\Model\\\\Table\\\\SocialAccountsTable\\:\\:validateAccount\\(\\)\\.$#" - count: 1 - path: src\Controller\SocialAccountsController.php + message: "#^Access to an undefined property CakeDC\\\\Users\\\\Controller\\\\UsersController\\:\\:\\$Authentication\\.$#" + count: 2 + path: src/Controller/UsersController.php - - message: "#^Call to an undefined method CakeDC\\\\Users\\\\Model\\\\Table\\\\SocialAccountsTable\\:\\:resendValidation\\(\\)\\.$#" - count: 1 - path: src\Controller\SocialAccountsController.php + message: "#^Access to an undefined property CakeDC\\\\Users\\\\Controller\\\\UsersController\\:\\:\\$OneTimePasswordAuthenticator\\.$#" + count: 3 + path: src/Controller/UsersController.php - - message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:linkSocialAccount\\(\\)\\.$#" + message: "#^Access to an undefined property Cake\\\\Datasource\\\\EntityInterface\\:\\:\\$u2f_registration\\.$#" count: 1 - path: src\Controller\UsersController.php + path: src/Controller/UsersController.php - message: "#^Call to an undefined method Cake\\\\Controller\\\\Component\\:\\:handleLogin\\(\\)\\.$#" count: 3 - path: src\Controller\UsersController.php - - - - message: "#^Access to an undefined property CakeDC\\\\Users\\\\Controller\\\\UsersController\\:\\:\\$Authentication\\.$#" - count: 2 - path: src\Controller\UsersController.php + path: src/Controller/UsersController.php - - message: "#^Access to an undefined property CakeDC\\\\Users\\\\Controller\\\\UsersController\\:\\:\\$OneTimePasswordAuthenticator\\.$#" - count: 3 - path: src\Controller\UsersController.php - - - - message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:validationPasswordConfirm\\(\\)\\.$#" + message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:changePassword\\(\\)\\.$#" count: 1 - path: src\Controller\UsersController.php + path: src/Controller/UsersController.php - - message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:validationCurrentPassword\\(\\)\\.$#" + message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:linkSocialAccount\\(\\)\\.$#" count: 1 - path: src\Controller\UsersController.php + path: src/Controller/UsersController.php - - message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:changePassword\\(\\)\\.$#" - count: 1 - path: src\Controller\UsersController.php + message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:register\\(\\)\\.$#" + count: 2 + path: src/Controller/UsersController.php - message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:resetToken\\(\\)\\.$#" count: 2 - path: src\Controller\UsersController.php + path: src/Controller/UsersController.php - - message: "#^Parameter \\#2 \\$options of method Cake\\\\Controller\\\\Component\\\\FlashComponent\\:\\:success\\(\\) expects array, string given\\.$#" + message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:validate\\(\\)\\.$#" + count: 2 + path: src/Controller/UsersController.php + + - + message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:validationCurrentPassword\\(\\)\\.$#" count: 1 - path: src\Controller\UsersController.php + path: src/Controller/UsersController.php - - message: "#^Parameter \\#2 \\$options of method Cake\\\\Controller\\\\Component\\\\FlashComponent\\:\\:error\\(\\) expects array, string given\\.$#" + message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:validationPasswordConfirm\\(\\)\\.$#" count: 1 - path: src\Controller\UsersController.php + path: src/Controller/UsersController.php - - message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:register\\(\\)\\.$#" - count: 2 - path: src\Controller\UsersController.php + message: "#^Parameter \\#1 \\$message of method Cake\\\\Controller\\\\Controller\\:\\:log\\(\\) expects string, Exception given\\.$#" + count: 1 + path: src/Controller/UsersController.php - message: "#^Parameter \\#1 \\$object of method Cake\\\\Controller\\\\Controller\\:\\:paginate\\(\\) expects Cake\\\\ORM\\\\Query\\|Cake\\\\ORM\\\\Table\\|string\\|null, Cake\\\\Datasource\\\\RepositoryInterface given\\.$#" count: 1 - path: src\Controller\UsersController.php + path: src/Controller/UsersController.php - - message: "#^Access to an undefined property Cake\\\\Datasource\\\\EntityInterface\\:\\:\\$u2f_registration\\.$#" + message: "#^Parameter \\#2 \\$options of method Cake\\\\Controller\\\\Component\\\\FlashComponent\\:\\:error\\(\\) expects array, string given\\.$#" count: 1 - path: src\Controller\UsersController.php + path: src/Controller/UsersController.php - - message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:validate\\(\\)\\.$#" - count: 2 - path: src\Controller\UsersController.php + message: "#^Parameter \\#2 \\$options of method Cake\\\\Controller\\\\Component\\\\FlashComponent\\:\\:success\\(\\) expects array, string given\\.$#" + count: 1 + path: src/Controller/UsersController.php - - message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:socialLogin\\(\\)\\.$#" + message: "#^Parameter \\#2 \\$usersTable of class CakeDC\\\\Users\\\\Webauthn\\\\RegisterAdapter constructor expects CakeDC\\\\Users\\\\Model\\\\Table\\\\UsersTable\\|null, Cake\\\\ORM\\\\Table given\\.$#" count: 1 - path: src\Identifier\SocialIdentifier.php + path: src/Controller/UsersController.php - - message: "#^Parameter \\#2 \\$request of method CakeDC\\\\Users\\\\Utility\\\\UsersUrl\\:\\:checkActionOnRequest\\(\\) expects Cake\\\\Http\\\\ServerRequest, Psr\\\\Http\\\\Message\\\\ServerRequestInterface given\\.$#" + message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:socialLogin\\(\\)\\.$#" count: 1 - path: src\Middleware\SocialAuthMiddleware.php + path: src/Identifier/SocialIdentifier.php - message: "#^Parameter \\#1 \\$request of method CakeDC\\\\Users\\\\Middleware\\\\SocialAuthMiddleware\\:\\:goNext\\(\\) expects Cake\\\\Http\\\\ServerRequest, Psr\\\\Http\\\\Message\\\\ServerRequestInterface given\\.$#" count: 1 - path: src\Middleware\SocialAuthMiddleware.php + path: src/Middleware/SocialAuthMiddleware.php - message: "#^Parameter \\#2 \\$request of method CakeDC\\\\Users\\\\Utility\\\\UsersUrl\\:\\:checkActionOnRequest\\(\\) expects Cake\\\\Http\\\\ServerRequest, Psr\\\\Http\\\\Message\\\\ServerRequestInterface given\\.$#" count: 1 - path: src\Middleware\SocialEmailMiddleware.php + path: src/Middleware/SocialAuthMiddleware.php - message: "#^Parameter \\#1 \\$request of method CakeDC\\\\Users\\\\Middleware\\\\SocialAuthMiddleware\\:\\:goNext\\(\\) expects Cake\\\\Http\\\\ServerRequest, Psr\\\\Http\\\\Message\\\\ServerRequestInterface given\\.$#" count: 1 - path: src\Middleware\SocialEmailMiddleware.php + path: src/Middleware/SocialEmailMiddleware.php - - message: "#^Call to an undefined method Cake\\\\Datasource\\\\EntityInterface\\:\\:updateToken\\(\\)\\.$#" + message: "#^Parameter \\#2 \\$request of method CakeDC\\\\Users\\\\Utility\\\\UsersUrl\\:\\:checkActionOnRequest\\(\\) expects Cake\\\\Http\\\\ServerRequest, Psr\\\\Http\\\\Message\\\\ServerRequestInterface given\\.$#" count: 1 - path: src\Model\Behavior\BaseTokenBehavior.php - - - - message: "#^Access to an undefined property Cake\\\\ORM\\\\Table\\:\\:\\$SocialAccounts\\.$#" - count: 5 - path: src\Model\Behavior\LinkSocialBehavior.php + path: src/Middleware/SocialEmailMiddleware.php - - message: "#^Negated boolean expression is always false\\.$#" + message: "#^Call to an undefined method Cake\\\\Datasource\\\\EntityInterface\\:\\:updateToken\\(\\)\\.$#" count: 1 - path: src\Model\Behavior\LinkSocialBehavior.php + path: src/Model/Behavior/BaseTokenBehavior.php - message: "#^Access to an undefined property Cake\\\\Datasource\\\\EntityInterface\\:\\:\\$social_accounts\\.$#" count: 2 - path: src\Model\Behavior\LinkSocialBehavior.php + path: src/Model/Behavior/LinkSocialBehavior.php - - message: "#^Method CakeDC\\\\Users\\\\Model\\\\Behavior\\\\PasswordBehavior\\:\\:resetToken\\(\\) should return string but returns Cake\\\\Datasource\\\\EntityInterface\\|false\\.$#" - count: 1 - path: src\Model\Behavior\PasswordBehavior.php + message: "#^Access to an undefined property Cake\\\\ORM\\\\Table\\:\\:\\$SocialAccounts\\.$#" + count: 5 + path: src/Model/Behavior/LinkSocialBehavior.php - - message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:findByUsernameOrEmail\\(\\)\\.$#" + message: "#^Negated boolean expression is always false\\.$#" count: 1 - path: src\Model\Behavior\PasswordBehavior.php + path: src/Model/Behavior/LinkSocialBehavior.php - message: "#^Access to an undefined property Cake\\\\Datasource\\\\EntityInterface\\:\\:\\$password\\.$#" count: 1 - path: src\Model\Behavior\PasswordBehavior.php + path: src/Model/Behavior/PasswordBehavior.php - - message: "#^Call to an undefined method Cake\\\\Datasource\\\\EntityInterface\\:\\:checkPassword\\(\\)\\.$#" + message: "#^Access to an undefined property Cake\\\\Datasource\\\\EntityInterface\\:\\:\\$password_confirm\\.$#" count: 1 - path: src\Model\Behavior\PasswordBehavior.php + path: src/Model/Behavior/PasswordBehavior.php - - message: "#^Access to an undefined property Cake\\\\Datasource\\\\EntityInterface\\:\\:\\$password_confirm\\.$#" + message: "#^Call to an undefined method Cake\\\\Datasource\\\\EntityInterface\\:\\:checkPassword\\(\\)\\.$#" count: 1 - path: src\Model\Behavior\PasswordBehavior.php + path: src/Model/Behavior/PasswordBehavior.php - - message: "#^Access to an undefined property Cake\\\\Datasource\\\\EntityInterface\\:\\:\\$validated\\.$#" + message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:findByUsernameOrEmail\\(\\)\\.$#" count: 1 - path: src\Model\Behavior\RegisterBehavior.php + path: src/Model/Behavior/PasswordBehavior.php - - message: "#^Access to an undefined property Cake\\\\ORM\\\\Table\\:\\:\\$isValidateEmail\\.$#" + message: "#^Method CakeDC\\\\Users\\\\Model\\\\Behavior\\\\PasswordBehavior\\:\\:resetToken\\(\\) should return string but returns Cake\\\\Datasource\\\\EntityInterface\\|false\\.$#" count: 1 - path: src\Model\Behavior\RegisterBehavior.php + path: src/Model/Behavior/PasswordBehavior.php - - message: "#^Cannot call method tokenExpired\\(\\) on array\\|Cake\\\\Datasource\\\\EntityInterface\\.$#" + message: "#^Access to an undefined property Cake\\\\Datasource\\\\EntityInterface\\:\\:\\$activation_date\\.$#" count: 1 - path: src\Model\Behavior\RegisterBehavior.php + path: src/Model/Behavior/RegisterBehavior.php - message: "#^Access to an undefined property Cake\\\\Datasource\\\\EntityInterface\\:\\:\\$active\\.$#" count: 1 - path: src\Model\Behavior\RegisterBehavior.php + path: src/Model/Behavior/RegisterBehavior.php - - message: "#^Access to an undefined property Cake\\\\Datasource\\\\EntityInterface\\:\\:\\$activation_date\\.$#" + message: "#^Access to an undefined property Cake\\\\Datasource\\\\EntityInterface\\:\\:\\$token_expires\\.$#" count: 1 - path: src\Model\Behavior\RegisterBehavior.php + path: src/Model/Behavior/RegisterBehavior.php - - message: "#^Access to an undefined property Cake\\\\Datasource\\\\EntityInterface\\:\\:\\$token_expires\\.$#" + message: "#^Access to an undefined property Cake\\\\Datasource\\\\EntityInterface\\:\\:\\$validated\\.$#" + count: 1 + path: src/Model/Behavior/RegisterBehavior.php + + - + message: "#^Access to an undefined property Cake\\\\ORM\\\\Table\\:\\:\\$isValidateEmail\\.$#" count: 1 - path: src\Model\Behavior\RegisterBehavior.php + path: src/Model/Behavior/RegisterBehavior.php - message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:validationRegister\\(\\)\\.$#" count: 1 - path: src\Model\Behavior\RegisterBehavior.php + path: src/Model/Behavior/RegisterBehavior.php - - message: "#^Cannot access property \\$token on array\\|Cake\\\\Datasource\\\\EntityInterface\\.$#" + message: "#^Cannot call method tokenExpired\\(\\) on array\\|Cake\\\\Datasource\\\\EntityInterface\\.$#" + count: 1 + path: src/Model/Behavior/RegisterBehavior.php + + - + message: "#^Access to an undefined property CakeDC\\\\Users\\\\Model\\\\Entity\\\\SocialAccount\\:\\:\\$active\\.$#" count: 1 - path: src\Model\Behavior\SocialAccountBehavior.php + path: src/Model/Behavior/SocialAccountBehavior.php - message: "#^Cannot access property \\$active on array\\|Cake\\\\Datasource\\\\EntityInterface\\.$#" count: 2 - path: src\Model\Behavior\SocialAccountBehavior.php + path: src/Model/Behavior/SocialAccountBehavior.php - - message: "#^Method CakeDC\\\\Users\\\\Model\\\\Behavior\\\\SocialAccountBehavior\\:\\:validateAccount\\(\\) should return CakeDC\\\\Users\\\\Model\\\\Entity\\\\User but returns Cake\\\\Datasource\\\\EntityInterface\\.$#" + message: "#^Cannot access property \\$token on array\\|Cake\\\\Datasource\\\\EntityInterface\\.$#" count: 1 - path: src\Model\Behavior\SocialAccountBehavior.php + path: src/Model/Behavior/SocialAccountBehavior.php - - message: "#^Parameter \\#1 \\$socialAccount of method CakeDC\\\\Users\\\\Model\\\\Behavior\\\\SocialAccountBehavior\\:\\:_activateAccount\\(\\) expects CakeDC\\\\Users\\\\Model\\\\Entity\\\\SocialAccount, array\\|Cake\\\\Datasource\\\\EntityInterface given\\.$#" + message: "#^Cannot access property \\$user on array\\|Cake\\\\Datasource\\\\EntityInterface\\.$#" count: 1 - path: src\Model\Behavior\SocialAccountBehavior.php + path: src/Model/Behavior/SocialAccountBehavior.php - - message: "#^Cannot access property \\$user on array\\|Cake\\\\Datasource\\\\EntityInterface\\.$#" + message: "#^Method CakeDC\\\\Users\\\\Model\\\\Behavior\\\\SocialAccountBehavior\\:\\:resendValidation\\(\\) should return CakeDC\\\\Users\\\\Model\\\\Entity\\\\User but returns array\\.$#" count: 1 - path: src\Model\Behavior\SocialAccountBehavior.php + path: src/Model/Behavior/SocialAccountBehavior.php - - message: "#^Method CakeDC\\\\Users\\\\Model\\\\Behavior\\\\SocialAccountBehavior\\:\\:resendValidation\\(\\) should return CakeDC\\\\Users\\\\Model\\\\Entity\\\\User but returns array\\.$#" + message: "#^Method CakeDC\\\\Users\\\\Model\\\\Behavior\\\\SocialAccountBehavior\\:\\:validateAccount\\(\\) should return CakeDC\\\\Users\\\\Model\\\\Entity\\\\User but returns Cake\\\\Datasource\\\\EntityInterface\\.$#" count: 1 - path: src\Model\Behavior\SocialAccountBehavior.php + path: src/Model/Behavior/SocialAccountBehavior.php - - message: "#^Access to an undefined property CakeDC\\\\Users\\\\Model\\\\Entity\\\\SocialAccount\\:\\:\\$active\\.$#" + message: "#^Parameter \\#1 \\$socialAccount of method CakeDC\\\\Users\\\\Model\\\\Behavior\\\\SocialAccountBehavior\\:\\:_activateAccount\\(\\) expects CakeDC\\\\Users\\\\Model\\\\Entity\\\\SocialAccount, array\\|Cake\\\\Datasource\\\\EntityInterface given\\.$#" count: 1 - path: src\Model\Behavior\SocialAccountBehavior.php + path: src/Model/Behavior/SocialAccountBehavior.php - message: "#^Access to an undefined property Cake\\\\ORM\\\\Table\\:\\:\\$SocialAccounts\\.$#" count: 4 - path: src\Model\Behavior\SocialBehavior.php + path: src/Model/Behavior/SocialBehavior.php - message: "#^Access to an undefined property Cake\\\\ORM\\\\Table\\:\\:\\$isValidateEmail\\.$#" count: 1 - path: src\Model\Behavior\SocialBehavior.php + path: src/Model/Behavior/SocialBehavior.php - message: "#^Parameter \\#3 \\$tokenExpiration of method CakeDC\\\\Users\\\\Model\\\\Behavior\\\\BaseTokenBehavior\\:\\:_updateActive\\(\\) expects int, string given\\.$#" count: 1 - path: src\Model\Behavior\SocialBehavior.php + path: src/Model/Behavior/SocialBehavior.php - message: "#^Method CakeDC\\\\Users\\\\Model\\\\Entity\\\\User\\:\\:_setTos\\(\\) should return bool but returns string\\.$#" count: 1 - path: src\Model\Entity\User.php - - - - message: "#^Property CakeDC\\\\Users\\\\Shell\\\\UsersShell\\:\\:\\$Users \\(CakeDC\\\\Users\\\\Model\\\\Table\\\\UsersTable\\) does not accept Cake\\\\Datasource\\\\RepositoryInterface\\.$#" - count: 1 - path: src\Shell\UsersShell.php + path: src/Model/Entity/User.php - message: "#^Cannot access property \\$username on bool\\.$#" count: 2 - path: src\Shell\UsersShell.php + path: src/Shell/UsersShell.php + + - + message: "#^Property CakeDC\\\\Users\\\\Shell\\\\UsersShell\\:\\:\\$Users \\(CakeDC\\\\Users\\\\Model\\\\Table\\\\UsersTable\\) does not accept Cake\\\\Datasource\\\\RepositoryInterface\\.$#" + count: 1 + path: src/Shell/UsersShell.php - - message: "#^Parameter \\#1 \\$message of method Cake\\\\Controller\\\\Controller\\:\\:log\\(\\)\\ expects string, Exception given.$#" + message: "#^Parameter \\#2 \\$usersTable of class CakeDC\\\\Users\\\\Webauthn\\\\Repository\\\\UserCredentialSourceRepository constructor expects CakeDC\\\\Users\\\\Model\\\\Table\\\\UsersTable\\|null, Cake\\\\ORM\\\\Table given\\.$#" count: 1 - path: src\Controller\Traits\OneTimePasswordVerifyTrait.php + path: src/Webauthn/BaseAdapter.php From bebd6d443084c6f1c5a0a264b4dc1127a581e1ad Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Mon, 20 Dec 2021 12:40:04 -0300 Subject: [PATCH 40/53] Check if user already have setup webauthn2fa --- src/Controller/Traits/Webauthn2faTrait.php | 19 ++++++- .../Traits/Webauthn2faTraitTest.php | 57 +++++++++++++++++++ 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/src/Controller/Traits/Webauthn2faTrait.php b/src/Controller/Traits/Webauthn2faTrait.php index 95c6ad620..301c08acc 100644 --- a/src/Controller/Traits/Webauthn2faTrait.php +++ b/src/Controller/Traits/Webauthn2faTrait.php @@ -14,6 +14,7 @@ namespace CakeDC\Users\Controller\Traits; use Cake\Core\Configure; +use Cake\Http\Exception\BadRequestException; use Cake\Log\Log; use Cake\Routing\Router; use CakeDC\Auth\Authenticator\TwoFactorAuthenticator; @@ -43,8 +44,14 @@ public function webauthn2fa() public function webauthn2faRegisterOptions() { $adapter = $this->getWebauthn2faRegisterAdapter(); + if (!$adapter->hasCredential()) { + return $this->getResponse() + ->withStringBody(json_encode($adapter->getOptions())); + } - return $this->getResponse()->withStringBody(json_encode($adapter->getOptions())); + throw new BadRequestException( + __d('cake_d_c/users', 'User already has configured webauthn2fa') + ); } /** @@ -56,9 +63,15 @@ public function webauthn2faRegisterOptions() public function webauthn2faRegister(): \Cake\Http\Response { try { - $this->getWebauthn2faRegisterAdapter()->verifyResponse(); + $adapter = $this->getWebauthn2faRegisterAdapter(); + if (!$adapter->hasCredential()) { + $adapter->verifyResponse(); - return $this->getResponse()->withStringBody(json_encode(['success' => true])); + return $this->getResponse()->withStringBody(json_encode(['success' => true])); + } + throw new BadRequestException( + __d('cake_d_c/users', 'User already has configured webauthn2fa') + ); } catch (\Throwable $e) { $user = $this->request->getSession()->read('Webauthn2fa.User'); Log::debug(__('Register error with webauthn for user id: {0}', $user['id'] ?? 'empty')); diff --git a/tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php b/tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php index fa234bd63..b247289c8 100644 --- a/tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php +++ b/tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php @@ -15,6 +15,7 @@ use Base64Url\Base64Url; use Cake\Core\Configure; +use Cake\Http\Exception\BadRequestException; use Cake\Http\ServerRequest; use Cake\Http\ServerRequestFactory; use Cake\ORM\TableRegistry; @@ -164,6 +165,34 @@ public function testWebauthn2faDontRequireRegister() ); } + /** + * Test webauthn2faRegisterOptions method when DON'T require register + * + * @return void + */ + public function testWebauthn2faRegisterOptionsDontRequireRegister() + { + $request = $this->getMockBuilder('Cake\Http\ServerRequest') + ->setMethods(['getSession', 'is']) + ->getMock(); + $this->Trait->setRequest($request); + $request->expects($this->any()) + ->method('is') + ->with( + $this->equalTo('ssl') + )->will($this->returnValue(true)); + + $table = TableRegistry::getTableLocator() + ->get('CakeDC/Users.Users'); + $user = $table->get('00000000-0000-0000-0000-000000000001'); + $this->_mockSession([ + 'Webauthn2fa.User' => $user, + ]); + $this->expectException(BadRequestException::class); + $this->expectExceptionMessage('User already has configured webauthn2fa'); + $this->Trait->webauthn2faRegisterOptions(); + } + /** * Test webauthn2faRegisterOptions method * @@ -199,6 +228,34 @@ public function testWebauthn2faRegisterOptions() ); } + /** + * Test webauthn2faRegister method when DON'T require register + * + * @return void + */ + public function testWebauthn2faRegisterDontRequireRegister() + { + $request = $this->getMockBuilder('Cake\Http\ServerRequest') + ->setMethods(['getSession', 'is']) + ->getMock(); + $this->Trait->setRequest($request); + $request->expects($this->any()) + ->method('is') + ->with( + $this->equalTo('ssl') + )->will($this->returnValue(true)); + + $table = TableRegistry::getTableLocator() + ->get('CakeDC/Users.Users'); + $user = $table->get('00000000-0000-0000-0000-000000000001'); + $this->_mockSession([ + 'Webauthn2fa.User' => $user, + ]); + $this->expectException(BadRequestException::class); + $this->expectExceptionMessage('User already has configured webauthn2fa'); + $this->Trait->webauthn2faRegister(); + } + /** * Test webauthn2faRegisterOptions method * From 1ba39937a74969a38afbf6d6c20c3c369b4294c7 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Tue, 21 Dec 2021 07:17:37 -0300 Subject: [PATCH 41/53] improved translation message lookup --- templates/Users/webauthn2fa.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/templates/Users/webauthn2fa.php b/templates/Users/webauthn2fa.php index dab4aba4a..23977e0ca 100644 --- a/templates/Users/webauthn2fa.php +++ b/templates/Users/webauthn2fa.php @@ -15,16 +15,16 @@ Flash->render() ?>

Html->link( __('Reload'), From 318a45811ed3616561033e99e48be0289e27df9f Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Tue, 21 Dec 2021 07:19:20 -0300 Subject: [PATCH 42/53] Updated phpdoc --- templates/Users/webauthn2fa.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/Users/webauthn2fa.php b/templates/Users/webauthn2fa.php index 23977e0ca..ae1686af0 100644 --- a/templates/Users/webauthn2fa.php +++ b/templates/Users/webauthn2fa.php @@ -1,7 +1,8 @@ Html->script('CakeDC/Users.webauthn.js', ['block' => true]); $this->assign('title', __('Two-factor authentication')); From d6b45a1e0e58e2c6ed5028190f10d1bf56b9ac82 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Tue, 21 Dec 2021 08:09:29 -0300 Subject: [PATCH 43/53] Prepare release for 9.3.0 --- .semver | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.semver b/.semver index a0290e8f4..40fab0e1b 100644 --- a/.semver +++ b/.semver @@ -1,5 +1,5 @@ --- :major: 9 -:minor: 2 +:minor: 3 :patch: 0 :special: '' From d69543be6726d25a7e37b0c19f054ba680b4c6f9 Mon Sep 17 00:00:00 2001 From: Alberto Rodriguez Date: Sat, 22 Jan 2022 14:03:43 -0400 Subject: [PATCH 44/53] fix some tests --- .../Traits/Integration/LoginTraitIntegrationTest.php | 12 ++++++------ .../Integration/RegisterTraitIntegrationTest.php | 4 ++-- .../Integration/SimpleCrudTraitIntegrationTest.php | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php b/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php index 982806ea1..efd2dc80b 100644 --- a/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php +++ b/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php @@ -76,8 +76,8 @@ public function testLoginGetRequestNoSocialLogin() $this->assertResponseNotContains('Username or password is incorrect'); $this->assertResponseContains(''); $this->assertResponseContains('Please enter your username and password'); - $this->assertResponseContains(''); - $this->assertResponseContains(''); + $this->assertResponseContains(''); + $this->assertResponseContains('input type="password" name="password" required="required" id="password" aria-required="true"/>'); $this->assertResponseContains(''); $this->assertResponseContains(''); $this->assertResponseContains('Register'); @@ -102,8 +102,8 @@ public function testLoginGetRequest() $this->assertResponseNotContains('Username or password is incorrect'); $this->assertResponseContains(''); $this->assertResponseContains('Please enter your username and password'); - $this->assertResponseContains(''); - $this->assertResponseContains(''); + $this->assertResponseContains(''); + $this->assertResponseContains('input type="password" name="password" required="required" id="password" aria-required="true"/>'); $this->assertResponseContains(''); $this->assertResponseContains(''); $this->assertResponseContains('Register'); @@ -131,8 +131,8 @@ public function testLoginPostRequestInvalidPassword() $this->assertResponseContains('Username or password is incorrect'); $this->assertResponseContains(''); $this->assertResponseContains('Please enter your username and password'); - $this->assertResponseContains(''); - $this->assertResponseContains(''); + $this->assertResponseContains(''); + $this->assertResponseContains(''); $this->assertResponseContains(''); $this->assertResponseContains(''); } diff --git a/tests/TestCase/Controller/Traits/Integration/RegisterTraitIntegrationTest.php b/tests/TestCase/Controller/Traits/Integration/RegisterTraitIntegrationTest.php index 0866a5c84..8c7d4e852 100644 --- a/tests/TestCase/Controller/Traits/Integration/RegisterTraitIntegrationTest.php +++ b/tests/TestCase/Controller/Traits/Integration/RegisterTraitIntegrationTest.php @@ -48,7 +48,7 @@ public function testRegister() $this->assertResponseContains(''); $this->assertResponseContains(''); $this->assertResponseContains(''); - $this->assertResponseContains(''); + $this->assertResponseContains(''); $this->assertResponseContains(''); } @@ -83,7 +83,7 @@ public function testRegisterPostWithErrors() $this->assertResponseContains(''); $this->assertResponseContains(''); $this->assertResponseContains(''); - $this->assertResponseContains(''); + $this->assertResponseContains(''); $this->assertResponseContains(''); } diff --git a/tests/TestCase/Controller/Traits/Integration/SimpleCrudTraitIntegrationTest.php b/tests/TestCase/Controller/Traits/Integration/SimpleCrudTraitIntegrationTest.php index 90df2fcc2..9b606146e 100644 --- a/tests/TestCase/Controller/Traits/Integration/SimpleCrudTraitIntegrationTest.php +++ b/tests/TestCase/Controller/Traits/Integration/SimpleCrudTraitIntegrationTest.php @@ -95,9 +95,9 @@ public function testCrud() $this->get('/users/edit/00000000-0000-0000-0000-000000000006'); $this->assertResponseContains('assertResponseContains('id="username" aria-required="true" value="user-6"'); $this->assertResponseContains('assertResponseContains('id="email" aria-required="true" value="6@example.com"'); $this->assertResponseContains('Active'); From 8d888d92706efc9e5b793fd6d98b454da7c60998 Mon Sep 17 00:00:00 2001 From: Alberto Rodriguez Date: Sat, 22 Jan 2022 14:05:25 -0400 Subject: [PATCH 45/53] add CI tests for php 8.1 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb98b5723..032c79a25 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - php-version: ['7.3', '7.4', '8.0'] + php-version: ['7.3', '7.4', '8.0', '8.1'] db-type: [sqlite, mysql, pgsql] prefer-lowest: [''] From df6dfb4b0ccf5e22666f33e95227929ff12e73c9 Mon Sep 17 00:00:00 2001 From: Alberto Rodriguez Date: Mon, 24 Jan 2022 13:32:02 -0400 Subject: [PATCH 46/53] minor fix --- .../Traits/Integration/LoginTraitIntegrationTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php b/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php index efd2dc80b..1ae48b8bb 100644 --- a/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php +++ b/tests/TestCase/Controller/Traits/Integration/LoginTraitIntegrationTest.php @@ -77,7 +77,7 @@ public function testLoginGetRequestNoSocialLogin() $this->assertResponseContains(''); $this->assertResponseContains('Please enter your username and password'); $this->assertResponseContains(''); - $this->assertResponseContains('input type="password" name="password" required="required" id="password" aria-required="true"/>'); + $this->assertResponseContains(''); $this->assertResponseContains(''); $this->assertResponseContains(''); $this->assertResponseContains('Register'); @@ -103,7 +103,7 @@ public function testLoginGetRequest() $this->assertResponseContains(''); $this->assertResponseContains('Please enter your username and password'); $this->assertResponseContains(''); - $this->assertResponseContains('input type="password" name="password" required="required" id="password" aria-required="true"/>'); + $this->assertResponseContains(''); $this->assertResponseContains(''); $this->assertResponseContains(''); $this->assertResponseContains('Register'); From c1bb3b40e69a23be1415ac6829b25dd5f8ec71f1 Mon Sep 17 00:00:00 2001 From: Yevgeny Tomenko Date: Mon, 24 Jan 2022 22:44:22 +0300 Subject: [PATCH 47/53] prepare to release --- CHANGELOG.md | 7 +++++ composer.json | 1 + tests/Fixture/UsersFixture.php | 4 +-- .../Traits/Webauthn2faTraitTest.php | 28 ++++--------------- .../UserCredentialSourceRepositoryTest.php | 2 ++ 5 files changed, 18 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8eea9d31..78c464692 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ Changelog ========= +Releases for CakePHP 4.3 + +* 11.0.0 + * Switched tests to new cakephp schema + * Update to PHPUnit 9.5 + + Releases for CakePHP 4 ------------- * 9.2.0 diff --git a/composer.json b/composer.json index f03271f39..3a94372d7 100644 --- a/composer.json +++ b/composer.json @@ -27,6 +27,7 @@ "source": "https://github.com/CakeDC/users" }, "minimum-stability": "dev", + "prefer-stable": true, "require": { "php": ">=7.2.0", "cakephp/cakephp": "^4.0", diff --git a/tests/Fixture/UsersFixture.php b/tests/Fixture/UsersFixture.php index 485714e1e..d48f6dc55 100644 --- a/tests/Fixture/UsersFixture.php +++ b/tests/Fixture/UsersFixture.php @@ -47,7 +47,7 @@ public function init(): void 'role' => 'admin', 'created' => '2015-06-24 17:33:54', 'modified' => '2015-06-24 17:33:54', - 'additional_data' => json_encode([ + 'additional_data' => [ 'u2f_registration' => [ 'keyHandle' => 'fake key handle', 'publicKey' => 'afdoaj0-23u423-ad ujsf-as8-0-afsd', @@ -70,7 +70,7 @@ public function init(): void 'otherUI' => null, ], ], - ]), + ], 'last_login' => '2015-06-24 17:33:54', ], [ diff --git a/tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php b/tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php index b247289c8..e47f2c661 100644 --- a/tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php +++ b/tests/TestCase/Controller/Traits/Webauthn2faTraitTest.php @@ -104,18 +104,10 @@ public function testWebauthn2faIsRegister() $this->_mockSession([ 'Webauthn2fa.User' => $user, ]); - $this->Trait->expects($this->at(1)) - ->method('set') - ->with( - $this->equalTo('isRegister'), - $this->equalTo(true) - ); - $this->Trait->expects($this->at(2)) + $this->Trait + ->expects($this->exactly(2)) ->method('set') - ->with( - $this->equalTo('username'), - $this->equalTo('user-2') - ); + ->withConsecutive(['isRegister', true], ['username', 'user-2']); $this->Trait->webauthn2fa(); $this->assertSame( $user, @@ -146,18 +138,10 @@ public function testWebauthn2faDontRequireRegister() $this->_mockSession([ 'Webauthn2fa.User' => $user, ]); - $this->Trait->expects($this->at(1)) - ->method('set') - ->with( - $this->equalTo('isRegister'), - $this->equalTo(false) - ); - $this->Trait->expects($this->at(2)) + $this->Trait + ->expects($this->exactly(2)) ->method('set') - ->with( - $this->equalTo('username'), - $this->equalTo('user-1') - ); + ->withConsecutive(['isRegister', false], ['username', 'user-1']); $this->Trait->webauthn2fa(); $this->assertSame( $user, diff --git a/tests/TestCase/Webauthn/Repository/UserCredentialSourceRepositoryTest.php b/tests/TestCase/Webauthn/Repository/UserCredentialSourceRepositoryTest.php index 77a37a367..021d08c8b 100644 --- a/tests/TestCase/Webauthn/Repository/UserCredentialSourceRepositoryTest.php +++ b/tests/TestCase/Webauthn/Repository/UserCredentialSourceRepositoryTest.php @@ -74,6 +74,8 @@ public function testFindAllForUserEntity() */ public function testSaveCredentialSource() { + \Cake\Database\TypeFactory::map('json', \Cake\Database\Type\JsonType::class); + $publicKeyCredentialId = '12b37486-9299-4331-ac33-85b2d985b6fe'; $userId = '00000000-0000-0000-0000-000000000001'; $credentialData = [ From 012326d2af070efa80e9c080151acf8831984abd Mon Sep 17 00:00:00 2001 From: Evgeny Tomenko Date: Thu, 27 Jan 2022 15:18:44 +0300 Subject: [PATCH 48/53] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f87f94f60..fbd5af5f4 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Versions and branches | CakePHP | CakeDC Users Plugin | Tag | Notes | | :-------------: | :------------------------: | :--: | :---- | -| ^4.0 | [master](https://github.com/cakedc/users/tree/master) | 9.0.5 | stable | +| ^4.3 | [master](https://github.com/cakedc/users/tree/master) | 11.0.0 | stable | | ^4.3 | [11.0](https://github.com/cakedc/users/tree/11.next-cake4) | 11.0.0 | stable | | ^4.0 | [9.0](https://github.com/cakedc/users/tree/9.next) | 9.0.5 | stable | | ^3.7 <4.0 | [8.5](https://github.com/cakedc/users/tree/8.next) | 8.5.1 | stable | From 3ce830963d44f66d035d4063721b57cf8763e794 Mon Sep 17 00:00:00 2001 From: Kevin Pfeifer Date: Fri, 11 Mar 2022 13:32:37 +0100 Subject: [PATCH 49/53] added german translation --- resources/locales/de_DE/users.mo | Bin 0 -> 15667 bytes resources/locales/de_DE/users.po | 817 +++++++++++++++++++++++++++++++ 2 files changed, 817 insertions(+) create mode 100644 resources/locales/de_DE/users.mo create mode 100644 resources/locales/de_DE/users.po diff --git a/resources/locales/de_DE/users.mo b/resources/locales/de_DE/users.mo new file mode 100644 index 0000000000000000000000000000000000000000..61a8ac7065103115bcd5a87e1a3a01659e891cac GIT binary patch literal 15667 zcmcJV3y@@0dB?8=1QrxTd;n_huE;}~*&R^S*QG z6nr}TJUk2jC43Hi1a5&(KHWa=fG6_&a;S21@N9S^{8e}dq)PBv$FIOA^89Uh68tV4 zhW`Se49|Xw?f*h}9?zG+7s8`Z_1*#1-ffN_gy-^nKctD^+m8PV&*1rIQ0<@2!!zLy zI0BEr7sI!}P4G{k=JAhE?K}+6fsaDixBKF3eG{0HF%^8W@t4SwG}Kk9fgjcL4RLXBfLJPz)I8t)X0;1MXfd};hu~(Y#x;&DP~*MA@gUSZu7jFi1T~%osQKIg)$i+|8w55niehoQ#zOcsrw!A7X(Q7F0W zf~tQ2hOhypuWyDgfVV@*dBwf|52*If#2936+o0OL21?!;FXh=@ymY{dT7_{9C$I$7eKB5wT^Fv z3?cX|L^Q#-;5hsdJOa1T*?xE{RD1sbHJ*QlWANYMiSUIO4@(tngR+k!@Zu)~!Rz4x zowOQDU;8YS9(@s>0KWw#-@k*> zyPrCS7g~KDhmy-t$dUvZR6Do0_jg0B)2AIj2Q|J2;mPm>oXc_WG^lcCz!aVhHNKmn z?B)(gmEb=3BKU2naXkg+auOVJyc8bK^A30l9EY0!6jZsIV++27=N{C$eavwM%3l5& zN)F$HvZJ3sjrSCcRO|ONsP-CA>(GIhz~6%mE%-V_RKb5k*~<<-;dSs9I17IO zFNZV3c3!ta$?+=?Qwko2FNRN|6SX%1S^6M_EOl@z+zmemUkHy!$yENOP~&?Q#B~JA z@KpG2D0zGUYCZoLs@;d6^x%XM_5}DWsCMg6`uI_(e*YY9gFl9{qZeV^(vyRbCW0ey z1H2ikzgr+C6ub{I)!>WpH26cPevU_In&)1q@wTA!sRt$Rx4~23?T{r1-Vf*DpFu<$ zY-Q52(f_FiU_lIyNd=zS4V=RWo9YK~TcpH2={2bIee;sPv{{=OVjVPtY zI|`*QuY_8^&qKBM5R@JK6v}^|_fq>j0VVG?+yq|(HSW8i=JQpT{~h=IgwY__PX1Hj ziST7m>v9bohY7qKehl(2c!V#F_xU(C+1*}v8EnE=!4E;L%ULLg*6~e_Z--jv+oAgT zTPS&d4{AM5VGvJ%&migZNs`{bLl1mrN$)4M^}y$C7DmSKjmg5enwbe&!lbByY z@Q3go(g#Vh5q+LY`Y7oWq?n}7lSvDt2|e(6t3~A-u^Ih3;BwzodW%VzHF+A8F6gtL zr1jBfii8>mzfD>q9U=8d(yLoYzezew!sVFHrF_4V^dZt~NyVqZkJH@49njc2yvyYY z?;_n@dY0|%b2tG)*c# zXYk|Iq^n7(%Y3inGvPI)4oN=$jihTyBP4zPNMGCXPLzhVR+Q)4H#J&Ion}&Nr=kBf z)M__|l5|rTWl1#DOzQPG-M*=t_2NyJZQHEMmxYJpESXyh#-=92ZhJmXgRxpI?sUVM z1Jfam)st@0PV*hgZuin|7`3uEsxO7dqE=Fmx^dmV;eD$frtNMx8?W(NFXm{X&UY4e zn{n7kj>W0%HH^rQ7owy!?A!A_gn5rK<@sE%)ncSIGa|!d41lU(-id3;Tv7{rd7Sm% zEVX-C7`cHr<18L{mA7k2)C!SOl7{8?OgS8h53mwn8u5Yu@h%e zeSwxV2s_C*??Ihuh6?ztjjFwj1kHrXCciUk-ZpF;;x#6p~ zyp}~+uUHo6vC6Z6re`yRP1M;$C8QK(!LDYMHsWv!Wm#-z_2RiR@T`T&+O60$vr4qP z+UZ=9Erk7A?KF*R-EcAKHZ{h863U3{<*Y(6=u0BkqS;lsDD1T2D38N#wiHHytX9 zR97XexR57yS<06|b}A*YOG!J87GhUd#jECL+8i}Yy*r6pb@MZv$g+0E&ym*#yOS*M zhWpiq{b`!p({49Vs<9sW#nRQHuAG3;*TpEWbP*b54z5T{CzJJH(smL|&KbASO49i- z$&II}wUK_U+gb|iN#1EiOL5(=Rju7wG8N|9tyX(ch8WfBG@geTsnMLpcs88P+KVz+ zDeq)zoKB&g{yB?xMV)S~8TlMLbiCGP=26wBfih00MVo&0ynFNIU5=bhcc~Kxd!s^- z=9iJ32==2#5$oE{lI4>4tyjfcQ`=}P_r?fu;`?+e-w?lCavj5IReG=sV_;k){JX5CpI>u ztTuX^_G0VTI?iocWyOwgFF6yi&DrWk#r}8oe=*f%bl&HfPAeOm)~FcQFV=3UW}HT| z*zcNVY}R$*m9KSaeUgXIcquIxx?A+b)Z00ZL-rn2@icCIWq7|#)mq_sG#MX3vzQi2;^ zG%#mVVj*6bMUHsUgL3Be@URWI7JD4fsg=crM%FG&Z zN*qgMvqo5?_RSC~HS*{fdx(GMc4j9;pRH+0)y)xLxL+I}6Y8A}bn8{M^0kXnYQdFU zXM8GI8J;U{dR+CgKX$$H8^>TB|Dq_*B-uGR?(idvU)4)8iNJ*N<~y9Yp+jD7^ZcG4 zn_O42I!p@3U02)`i?mah97Md(=`NKE_|`SLLeE?ec9~32p0=&2yQ+RFro)Q3y+jAM zFP5vF+XK-dyAPnlc7HeOVEW0tQ$v)&A#J3CM&O-C^>t;r)+yt&DjH)EQqEoNdR)z^k)!b^Hjo|;XNq%Oa5#w-P6SZsHDTh$%h>Dp_m z+CQJx%T21PB2@*|y!K^_tx$r{k;K0AQm(pP8x{~k5-asj&)(qt>DM+=Jx>_7BsDQH zZQl@<;;wI?WU)y)+rGi9Uy|nB^|a~U&j zaG6oxh~}6RzL~tTtU?X@I>YnvQXu8l=@FWdTJPkgX#`xn8xW6^h+hZ7tm`=w4o%jF z(AJ#%(QxYEfh#9=&4h<14o*)V*uP=wz|cX)u9rjOSmkJV@s^7(9@=ur(8x$Qa>?i= zBQMx8vSrH#9lVBSvM9}45hvEskesy9iyHCJOdKtYhI_~M?>RKKXCmA-v2Sd0@8x6T z;|C|Er!Vv6M%k}6?3>&-(I3pn@Rki+lXMADhh{kFjfP#MxtZf@l5SN5ld%ANjoZVK z3%71KGPK9;?V86_JJ#L8CBs{WM>hPQTqN$~vTNm@ZbATIZWHZpIJRde-fVuEE5$@H z?~dRqX5L0mq|aHLbahs9HM_w$x@il{bC1M5QJU16Dp`~W7vqe}pI{;nU5WXaaL(45 zTX`Tu+$r%@BW}dAOg9bfevDt@RifL%;#D|dj&_ZBj!Q`j+Lnh)V_59bR~TnpAT$_i ztyvmKFg7nAsLArkB(@+{6do?otiA(*e znpQfWY-Y{^thu=B(PY_}+;F(_vAXW}>^jD2YRafyxZGO^s|OrilQ&SJxYgp)*Vy9Y zwZC#FhTdU0u`|xW82((`Y|%)q*}{>gk0Y7@iP`QVV;4QTF0C4usos(X*LSbe$iCSW zIvTiFgEe->Q8tTHV!gdW&i3<+$8cApWe;|&opA`0mRO%sab8+-4)7es3*Tk@&d*cE zF6UtD#+MbtGx4A8b+x0#iTjhL(2U#VZ( zSc=7{Y-=ViVFKMst;4#;As1W7x#bP^N42I|L;pjf;aZn7{c_L5$-ecdH`mhr5PMF# zz$)s;tovcs5~*bPvy7vFF5$j=wW^9D3B7 zi_^lZ*y$M+(7uCAxD zb_?@X3^UMtIfU@#{RFJ2tdriI$t9ZUmWN}anyEHxPw2-YN;Os^_QhEJ}{- zeAsa#W}nH)$hpkYZiAC*p*>QBtJn^gZP4K2ATF80u~el|O$seiL$$QC`RP=*<@uHS z`C}ih((Er4b6w9+*{W!}Y^ZE8nyE818yhh@@6=Ok1HVicY~*f?rB*dJr%dX9Enhe6 zYMD@SwJ=zV)g{c{Z9|TA-}lwb9NLYeG4-q;|2gY%54_d@aD^O~%ciMvt7amP8BPrC z(_aV+N4kDFvtyNtg{+!78dQvB{EZ^EHM^@$o4=Fe4SWOrzDr@4k-7S@q4aFbR%r5! zD~JjwIC!zXti5E;d74&?C(PBny+MY2RITp;)(NHq$^(2kZ1V;6nX<4y&)*Guh&_RF^8_QhaYt5r4e z)vj~hQV12xDO@JSv?~`YI#ivDQ#Buk59nTBUe}x5s46vMPr*1AYb~qfqbZ2hWDD1>}KWmO4!|eYiw&ZN-|2|;0Y>o?@3cI5L2XNioa;D@0j$;`QgcsVQ zwBak3_A3_hzf~3%y3TIQ_9C;9%9amnkE5m)L9vnWA1H`{e$KOk`YQCPIN;lWff%l% zBY3|pGcaFRiFL$T|pOtVUwkHLGg~0*??YVNl!2Hz9V>V*?T%**Q z0$6X%<$Pb!s%0MP2%vjWl%SUzRp}q+0<+fkI~<#B5t7+E39ADYW0^%xyvHy0m83E? zXU6VOP((p$%**ftJDs z?VTxCXg(an4!xO45;lr4_3Rad{oBa0xl*VIsNTzJO|t?`ZzTPrXL*)=pA$^7BYMTJ zG}kXw$XerVMzf5UT4g6wo_D`mzwfFWasJHrS{|7-I{&INpmHm2NTdBe)QUCl$7*Bn z>SLmM_vbs7N39!wT3qoJ`>R)r^44p2_<_a==-l<4^8{vK@?b7XufDDO%Ln(q;w~zZ z80e_qk1X{u#JPqesQ577kU2#bqc?$Nxm6-3cSTVtVkA>+*=87(LwTifD}kAIn=`yU zZ?F}t^+%$e#dWO?x%!h(P`LxjhxCUd7tgpzq_8#JSd{L^O!!5trg*Q*omZ|e$B29KkhL)E~- z$^$tYvAG(RPc?rtBEntJ7VrAL%3OSNv~d?&cuiF`UWiz}bjuv!S8Z%+Ox;$jM5pGC zzZ5WwR*L#rFbIka@7top70 z=f2XFdumM_bRfki25D6ONL9EUEY$p+N(R2sxqEYe<%D@ifBRhjPgQcnUe={}SBGq~ QL};nX42ZdGAGX~80Ht9l2><{9 literal 0 HcmV?d00001 diff --git a/resources/locales/de_DE/users.po b/resources/locales/de_DE/users.po new file mode 100644 index 000000000..dd14a43b3 --- /dev/null +++ b/resources/locales/de_DE/users.po @@ -0,0 +1,817 @@ +# LANGUAGE translation of CakePHP Application +# Copyright YEAR NAME +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"POT-Creation-Date: 2019-01-10 14:38+0000\n" +"PO-Revision-Date: 2022-03-11 13:31+0100\n" +"Last-Translator: \n" +"Language-Team: LANGUAGE \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 3.0.1\n" + +#: Controller/SocialAccountsController.php:50 +msgid "Account validated successfully" +msgstr "Das Konto wurde erfolgreich bestätigt" + +#: Controller/SocialAccountsController.php:52 +msgid "Account could not be validated" +msgstr "Das Konto konnte nicht bestätigt werden" + +#: Controller/SocialAccountsController.php:55 +msgid "Invalid token and/or social account" +msgstr "Ungültiger Token und/oder Social Account" + +#: Controller/SocialAccountsController.php:57;85 +msgid "Social Account already active" +msgstr "Social Account ist bereits aktiviert" + +#: Controller/SocialAccountsController.php:59 +msgid "Social Account could not be validated" +msgstr "Social Account konnte nicht bestätigt werden" + +#: Controller/SocialAccountsController.php:78 +msgid "Email sent successfully" +msgstr "Email wurde erfolgreich versendet" + +#: Controller/SocialAccountsController.php:80 +msgid "Email could not be sent" +msgstr "Email konnte nicht versendet werden" + +#: Controller/SocialAccountsController.php:83 +msgid "Invalid account" +msgstr "Ungültiges Konto" + +#: Controller/SocialAccountsController.php:87 +msgid "Email could not be resent" +msgstr "Email konnte nicht versendet werden" + +#: Controller/Traits/LinkSocialTrait.php:52 +msgid "Could not associate account, please try again." +msgstr "" +"Wir konnten das Konto leider nicht verbinden, bitte versuchen Sie es erneut." + +#: Controller/Traits/LinkSocialTrait.php:76 +msgid "Social account was associated." +msgstr "Social Account wurde verbunden." + +#: Controller/Traits/LoginTrait.php:73 +msgid "You've successfully logged out" +msgstr "Sie wurden erfolgreich abgemeldet" + +#: Controller/Traits/OneTimePasswordVerifyTrait.php:71 +msgid "Please enable Google Authenticator first." +msgstr "Bitte aktivieren Sie zuerst den Google Authenticator." + +#: Controller/Traits/OneTimePasswordVerifyTrait.php:80 +msgid "Could not find user data" +msgstr "Es konnten keine Benutzer-Daten gefunden werden" + +#: Controller/Traits/OneTimePasswordVerifyTrait.php:116 +msgid "Could not verify, please try again" +msgstr "Überprüfung ist fehlgeschlagen, bitte versuchen Sie es erneut" + +#: Controller/Traits/OneTimePasswordVerifyTrait.php:144 +msgid "Verification code is invalid. Try again" +msgstr "Überprüfungs-Code ist ungültig. Bitte versuchen Sie erneut" + +#: Controller/Traits/PasswordManagementTrait.php:53;91 +#: Controller/Traits/ProfileTrait.php:53 +msgid "User was not found" +msgstr "Der Benutzer konnte nicht gefunden werden" + +#: Controller/Traits/PasswordManagementTrait.php:75;87;95 +msgid "Password could not be changed" +msgstr "Das Passwort konnte nicht geändert werden" + +#: Controller/Traits/PasswordManagementTrait.php:83 +msgid "Password has been changed successfully" +msgstr "Das Passwort wurde erfolgreich geändert" + +#: Controller/Traits/PasswordManagementTrait.php:137 +msgid "Please check your email to continue with password reset process" +msgstr "" +"Sie erhalten in Kürze eine Email mit Anweisungen wie Sie das Passwort " +"zurücksetzen können" + +#: Controller/Traits/PasswordManagementTrait.php:140 Shell/UsersShell.php:247 +msgid "The password token could not be generated. Please try again" +msgstr "" +"Der Token für das Zurücksetzen des Passworts konnte nicht generiert werden. " +"Bitte versuchen Sie es erneut" + +#: Controller/Traits/PasswordManagementTrait.php:146 +#: Controller/Traits/UserValidationTrait.php:116 +msgid "User {0} was not found" +msgstr "Benutzer {0} konnte nicht gefunden werden" + +#: Controller/Traits/PasswordManagementTrait.php:148 +msgid "The user is not active" +msgstr "Der Benutzer ist inaktiv" + +#: Controller/Traits/PasswordManagementTrait.php:150 +#: Controller/Traits/UserValidationTrait.php:111;120 +msgid "Token could not be reset" +msgstr "Der Token konnte nicht zurückgesetzt werden" + +#: Controller/Traits/PasswordManagementTrait.php:174 +msgid "Google Authenticator token was successfully reset" +msgstr "Der Google Authenticator Token wurde erfolgreich zurückgesetzt" + +#: Controller/Traits/ProfileTrait.php:57 +msgid "Not authorized, please login first" +msgstr "" +"Sie sind nicht berechtigt diese Seite aufzurufen. Bitte melden Sie sich " +"zuerst an" + +#: Controller/Traits/RegisterTrait.php:46 +msgid "You must log out to register a new user account" +msgstr "Sie müssen sich ausloggen um ein neues Konto zu erstellen" + +#: Controller/Traits/RegisterTrait.php:75;99 +msgid "The user could not be saved" +msgstr "Der Benutzer konnte nicht gespeichert werden" + +#: Controller/Traits/RegisterTrait.php:92 Loader/LoginComponentLoader.php:32 +msgid "Invalid reCaptcha" +msgstr "Ungültiger reCaptcha" + +#: Controller/Traits/RegisterTrait.php:133 +msgid "You have registered successfully, please log in" +msgstr "Sie haben sich erfolgreich registriert. Bitte melden Sie sich an" + +#: Controller/Traits/RegisterTrait.php:135 +msgid "Please validate your account before log in" +msgstr "Bitte bestätigen Sie ihr Konto bevor Sie sich anmelden" + +#: Controller/Traits/SimpleCrudTrait.php:77;107 +msgid "The {0} has been saved" +msgstr "Der {0} wurde gespeichert" + +#: Controller/Traits/SimpleCrudTrait.php:81;111 +msgid "The {0} could not be saved" +msgstr "Der {0} konnte nicht gespeichert werden" + +#: Controller/Traits/SimpleCrudTrait.php:131 +msgid "The {0} has been deleted" +msgstr "Der {0} wurde gelöscht" + +#: Controller/Traits/SimpleCrudTrait.php:133 +msgid "The {0} could not be deleted" +msgstr "Der {0} konnte nicht gelöscht werden" + +#: Controller/Traits/UserValidationTrait.php:44 +msgid "User account validated successfully" +msgstr "Sie haben Ihren Benutzer erfolgreich bestätigt" + +#: Controller/Traits/UserValidationTrait.php:46 +msgid "User account could not be validated" +msgstr "Ihr Benutzer konnte nicht bestätigt werden" + +#: Controller/Traits/UserValidationTrait.php:49 +msgid "User already active" +msgstr "Dieser Benutzer ist bereits aktiv" + +#: Controller/Traits/UserValidationTrait.php:55 +msgid "Reset password token was validated successfully" +msgstr "Der angegebenen Token für das Zurücksetzen des Passworts ist gültig" + +#: Controller/Traits/UserValidationTrait.php:63 +msgid "Reset password token could not be validated" +msgstr "Der angegebenen Token für das Zurücksetzen des Passworts ist unültig" + +#: Controller/Traits/UserValidationTrait.php:67 +msgid "Invalid validation type" +msgstr "Ungültiger Validierungstyp" + +#: Controller/Traits/UserValidationTrait.php:70 +msgid "Invalid token or user account already validated" +msgstr "Ungültiger Token oder Benutzer ist bereits validiert" + +#: Controller/Traits/UserValidationTrait.php:76 +msgid "Token already expired" +msgstr "Token ist bereits abgelaufen" + +#: Controller/Traits/UserValidationTrait.php:106 +msgid "Token has been reset successfully. Please check your email." +msgstr "" +"Der Token wurde erfolgreich zurückgesetzt. Bitte überprüfen Sie ihren " +"Posteingang." + +#: Controller/Traits/UserValidationTrait.php:118 +msgid "User {0} is already active" +msgstr "Benutzer {0} ist bereits aktiv" + +#: Loader/LoginComponentLoader.php:30 +msgid "Username or password is incorrect" +msgstr "Benutzername oder Passwort ist nicht korrekt" + +#: Loader/LoginComponentLoader.php:51 +msgid "Could not proceed with social account. Please try again" +msgstr "" +"Es konnte keine Verbindung zum Social Account hergestellt werden. Bitte " +"versuchen Sie es erneut" + +#: Loader/LoginComponentLoader.php:53 +msgid "" +"Your user has not been validated yet. Please check your inbox for " +"instructions" +msgstr "" +"Ihr Benutzer wurde noch nicht bestätigt. Bitte überprüfen Sie ihren " +"Posteingang für weitere Anweisungen" + +#: Loader/LoginComponentLoader.php:57 +msgid "" +"Your social account has not been validated yet. Please check your inbox for " +"instructions" +msgstr "" +"Ihr Social Account wurde noch nicht bestätigt. Bitte überprüfen Sie ihren " +"Posteingang für weitere Anweisungen" + +#: Mailer/UsersMailer.php:33 +msgid "Your account validation link" +msgstr "Ihr Konto Validierungs-Link" + +#: Mailer/UsersMailer.php:51 +msgid "{0}Your reset password link" +msgstr "{0}Ihr Passwort Zurücksetzten Link" + +#: Mailer/UsersMailer.php:74 +msgid "{0}Your social account validation link" +msgstr "{0}Ihr Social Account Bestätigung Link" + +#: Middleware/SocialAuthMiddleware.php:65 Template/Users/social_email.ctp:16 +msgid "Please enter your email" +msgstr "Bitte geben Sie ihre E-Mail Adresse ein" + +#: Middleware/SocialAuthMiddleware.php:75 +msgid "Could not identify your account, please try again" +msgstr "" +"Wir konnten Ihr Konto nicht identifizieren. Bitte versuchen Sie es erneut" + +#: Model/Behavior/AuthFinderBehavior.php:48 +msgid "Missing 'username' in options data" +msgstr "Fehlender Parameter ‘username’ in angegeben Optionen" + +#: Model/Behavior/LinkSocialBehavior.php:53 +msgid "Social account already associated to another user" +msgstr "Dieser Social Account ist bereits mit einem anderen Benutzer verknüpft" + +#: Model/Behavior/PasswordBehavior.php:45 +msgid "Reference cannot be null" +msgstr "Referenz darf nicht leer sein" + +#: Model/Behavior/PasswordBehavior.php:50 +msgid "Token expiration cannot be empty" +msgstr "Token Ablaufdatum darf nicht leer sein" + +#: Model/Behavior/PasswordBehavior.php:56;138 +msgid "User not found" +msgstr "Benutzer konnte nicht gefunden werden" + +#: Model/Behavior/PasswordBehavior.php:60 +#: Model/Behavior/RegisterBehavior.php:112 +msgid "User account already validated" +msgstr "Benutzer ist bereits bestätigt" + +#: Model/Behavior/PasswordBehavior.php:67 +msgid "User not active" +msgstr "Benutzer ist nicht aktiv" + +#: Model/Behavior/PasswordBehavior.php:143 +msgid "The current password does not match" +msgstr "Das aktuelle Passwort stimmt nicht überein" + +#: Model/Behavior/PasswordBehavior.php:146 +msgid "You cannot use the current password as the new one" +msgstr "Sie dürfen nicht das aktuelle Passwort als neues Passwort verwenden" + +#: Model/Behavior/RegisterBehavior.php:90 +msgid "User not found for the given token and email." +msgstr "" +"Es konnte kein Benutzer mit dem angegebenen Token oder der angegebenen Email " +"gefunden werden." + +#: Model/Behavior/RegisterBehavior.php:93 +msgid "Token has already expired user with no token" +msgstr "Der Token ist bereits abgelaufen oder der Benutzer hat keinen Token" + +#: Model/Behavior/RegisterBehavior.php:151 +msgid "This field is required" +msgstr "Dieses Feld muss ausgefüllt werden" + +#: Model/Behavior/SocialAccountBehavior.php:102;129 +msgid "Account already validated" +msgstr "Konto ist bereits bestätigt" + +#: Model/Behavior/SocialAccountBehavior.php:105;132 +msgid "Account not found for the given token and email." +msgstr "" +"Es konnte kein Konto für den angegebenen Token oder die angegebene Email " +"gefunden werden." + +#: Model/Behavior/SocialBehavior.php:83 +msgid "Unable to login user with reference {0}" +msgstr "Der Login über die Referenz {0} konnte nicht durchgeführt werden" + +#: Model/Behavior/SocialBehavior.php:122 +msgid "Email not present" +msgstr "Email nicht vorhanden" + +#: Model/Table/UsersTable.php:79 +msgid "Your password does not match your confirm password. Please try again" +msgstr "Bitte bestätigen Sie ihr Passwort und versuchen Sie es erneut" + +#: Model/Table/UsersTable.php:171 +msgid "Username already exists" +msgstr "Benutzername ist bereits vorhanden" + +#: Model/Table/UsersTable.php:177 +msgid "Email already exists" +msgstr "Email ist bereits vorhanden" + +#: Shell/UsersShell.php:58 +msgid "Utilities for CakeDC Users Plugin" +msgstr "Tools für das CakeDC Users Plugin" + +#: Shell/UsersShell.php:60 +msgid "Activate an specific user" +msgstr "Aktiviere einen Benutzer" + +#: Shell/UsersShell.php:63 +msgid "Add a new superadmin user for testing purposes" +msgstr "Füge einen neuen Superadmin Benutzer für Testzwecke hinzu" + +#: Shell/UsersShell.php:66 +msgid "Add a new user" +msgstr "Füge einen neuen Benutzer hinzu" + +#: Shell/UsersShell.php:69 +msgid "Change the role for an specific user" +msgstr "Ändere die Rolle für einen Benutzer" + +#: Shell/UsersShell.php:72 +msgid "Deactivate an specific user" +msgstr "Deaktiviere einen Benutzer" + +#: Shell/UsersShell.php:75 +msgid "Delete an specific user" +msgstr "Lösche einen Benutzer" + +#: Shell/UsersShell.php:78 +msgid "Reset the password via email" +msgstr "Sende Passwort-Zurücksetzen E-Mail für eine E-Mail Adresse" + +#: Shell/UsersShell.php:81 +msgid "Reset the password for all users" +msgstr "Setze das Passwort für alle Benutzer" + +#: Shell/UsersShell.php:84 +msgid "Reset the password for an specific user" +msgstr "Setze das Passwort für einen Benutzer" + +#: Shell/UsersShell.php:133;159 +msgid "Please enter a password." +msgstr "Bitte geben Sie das Passwort ein." + +#: Shell/UsersShell.php:137 +msgid "Password changed for all users" +msgstr "Das Passwort wurde für alle Benutzer geändert" + +#: Shell/UsersShell.php:138;166 +msgid "New password: {0}" +msgstr "Neues Passwort: {0}" + +#: Shell/UsersShell.php:156;184;262;359 +msgid "Please enter a username." +msgstr "Bitte geben Sie einen Benutzernamen ein." + +#: Shell/UsersShell.php:165 +msgid "Password changed for user: {0}" +msgstr "Das Passwort wurde für den Benutzer: {0} geändert" + +#: Shell/UsersShell.php:187 +msgid "Please enter a role." +msgstr "Bitte geben Sie eine Rolle ein." + +#: Shell/UsersShell.php:193 +msgid "Role changed for user: {0}" +msgstr "Die Rolle wurde für den Benutzer: {0} geändert" + +#: Shell/UsersShell.php:194 +msgid "New role: {0}" +msgstr "Neue Rolle: {0}" + +#: Shell/UsersShell.php:209 +msgid "User was activated: {0}" +msgstr "Benutzer wurde aktiviert: {0}" + +#: Shell/UsersShell.php:224 +msgid "User was de-activated: {0}" +msgstr "Benutzer wurde deaktiviert: {0}" + +#: Shell/UsersShell.php:236 +msgid "Please enter a username or email." +msgstr "Bitte geben Sie einen Benutzername oder eine Email ein." + +#: Shell/UsersShell.php:244 +msgid "" +"Please ask the user to check the email to continue with password reset " +"process" +msgstr "" +"Bitte Fragen Sie den Benutzer den Posteingang zu überprüfen für die weitere " +"Vorgehensweise" + +#: Shell/UsersShell.php:308 +msgid "Superuser added:" +msgstr "Superuser hinzugefügt:" + +#: Shell/UsersShell.php:310 +msgid "User added:" +msgstr "Benutzer hinzugefügt:" + +#: Shell/UsersShell.php:312 +msgid "Id: {0}" +msgstr "ID: {0}" + +#: Shell/UsersShell.php:313 +msgid "Username: {0}" +msgstr "Benutzername: {0}" + +#: Shell/UsersShell.php:314 +msgid "Email: {0}" +msgstr "Email: {0}" + +#: Shell/UsersShell.php:315 +msgid "Role: {0}" +msgstr "Rolle: {0}" + +#: Shell/UsersShell.php:316 +msgid "Password: {0}" +msgstr "Passwort: {0}" + +#: Shell/UsersShell.php:318 +msgid "User could not be added:" +msgstr "Benutzer konnte nicht hinzugefügt werden:" + +#: Shell/UsersShell.php:321 +msgid "Field: {0} Error: {1}" +msgstr "Feld: {0} Fehler: {0}" + +#: Shell/UsersShell.php:337 +msgid "The user was not found." +msgstr "Der Benutzer konnte nicht gefunden werden." + +#: Shell/UsersShell.php:367 +msgid "The user {0} was not deleted. Please try again" +msgstr "" +"Der Benutzer {0} konnte nicht gelöscht werden. Bitte versuchen Sie es erneut" + +#: Shell/UsersShell.php:369 +msgid "The user {0} was deleted successfully" +msgstr "Der Benutzer {0} wurde gelöscht" + +#: Template/Email/html/reset_password.ctp:21 +#: Template/Email/html/social_account_validation.ctp:14 +#: Template/Email/html/validation.ctp:21 +#: Template/Email/text/reset_password.ctp:20 +#: Template/Email/text/social_account_validation.ctp:22 +#: Template/Email/text/validation.ctp:20 +msgid "Hi {0}" +msgstr "Hallo {0}" + +#: Template/Email/html/reset_password.ctp:24 +msgid "Reset your password here" +msgstr "Passwort jetzt zurücksetzen" + +#: Template/Email/html/reset_password.ctp:27 +#: Template/Email/html/social_account_validation.ctp:32 +#: Template/Email/html/validation.ctp:27 +msgid "" +"If the link is not correctly displayed, please copy the following address in " +"your web browser {0}" +msgstr "" +"Falls der Link nicht korrekt dargestellt wird kopieren Sie bitte die " +"folgende Adresse in ihren Web Browser {0}" + +#: Template/Email/html/reset_password.ctp:34 +#: Template/Email/html/social_account_validation.ctp:39 +#: Template/Email/html/validation.ctp:34 +#: Template/Email/text/reset_password.ctp:28 +#: Template/Email/text/social_account_validation.ctp:30 +#: Template/Email/text/validation.ctp:28 +msgid "Thank you" +msgstr "Vielen Dank" + +#: Template/Email/html/social_account_validation.ctp:18 +msgid "Activate your social login here" +msgstr "Social Login aktivieren" + +#: Template/Email/html/validation.ctp:24 +msgid "Activate your account here" +msgstr "Konto aktivieren" + +#: Template/Email/text/reset_password.ctp:22 +#: Template/Email/text/validation.ctp:22 +msgid "Please copy the following address in your web browser {0}" +msgstr "Bitte kopieren Sie die folgende Adresse in Ihren Web Browser {0}" + +#: Template/Email/text/social_account_validation.ctp:24 +msgid "" +"Please copy the following address in your web browser to activate your " +"social login {0}" +msgstr "" +"Bitte kopieren Sie die folgende Adresse in Ihren Web Browser um den Social " +"Login zu aktivieren {0}" + +#: Template/Users/add.ctp:13 Template/Users/edit.ctp:17 +#: Template/Users/index.ctp:13;26 Template/Users/view.ctp:15 +msgid "Actions" +msgstr "Aktionen" + +#: Template/Users/add.ctp:15 Template/Users/edit.ctp:28 +#: Template/Users/view.ctp:23 +msgid "List Users" +msgstr "Benutzer auflisten" + +#: Template/Users/add.ctp:21 Template/Users/register.ctp:18 +msgid "Add User" +msgstr "Benutzer hinzufügen" + +#: Template/Users/add.ctp:23 Template/Users/edit.ctp:36 +#: Template/Users/index.ctp:22 Template/Users/login.ctp:20 +#: Template/Users/profile.ctp:30 Template/Users/register.ctp:20 +#: Template/Users/view.ctp:33 +msgid "Username" +msgstr "Benutzername" + +#: Template/Users/add.ctp:24 Template/Users/edit.ctp:37 +#: Template/Users/index.ctp:23 Template/Users/profile.ctp:32 +#: Template/Users/register.ctp:21 Template/Users/view.ctp:35 +msgid "Email" +msgstr "Email" + +#: Template/Users/add.ctp:25 Template/Users/login.ctp:21 +#: Template/Users/register.ctp:22 +msgid "Password" +msgstr "Passwort" + +#: Template/Users/add.ctp:26 Template/Users/edit.ctp:38 +#: Template/Users/index.ctp:24 Template/Users/register.ctp:27 +msgid "First name" +msgstr "Vorname" + +#: Template/Users/add.ctp:27 Template/Users/edit.ctp:39 +#: Template/Users/index.ctp:25 Template/Users/register.ctp:28 +msgid "Last name" +msgstr "Nachname" + +#: Template/Users/add.ctp:30 Template/Users/edit.ctp:54 +#: Template/Users/view.ctp:49;74 +msgid "Active" +msgstr "Aktiv" + +#: Template/Users/add.ctp:34 Template/Users/change_password.ctp:25 +#: Template/Users/edit.ctp:58 Template/Users/register.ctp:37 +#: Template/Users/request_reset_password.ctp:8 +#: Template/Users/resend_token_validation.ctp:20 +#: Template/Users/social_email.ctp:19 +msgid "Submit" +msgstr "Absenden" + +#: Template/Users/change_password.ctp:5 +msgid "Please enter the new password" +msgstr "Bitte geben Sie ihr neues Passwort ein" + +#: Template/Users/change_password.ctp:10 +msgid "Current password" +msgstr "Aktuelles Passwort" + +#: Template/Users/change_password.ctp:16 +msgid "New password" +msgstr "Neues Passwort" + +#: Template/Users/change_password.ctp:21 Template/Users/register.ctp:25 +msgid "Confirm password" +msgstr "Neues Passwort bestätigen" + +#: Template/Users/edit.ctp:22 Template/Users/index.ctp:40 +msgid "Delete" +msgstr "Löschen" + +#: Template/Users/edit.ctp:24 Template/Users/index.ctp:40 +#: Template/Users/view.ctp:21 +msgid "Are you sure you want to delete # {0}?" +msgstr "Sind Sie sicher, dass Sie # {0} löschen wollen?" + +#: Template/Users/edit.ctp:34 Template/Users/view.ctp:17 +msgid "Edit User" +msgstr "Benutzer bearbeiten" + +#: Template/Users/edit.ctp:40 Template/Users/view.ctp:43 +msgid "Token" +msgstr "Token" + +#: Template/Users/edit.ctp:42 +msgid "Token expires" +msgstr "Token Ablaufdatum" + +#: Template/Users/edit.ctp:45 +msgid "API token" +msgstr "API Token" + +#: Template/Users/edit.ctp:48 +msgid "Activation date" +msgstr "Aktivierungsdatum" + +#: Template/Users/edit.ctp:51 +msgid "TOS date" +msgstr "AGB Datum" + +#: Template/Users/edit.ctp:64 +msgid "Reset Google Authenticator Token" +msgstr "Google Authenticator Token zurücksetzen" + +#: Template/Users/edit.ctp:70 +msgid "Are you sure you want to reset token for user \"{0}\"?" +msgstr "" +"Sind Sie sicher, dass Sie den Token für den Benutzer # {0} zurücksetzen " +"wollen?" + +#: Template/Users/index.ctp:15 +msgid "New {0}" +msgstr "Neuer {0}" + +#: Template/Users/index.ctp:37 +msgid "View" +msgstr "Ansicht" + +#: Template/Users/index.ctp:38 +msgid "Change password" +msgstr "Passwort ändern" + +#: Template/Users/index.ctp:39 +msgid "Edit" +msgstr "Bearbeiten" + +#: Template/Users/index.ctp:49 +msgid "previous" +msgstr "vorherig" + +#: Template/Users/index.ctp:51 +msgid "next" +msgstr "nächste" + +#: Template/Users/login.ctp:19 +msgid "Please enter your username and password" +msgstr "Bitte geben Sie ihren Benutzernamen und Passwort ein" + +#: Template/Users/login.ctp:29 +msgid "Remember me" +msgstr "Angemeldet bleiben" + +#: Template/Users/login.ctp:37 +msgid "Register" +msgstr "Registrieren" + +#: Template/Users/login.ctp:43 +msgid "Reset Password" +msgstr "Passwort zurücksetzen" + +#: Template/Users/login.ctp:48 +msgid "Login" +msgstr "Anmelden" + +#: Template/Users/profile.ctp:21 +msgid "{0} {1}" +msgstr "{0} {1}" + +#: Template/Users/profile.ctp:27 +msgid "Change Password" +msgstr "Passwort ändern" + +#: Template/Users/profile.ctp:38 Template/Users/view.ctp:68 +msgid "Social Accounts" +msgstr "Social Accounts" + +#: Template/Users/profile.ctp:42 Template/Users/view.ctp:73 +msgid "Avatar" +msgstr "Avatar" + +#: Template/Users/profile.ctp:43 Template/Users/view.ctp:72 +msgid "Provider" +msgstr "Provider" + +#: Template/Users/profile.ctp:44 +msgid "Link" +msgstr "Link" + +#: Template/Users/profile.ctp:51 +msgid "Link to {0}" +msgstr "Link zu {0}" + +#: Template/Users/register.ctp:30 +msgid "Accept TOS conditions?" +msgstr "AGBs akzeptieren?" + +#: Template/Users/request_reset_password.ctp:5 +msgid "Please enter your email to reset your password" +msgstr "Bitte geben Sie ihre Email Adresse ein um ihr Passwort zurückzusetzen" + +#: Template/Users/resend_token_validation.ctp:15 +msgid "Resend Validation email" +msgstr "Validierungs-Email erneut versenden" + +#: Template/Users/resend_token_validation.ctp:17 +msgid "Email or username" +msgstr "Email oder Benutzername" + +#: Template/Users/verify.ctp:13 +msgid "Verification Code" +msgstr "Bestätigungscode" + +#: Template/Users/verify.ctp:15 +msgid "" +" " +"Verify" +msgstr "" +" " +"Bestätigen" + +#: Template/Users/view.ctp:19 +msgid "Delete User" +msgstr "Benutzer löschen" + +#: Template/Users/view.ctp:24 +msgid "New User" +msgstr "Benutzer hinzufügen" + +#: Template/Users/view.ctp:31 +msgid "Id" +msgstr "ID" + +#: Template/Users/view.ctp:37 +msgid "First Name" +msgstr "Vorname" + +#: Template/Users/view.ctp:39 +msgid "Last Name" +msgstr "Nachname" + +#: Template/Users/view.ctp:41 +msgid "Role" +msgstr "Rolle" + +#: Template/Users/view.ctp:45 +msgid "Api Token" +msgstr "API Token" + +#: Template/Users/view.ctp:53 +msgid "Token Expires" +msgstr "Token Ablaufdatum" + +#: Template/Users/view.ctp:55 +msgid "Activation Date" +msgstr "Aktivierungsdatum" + +#: Template/Users/view.ctp:57 +msgid "Tos Date" +msgstr "AGB Datum" + +#: Template/Users/view.ctp:59;75 +msgid "Created" +msgstr "erstellt am" + +#: Template/Users/view.ctp:61;76 +msgid "Modified" +msgstr "zuletzt aktualisiert" + +#: View/Helper/UserHelper.php:46 +msgid "Sign in with" +msgstr "Anmelden mit" + +#: View/Helper/UserHelper.php:103 +msgid "Logout" +msgstr "Abmelden" + +#: View/Helper/UserHelper.php:121 +msgid "Welcome, {0}" +msgstr "Willkommen, {0}" + +#: View/Helper/UserHelper.php:151 +msgid "reCaptcha is not configured! Please configure Users.reCaptcha.key" +msgstr "" +"reCaptcha ist nicht konfiguriert! Bitte setzten Sie Users.reCaptcha.key" + +#: View/Helper/UserHelper.php:215 +msgid "Connected with {0}" +msgstr "Mit {0} verbunden" + +#: View/Helper/UserHelper.php:220 +msgid "Connect with {0}" +msgstr "Mit {0} verbinden" From c18b32bb11df611d9916fd8d2d8ebbbf70ec7dac Mon Sep 17 00:00:00 2001 From: Kevin Pfeifer Date: Fri, 11 Mar 2022 14:23:13 +0100 Subject: [PATCH 50/53] fix not all translations using __d() function and update .pot file --- resources/locales/de_DE/users.mo | Bin 15667 -> 18251 bytes resources/locales/de_DE/users.po | 636 ++++++++++++-------- resources/locales/users.pot | 644 ++++++++++++--------- src/Controller/Traits/Webauthn2faTrait.php | 4 +- src/Loader/AuthenticationServiceLoader.php | 2 +- templates/Users/u2f_authenticate.php | 2 +- templates/Users/webauthn2fa.php | 4 +- 7 files changed, 767 insertions(+), 525 deletions(-) diff --git a/resources/locales/de_DE/users.mo b/resources/locales/de_DE/users.mo index 61a8ac7065103115bcd5a87e1a3a01659e891cac..8408ca51fb24120d9ddbe4f1407ff9722252af11 100644 GIT binary patch delta 6450 zcmZ9N3v?9K9fxl)pnw*FA_OH|2#|;&kTgPxA_NgcDMEum#Hy3s+1)Xl*>rZ7nAp0O z)>=WKb$wLSTB~hsEh=u2N@=U6)MwAJw(9BQXl>82w#Pn>mA1<11;TW`zCk_TngpTW;h;R0jIz$tcIV3W7*%_z+f~iL)J8R zK!xlfs1KfjMA7^v<~#9xHF+4v{Hc&B(-7Y;jd>oF;hj*1Tn9(N8=+FO3zi%Pw=-A^ z--8O>Dt6U|U9cWzph9*dZA>DJT4xDd2Uo*o z@HV&!zEVs4moR9iG*u)S$hYQZs8l=%71}4@O!!Lt{GU)8)sSeMGz(xmTn!uGHaG`< z3(kWtLal!U%J4a~n)YcfG0?~wA*jf_2~UTIp;9yx zt1R3AWk>*dWOhSsxF0H2FG74UrI#5fmw$yy!TXRub1ZR{2Xmlm;sU5*+5-7AH}N9_ zzYSIO6(~o31hw8zplat;cqTjo<=EmG(fQE{aj0ajWT1_ix-38!v^5SQk`^Qc$TaLZ$FND964BC$YbIg@JPThxox{ zcB2!`LKxNt)Q9UKdzuWCN8983FF_f80LqXT;`w)>a((jbD7B}+N4Rf|PDehBEvd*a6qVW8qGy8n_Me ziFvev__tPJ2@@AGar|jf@mvb!Q3`6~t6(F%6mxxPZ26~JK#)sAJjsJ;^!|y zNTA$1PO0xC(vJJ;kpK0TVaxod$2+QyUco2RZJ_j#_jcpVnybdmhZ$UYD8b6Bc3<%)K@J_fIJ_=XC_o1r1gMXbeFb&UwJE7Y3d8lH0 z3zBa0XQ&#Q(HzH}!86~`es z3%&%k(P1c$XJMx1+o0|@L7X!llp)`M>hqVNQus^w6#OG>fqMwYCGd5q{~hi8EQcjX zc1z|71~ZvB0#AX{&*dKoTmnAu# zg1QtTT^xqy>TpzwDqZ8xpsT{k=;zsZd?A!ay1s>WpzD$9M8&HxKY^4kopi(3Xa*-D zb-``V`uZQs_(QV)QknD}hwmV|2rH0Bu2+p$f_& z9Zn~p+tKHd(t0P-wL3!Cehf?DQEB5z;Y>sqny1k~cqw~?ucur^;F@7i>pBJ$do_2R|^AWTO{V;l9J`KNs9!Ce`xot7O9e2n-5yoIDIu_~Lht5V#=w9>)%A)e#`g1FLCR|@t*XP+n zuE+IlBIEdJH)&J3z!vjv&~C3@_^EPh&8|A<=sSAm_%`3?CcKoF2;VF3t7&M~x1OKo zVXlyIqXioBw$I7u`*T6kdU?we>twUJem7bEWz9R&0ypm#?CM-Dopo(Tk{j%k@)tjE{ZwonE)RXX@%Y&&PA@^pm#W^hH|^ zuw^1=d)$HY+fx_JWs|~yRdAXUS^Qhaw>F>26|+g(?b@W9@_byJQro;vHsFFA1nkrA z6*9Kp?RIe0KN@B0CBs0QkA)Vyfub0C`oXq5%jTS9Wkv1o>eDy&=jNxJ1YxpTDGnNL znu}Uj;rAzs9+9=3ook!RznZ>pmXi%!Cplm4wyuoPAE1X0kvehZrY>tXmjfEd1=nX&DPht9AJ`?Wxzd6FqT=;P@ zFCS#qh!7^-tzN=yt~A*1SIw6tQPsuCPy$nqmsNWDvaW+WgH} z^vm+LxeXni9@WiyDIWMI&{7=x~cA%Xjophps~DQ!QM$Z9Vx@{P}lqA*B4w7ZcDIrusA+Fo6vz+ z7bmzrN39v(#qT^cX<@xL!H8a8c=8ikag_5wWY>OqOlCU5^(7 z8+CfX>qTlHNwaQ>PX(IMh8ar|kYOwBnw8-RLA$3+Top2Oq}yhlTeMk+c$p3k@m{y` z;KI%^tvV}KMQ6qE0h{s$qm9Gkf)jX<@OQ2ydx2BTH<$ml=)Jis72XfR#L-c}_`v0m z9bswZFN+sfl{0N8ly|mGC|`WW#B#@3C)UxouIX$TE&0FE- z3q$u8ytD!r*4YQX(3~T(QC{C(TiWFM90wRQTpW&{qN36T3LrW`_$C<)9SpOk`snrw zN(!UsFmdflFW=*8ve-+dsdTKSAjW#|Jdt6qnVWQU_g++04XsdbByX~X607v&Re7oXur^TN_W zZcqn|IwMIt{cmtqy8*GYDT>WYQ>xjlsy@>`ebk&0#W8dj3F{*!Db<*Akan>&>!iy! MHOwksch*Dy1sVdSX8-^I delta 4178 zcmY+_3s6->9LMp!2nvCMAc_}M@QF`E1p-V!0U!BJ6VOspQ1XFMhLU-;GGD3b5wY-* zsijF49djBTYbG;iGNckj~F4*&DnJ@?+TyZ_xioZaMcYkZ$Z z2iF?PF(QF@GsKu{xICN-WnQE)W3U3l@F=#&m$4(hkDc){4#YN5fp!kIqJ2B+d*$fG zmDmOAk)Qa?i9p4i#g=?<0YmXi9Dv_r8}zmf44^mmq&*miUjp;V9Z{ts}E?O#vAYxjKyzl`?@uZr)j|LPy;E%ARLDp z@I)-dB2;FMqxyLRyI~XhQmE+Z>Tn=x#G|Z}Q8Sr=nqetwKnqbbT8ip;1u9dkQA>Bw z+K3UfkE1$1gRSu+)BrEWl7Id1GB@+}~z-}Cm4VZ)1a2jSa zi%fK|H=aW+?H|ZwO%#nJ?18#JGLHPKgITorj#+`)blb5Pp2j|S2{pht7LKY(LbbC{ znHhol{RB+Jxu{LM7W-lyD$}Rz=f9$!-_FOG(6JkedT=o++*gDLD#EzNk;rk#o!a5?IEm8d=7TSg_D$}Z&3e8h!pn%__}iePl= zs5>e%V^9NL#)ZDjcGSQdQNMe``UPr_TtzL}b!3%I1f$pYyJDiwe>N3;VFvcYJ24yU zP$Pa9m8vGx%!0XScmkj9#7pur7fYLU+`R zGEg&_j2b`%>IYS*&9fdg;0D`1j@sQ#sDb{7TEg~;fhCK_L9~-mdtkbC19tXt;{+A5 zV?M@w`~!<{D7}rxTGRu-LJjCU%)?)>6%OGDv07#*>Xa1WAP>343AA4zU0U;??ty_u zp!(~KzGN!dR7!9T>H)8yI{F5c%FC!t6v{?WCOV>KoN66`A+)EWj@fk75-!F>Y(Nd* z91g_uI0-|0l7H>e5_(k!rPeCsJLW<3;vrOruUS99v9!NMr8JGakH#{biO*pUMlw6? zjS0xx+Du11ZxM##x@7XN2XEzu)_gze4R->ynO?^b{1}zu&rzH2PitcDz^=|mWu^pK z6;p+J&Nln`9@J93Xg!G<;AtO~Fe)LuWr8pg^~GphgkIDD*P)J2J@OONh-r8MHL$k4 zWkRvPH64R#=U_PIqcSiN^}X3v-vTN(a$^Z>JHIrAcEuKcr@I0!cE2s=4r3XGAg(GN};8fg=8t@eyj@MBW%VRw> z@KR)z%*G7z-=4}zZfLFFLyi1*)J&4tnHq5xYSYX@t>Me42R5Nj!=K3O-1N*0vsO# zce*mPooG!Qv-h9FF~n@!W~=!pREx@-??L;)2vOryLCbvc#hHgUWb9OQbSU=bz1N9wn9$MZ^+9n`;}fhPajB-QzFmTxSta6ZaF% zWiFLS+o(tXxyRl1o^Xgg|7q(4jirBo*~E3Lt!rbAA|57M*n1pZe_>ylIYcKyJ9`qL zqTQ{c&88#9f$$&UmDpT%*vd1_H_+E$8;5WyF_V}=v?7|zPAXFg9mN&)rjF^IL?cm7 zc!?_F01-~~Cz?wbm2tMw5+5Vt1MR^4YiQg<nIeF^@_ZYD+& zg~TmHbBU&MA2E$sWN+@W#$hqBn9%$GA!0g_O8C?KJUZdM3*2#S*La*O(Hot^G4amt zF+aKCu|Ij-Q5{k|?!$4TgPgCuHO~C_Lg##ZFDE&nkGn6SF4!qgI_eDX+TeJ*U5KhE zt2Wch7R;_(Sk^D`zJd3Bsv~Rt0Q@>8`&VJ<{ zH+MiykQjn2XJnNCVZnzJP%)y=-KxTSMu*j1-KFU-lzZS4l- YdOhy)yuKdio#AnAR(?W|v#apPKTQ0z_5c6? diff --git a/resources/locales/de_DE/users.po b/resources/locales/de_DE/users.po index dd14a43b3..7e9a16bce 100644 --- a/resources/locales/de_DE/users.po +++ b/resources/locales/de_DE/users.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" -"POT-Creation-Date: 2019-01-10 14:38+0000\n" -"PO-Revision-Date: 2022-03-11 13:31+0100\n" +"POT-Creation-Date: 2022-03-11 13:46+0100\n" +"PO-Revision-Date: 2022-03-11 14:19+0100\n" "Last-Translator: \n" "Language-Team: LANGUAGE \n" "Language: de\n" @@ -15,209 +15,247 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.0.1\n" -#: Controller/SocialAccountsController.php:50 +#: src/Controller/SocialAccountsController.php:50 msgid "Account validated successfully" msgstr "Das Konto wurde erfolgreich bestätigt" -#: Controller/SocialAccountsController.php:52 +#: src/Controller/SocialAccountsController.php:52 msgid "Account could not be validated" msgstr "Das Konto konnte nicht bestätigt werden" -#: Controller/SocialAccountsController.php:55 +#: src/Controller/SocialAccountsController.php:55 msgid "Invalid token and/or social account" msgstr "Ungültiger Token und/oder Social Account" -#: Controller/SocialAccountsController.php:57;85 +#: src/Controller/SocialAccountsController.php:57 +#: src/Controller/SocialAccountsController.php:85 msgid "Social Account already active" msgstr "Social Account ist bereits aktiviert" -#: Controller/SocialAccountsController.php:59 +#: src/Controller/SocialAccountsController.php:59 msgid "Social Account could not be validated" msgstr "Social Account konnte nicht bestätigt werden" -#: Controller/SocialAccountsController.php:78 +#: src/Controller/SocialAccountsController.php:78 msgid "Email sent successfully" msgstr "Email wurde erfolgreich versendet" -#: Controller/SocialAccountsController.php:80 +#: src/Controller/SocialAccountsController.php:80 msgid "Email could not be sent" msgstr "Email konnte nicht versendet werden" -#: Controller/SocialAccountsController.php:83 +#: src/Controller/SocialAccountsController.php:83 msgid "Invalid account" msgstr "Ungültiges Konto" -#: Controller/SocialAccountsController.php:87 +#: src/Controller/SocialAccountsController.php:87 msgid "Email could not be resent" msgstr "Email konnte nicht versendet werden" -#: Controller/Traits/LinkSocialTrait.php:52 +#: src/Controller/Traits/LinkSocialTrait.php:56 msgid "Could not associate account, please try again." msgstr "" "Wir konnten das Konto leider nicht verbinden, bitte versuchen Sie es erneut." -#: Controller/Traits/LinkSocialTrait.php:76 +#: src/Controller/Traits/LinkSocialTrait.php:80 msgid "Social account was associated." msgstr "Social Account wurde verbunden." -#: Controller/Traits/LoginTrait.php:73 +#: src/Controller/Traits/LoginTrait.php:73 msgid "You've successfully logged out" msgstr "Sie wurden erfolgreich abgemeldet" -#: Controller/Traits/OneTimePasswordVerifyTrait.php:71 +#: src/Controller/Traits/OneTimePasswordVerifyTrait.php:75 msgid "Please enable Google Authenticator first." msgstr "Bitte aktivieren Sie zuerst den Google Authenticator." -#: Controller/Traits/OneTimePasswordVerifyTrait.php:80 +#: src/Controller/Traits/OneTimePasswordVerifyTrait.php:90 msgid "Could not find user data" msgstr "Es konnten keine Benutzer-Daten gefunden werden" -#: Controller/Traits/OneTimePasswordVerifyTrait.php:116 +#: src/Controller/Traits/OneTimePasswordVerifyTrait.php:129 msgid "Could not verify, please try again" msgstr "Überprüfung ist fehlgeschlagen, bitte versuchen Sie es erneut" -#: Controller/Traits/OneTimePasswordVerifyTrait.php:144 +#: src/Controller/Traits/OneTimePasswordVerifyTrait.php:161 msgid "Verification code is invalid. Try again" msgstr "Überprüfungs-Code ist ungültig. Bitte versuchen Sie erneut" -#: Controller/Traits/PasswordManagementTrait.php:53;91 -#: Controller/Traits/ProfileTrait.php:53 +#: src/Controller/Traits/PasswordManagementTrait.php:63 +msgid "Changing another user's password is not allowed" +msgstr "Sie dürfen nicht das Passwort von einem anderen Benutzer ändern" + +#: src/Controller/Traits/PasswordManagementTrait.php:77 +#: src/Controller/Traits/PasswordManagementTrait.php:121 +#: src/Controller/Traits/ProfileTrait.php:54 msgid "User was not found" msgstr "Der Benutzer konnte nicht gefunden werden" -#: Controller/Traits/PasswordManagementTrait.php:75;87;95 +#: src/Controller/Traits/PasswordManagementTrait.php:105 +#: src/Controller/Traits/PasswordManagementTrait.php:117 +#: src/Controller/Traits/PasswordManagementTrait.php:125 msgid "Password could not be changed" msgstr "Das Passwort konnte nicht geändert werden" -#: Controller/Traits/PasswordManagementTrait.php:83 +#: src/Controller/Traits/PasswordManagementTrait.php:113 msgid "Password has been changed successfully" msgstr "Das Passwort wurde erfolgreich geändert" -#: Controller/Traits/PasswordManagementTrait.php:137 +#: src/Controller/Traits/PasswordManagementTrait.php:167 msgid "Please check your email to continue with password reset process" msgstr "" "Sie erhalten in Kürze eine Email mit Anweisungen wie Sie das Passwort " "zurücksetzen können" -#: Controller/Traits/PasswordManagementTrait.php:140 Shell/UsersShell.php:247 +#: src/Controller/Traits/PasswordManagementTrait.php:170 +#: src/Shell/UsersShell.php:286 msgid "The password token could not be generated. Please try again" msgstr "" "Der Token für das Zurücksetzen des Passworts konnte nicht generiert werden. " "Bitte versuchen Sie es erneut" -#: Controller/Traits/PasswordManagementTrait.php:146 -#: Controller/Traits/UserValidationTrait.php:116 +#: src/Controller/Traits/PasswordManagementTrait.php:176 +#: src/Controller/Traits/UserValidationTrait.php:124 msgid "User {0} was not found" msgstr "Benutzer {0} konnte nicht gefunden werden" -#: Controller/Traits/PasswordManagementTrait.php:148 +#: src/Controller/Traits/PasswordManagementTrait.php:178 msgid "The user is not active" msgstr "Der Benutzer ist inaktiv" -#: Controller/Traits/PasswordManagementTrait.php:150 -#: Controller/Traits/UserValidationTrait.php:111;120 +#: src/Controller/Traits/PasswordManagementTrait.php:180 +#: src/Controller/Traits/UserValidationTrait.php:119 +#: src/Controller/Traits/UserValidationTrait.php:128 msgid "Token could not be reset" msgstr "Der Token konnte nicht zurückgesetzt werden" -#: Controller/Traits/PasswordManagementTrait.php:174 +#: src/Controller/Traits/PasswordManagementTrait.php:204 msgid "Google Authenticator token was successfully reset" msgstr "Der Google Authenticator Token wurde erfolgreich zurückgesetzt" -#: Controller/Traits/ProfileTrait.php:57 +#: src/Controller/Traits/PasswordManagementTrait.php:207 +msgid "Could not reset Google Authenticator" +msgstr "Google Authenticator Token konnte nicht zurückgesetzt werden" + +#: src/Controller/Traits/ProfileTrait.php:58 msgid "Not authorized, please login first" msgstr "" "Sie sind nicht berechtigt diese Seite aufzurufen. Bitte melden Sie sich " "zuerst an" -#: Controller/Traits/RegisterTrait.php:46 +#: src/Controller/Traits/RegisterTrait.php:47 msgid "You must log out to register a new user account" msgstr "Sie müssen sich ausloggen um ein neues Konto zu erstellen" -#: Controller/Traits/RegisterTrait.php:75;99 +#: src/Controller/Traits/RegisterTrait.php:86 +#: src/Controller/Traits/RegisterTrait.php:117 msgid "The user could not be saved" msgstr "Der Benutzer konnte nicht gespeichert werden" -#: Controller/Traits/RegisterTrait.php:92 Loader/LoginComponentLoader.php:32 +#: src/Controller/Traits/RegisterTrait.php:103 +#: src/Loader/LoginComponentLoader.php:33 msgid "Invalid reCaptcha" msgstr "Ungültiger reCaptcha" -#: Controller/Traits/RegisterTrait.php:133 +#: src/Controller/Traits/RegisterTrait.php:151 msgid "You have registered successfully, please log in" msgstr "Sie haben sich erfolgreich registriert. Bitte melden Sie sich an" -#: Controller/Traits/RegisterTrait.php:135 +#: src/Controller/Traits/RegisterTrait.php:153 msgid "Please validate your account before log in" msgstr "Bitte bestätigen Sie ihr Konto bevor Sie sich anmelden" -#: Controller/Traits/SimpleCrudTrait.php:77;107 +#: src/Controller/Traits/SimpleCrudTrait.php:77 +#: src/Controller/Traits/SimpleCrudTrait.php:107 msgid "The {0} has been saved" msgstr "Der {0} wurde gespeichert" -#: Controller/Traits/SimpleCrudTrait.php:81;111 +#: src/Controller/Traits/SimpleCrudTrait.php:81 +#: src/Controller/Traits/SimpleCrudTrait.php:111 msgid "The {0} could not be saved" msgstr "Der {0} konnte nicht gespeichert werden" -#: Controller/Traits/SimpleCrudTrait.php:131 +#: src/Controller/Traits/SimpleCrudTrait.php:131 msgid "The {0} has been deleted" msgstr "Der {0} wurde gelöscht" -#: Controller/Traits/SimpleCrudTrait.php:133 +#: src/Controller/Traits/SimpleCrudTrait.php:133 msgid "The {0} could not be deleted" msgstr "Der {0} konnte nicht gelöscht werden" -#: Controller/Traits/UserValidationTrait.php:44 +#: src/Controller/Traits/U2fTrait.php:216 +msgid "U2F requires SSL." +msgstr "U2F setzt SSL voraus." + +#: src/Controller/Traits/UserValidationTrait.php:49 msgid "User account validated successfully" msgstr "Sie haben Ihren Benutzer erfolgreich bestätigt" -#: Controller/Traits/UserValidationTrait.php:46 +#: src/Controller/Traits/UserValidationTrait.php:51 msgid "User account could not be validated" msgstr "Ihr Benutzer konnte nicht bestätigt werden" -#: Controller/Traits/UserValidationTrait.php:49 +#: src/Controller/Traits/UserValidationTrait.php:54 msgid "User already active" msgstr "Dieser Benutzer ist bereits aktiv" -#: Controller/Traits/UserValidationTrait.php:55 +#: src/Controller/Traits/UserValidationTrait.php:60 msgid "Reset password token was validated successfully" msgstr "Der angegebenen Token für das Zurücksetzen des Passworts ist gültig" -#: Controller/Traits/UserValidationTrait.php:63 +#: src/Controller/Traits/UserValidationTrait.php:68 msgid "Reset password token could not be validated" msgstr "Der angegebenen Token für das Zurücksetzen des Passworts ist unültig" -#: Controller/Traits/UserValidationTrait.php:67 +#: src/Controller/Traits/UserValidationTrait.php:72 msgid "Invalid validation type" msgstr "Ungültiger Validierungstyp" -#: Controller/Traits/UserValidationTrait.php:70 +#: src/Controller/Traits/UserValidationTrait.php:75 msgid "Invalid token or user account already validated" msgstr "Ungültiger Token oder Benutzer ist bereits validiert" -#: Controller/Traits/UserValidationTrait.php:76 +#: src/Controller/Traits/UserValidationTrait.php:81 msgid "Token already expired" msgstr "Token ist bereits abgelaufen" -#: Controller/Traits/UserValidationTrait.php:106 +#: src/Controller/Traits/UserValidationTrait.php:114 msgid "Token has been reset successfully. Please check your email." msgstr "" "Der Token wurde erfolgreich zurückgesetzt. Bitte überprüfen Sie ihren " "Posteingang." -#: Controller/Traits/UserValidationTrait.php:118 +#: src/Controller/Traits/UserValidationTrait.php:126 msgid "User {0} is already active" msgstr "Benutzer {0} ist bereits aktiv" -#: Loader/LoginComponentLoader.php:30 +#: src/Controller/Traits/Webauthn2faTrait.php:53 +#: src/Controller/Traits/Webauthn2faTrait.php:73 +msgid "User already has configured webauthn2fa" +msgstr "Dieser Benutzer hat bereis Webauthn2fa aktiv" + +#: src/Controller/Traits/Webauthn2faTrait.php:77 +#: src/Controller/Traits/Webauthn2faTrait.php:122 +msgid "Register error with webauthn for user id: {0}" +msgstr "" +"Es trat ein Fehler bei der Registrierung des Webauthn2fa für die Benutzer " +"ID: {0} auf" + +#: src/Loader/AuthenticationServiceLoader.php:109 +msgid "Property {0}.className should be defined" +msgstr "Property {0}.className sollte definiert sein" + +#: src/Loader/LoginComponentLoader.php:31 msgid "Username or password is incorrect" msgstr "Benutzername oder Passwort ist nicht korrekt" -#: Loader/LoginComponentLoader.php:51 +#: src/Loader/LoginComponentLoader.php:52 msgid "Could not proceed with social account. Please try again" msgstr "" "Es konnte keine Verbindung zum Social Account hergestellt werden. Bitte " "versuchen Sie es erneut" -#: Loader/LoginComponentLoader.php:53 +#: src/Loader/LoginComponentLoader.php:54 msgid "" "Your user has not been validated yet. Please check your inbox for " "instructions" @@ -225,7 +263,7 @@ msgstr "" "Ihr Benutzer wurde noch nicht bestätigt. Bitte überprüfen Sie ihren " "Posteingang für weitere Anweisungen" -#: Loader/LoginComponentLoader.php:57 +#: src/Loader/LoginComponentLoader.php:58 msgid "" "Your social account has not been validated yet. Please check your inbox for " "instructions" @@ -233,193 +271,225 @@ msgstr "" "Ihr Social Account wurde noch nicht bestätigt. Bitte überprüfen Sie ihren " "Posteingang für weitere Anweisungen" -#: Mailer/UsersMailer.php:33 +#: src/Mailer/UsersMailer.php:36 msgid "Your account validation link" msgstr "Ihr Konto Validierungs-Link" -#: Mailer/UsersMailer.php:51 +#: src/Mailer/UsersMailer.php:63 msgid "{0}Your reset password link" msgstr "{0}Ihr Passwort Zurücksetzten Link" -#: Mailer/UsersMailer.php:74 +#: src/Mailer/UsersMailer.php:95 msgid "{0}Your social account validation link" msgstr "{0}Ihr Social Account Bestätigung Link" -#: Middleware/SocialAuthMiddleware.php:65 Template/Users/social_email.ctp:16 +#: src/Middleware/SocialAuthMiddleware.php:47 +#: templates/Users/social_email.php:16 msgid "Please enter your email" msgstr "Bitte geben Sie ihre E-Mail Adresse ein" -#: Middleware/SocialAuthMiddleware.php:75 +#: src/Middleware/SocialAuthMiddleware.php:57 msgid "Could not identify your account, please try again" msgstr "" "Wir konnten Ihr Konto nicht identifizieren. Bitte versuchen Sie es erneut" -#: Model/Behavior/AuthFinderBehavior.php:48 +#: src/Middleware/UnauthorizedHandler/DefaultRedirectHandler.php:112 +msgid "You are not authorized to access that location." +msgstr "Sie sind nicht berechtigt diese Seite aufzurufen." + +#: src/Model/Behavior/AuthFinderBehavior.php:49 msgid "Missing 'username' in options data" msgstr "Fehlender Parameter ‘username’ in angegeben Optionen" -#: Model/Behavior/LinkSocialBehavior.php:53 +#: src/Model/Behavior/LinkSocialBehavior.php:52 msgid "Social account already associated to another user" msgstr "Dieser Social Account ist bereits mit einem anderen Benutzer verknüpft" -#: Model/Behavior/PasswordBehavior.php:45 +#: src/Model/Behavior/PasswordBehavior.php:45 msgid "Reference cannot be null" msgstr "Referenz darf nicht leer sein" -#: Model/Behavior/PasswordBehavior.php:50 +#: src/Model/Behavior/PasswordBehavior.php:50 msgid "Token expiration cannot be empty" msgstr "Token Ablaufdatum darf nicht leer sein" -#: Model/Behavior/PasswordBehavior.php:56;138 +#: src/Model/Behavior/PasswordBehavior.php:56 +#: src/Model/Behavior/PasswordBehavior.php:136 msgid "User not found" msgstr "Benutzer konnte nicht gefunden werden" -#: Model/Behavior/PasswordBehavior.php:60 -#: Model/Behavior/RegisterBehavior.php:112 +#: src/Model/Behavior/PasswordBehavior.php:60 +#: src/Model/Behavior/RegisterBehavior.php:129 msgid "User account already validated" msgstr "Benutzer ist bereits bestätigt" -#: Model/Behavior/PasswordBehavior.php:67 +#: src/Model/Behavior/PasswordBehavior.php:66 msgid "User not active" msgstr "Benutzer ist nicht aktiv" -#: Model/Behavior/PasswordBehavior.php:143 +#: src/Model/Behavior/PasswordBehavior.php:141 msgid "The current password does not match" msgstr "Das aktuelle Passwort stimmt nicht überein" -#: Model/Behavior/PasswordBehavior.php:146 +#: src/Model/Behavior/PasswordBehavior.php:144 msgid "You cannot use the current password as the new one" msgstr "Sie dürfen nicht das aktuelle Passwort als neues Passwort verwenden" -#: Model/Behavior/RegisterBehavior.php:90 +#: src/Model/Behavior/RegisterBehavior.php:107 msgid "User not found for the given token and email." msgstr "" "Es konnte kein Benutzer mit dem angegebenen Token oder der angegebenen Email " "gefunden werden." -#: Model/Behavior/RegisterBehavior.php:93 +#: src/Model/Behavior/RegisterBehavior.php:110 msgid "Token has already expired user with no token" msgstr "Der Token ist bereits abgelaufen oder der Benutzer hat keinen Token" -#: Model/Behavior/RegisterBehavior.php:151 +#: src/Model/Behavior/RegisterBehavior.php:167 msgid "This field is required" msgstr "Dieses Feld muss ausgefüllt werden" -#: Model/Behavior/SocialAccountBehavior.php:102;129 +#: src/Model/Behavior/SocialAccountBehavior.php:100 +#: src/Model/Behavior/SocialAccountBehavior.php:130 msgid "Account already validated" msgstr "Konto ist bereits bestätigt" -#: Model/Behavior/SocialAccountBehavior.php:105;132 +#: src/Model/Behavior/SocialAccountBehavior.php:104 +#: src/Model/Behavior/SocialAccountBehavior.php:135 msgid "Account not found for the given token and email." msgstr "" "Es konnte kein Konto für den angegebenen Token oder die angegebene Email " "gefunden werden." -#: Model/Behavior/SocialBehavior.php:83 +#: src/Model/Behavior/SocialBehavior.php:85 msgid "Unable to login user with reference {0}" msgstr "Der Login über die Referenz {0} konnte nicht durchgeführt werden" -#: Model/Behavior/SocialBehavior.php:122 +#: src/Model/Behavior/SocialBehavior.php:136 msgid "Email not present" msgstr "Email nicht vorhanden" -#: Model/Table/UsersTable.php:79 +#: src/Model/Table/UsersTable.php:107 msgid "Your password does not match your confirm password. Please try again" msgstr "Bitte bestätigen Sie ihr Passwort und versuchen Sie es erneut" -#: Model/Table/UsersTable.php:171 +#: src/Model/Table/UsersTable.php:201 msgid "Username already exists" msgstr "Benutzername ist bereits vorhanden" -#: Model/Table/UsersTable.php:177 +#: src/Model/Table/UsersTable.php:207 msgid "Email already exists" msgstr "Email ist bereits vorhanden" -#: Shell/UsersShell.php:58 +#: src/Shell/UsersShell.php:46 msgid "Utilities for CakeDC Users Plugin" msgstr "Tools für das CakeDC Users Plugin" -#: Shell/UsersShell.php:60 +#: src/Shell/UsersShell.php:48 msgid "Activate an specific user" msgstr "Aktiviere einen Benutzer" -#: Shell/UsersShell.php:63 +#: src/Shell/UsersShell.php:51 msgid "Add a new superadmin user for testing purposes" msgstr "Füge einen neuen Superadmin Benutzer für Testzwecke hinzu" -#: Shell/UsersShell.php:66 +#: src/Shell/UsersShell.php:54 msgid "Add a new user" msgstr "Füge einen neuen Benutzer hinzu" -#: Shell/UsersShell.php:69 +#: src/Shell/UsersShell.php:57 msgid "Change the role for an specific user" msgstr "Ändere die Rolle für einen Benutzer" -#: Shell/UsersShell.php:72 +#: src/Shell/UsersShell.php:60 +msgid "Change the api token for an specific user" +msgstr "Ändere den API Token für einen Benutzer" + +#: src/Shell/UsersShell.php:63 msgid "Deactivate an specific user" msgstr "Deaktiviere einen Benutzer" -#: Shell/UsersShell.php:75 +#: src/Shell/UsersShell.php:66 msgid "Delete an specific user" msgstr "Lösche einen Benutzer" -#: Shell/UsersShell.php:78 +#: src/Shell/UsersShell.php:69 msgid "Reset the password via email" msgstr "Sende Passwort-Zurücksetzen E-Mail für eine E-Mail Adresse" -#: Shell/UsersShell.php:81 +#: src/Shell/UsersShell.php:72 msgid "Reset the password for all users" msgstr "Setze das Passwort für alle Benutzer" -#: Shell/UsersShell.php:84 +#: src/Shell/UsersShell.php:75 msgid "Reset the password for an specific user" msgstr "Setze das Passwort für einen Benutzer" -#: Shell/UsersShell.php:133;159 +#: src/Shell/UsersShell.php:135 src/Shell/UsersShell.php:161 msgid "Please enter a password." msgstr "Bitte geben Sie das Passwort ein." -#: Shell/UsersShell.php:137 +#: src/Shell/UsersShell.php:139 msgid "Password changed for all users" msgstr "Das Passwort wurde für alle Benutzer geändert" -#: Shell/UsersShell.php:138;166 +#: src/Shell/UsersShell.php:140 src/Shell/UsersShell.php:168 msgid "New password: {0}" msgstr "Neues Passwort: {0}" -#: Shell/UsersShell.php:156;184;262;359 +#: src/Shell/UsersShell.php:158 src/Shell/UsersShell.php:186 +#: src/Shell/UsersShell.php:214 src/Shell/UsersShell.php:304 +#: src/Shell/UsersShell.php:403 msgid "Please enter a username." msgstr "Bitte geben Sie einen Benutzernamen ein." -#: Shell/UsersShell.php:165 +#: src/Shell/UsersShell.php:167 msgid "Password changed for user: {0}" msgstr "Das Passwort wurde für den Benutzer: {0} geändert" -#: Shell/UsersShell.php:187 +#: src/Shell/UsersShell.php:189 msgid "Please enter a role." msgstr "Bitte geben Sie eine Rolle ein." -#: Shell/UsersShell.php:193 +#: src/Shell/UsersShell.php:195 msgid "Role changed for user: {0}" msgstr "Die Rolle wurde für den Benutzer: {0} geändert" -#: Shell/UsersShell.php:194 +#: src/Shell/UsersShell.php:196 msgid "New role: {0}" msgstr "Neue Rolle: {0}" -#: Shell/UsersShell.php:209 +#: src/Shell/UsersShell.php:217 +msgid "Please enter a token." +msgstr "Bitte geben Sie einen Token ein." + +#: src/Shell/UsersShell.php:224 +msgid "User was not saved, check validation errors" +msgstr "" +"Der Benutzer konnte nicht gespeichert werden. Bitte überprüfe die " +"Validierungsfehlermeldungen" + +#: src/Shell/UsersShell.php:229 +msgid "Api token changed for user: {0}" +msgstr "API Token für Benutzer: {0} wurde geändert" + +#: src/Shell/UsersShell.php:230 +msgid "New token: {0}" +msgstr "Neuer Token: {0}" + +#: src/Shell/UsersShell.php:245 msgid "User was activated: {0}" msgstr "Benutzer wurde aktiviert: {0}" -#: Shell/UsersShell.php:224 +#: src/Shell/UsersShell.php:260 msgid "User was de-activated: {0}" msgstr "Benutzer wurde deaktiviert: {0}" -#: Shell/UsersShell.php:236 +#: src/Shell/UsersShell.php:272 msgid "Please enter a username or email." msgstr "Bitte geben Sie einen Benutzername oder eine Email ein." -#: Shell/UsersShell.php:244 +#: src/Shell/UsersShell.php:280 msgid "" "Please ask the user to check the email to continue with password reset " "process" @@ -427,315 +497,330 @@ msgstr "" "Bitte Fragen Sie den Benutzer den Posteingang zu überprüfen für die weitere " "Vorgehensweise" -#: Shell/UsersShell.php:308 +#: src/Shell/UsersShell.php:350 msgid "Superuser added:" msgstr "Superuser hinzugefügt:" -#: Shell/UsersShell.php:310 +#: src/Shell/UsersShell.php:352 msgid "User added:" msgstr "Benutzer hinzugefügt:" -#: Shell/UsersShell.php:312 +#: src/Shell/UsersShell.php:354 msgid "Id: {0}" msgstr "ID: {0}" -#: Shell/UsersShell.php:313 +#: src/Shell/UsersShell.php:355 msgid "Username: {0}" msgstr "Benutzername: {0}" -#: Shell/UsersShell.php:314 +#: src/Shell/UsersShell.php:356 msgid "Email: {0}" msgstr "Email: {0}" -#: Shell/UsersShell.php:315 +#: src/Shell/UsersShell.php:357 msgid "Role: {0}" msgstr "Rolle: {0}" -#: Shell/UsersShell.php:316 +#: src/Shell/UsersShell.php:358 msgid "Password: {0}" msgstr "Passwort: {0}" -#: Shell/UsersShell.php:318 +#: src/Shell/UsersShell.php:360 msgid "User could not be added:" msgstr "Benutzer konnte nicht hinzugefügt werden:" -#: Shell/UsersShell.php:321 +#: src/Shell/UsersShell.php:363 msgid "Field: {0} Error: {1}" msgstr "Feld: {0} Fehler: {0}" -#: Shell/UsersShell.php:337 +#: src/Shell/UsersShell.php:379 msgid "The user was not found." msgstr "Der Benutzer konnte nicht gefunden werden." -#: Shell/UsersShell.php:367 +#: src/Shell/UsersShell.php:414 msgid "The user {0} was not deleted. Please try again" msgstr "" "Der Benutzer {0} konnte nicht gelöscht werden. Bitte versuchen Sie es erneut" -#: Shell/UsersShell.php:369 +#: src/Shell/UsersShell.php:416 msgid "The user {0} was deleted successfully" msgstr "Der Benutzer {0} wurde gelöscht" -#: Template/Email/html/reset_password.ctp:21 -#: Template/Email/html/social_account_validation.ctp:14 -#: Template/Email/html/validation.ctp:21 -#: Template/Email/text/reset_password.ctp:20 -#: Template/Email/text/social_account_validation.ctp:22 -#: Template/Email/text/validation.ctp:20 -msgid "Hi {0}" -msgstr "Hallo {0}" - -#: Template/Email/html/reset_password.ctp:24 -msgid "Reset your password here" -msgstr "Passwort jetzt zurücksetzen" - -#: Template/Email/html/reset_password.ctp:27 -#: Template/Email/html/social_account_validation.ctp:32 -#: Template/Email/html/validation.ctp:27 -msgid "" -"If the link is not correctly displayed, please copy the following address in " -"your web browser {0}" -msgstr "" -"Falls der Link nicht korrekt dargestellt wird kopieren Sie bitte die " -"folgende Adresse in ihren Web Browser {0}" +#: src/View/Helper/UserHelper.php:50 +msgid "Sign in with" +msgstr "Anmelden mit" -#: Template/Email/html/reset_password.ctp:34 -#: Template/Email/html/social_account_validation.ctp:39 -#: Template/Email/html/validation.ctp:34 -#: Template/Email/text/reset_password.ctp:28 -#: Template/Email/text/social_account_validation.ctp:30 -#: Template/Email/text/validation.ctp:28 -msgid "Thank you" -msgstr "Vielen Dank" +#: src/View/Helper/UserHelper.php:113 +msgid "Logout" +msgstr "Abmelden" -#: Template/Email/html/social_account_validation.ctp:18 -msgid "Activate your social login here" -msgstr "Social Login aktivieren" +#: src/View/Helper/UserHelper.php:134 +msgid "Welcome, {0}" +msgstr "Willkommen, {0}" -#: Template/Email/html/validation.ctp:24 -msgid "Activate your account here" -msgstr "Konto aktivieren" +#: src/View/Helper/UserHelper.php:165 +msgid "reCaptcha is not configured! Please configure Users.reCaptcha.key" +msgstr "" +"reCaptcha ist nicht konfiguriert! Bitte setzten Sie Users.reCaptcha.key" -#: Template/Email/text/reset_password.ctp:22 -#: Template/Email/text/validation.ctp:22 -msgid "Please copy the following address in your web browser {0}" -msgstr "Bitte kopieren Sie die folgende Adresse in Ihren Web Browser {0}" +#: src/View/Helper/UserHelper.php:218 +msgid "Connected with {0}" +msgstr "Mit {0} verbunden" -#: Template/Email/text/social_account_validation.ctp:24 -msgid "" -"Please copy the following address in your web browser to activate your " -"social login {0}" -msgstr "" -"Bitte kopieren Sie die folgende Adresse in Ihren Web Browser um den Social " -"Login zu aktivieren {0}" +#: src/View/Helper/UserHelper.php:223 +msgid "Connect with {0}" +msgstr "Mit {0} verbinden" -#: Template/Users/add.ctp:13 Template/Users/edit.ctp:17 -#: Template/Users/index.ctp:13;26 Template/Users/view.ctp:15 +#: templates/Users/add.php:13 templates/Users/edit.php:17 +#: templates/Users/index.php:13 templates/Users/index.php:26 +#: templates/Users/view.php:15 msgid "Actions" msgstr "Aktionen" -#: Template/Users/add.ctp:15 Template/Users/edit.ctp:28 -#: Template/Users/view.ctp:23 +#: templates/Users/add.php:15 templates/Users/edit.php:28 +#: templates/Users/view.php:23 msgid "List Users" msgstr "Benutzer auflisten" -#: Template/Users/add.ctp:21 Template/Users/register.ctp:18 +#: templates/Users/add.php:21 templates/Users/register.php:18 msgid "Add User" msgstr "Benutzer hinzufügen" -#: Template/Users/add.ctp:23 Template/Users/edit.ctp:36 -#: Template/Users/index.ctp:22 Template/Users/login.ctp:20 -#: Template/Users/profile.ctp:30 Template/Users/register.ctp:20 -#: Template/Users/view.ctp:33 +#: templates/Users/add.php:23 templates/Users/edit.php:36 +#: templates/Users/index.php:22 templates/Users/login.php:20 +#: templates/Users/profile.php:30 templates/Users/register.php:20 +#: templates/Users/view.php:33 msgid "Username" msgstr "Benutzername" -#: Template/Users/add.ctp:24 Template/Users/edit.ctp:37 -#: Template/Users/index.ctp:23 Template/Users/profile.ctp:32 -#: Template/Users/register.ctp:21 Template/Users/view.ctp:35 +#: templates/Users/add.php:24 templates/Users/edit.php:37 +#: templates/Users/index.php:23 templates/Users/profile.php:32 +#: templates/Users/register.php:21 templates/Users/view.php:35 msgid "Email" msgstr "Email" -#: Template/Users/add.ctp:25 Template/Users/login.ctp:21 -#: Template/Users/register.ctp:22 +#: templates/Users/add.php:25 templates/Users/login.php:21 +#: templates/Users/register.php:22 msgid "Password" msgstr "Passwort" -#: Template/Users/add.ctp:26 Template/Users/edit.ctp:38 -#: Template/Users/index.ctp:24 Template/Users/register.ctp:27 +#: templates/Users/add.php:26 templates/Users/edit.php:38 +#: templates/Users/index.php:24 templates/Users/register.php:28 msgid "First name" msgstr "Vorname" -#: Template/Users/add.ctp:27 Template/Users/edit.ctp:39 -#: Template/Users/index.ctp:25 Template/Users/register.ctp:28 +#: templates/Users/add.php:27 templates/Users/edit.php:39 +#: templates/Users/index.php:25 templates/Users/register.php:29 msgid "Last name" msgstr "Nachname" -#: Template/Users/add.ctp:30 Template/Users/edit.ctp:54 -#: Template/Users/view.ctp:49;74 +#: templates/Users/add.php:30 templates/Users/edit.php:54 +#: templates/Users/view.php:49 templates/Users/view.php:74 msgid "Active" msgstr "Aktiv" -#: Template/Users/add.ctp:34 Template/Users/change_password.ctp:25 -#: Template/Users/edit.ctp:58 Template/Users/register.ctp:37 -#: Template/Users/request_reset_password.ctp:8 -#: Template/Users/resend_token_validation.ctp:20 -#: Template/Users/social_email.ctp:19 +#: templates/Users/add.php:34 templates/Users/change_password.php:25 +#: templates/Users/edit.php:58 templates/Users/register.php:38 +#: templates/Users/request_reset_password.php:23 +#: templates/Users/resend_token_validation.php:20 +#: templates/Users/social_email.php:19 msgid "Submit" msgstr "Absenden" -#: Template/Users/change_password.ctp:5 +#: templates/Users/change_password.php:5 msgid "Please enter the new password" msgstr "Bitte geben Sie ihr neues Passwort ein" -#: Template/Users/change_password.ctp:10 +#: templates/Users/change_password.php:10 msgid "Current password" msgstr "Aktuelles Passwort" -#: Template/Users/change_password.ctp:16 +#: templates/Users/change_password.php:16 msgid "New password" msgstr "Neues Passwort" -#: Template/Users/change_password.ctp:21 Template/Users/register.ctp:25 +#: templates/Users/change_password.php:21 templates/Users/register.php:26 msgid "Confirm password" msgstr "Neues Passwort bestätigen" -#: Template/Users/edit.ctp:22 Template/Users/index.ctp:40 +#: templates/Users/edit.php:22 templates/Users/index.php:40 msgid "Delete" msgstr "Löschen" -#: Template/Users/edit.ctp:24 Template/Users/index.ctp:40 -#: Template/Users/view.ctp:21 +#: templates/Users/edit.php:24 templates/Users/index.php:40 +#: templates/Users/view.php:21 msgid "Are you sure you want to delete # {0}?" msgstr "Sind Sie sicher, dass Sie # {0} löschen wollen?" -#: Template/Users/edit.ctp:34 Template/Users/view.ctp:17 +#: templates/Users/edit.php:34 templates/Users/view.php:17 msgid "Edit User" msgstr "Benutzer bearbeiten" -#: Template/Users/edit.ctp:40 Template/Users/view.ctp:43 +#: templates/Users/edit.php:40 templates/Users/view.php:43 msgid "Token" msgstr "Token" -#: Template/Users/edit.ctp:42 +#: templates/Users/edit.php:42 msgid "Token expires" msgstr "Token Ablaufdatum" -#: Template/Users/edit.ctp:45 +#: templates/Users/edit.php:45 msgid "API token" msgstr "API Token" -#: Template/Users/edit.ctp:48 +#: templates/Users/edit.php:48 msgid "Activation date" msgstr "Aktivierungsdatum" -#: Template/Users/edit.ctp:51 +#: templates/Users/edit.php:51 msgid "TOS date" msgstr "AGB Datum" -#: Template/Users/edit.ctp:64 +#: templates/Users/edit.php:64 msgid "Reset Google Authenticator Token" msgstr "Google Authenticator Token zurücksetzen" -#: Template/Users/edit.ctp:70 +#: templates/Users/edit.php:70 msgid "Are you sure you want to reset token for user \"{0}\"?" msgstr "" "Sind Sie sicher, dass Sie den Token für den Benutzer # {0} zurücksetzen " "wollen?" -#: Template/Users/index.ctp:15 +#: templates/Users/index.php:15 msgid "New {0}" msgstr "Neuer {0}" -#: Template/Users/index.ctp:37 +#: templates/Users/index.php:37 msgid "View" msgstr "Ansicht" -#: Template/Users/index.ctp:38 +#: templates/Users/index.php:38 msgid "Change password" msgstr "Passwort ändern" -#: Template/Users/index.ctp:39 +#: templates/Users/index.php:39 msgid "Edit" msgstr "Bearbeiten" -#: Template/Users/index.ctp:49 +#: templates/Users/index.php:49 msgid "previous" msgstr "vorherig" -#: Template/Users/index.ctp:51 +#: templates/Users/index.php:51 msgid "next" msgstr "nächste" -#: Template/Users/login.ctp:19 +#: templates/Users/login.php:19 msgid "Please enter your username and password" msgstr "Bitte geben Sie ihren Benutzernamen und Passwort ein" -#: Template/Users/login.ctp:29 +#: templates/Users/login.php:29 msgid "Remember me" msgstr "Angemeldet bleiben" -#: Template/Users/login.ctp:37 +#: templates/Users/login.php:37 msgid "Register" msgstr "Registrieren" -#: Template/Users/login.ctp:43 +#: templates/Users/login.php:43 msgid "Reset Password" msgstr "Passwort zurücksetzen" -#: Template/Users/login.ctp:48 +#: templates/Users/login.php:48 msgid "Login" msgstr "Anmelden" -#: Template/Users/profile.ctp:21 +#: templates/Users/profile.php:21 msgid "{0} {1}" msgstr "{0} {1}" -#: Template/Users/profile.ctp:27 +#: templates/Users/profile.php:27 msgid "Change Password" msgstr "Passwort ändern" -#: Template/Users/profile.ctp:38 Template/Users/view.ctp:68 +#: templates/Users/profile.php:38 templates/Users/view.php:68 msgid "Social Accounts" msgstr "Social Accounts" -#: Template/Users/profile.ctp:42 Template/Users/view.ctp:73 +#: templates/Users/profile.php:42 templates/Users/view.php:73 msgid "Avatar" msgstr "Avatar" -#: Template/Users/profile.ctp:43 Template/Users/view.ctp:72 +#: templates/Users/profile.php:43 templates/Users/view.php:72 msgid "Provider" msgstr "Provider" -#: Template/Users/profile.ctp:44 +#: templates/Users/profile.php:44 msgid "Link" msgstr "Link" -#: Template/Users/profile.ctp:51 +#: templates/Users/profile.php:51 msgid "Link to {0}" msgstr "Link zu {0}" -#: Template/Users/register.ctp:30 +#: templates/Users/register.php:31 msgid "Accept TOS conditions?" msgstr "AGBs akzeptieren?" -#: Template/Users/request_reset_password.ctp:5 -msgid "Please enter your email to reset your password" -msgstr "Bitte geben Sie ihre Email Adresse ein um ihr Passwort zurückzusetzen" +#: templates/Users/request_reset_password.php:20 +msgid "Please enter your email or username to reset your password" +msgstr "" +"Bitte geben Sie ihre Email Adresse oder Benutzernamen ein um ihr Passwort " +"zurückzusetzen" -#: Template/Users/resend_token_validation.ctp:15 +#: templates/Users/resend_token_validation.php:15 msgid "Resend Validation email" msgstr "Validierungs-Email erneut versenden" -#: Template/Users/resend_token_validation.ctp:17 +#: templates/Users/resend_token_validation.php:17 msgid "Email or username" msgstr "Email oder Benutzername" -#: Template/Users/verify.ctp:13 +#: templates/Users/u2f_authenticate.php:22 templates/Users/webauthn2fa.php:25 +msgid "Verify your registered yubico key" +msgstr "Bestätige deinen registrierten Yubico Key" + +#: templates/Users/u2f_authenticate.php:23 templates/Users/u2f_register.php:23 +#: templates/Users/webauthn2fa.php:20 templates/Users/webauthn2fa.php:26 +msgid "Please insert and tap your yubico key" +msgstr "Bitte Yubico Key anstecken und antippen" + +#: templates/Users/u2f_authenticate.php:24 templates/Users/webauthn2fa.php:27 +msgid "" +"You can now finish the authentication process using the registered device." +msgstr "" +"Sie können nun den Authentifizierungs-Prozess mit dem registrierten Gerät " +"fertigstellen." + +#: templates/Users/u2f_authenticate.php:25 templates/Users/webauthn2fa.php:22 +#: templates/Users/webauthn2fa.php:28 +msgid "" +"When the YubiKey starts blinking, press the golden disc to activate it. " +"Depending on the web browser you might need to confirm the use of extended " +"information from the YubiKey." +msgstr "" +"Wenn der YubiKey anfängt zu blinken drücken Sie bitte die goldene Diskette " +"um ihn zu aktivieren. Abhängig vom Web-Browser könnten Berechtigungs-Popups " +"erscheinen um den YubiKey zu verwenden." + +#: templates/Users/u2f_authenticate.php:27 templates/Users/u2f_register.php:27 +#: templates/Users/webauthn2fa.php:31 +msgid "Reload" +msgstr "Neu laden" + +#: templates/Users/u2f_authenticate.php:51 templates/Users/u2f_register.php:56 +msgid "Yubico key check has failed, please try again" +msgstr "Yubico Key Überprüfung fehlgeschlagen. Bitte versuchen Sie es erneut" + +#: templates/Users/u2f_register.php:22 templates/Users/webauthn2fa.php:19 +msgid "Registering your yubico key" +msgstr "Yubico Key registrieren" + +#: templates/Users/verify.php:13 msgid "Verification Code" msgstr "Bestätigungscode" -#: Template/Users/verify.ctp:15 +#: templates/Users/verify.php:15 msgid "" " " "Verify" @@ -743,75 +828,114 @@ msgstr "" " " "Bestätigen" -#: Template/Users/view.ctp:19 +#: templates/Users/view.php:19 msgid "Delete User" msgstr "Benutzer löschen" -#: Template/Users/view.ctp:24 +#: templates/Users/view.php:24 msgid "New User" msgstr "Benutzer hinzufügen" -#: Template/Users/view.ctp:31 +#: templates/Users/view.php:31 msgid "Id" msgstr "ID" -#: Template/Users/view.ctp:37 +#: templates/Users/view.php:37 msgid "First Name" msgstr "Vorname" -#: Template/Users/view.ctp:39 +#: templates/Users/view.php:39 msgid "Last Name" msgstr "Nachname" -#: Template/Users/view.ctp:41 +#: templates/Users/view.php:41 msgid "Role" msgstr "Rolle" -#: Template/Users/view.ctp:45 +#: templates/Users/view.php:45 msgid "Api Token" msgstr "API Token" -#: Template/Users/view.ctp:53 +#: templates/Users/view.php:53 msgid "Token Expires" msgstr "Token Ablaufdatum" -#: Template/Users/view.ctp:55 +#: templates/Users/view.php:55 msgid "Activation Date" msgstr "Aktivierungsdatum" -#: Template/Users/view.ctp:57 +#: templates/Users/view.php:57 msgid "Tos Date" msgstr "AGB Datum" -#: Template/Users/view.ctp:59;75 +#: templates/Users/view.php:59 templates/Users/view.php:75 msgid "Created" msgstr "erstellt am" -#: Template/Users/view.ctp:61;76 +#: templates/Users/view.php:61 templates/Users/view.php:76 msgid "Modified" msgstr "zuletzt aktualisiert" -#: View/Helper/UserHelper.php:46 -msgid "Sign in with" -msgstr "Anmelden mit" +#: templates/Users/webauthn2fa.php:8 +msgid "Two-factor authentication" +msgstr "Two-Factor Authentifizierung" -#: View/Helper/UserHelper.php:103 -msgid "Logout" -msgstr "Abmelden" +#: templates/Users/webauthn2fa.php:21 +msgid "" +"In order to enable your YubiKey the first step is to perform a registration." +msgstr "" +"Damit Sie ihren YubiKey verwenden können müssen Sie diesen zuerst " +"registrieren." + +#: templates/email/html/reset_password.php:14 +#: templates/email/html/social_account_validation.php:14 +#: templates/email/html/validation.php:13 +#: templates/email/text/reset_password.php:12 +#: templates/email/text/social_account_validation.php:12 +#: templates/email/text/validation.php:12 +msgid "Hi {0}" +msgstr "Hallo {0}" -#: View/Helper/UserHelper.php:121 -msgid "Welcome, {0}" -msgstr "Willkommen, {0}" +#: templates/email/html/reset_password.php:17 +msgid "Reset your password here" +msgstr "Passwort jetzt zurücksetzen" -#: View/Helper/UserHelper.php:151 -msgid "reCaptcha is not configured! Please configure Users.reCaptcha.key" +#: templates/email/html/reset_password.php:20 +#: templates/email/html/social_account_validation.php:23 +#: templates/email/html/validation.php:19 +msgid "" +"If the link is not correctly displayed, please copy the following address in " +"your web browser {0}" msgstr "" -"reCaptcha ist nicht konfiguriert! Bitte setzten Sie Users.reCaptcha.key" +"Falls der Link nicht korrekt dargestellt wird kopieren Sie bitte die " +"folgende Adresse in ihren Web Browser {0}" -#: View/Helper/UserHelper.php:215 -msgid "Connected with {0}" -msgstr "Mit {0} verbunden" +#: templates/email/html/reset_password.php:27 +#: templates/email/html/social_account_validation.php:30 +#: templates/email/html/validation.php:26 +#: templates/email/text/reset_password.php:20 +#: templates/email/text/social_account_validation.php:20 +#: templates/email/text/validation.php:20 +msgid "Thank you" +msgstr "Vielen Dank" -#: View/Helper/UserHelper.php:220 -msgid "Connect with {0}" -msgstr "Mit {0} verbinden" +#: templates/email/html/social_account_validation.php:18 +msgid "Activate your social login here" +msgstr "Social Login aktivieren" + +#: templates/email/html/validation.php:16 +msgid "Activate your account here" +msgstr "Konto aktivieren" + +#: templates/email/text/reset_password.php:14 +#: templates/email/text/validation.php:14 +msgid "Please copy the following address in your web browser {0}" +msgstr "Bitte kopieren Sie die folgende Adresse in Ihren Web Browser {0}" + +#: templates/email/text/social_account_validation.php:14 +msgid "" +"Please copy the following address in your web browser to activate your " +"social login {0}" +msgstr "" +"Bitte kopieren Sie die folgende Adresse in Ihren Web Browser um den Social " +"Login zu aktivieren {0}" diff --git a/resources/locales/users.pot b/resources/locales/users.pot index 59f36b747..c2992ab07 100644 --- a/resources/locales/users.pot +++ b/resources/locales/users.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" -"POT-Creation-Date: 2019-01-10 14:38+0000\n" +"POT-Creation-Date: 2022-03-11 13:46+0100\n" "PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n" "Last-Translator: NAME \n" "Language-Team: LANGUAGE \n" @@ -14,789 +14,907 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" -#: Controller/SocialAccountsController.php:50 +#: src/Controller/SocialAccountsController.php:50 msgid "Account validated successfully" msgstr "" -#: Controller/SocialAccountsController.php:52 +#: src/Controller/SocialAccountsController.php:52 msgid "Account could not be validated" msgstr "" -#: Controller/SocialAccountsController.php:55 +#: src/Controller/SocialAccountsController.php:55 msgid "Invalid token and/or social account" msgstr "" -#: Controller/SocialAccountsController.php:57;85 +#: src/Controller/SocialAccountsController.php:57 +#: src/Controller/SocialAccountsController.php:85 msgid "Social Account already active" msgstr "" -#: Controller/SocialAccountsController.php:59 +#: src/Controller/SocialAccountsController.php:59 msgid "Social Account could not be validated" msgstr "" -#: Controller/SocialAccountsController.php:78 +#: src/Controller/SocialAccountsController.php:78 msgid "Email sent successfully" msgstr "" -#: Controller/SocialAccountsController.php:80 +#: src/Controller/SocialAccountsController.php:80 msgid "Email could not be sent" msgstr "" -#: Controller/SocialAccountsController.php:83 +#: src/Controller/SocialAccountsController.php:83 msgid "Invalid account" msgstr "" -#: Controller/SocialAccountsController.php:87 +#: src/Controller/SocialAccountsController.php:87 msgid "Email could not be resent" msgstr "" -#: Controller/Traits/LinkSocialTrait.php:52 +#: src/Controller/Traits/LinkSocialTrait.php:56 msgid "Could not associate account, please try again." msgstr "" -#: Controller/Traits/LinkSocialTrait.php:76 +#: src/Controller/Traits/LinkSocialTrait.php:80 msgid "Social account was associated." msgstr "" -#: Controller/Traits/LoginTrait.php:73 +#: src/Controller/Traits/LoginTrait.php:73 msgid "You've successfully logged out" msgstr "" -#: Controller/Traits/OneTimePasswordVerifyTrait.php:71 +#: src/Controller/Traits/OneTimePasswordVerifyTrait.php:75 msgid "Please enable Google Authenticator first." msgstr "" -#: Controller/Traits/OneTimePasswordVerifyTrait.php:80 +#: src/Controller/Traits/OneTimePasswordVerifyTrait.php:90 msgid "Could not find user data" msgstr "" -#: Controller/Traits/OneTimePasswordVerifyTrait.php:116 +#: src/Controller/Traits/OneTimePasswordVerifyTrait.php:129 msgid "Could not verify, please try again" msgstr "" -#: Controller/Traits/OneTimePasswordVerifyTrait.php:144 +#: src/Controller/Traits/OneTimePasswordVerifyTrait.php:161 msgid "Verification code is invalid. Try again" msgstr "" -#: Controller/Traits/PasswordManagementTrait.php:53;91 -#: Controller/Traits/ProfileTrait.php:53 +#: src/Controller/Traits/PasswordManagementTrait.php:63 +msgid "Changing another user's password is not allowed" +msgstr "" + +#: src/Controller/Traits/PasswordManagementTrait.php:77 +#: src/Controller/Traits/PasswordManagementTrait.php:121 +#: src/Controller/Traits/ProfileTrait.php:54 msgid "User was not found" msgstr "" -#: Controller/Traits/PasswordManagementTrait.php:75;87;95 +#: src/Controller/Traits/PasswordManagementTrait.php:105 +#: src/Controller/Traits/PasswordManagementTrait.php:117 +#: src/Controller/Traits/PasswordManagementTrait.php:125 msgid "Password could not be changed" msgstr "" -#: Controller/Traits/PasswordManagementTrait.php:83 +#: src/Controller/Traits/PasswordManagementTrait.php:113 msgid "Password has been changed successfully" msgstr "" -#: Controller/Traits/PasswordManagementTrait.php:137 +#: src/Controller/Traits/PasswordManagementTrait.php:167 msgid "Please check your email to continue with password reset process" msgstr "" -#: Controller/Traits/PasswordManagementTrait.php:140 -#: Shell/UsersShell.php:247 +#: src/Controller/Traits/PasswordManagementTrait.php:170 +#: src/Shell/UsersShell.php:286 msgid "The password token could not be generated. Please try again" msgstr "" -#: Controller/Traits/PasswordManagementTrait.php:146 -#: Controller/Traits/UserValidationTrait.php:116 +#: src/Controller/Traits/PasswordManagementTrait.php:176 +#: src/Controller/Traits/UserValidationTrait.php:124 msgid "User {0} was not found" msgstr "" -#: Controller/Traits/PasswordManagementTrait.php:148 +#: src/Controller/Traits/PasswordManagementTrait.php:178 msgid "The user is not active" msgstr "" -#: Controller/Traits/PasswordManagementTrait.php:150 -#: Controller/Traits/UserValidationTrait.php:111;120 +#: src/Controller/Traits/PasswordManagementTrait.php:180 +#: src/Controller/Traits/UserValidationTrait.php:119 +#: src/Controller/Traits/UserValidationTrait.php:128 msgid "Token could not be reset" msgstr "" -#: Controller/Traits/PasswordManagementTrait.php:174 +#: src/Controller/Traits/PasswordManagementTrait.php:204 msgid "Google Authenticator token was successfully reset" msgstr "" -#: Controller/Traits/ProfileTrait.php:57 +#: src/Controller/Traits/PasswordManagementTrait.php:207 +msgid "Could not reset Google Authenticator" +msgstr "" + +#: src/Controller/Traits/ProfileTrait.php:58 msgid "Not authorized, please login first" msgstr "" -#: Controller/Traits/RegisterTrait.php:46 +#: src/Controller/Traits/RegisterTrait.php:47 msgid "You must log out to register a new user account" msgstr "" -#: Controller/Traits/RegisterTrait.php:75;99 +#: src/Controller/Traits/RegisterTrait.php:86 +#: src/Controller/Traits/RegisterTrait.php:117 msgid "The user could not be saved" msgstr "" -#: Controller/Traits/RegisterTrait.php:92 -#: Loader/LoginComponentLoader.php:32 +#: src/Controller/Traits/RegisterTrait.php:103 +#: src/Loader/LoginComponentLoader.php:33 msgid "Invalid reCaptcha" msgstr "" -#: Controller/Traits/RegisterTrait.php:133 +#: src/Controller/Traits/RegisterTrait.php:151 msgid "You have registered successfully, please log in" msgstr "" -#: Controller/Traits/RegisterTrait.php:135 +#: src/Controller/Traits/RegisterTrait.php:153 msgid "Please validate your account before log in" msgstr "" -#: Controller/Traits/SimpleCrudTrait.php:77;107 +#: src/Controller/Traits/SimpleCrudTrait.php:77 +#: src/Controller/Traits/SimpleCrudTrait.php:107 msgid "The {0} has been saved" msgstr "" -#: Controller/Traits/SimpleCrudTrait.php:81;111 +#: src/Controller/Traits/SimpleCrudTrait.php:81 +#: src/Controller/Traits/SimpleCrudTrait.php:111 msgid "The {0} could not be saved" msgstr "" -#: Controller/Traits/SimpleCrudTrait.php:131 +#: src/Controller/Traits/SimpleCrudTrait.php:131 msgid "The {0} has been deleted" msgstr "" -#: Controller/Traits/SimpleCrudTrait.php:133 +#: src/Controller/Traits/SimpleCrudTrait.php:133 msgid "The {0} could not be deleted" msgstr "" -#: Controller/Traits/UserValidationTrait.php:44 +#: src/Controller/Traits/U2fTrait.php:216 +msgid "U2F requires SSL." +msgstr "" + +#: src/Controller/Traits/UserValidationTrait.php:49 msgid "User account validated successfully" msgstr "" -#: Controller/Traits/UserValidationTrait.php:46 +#: src/Controller/Traits/UserValidationTrait.php:51 msgid "User account could not be validated" msgstr "" -#: Controller/Traits/UserValidationTrait.php:49 +#: src/Controller/Traits/UserValidationTrait.php:54 msgid "User already active" msgstr "" -#: Controller/Traits/UserValidationTrait.php:55 +#: src/Controller/Traits/UserValidationTrait.php:60 msgid "Reset password token was validated successfully" msgstr "" -#: Controller/Traits/UserValidationTrait.php:63 +#: src/Controller/Traits/UserValidationTrait.php:68 msgid "Reset password token could not be validated" msgstr "" -#: Controller/Traits/UserValidationTrait.php:67 +#: src/Controller/Traits/UserValidationTrait.php:72 msgid "Invalid validation type" msgstr "" -#: Controller/Traits/UserValidationTrait.php:70 +#: src/Controller/Traits/UserValidationTrait.php:75 msgid "Invalid token or user account already validated" msgstr "" -#: Controller/Traits/UserValidationTrait.php:76 +#: src/Controller/Traits/UserValidationTrait.php:81 msgid "Token already expired" msgstr "" -#: Controller/Traits/UserValidationTrait.php:106 +#: src/Controller/Traits/UserValidationTrait.php:114 msgid "Token has been reset successfully. Please check your email." msgstr "" -#: Controller/Traits/UserValidationTrait.php:118 +#: src/Controller/Traits/UserValidationTrait.php:126 msgid "User {0} is already active" msgstr "" -#: Loader/LoginComponentLoader.php:30 +#: src/Controller/Traits/Webauthn2faTrait.php:53 +#: src/Controller/Traits/Webauthn2faTrait.php:73 +msgid "User already has configured webauthn2fa" +msgstr "" + +#: src/Controller/Traits/Webauthn2faTrait.php:77 +#: src/Controller/Traits/Webauthn2faTrait.php:122 +msgid "Register error with webauthn for user id: {0}" +msgstr "" + +#: src/Loader/AuthenticationServiceLoader.php:109 +msgid "Property {0}.className should be defined" +msgstr "" + +#: src/Loader/LoginComponentLoader.php:31 msgid "Username or password is incorrect" msgstr "" -#: Loader/LoginComponentLoader.php:51 +#: src/Loader/LoginComponentLoader.php:52 msgid "Could not proceed with social account. Please try again" msgstr "" -#: Loader/LoginComponentLoader.php:53 +#: src/Loader/LoginComponentLoader.php:54 msgid "Your user has not been validated yet. Please check your inbox for instructions" msgstr "" -#: Loader/LoginComponentLoader.php:57 +#: src/Loader/LoginComponentLoader.php:58 msgid "Your social account has not been validated yet. Please check your inbox for instructions" msgstr "" -#: Mailer/UsersMailer.php:33 +#: src/Mailer/UsersMailer.php:36 msgid "Your account validation link" msgstr "" -#: Mailer/UsersMailer.php:51 +#: src/Mailer/UsersMailer.php:63 msgid "{0}Your reset password link" msgstr "" -#: Mailer/UsersMailer.php:74 +#: src/Mailer/UsersMailer.php:95 msgid "{0}Your social account validation link" msgstr "" -#: Middleware/SocialAuthMiddleware.php:65 -#: Template/Users/social_email.ctp:16 +#: src/Middleware/SocialAuthMiddleware.php:47 +#: templates/Users/social_email.php:16 msgid "Please enter your email" msgstr "" -#: Middleware/SocialAuthMiddleware.php:75 +#: src/Middleware/SocialAuthMiddleware.php:57 msgid "Could not identify your account, please try again" msgstr "" -#: Model/Behavior/AuthFinderBehavior.php:48 +#: src/Middleware/UnauthorizedHandler/DefaultRedirectHandler.php:112 +msgid "You are not authorized to access that location." +msgstr "" + +#: src/Model/Behavior/AuthFinderBehavior.php:49 msgid "Missing 'username' in options data" msgstr "" -#: Model/Behavior/LinkSocialBehavior.php:53 +#: src/Model/Behavior/LinkSocialBehavior.php:52 msgid "Social account already associated to another user" msgstr "" -#: Model/Behavior/PasswordBehavior.php:45 +#: src/Model/Behavior/PasswordBehavior.php:45 msgid "Reference cannot be null" msgstr "" -#: Model/Behavior/PasswordBehavior.php:50 +#: src/Model/Behavior/PasswordBehavior.php:50 msgid "Token expiration cannot be empty" msgstr "" -#: Model/Behavior/PasswordBehavior.php:56;138 +#: src/Model/Behavior/PasswordBehavior.php:56 +#: src/Model/Behavior/PasswordBehavior.php:136 msgid "User not found" msgstr "" -#: Model/Behavior/PasswordBehavior.php:60 -#: Model/Behavior/RegisterBehavior.php:112 +#: src/Model/Behavior/PasswordBehavior.php:60 +#: src/Model/Behavior/RegisterBehavior.php:129 msgid "User account already validated" msgstr "" -#: Model/Behavior/PasswordBehavior.php:67 +#: src/Model/Behavior/PasswordBehavior.php:66 msgid "User not active" msgstr "" -#: Model/Behavior/PasswordBehavior.php:143 +#: src/Model/Behavior/PasswordBehavior.php:141 msgid "The current password does not match" msgstr "" -#: Model/Behavior/PasswordBehavior.php:146 +#: src/Model/Behavior/PasswordBehavior.php:144 msgid "You cannot use the current password as the new one" msgstr "" -#: Model/Behavior/RegisterBehavior.php:90 +#: src/Model/Behavior/RegisterBehavior.php:107 msgid "User not found for the given token and email." msgstr "" -#: Model/Behavior/RegisterBehavior.php:93 +#: src/Model/Behavior/RegisterBehavior.php:110 msgid "Token has already expired user with no token" msgstr "" -#: Model/Behavior/RegisterBehavior.php:151 +#: src/Model/Behavior/RegisterBehavior.php:167 msgid "This field is required" msgstr "" -#: Model/Behavior/SocialAccountBehavior.php:102;129 +#: src/Model/Behavior/SocialAccountBehavior.php:100 +#: src/Model/Behavior/SocialAccountBehavior.php:130 msgid "Account already validated" msgstr "" -#: Model/Behavior/SocialAccountBehavior.php:105;132 +#: src/Model/Behavior/SocialAccountBehavior.php:104 +#: src/Model/Behavior/SocialAccountBehavior.php:135 msgid "Account not found for the given token and email." msgstr "" -#: Model/Behavior/SocialBehavior.php:83 +#: src/Model/Behavior/SocialBehavior.php:85 msgid "Unable to login user with reference {0}" msgstr "" -#: Model/Behavior/SocialBehavior.php:122 +#: src/Model/Behavior/SocialBehavior.php:136 msgid "Email not present" msgstr "" -#: Model/Table/UsersTable.php:79 +#: src/Model/Table/UsersTable.php:107 msgid "Your password does not match your confirm password. Please try again" msgstr "" -#: Model/Table/UsersTable.php:171 +#: src/Model/Table/UsersTable.php:201 msgid "Username already exists" msgstr "" -#: Model/Table/UsersTable.php:177 +#: src/Model/Table/UsersTable.php:207 msgid "Email already exists" msgstr "" -#: Shell/UsersShell.php:58 +#: src/Shell/UsersShell.php:46 msgid "Utilities for CakeDC Users Plugin" msgstr "" -#: Shell/UsersShell.php:60 +#: src/Shell/UsersShell.php:48 msgid "Activate an specific user" msgstr "" -#: Shell/UsersShell.php:63 +#: src/Shell/UsersShell.php:51 msgid "Add a new superadmin user for testing purposes" msgstr "" -#: Shell/UsersShell.php:66 +#: src/Shell/UsersShell.php:54 msgid "Add a new user" msgstr "" -#: Shell/UsersShell.php:69 +#: src/Shell/UsersShell.php:57 msgid "Change the role for an specific user" msgstr "" -#: Shell/UsersShell.php:72 +#: src/Shell/UsersShell.php:60 +msgid "Change the api token for an specific user" +msgstr "" + +#: src/Shell/UsersShell.php:63 msgid "Deactivate an specific user" msgstr "" -#: Shell/UsersShell.php:75 +#: src/Shell/UsersShell.php:66 msgid "Delete an specific user" msgstr "" -#: Shell/UsersShell.php:78 +#: src/Shell/UsersShell.php:69 msgid "Reset the password via email" msgstr "" -#: Shell/UsersShell.php:81 +#: src/Shell/UsersShell.php:72 msgid "Reset the password for all users" msgstr "" -#: Shell/UsersShell.php:84 +#: src/Shell/UsersShell.php:75 msgid "Reset the password for an specific user" msgstr "" -#: Shell/UsersShell.php:133;159 +#: src/Shell/UsersShell.php:135 +#: src/Shell/UsersShell.php:161 msgid "Please enter a password." msgstr "" -#: Shell/UsersShell.php:137 +#: src/Shell/UsersShell.php:139 msgid "Password changed for all users" msgstr "" -#: Shell/UsersShell.php:138;166 +#: src/Shell/UsersShell.php:140 +#: src/Shell/UsersShell.php:168 msgid "New password: {0}" msgstr "" -#: Shell/UsersShell.php:156;184;262;359 +#: src/Shell/UsersShell.php:158 +#: src/Shell/UsersShell.php:186 +#: src/Shell/UsersShell.php:214 +#: src/Shell/UsersShell.php:304 +#: src/Shell/UsersShell.php:403 msgid "Please enter a username." msgstr "" -#: Shell/UsersShell.php:165 +#: src/Shell/UsersShell.php:167 msgid "Password changed for user: {0}" msgstr "" -#: Shell/UsersShell.php:187 +#: src/Shell/UsersShell.php:189 msgid "Please enter a role." msgstr "" -#: Shell/UsersShell.php:193 +#: src/Shell/UsersShell.php:195 msgid "Role changed for user: {0}" msgstr "" -#: Shell/UsersShell.php:194 +#: src/Shell/UsersShell.php:196 msgid "New role: {0}" msgstr "" -#: Shell/UsersShell.php:209 +#: src/Shell/UsersShell.php:217 +msgid "Please enter a token." +msgstr "" + +#: src/Shell/UsersShell.php:224 +msgid "User was not saved, check validation errors" +msgstr "" + +#: src/Shell/UsersShell.php:229 +msgid "Api token changed for user: {0}" +msgstr "" + +#: src/Shell/UsersShell.php:230 +msgid "New token: {0}" +msgstr "" + +#: src/Shell/UsersShell.php:245 msgid "User was activated: {0}" msgstr "" -#: Shell/UsersShell.php:224 +#: src/Shell/UsersShell.php:260 msgid "User was de-activated: {0}" msgstr "" -#: Shell/UsersShell.php:236 +#: src/Shell/UsersShell.php:272 msgid "Please enter a username or email." msgstr "" -#: Shell/UsersShell.php:244 +#: src/Shell/UsersShell.php:280 msgid "Please ask the user to check the email to continue with password reset process" msgstr "" -#: Shell/UsersShell.php:308 +#: src/Shell/UsersShell.php:350 msgid "Superuser added:" msgstr "" -#: Shell/UsersShell.php:310 +#: src/Shell/UsersShell.php:352 msgid "User added:" msgstr "" -#: Shell/UsersShell.php:312 +#: src/Shell/UsersShell.php:354 msgid "Id: {0}" msgstr "" -#: Shell/UsersShell.php:313 +#: src/Shell/UsersShell.php:355 msgid "Username: {0}" msgstr "" -#: Shell/UsersShell.php:314 +#: src/Shell/UsersShell.php:356 msgid "Email: {0}" msgstr "" -#: Shell/UsersShell.php:315 +#: src/Shell/UsersShell.php:357 msgid "Role: {0}" msgstr "" -#: Shell/UsersShell.php:316 +#: src/Shell/UsersShell.php:358 msgid "Password: {0}" msgstr "" -#: Shell/UsersShell.php:318 +#: src/Shell/UsersShell.php:360 msgid "User could not be added:" msgstr "" -#: Shell/UsersShell.php:321 +#: src/Shell/UsersShell.php:363 msgid "Field: {0} Error: {1}" msgstr "" -#: Shell/UsersShell.php:337 +#: src/Shell/UsersShell.php:379 msgid "The user was not found." msgstr "" -#: Shell/UsersShell.php:367 +#: src/Shell/UsersShell.php:414 msgid "The user {0} was not deleted. Please try again" msgstr "" -#: Shell/UsersShell.php:369 +#: src/Shell/UsersShell.php:416 msgid "The user {0} was deleted successfully" msgstr "" -#: Template/Email/html/reset_password.ctp:21 -#: Template/Email/html/social_account_validation.ctp:14 -#: Template/Email/html/validation.ctp:21 -#: Template/Email/text/reset_password.ctp:20 -#: Template/Email/text/social_account_validation.ctp:22 -#: Template/Email/text/validation.ctp:20 -msgid "Hi {0}" -msgstr "" - -#: Template/Email/html/reset_password.ctp:24 -msgid "Reset your password here" -msgstr "" - -#: Template/Email/html/reset_password.ctp:27 -#: Template/Email/html/social_account_validation.ctp:32 -#: Template/Email/html/validation.ctp:27 -msgid "If the link is not correctly displayed, please copy the following address in your web browser {0}" +#: src/View/Helper/UserHelper.php:50 +msgid "Sign in with" msgstr "" -#: Template/Email/html/reset_password.ctp:34 -#: Template/Email/html/social_account_validation.ctp:39 -#: Template/Email/html/validation.ctp:34 -#: Template/Email/text/reset_password.ctp:28 -#: Template/Email/text/social_account_validation.ctp:30 -#: Template/Email/text/validation.ctp:28 -msgid "Thank you" +#: src/View/Helper/UserHelper.php:113 +msgid "Logout" msgstr "" -#: Template/Email/html/social_account_validation.ctp:18 -msgid "Activate your social login here" +#: src/View/Helper/UserHelper.php:134 +msgid "Welcome, {0}" msgstr "" -#: Template/Email/html/validation.ctp:24 -msgid "Activate your account here" +#: src/View/Helper/UserHelper.php:165 +msgid "reCaptcha is not configured! Please configure Users.reCaptcha.key" msgstr "" -#: Template/Email/text/reset_password.ctp:22 -#: Template/Email/text/validation.ctp:22 -msgid "Please copy the following address in your web browser {0}" +#: src/View/Helper/UserHelper.php:218 +msgid "Connected with {0}" msgstr "" -#: Template/Email/text/social_account_validation.ctp:24 -msgid "Please copy the following address in your web browser to activate your social login {0}" +#: src/View/Helper/UserHelper.php:223 +msgid "Connect with {0}" msgstr "" -#: Template/Users/add.ctp:13 -#: Template/Users/edit.ctp:17 -#: Template/Users/index.ctp:13;26 -#: Template/Users/view.ctp:15 +#: templates/Users/add.php:13 +#: templates/Users/edit.php:17 +#: templates/Users/index.php:13 +#: templates/Users/index.php:26 +#: templates/Users/view.php:15 msgid "Actions" msgstr "" -#: Template/Users/add.ctp:15 -#: Template/Users/edit.ctp:28 -#: Template/Users/view.ctp:23 +#: templates/Users/add.php:15 +#: templates/Users/edit.php:28 +#: templates/Users/view.php:23 msgid "List Users" msgstr "" -#: Template/Users/add.ctp:21 -#: Template/Users/register.ctp:18 +#: templates/Users/add.php:21 +#: templates/Users/register.php:18 msgid "Add User" msgstr "" -#: Template/Users/add.ctp:23 -#: Template/Users/edit.ctp:36 -#: Template/Users/index.ctp:22 -#: Template/Users/login.ctp:20 -#: Template/Users/profile.ctp:30 -#: Template/Users/register.ctp:20 -#: Template/Users/view.ctp:33 +#: templates/Users/add.php:23 +#: templates/Users/edit.php:36 +#: templates/Users/index.php:22 +#: templates/Users/login.php:20 +#: templates/Users/profile.php:30 +#: templates/Users/register.php:20 +#: templates/Users/view.php:33 msgid "Username" msgstr "" -#: Template/Users/add.ctp:24 -#: Template/Users/edit.ctp:37 -#: Template/Users/index.ctp:23 -#: Template/Users/profile.ctp:32 -#: Template/Users/register.ctp:21 -#: Template/Users/view.ctp:35 +#: templates/Users/add.php:24 +#: templates/Users/edit.php:37 +#: templates/Users/index.php:23 +#: templates/Users/profile.php:32 +#: templates/Users/register.php:21 +#: templates/Users/view.php:35 msgid "Email" msgstr "" -#: Template/Users/add.ctp:25 -#: Template/Users/login.ctp:21 -#: Template/Users/register.ctp:22 +#: templates/Users/add.php:25 +#: templates/Users/login.php:21 +#: templates/Users/register.php:22 msgid "Password" msgstr "" -#: Template/Users/add.ctp:26 -#: Template/Users/edit.ctp:38 -#: Template/Users/index.ctp:24 -#: Template/Users/register.ctp:27 +#: templates/Users/add.php:26 +#: templates/Users/edit.php:38 +#: templates/Users/index.php:24 +#: templates/Users/register.php:28 msgid "First name" msgstr "" -#: Template/Users/add.ctp:27 -#: Template/Users/edit.ctp:39 -#: Template/Users/index.ctp:25 -#: Template/Users/register.ctp:28 +#: templates/Users/add.php:27 +#: templates/Users/edit.php:39 +#: templates/Users/index.php:25 +#: templates/Users/register.php:29 msgid "Last name" msgstr "" -#: Template/Users/add.ctp:30 -#: Template/Users/edit.ctp:54 -#: Template/Users/view.ctp:49;74 +#: templates/Users/add.php:30 +#: templates/Users/edit.php:54 +#: templates/Users/view.php:49 +#: templates/Users/view.php:74 msgid "Active" msgstr "" -#: Template/Users/add.ctp:34 -#: Template/Users/change_password.ctp:25 -#: Template/Users/edit.ctp:58 -#: Template/Users/register.ctp:37 -#: Template/Users/request_reset_password.ctp:8 -#: Template/Users/resend_token_validation.ctp:20 -#: Template/Users/social_email.ctp:19 +#: templates/Users/add.php:34 +#: templates/Users/change_password.php:25 +#: templates/Users/edit.php:58 +#: templates/Users/register.php:38 +#: templates/Users/request_reset_password.php:23 +#: templates/Users/resend_token_validation.php:20 +#: templates/Users/social_email.php:19 msgid "Submit" msgstr "" -#: Template/Users/change_password.ctp:5 +#: templates/Users/change_password.php:5 msgid "Please enter the new password" msgstr "" -#: Template/Users/change_password.ctp:10 +#: templates/Users/change_password.php:10 msgid "Current password" msgstr "" -#: Template/Users/change_password.ctp:16 +#: templates/Users/change_password.php:16 msgid "New password" msgstr "" -#: Template/Users/change_password.ctp:21 -#: Template/Users/register.ctp:25 +#: templates/Users/change_password.php:21 +#: templates/Users/register.php:26 msgid "Confirm password" msgstr "" -#: Template/Users/edit.ctp:22 -#: Template/Users/index.ctp:40 +#: templates/Users/edit.php:22 +#: templates/Users/index.php:40 msgid "Delete" msgstr "" -#: Template/Users/edit.ctp:24 -#: Template/Users/index.ctp:40 -#: Template/Users/view.ctp:21 +#: templates/Users/edit.php:24 +#: templates/Users/index.php:40 +#: templates/Users/view.php:21 msgid "Are you sure you want to delete # {0}?" msgstr "" -#: Template/Users/edit.ctp:34 -#: Template/Users/view.ctp:17 +#: templates/Users/edit.php:34 +#: templates/Users/view.php:17 msgid "Edit User" msgstr "" -#: Template/Users/edit.ctp:40 -#: Template/Users/view.ctp:43 +#: templates/Users/edit.php:40 +#: templates/Users/view.php:43 msgid "Token" msgstr "" -#: Template/Users/edit.ctp:42 +#: templates/Users/edit.php:42 msgid "Token expires" msgstr "" -#: Template/Users/edit.ctp:45 +#: templates/Users/edit.php:45 msgid "API token" msgstr "" -#: Template/Users/edit.ctp:48 +#: templates/Users/edit.php:48 msgid "Activation date" msgstr "" -#: Template/Users/edit.ctp:51 +#: templates/Users/edit.php:51 msgid "TOS date" msgstr "" -#: Template/Users/edit.ctp:64 +#: templates/Users/edit.php:64 msgid "Reset Google Authenticator Token" msgstr "" -#: Template/Users/edit.ctp:70 +#: templates/Users/edit.php:70 msgid "Are you sure you want to reset token for user \"{0}\"?" msgstr "" -#: Template/Users/index.ctp:15 +#: templates/Users/index.php:15 msgid "New {0}" msgstr "" -#: Template/Users/index.ctp:37 +#: templates/Users/index.php:37 msgid "View" msgstr "" -#: Template/Users/index.ctp:38 +#: templates/Users/index.php:38 msgid "Change password" msgstr "" -#: Template/Users/index.ctp:39 +#: templates/Users/index.php:39 msgid "Edit" msgstr "" -#: Template/Users/index.ctp:49 +#: templates/Users/index.php:49 msgid "previous" msgstr "" -#: Template/Users/index.ctp:51 +#: templates/Users/index.php:51 msgid "next" msgstr "" -#: Template/Users/login.ctp:19 +#: templates/Users/login.php:19 msgid "Please enter your username and password" msgstr "" -#: Template/Users/login.ctp:29 +#: templates/Users/login.php:29 msgid "Remember me" msgstr "" -#: Template/Users/login.ctp:37 +#: templates/Users/login.php:37 msgid "Register" msgstr "" -#: Template/Users/login.ctp:43 +#: templates/Users/login.php:43 msgid "Reset Password" msgstr "" -#: Template/Users/login.ctp:48 +#: templates/Users/login.php:48 msgid "Login" msgstr "" -#: Template/Users/profile.ctp:21 +#: templates/Users/profile.php:21 msgid "{0} {1}" msgstr "" -#: Template/Users/profile.ctp:27 +#: templates/Users/profile.php:27 msgid "Change Password" msgstr "" -#: Template/Users/profile.ctp:38 -#: Template/Users/view.ctp:68 +#: templates/Users/profile.php:38 +#: templates/Users/view.php:68 msgid "Social Accounts" msgstr "" -#: Template/Users/profile.ctp:42 -#: Template/Users/view.ctp:73 +#: templates/Users/profile.php:42 +#: templates/Users/view.php:73 msgid "Avatar" msgstr "" -#: Template/Users/profile.ctp:43 -#: Template/Users/view.ctp:72 +#: templates/Users/profile.php:43 +#: templates/Users/view.php:72 msgid "Provider" msgstr "" -#: Template/Users/profile.ctp:44 +#: templates/Users/profile.php:44 msgid "Link" msgstr "" -#: Template/Users/profile.ctp:51 +#: templates/Users/profile.php:51 msgid "Link to {0}" msgstr "" -#: Template/Users/register.ctp:30 +#: templates/Users/register.php:31 msgid "Accept TOS conditions?" msgstr "" -#: Template/Users/request_reset_password.ctp:5 -msgid "Please enter your email to reset your password" +#: templates/Users/request_reset_password.php:20 +msgid "Please enter your email or username to reset your password" msgstr "" -#: Template/Users/resend_token_validation.ctp:15 +#: templates/Users/resend_token_validation.php:15 msgid "Resend Validation email" msgstr "" -#: Template/Users/resend_token_validation.ctp:17 +#: templates/Users/resend_token_validation.php:17 msgid "Email or username" msgstr "" -#: Template/Users/verify.ctp:13 +#: templates/Users/u2f_authenticate.php:22 +#: templates/Users/webauthn2fa.php:25 +msgid "Verify your registered yubico key" +msgstr "" + +#: templates/Users/u2f_authenticate.php:23 +#: templates/Users/u2f_register.php:23 +#: templates/Users/webauthn2fa.php:20 +#: templates/Users/webauthn2fa.php:26 +msgid "Please insert and tap your yubico key" +msgstr "" + +#: templates/Users/u2f_authenticate.php:24 +#: templates/Users/webauthn2fa.php:27 +msgid "You can now finish the authentication process using the registered device." +msgstr "" + +#: templates/Users/u2f_authenticate.php:25 +#: templates/Users/webauthn2fa.php:22 +#: templates/Users/webauthn2fa.php:28 +msgid "When the YubiKey starts blinking, press the golden disc to activate it. Depending on the web browser you might need to confirm the use of extended information from the YubiKey." +msgstr "" + +#: templates/Users/u2f_authenticate.php:27 +#: templates/Users/u2f_register.php:27 +#: templates/Users/webauthn2fa.php:31 +msgid "Reload" +msgstr "" + +#: templates/Users/u2f_authenticate.php:51 +#: templates/Users/u2f_register.php:56 +msgid "Yubico key check has failed, please try again" +msgstr "" + +#: templates/Users/u2f_register.php:22 +#: templates/Users/webauthn2fa.php:19 +msgid "Registering your yubico key" +msgstr "" + +#: templates/Users/verify.php:13 msgid "Verification Code" msgstr "" -#: Template/Users/verify.ctp:15 +#: templates/Users/verify.php:15 msgid " Verify" msgstr "" -#: Template/Users/view.ctp:19 +#: templates/Users/view.php:19 msgid "Delete User" msgstr "" -#: Template/Users/view.ctp:24 +#: templates/Users/view.php:24 msgid "New User" msgstr "" -#: Template/Users/view.ctp:31 +#: templates/Users/view.php:31 msgid "Id" msgstr "" -#: Template/Users/view.ctp:37 +#: templates/Users/view.php:37 msgid "First Name" msgstr "" -#: Template/Users/view.ctp:39 +#: templates/Users/view.php:39 msgid "Last Name" msgstr "" -#: Template/Users/view.ctp:41 +#: templates/Users/view.php:41 msgid "Role" msgstr "" -#: Template/Users/view.ctp:45 +#: templates/Users/view.php:45 msgid "Api Token" msgstr "" -#: Template/Users/view.ctp:53 +#: templates/Users/view.php:53 msgid "Token Expires" msgstr "" -#: Template/Users/view.ctp:55 +#: templates/Users/view.php:55 msgid "Activation Date" msgstr "" -#: Template/Users/view.ctp:57 +#: templates/Users/view.php:57 msgid "Tos Date" msgstr "" -#: Template/Users/view.ctp:59;75 +#: templates/Users/view.php:59 +#: templates/Users/view.php:75 msgid "Created" msgstr "" -#: Template/Users/view.ctp:61;76 +#: templates/Users/view.php:61 +#: templates/Users/view.php:76 msgid "Modified" msgstr "" -#: View/Helper/UserHelper.php:46 -msgid "Sign in with" +#: templates/Users/webauthn2fa.php:8 +msgid "Two-factor authentication" msgstr "" -#: View/Helper/UserHelper.php:103 -msgid "Logout" +#: templates/Users/webauthn2fa.php:21 +msgid "In order to enable your YubiKey the first step is to perform a registration." msgstr "" -#: View/Helper/UserHelper.php:121 -msgid "Welcome, {0}" +#: templates/email/html/reset_password.php:14 +#: templates/email/html/social_account_validation.php:14 +#: templates/email/html/validation.php:13 +#: templates/email/text/reset_password.php:12 +#: templates/email/text/social_account_validation.php:12 +#: templates/email/text/validation.php:12 +msgid "Hi {0}" msgstr "" -#: View/Helper/UserHelper.php:151 -msgid "reCaptcha is not configured! Please configure Users.reCaptcha.key" +#: templates/email/html/reset_password.php:17 +msgid "Reset your password here" msgstr "" -#: View/Helper/UserHelper.php:215 -msgid "Connected with {0}" +#: templates/email/html/reset_password.php:20 +#: templates/email/html/social_account_validation.php:23 +#: templates/email/html/validation.php:19 +msgid "If the link is not correctly displayed, please copy the following address in your web browser {0}" msgstr "" -#: View/Helper/UserHelper.php:220 -msgid "Connect with {0}" +#: templates/email/html/reset_password.php:27 +#: templates/email/html/social_account_validation.php:30 +#: templates/email/html/validation.php:26 +#: templates/email/text/reset_password.php:20 +#: templates/email/text/social_account_validation.php:20 +#: templates/email/text/validation.php:20 +msgid "Thank you" +msgstr "" + +#: templates/email/html/social_account_validation.php:18 +msgid "Activate your social login here" +msgstr "" + +#: templates/email/html/validation.php:16 +msgid "Activate your account here" +msgstr "" + +#: templates/email/text/reset_password.php:14 +#: templates/email/text/validation.php:14 +msgid "Please copy the following address in your web browser {0}" +msgstr "" + +#: templates/email/text/social_account_validation.php:14 +msgid "Please copy the following address in your web browser to activate your social login {0}" msgstr "" diff --git a/src/Controller/Traits/Webauthn2faTrait.php b/src/Controller/Traits/Webauthn2faTrait.php index 301c08acc..93f9fe313 100644 --- a/src/Controller/Traits/Webauthn2faTrait.php +++ b/src/Controller/Traits/Webauthn2faTrait.php @@ -74,7 +74,7 @@ public function webauthn2faRegister(): \Cake\Http\Response ); } catch (\Throwable $e) { $user = $this->request->getSession()->read('Webauthn2fa.User'); - Log::debug(__('Register error with webauthn for user id: {0}', $user['id'] ?? 'empty')); + Log::debug(__d('cake_d_c/users','Register error with webauthn for user id: {0}', $user['id'] ?? 'empty')); throw $e; } } @@ -119,7 +119,7 @@ public function webauthn2faAuthenticate(): \Cake\Http\Response ])); } catch (\Throwable $e) { $user = $this->request->getSession()->read('Webauthn2fa.User'); - Log::debug(__('Register error with webauthn for user id: {0}', $user['id'] ?? 'empty')); + Log::debug(__d('cake_d_c/users','Register error with webauthn for user id: {0}', $user['id'] ?? 'empty')); throw $e; } } diff --git a/src/Loader/AuthenticationServiceLoader.php b/src/Loader/AuthenticationServiceLoader.php index 5ef982860..efb81c728 100644 --- a/src/Loader/AuthenticationServiceLoader.php +++ b/src/Loader/AuthenticationServiceLoader.php @@ -106,7 +106,7 @@ protected function _getItemLoadData($item, $key) $options = $item; if (!isset($options['className'])) { throw new \InvalidArgumentException( - __('Property {0}.className should be defined', $key) + __d('cake_d_c/users','Property {0}.className should be defined', $key) ); } $className = $options['className']; diff --git a/templates/Users/u2f_authenticate.php b/templates/Users/u2f_authenticate.php index 5b48555de..16ce1bf01 100644 --- a/templates/Users/u2f_authenticate.php +++ b/templates/Users/u2f_authenticate.php @@ -21,7 +21,7 @@

-

+

Html->link( __d('cake_d_c/users', 'Reload'), diff --git a/templates/Users/webauthn2fa.php b/templates/Users/webauthn2fa.php index ae1686af0..3c2b7c898 100644 --- a/templates/Users/webauthn2fa.php +++ b/templates/Users/webauthn2fa.php @@ -5,7 +5,7 @@ * @var string $username */ $this->Html->script('CakeDC/Users.webauthn.js', ['block' => true]); -$this->assign('title', __('Two-factor authentication')); +$this->assign('title', __d('cake_d_c/users','Two-factor authentication')); ?>

@@ -28,7 +28,7 @@

Html->link( - __('Reload'), + __d('cake_d_c/users','Reload'), ['action' => 'webauthn2fa'], ['class' => 'btn btn-primary'] )?>

From 5d5176c3e1b8446c42adf6fa5b97ac126c912345 Mon Sep 17 00:00:00 2001 From: Kevin Pfeifer Date: Fri, 11 Mar 2022 14:32:28 +0100 Subject: [PATCH 51/53] fix CS problems --- src/Controller/Traits/Webauthn2faTrait.php | 4 ++-- src/Loader/AuthenticationServiceLoader.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Controller/Traits/Webauthn2faTrait.php b/src/Controller/Traits/Webauthn2faTrait.php index 93f9fe313..034a17442 100644 --- a/src/Controller/Traits/Webauthn2faTrait.php +++ b/src/Controller/Traits/Webauthn2faTrait.php @@ -74,7 +74,7 @@ public function webauthn2faRegister(): \Cake\Http\Response ); } catch (\Throwable $e) { $user = $this->request->getSession()->read('Webauthn2fa.User'); - Log::debug(__d('cake_d_c/users','Register error with webauthn for user id: {0}', $user['id'] ?? 'empty')); + Log::debug(__d('cake_d_c/users', 'Register error with webauthn for user id: {0}', $user['id'] ?? 'empty')); throw $e; } } @@ -119,7 +119,7 @@ public function webauthn2faAuthenticate(): \Cake\Http\Response ])); } catch (\Throwable $e) { $user = $this->request->getSession()->read('Webauthn2fa.User'); - Log::debug(__d('cake_d_c/users','Register error with webauthn for user id: {0}', $user['id'] ?? 'empty')); + Log::debug(__d('cake_d_c/users', 'Register error with webauthn for user id: {0}', $user['id'] ?? 'empty')); throw $e; } } diff --git a/src/Loader/AuthenticationServiceLoader.php b/src/Loader/AuthenticationServiceLoader.php index efb81c728..c4d2b6958 100644 --- a/src/Loader/AuthenticationServiceLoader.php +++ b/src/Loader/AuthenticationServiceLoader.php @@ -106,7 +106,7 @@ protected function _getItemLoadData($item, $key) $options = $item; if (!isset($options['className'])) { throw new \InvalidArgumentException( - __d('cake_d_c/users','Property {0}.className should be defined', $key) + __d('cake_d_c/users', 'Property {0}.className should be defined', $key) ); } $className = $options['className']; From 771805e337a3d50d96a998fc56d12fe520d070db Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Fri, 18 Mar 2022 12:24:39 -0300 Subject: [PATCH 52/53] Updated doc `Using the user's email to login` Updated doc `Using the user's email to login` to include required config for 'Remember me'. Related to issue #992 --- Docs/Documentation/Configuration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Docs/Documentation/Configuration.md b/Docs/Documentation/Configuration.md index c502651e2..a7118a99b 100644 --- a/Docs/Documentation/Configuration.md +++ b/Docs/Documentation/Configuration.md @@ -174,6 +174,7 @@ user identify. Add this to your config/users.php: ```php 'Auth.Identifiers.Password.fields.username' => 'email', 'Auth.Authenticators.Form.fields.username' => 'email', +'Auth.Authenticators.Cookie.fields.username' => 'email', ``` * Override the login.php template to change the Form->control to "email". From b5993e1c65b99451869005542518e3f09fdfc4d3 Mon Sep 17 00:00:00 2001 From: TerryKern <56536101+TerryKern@users.noreply.github.com> Date: Mon, 21 Nov 2022 10:40:02 +0100 Subject: [PATCH 53/53] Correct casing of users.po in translation docs The users.po file which is used to overwrite the translations provided by the plugin should be lowercase. Seeing as though translation files are generally lowercase in CakePHP in addition to the fact that as soon as you deploy to a Linux environment this will bite you in the ass. --- Docs/Documentation/Translations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Docs/Documentation/Translations.md b/Docs/Documentation/Translations.md index 7576ed50e..d9cc74def 100644 --- a/Docs/Documentation/Translations.md +++ b/Docs/Documentation/Translations.md @@ -13,6 +13,6 @@ The Plugin is translated into several languages: * Turkish (tr_TR) by @sayinserdar * Ukrainian (uk) by @yarkm13 -**Note:** To overwrite the plugin translations, create a file inside your project 'resources/locales//{$lang}/' folder, with the name 'Users.po' and add the strings with the new translations. +**Note:** To overwrite the plugin translations, create a file inside your project 'resources/locales//{$lang}/' folder, with the name 'users.po' and add the strings with the new translations. Remember to clean the translations cache!