diff --git a/.semver b/.semver index 63e74f429..24895fae3 100644 --- a/.semver +++ b/.semver @@ -1,5 +1,5 @@ --- :major: 3 -:minor: 1 -:patch: 5 +:minor: 2 +:patch: 0 :special: '' diff --git a/Docs/Documentation/Configuration.md b/Docs/Documentation/Configuration.md index 95a1746ea..0dacc6307 100644 --- a/Docs/Documentation/Configuration.md +++ b/Docs/Documentation/Configuration.md @@ -38,6 +38,16 @@ Configure::write('OAuth.providers.twitter.options.clientSecret', 'YOUR APP SECRE Or use the config override option when loading the plugin (see above) +Configuration for reCaptcha +--------------------- +``` +Configure::write('Users.reCaptcha.key', 'YOUR RECAPTCHA KEY'); +Configure::write('Users.reCaptcha.secret', 'YOUR RECAPTCHA SECRET'); +Configure::write('Users.reCaptcha.registration', true); //enable on registration +Configure::write('Users.reCaptcha.login', true); //enable on login +``` + + Configuration options --------------------- diff --git a/Docs/Documentation/Installation.md b/Docs/Documentation/Installation.md index 0a4278478..35a6b8967 100644 --- a/Docs/Documentation/Installation.md +++ b/Docs/Documentation/Installation.md @@ -8,7 +8,7 @@ Composer composer require cakedc/users ``` -if you want to use social login features... +If you want to use social login features... ``` composer require league/oauth2-facebook:@stable @@ -25,6 +25,15 @@ login is disabled by default. Check the [Configuration](Configuration.md) page f Configure::write('Users.Social.login', true); //to enable social login ``` +If you want to use reCaptcha features... + +``` +composer require google/recaptcha:@stable +``` + +NOTE: you'll need to configure the reCaptcha key and secret, check the [Configuration](Configuration.md) +page for more details. + Creating Required Tables ------------------------ If you want to use the Users tables to store your users and social accounts: diff --git a/Docs/Documentation/Overview.md b/Docs/Documentation/Overview.md index 0e98fc30e..4e2e392ba 100644 --- a/Docs/Documentation/Overview.md +++ b/Docs/Documentation/Overview.md @@ -14,5 +14,5 @@ The plugin itself is already capable of: * Simple roles management * Simple Rbac and SuperUser Authorize * RememberMe using cookie feature -* reCaptcha for user registration +* reCaptcha for user registration and login diff --git a/Docs/Documentation/UserHelper.md b/Docs/Documentation/UserHelper.md index 3d36e41df..2191f4c1f 100644 --- a/Docs/Documentation/UserHelper.md +++ b/Docs/Documentation/UserHelper.md @@ -57,12 +57,10 @@ It displays a welcome message for the user including the name and a link to the $this->User->welcome(); ``` -reCAPTCHA +reCaptcha ----------------- -If you have configured reCAPTCHA for registration and have the proper key/secret configured then you will see the reCAPTCHA in registration page automatically. - -You could also use it in another templates with the following methods: +Handles the reCaptcha input display: ```php $this->User->addReCaptchaScript(); @@ -70,4 +68,4 @@ $this->User->addReCaptchaScript(); $this->User->addReCaptcha(); ``` -Note that the script is added automatically if the feature is enabled in config. +Note reCaptcha script is added to script block when `addReCaptcha` method is called. diff --git a/config/routes.php b/config/routes.php index 191175c95..6c4331798 100644 --- a/config/routes.php +++ b/config/routes.php @@ -15,9 +15,6 @@ $routes->fallbacks('DashedRoute'); }); -//if (!Configure::check('OAuth.path')) { -// Configure::load('CakeDC/Users.users'); -//} Router::connect('/auth/twitter', [ 'plugin' => 'CakeDC/Users', 'controller' => 'Users', diff --git a/config/users.php b/config/users.php index 3bde0bae0..52fa057ee 100644 --- a/config/users.php +++ b/config/users.php @@ -34,6 +34,16 @@ //determines if the reCaptcha is enabled for registration 'reCaptcha' => true, ], + 'reCaptcha' => [ + //reCaptcha key goes here + 'key' => null, + //reCaptcha secret + 'secret' => null, + //use reCaptcha in registration + 'registration' => false, + //use reCaptcha in login, valid values are false, true + 'login' => false, + ], 'Tos' => [ //determines if the user should include tos accepted 'required' => true, @@ -109,26 +119,31 @@ 'className' => 'League\OAuth2\Client\Provider\Facebook', 'options' => [ 'graphApiVersion' => 'v2.5', - 'redirectUri' => Router::url('/auth/facebook', true) + 'redirectUri' => Router::fullBaseUrl() . '/auth/facebook', + ] + ], + 'twitter' => [ + 'options' => [ + 'redirectUri' => Router::fullBaseUrl() . '/auth/twitter', ] ], 'linkedIn' => [ 'className' => 'League\OAuth2\Client\Provider\LinkedIn', 'options' => [ - 'redirectUri' => Router::url('/auth/linkedIn', true) + 'redirectUri' => Router::fullBaseUrl() . '/auth/linkedIn', ] ], 'instagram' => [ 'className' => 'League\OAuth2\Client\Provider\Instagram', 'options' => [ - 'redirectUri' => Router::url('/auth/instagram', true) + 'redirectUri' => Router::fullBaseUrl() . '/auth/instagram', ] ], 'google' => [ 'className' => 'League\OAuth2\Client\Provider\Google', 'options' => [ 'userFields' => ['url', 'aboutMe'], - 'redirectUri' => Router::url('/auth/google', true) + 'redirectUri' => Router::fullBaseUrl() . '/auth/google', ] ], ], diff --git a/src/Auth/Social/Mapper/Facebook.php b/src/Auth/Social/Mapper/Facebook.php index 0a2193984..95f3c52c3 100644 --- a/src/Auth/Social/Mapper/Facebook.php +++ b/src/Auth/Social/Mapper/Facebook.php @@ -39,6 +39,6 @@ class Facebook extends AbstractMapper */ protected function _avatar() { - return self::FB_GRAPH_BASE_URL . Hash::get($this->_rawData, 'id') . '/picture?type=normal'; + return self::FB_GRAPH_BASE_URL . Hash::get($this->_rawData, 'id') . '/picture?type=large'; } } diff --git a/src/Auth/SocialAuthenticate.php b/src/Auth/SocialAuthenticate.php index a18c7e5e9..26cc411ed 100755 --- a/src/Auth/SocialAuthenticate.php +++ b/src/Auth/SocialAuthenticate.php @@ -354,7 +354,12 @@ public function getUser(Request $request) } $provider = $this->_getProviderName($request); - $user = $this->_mapUser($provider, $rawData); + try { + $user = $this->_mapUser($provider, $rawData); + } catch (MissingProviderException $ex) { + $request->session()->delete(Configure::read('Users.Key.Session.social')); + throw $ex; + } if ($user['provider'] === SocialAccountsTable::PROVIDER_TWITTER) { $request->session()->write(Configure::read('Users.Key.Session.social'), $user); } diff --git a/src/Controller/Traits/LoginTrait.php b/src/Controller/Traits/LoginTrait.php index bf00fca59..9d9e0a19b 100644 --- a/src/Controller/Traits/LoginTrait.php +++ b/src/Controller/Traits/LoginTrait.php @@ -70,10 +70,11 @@ public function twitterLogin() } else { $temporaryCredentials = $server->getTemporaryCredentials(); $this->request->session()->write('temporary_credentials', $temporaryCredentials); - $server->authorize($temporaryCredentials); - return $this->response; + $url = $server->getAuthorizationUrl($temporaryCredentials); + return $this->redirect($url); } } + /** * @param Event $event event * @return void @@ -151,18 +152,39 @@ public function login() $socialLogin = $this->_isSocialLogin(); + if ($this->request->is('post')) { + if (!$this->_checkReCaptcha()) { + $this->Flash->error(__d('Users', 'Invalid reCaptcha')); + return; + } + $user = $this->Auth->identify(); + return $this->_afterIdentifyUser($user, $socialLogin); + } if (!$this->request->is('post') && !$socialLogin) { if ($this->Auth->user()) { $msg = __d('Users', 'You are already logged in'); $this->Flash->error($msg); - return $this->redirect($this->referer()); + $url = $this->Auth->redirectUrl(); + return $this->redirect($url); } - return; } - if ($this->request->is('post')) { - $user = $this->Auth->identify(); - return $this->_afterIdentifyUser($user, $socialLogin); + } + + /** + * Check reCaptcha if enabled for login + * + * @return bool + */ + protected function _checkReCaptcha() + { + if (!Configure::read('Users.reCaptcha.login')) { + return true; } + + return $this->validateReCaptcha( + $this->request->data('g-recaptcha-response'), + $this->request->clientIp() + ); } /** diff --git a/src/Controller/Traits/PasswordManagementTrait.php b/src/Controller/Traits/PasswordManagementTrait.php index b7045ce57..5c76767d1 100644 --- a/src/Controller/Traits/PasswordManagementTrait.php +++ b/src/Controller/Traits/PasswordManagementTrait.php @@ -11,6 +11,7 @@ namespace CakeDC\Users\Controller\Traits; +use CakeDC\Users\Exception\UserNotActiveException; use CakeDC\Users\Exception\UserNotFoundException; use CakeDC\Users\Exception\WrongPasswordException; use Cake\Core\Configure; @@ -101,6 +102,7 @@ public function requestResetPassword() 'expiration' => Configure::read('Users.Token.expiration'), 'checkActive' => false, 'sendEmail' => true, + 'ensureActive' => true ]); if ($resetUser) { $msg = __d('Users', 'Please check your email to continue with password reset process'); @@ -112,6 +114,8 @@ public function requestResetPassword() return $this->redirect(['action' => 'login']); } catch (UserNotFoundException $exception) { $this->Flash->error(__d('Users', 'User {0} was not found', $reference)); + } catch (UserNotActiveException $exception) { + $this->Flash->error(__d('Users', 'The user is not active')); } catch (Exception $exception) { $this->Flash->error(__d('Users', 'Token could not be reset')); } diff --git a/src/Controller/Traits/ReCaptchaTrait.php b/src/Controller/Traits/ReCaptchaTrait.php index 825e410f8..44359a202 100644 --- a/src/Controller/Traits/ReCaptchaTrait.php +++ b/src/Controller/Traits/ReCaptchaTrait.php @@ -12,7 +12,6 @@ namespace CakeDC\Users\Controller\Traits; use Cake\Core\Configure; -use ReCaptcha\ReCaptcha; /** * Covers registration features and email token validation @@ -30,13 +29,12 @@ trait ReCaptchaTrait */ public function validateReCaptcha($recaptchaResponse, $clientIp) { - $validReCaptcha = true; $recaptcha = $this->_getReCaptchaInstance(); if (!empty($recaptcha)) { $response = $recaptcha->verify($recaptchaResponse, $clientIp); - $validReCaptcha = $response->isSuccess(); + return $response->isSuccess(); } - return $validReCaptcha; + return false; } /** @@ -46,10 +44,9 @@ public function validateReCaptcha($recaptchaResponse, $clientIp) */ protected function _getReCaptchaInstance() { - $useReCaptcha = (bool)Configure::read('Users.Registration.reCaptcha'); - $reCaptchaSecret = Configure::read('reCaptcha.secret'); - if ($useReCaptcha && !empty($reCaptchaSecret)) { - return new ReCaptcha($reCaptchaSecret); + $reCaptchaSecret = Configure::read('Users.reCaptcha.secret'); + if (!empty($reCaptchaSecret)) { + return new \ReCaptcha\ReCaptcha($reCaptchaSecret); } return null; } diff --git a/src/Controller/Traits/RegisterTrait.php b/src/Controller/Traits/RegisterTrait.php index 822618cd9..1aa65882f 100644 --- a/src/Controller/Traits/RegisterTrait.php +++ b/src/Controller/Traits/RegisterTrait.php @@ -24,7 +24,6 @@ trait RegisterTrait { use PasswordManagementTrait; - use ReCaptchaTrait; /** * Register a new user @@ -70,9 +69,8 @@ public function register() return; } - $validPost = $this->_validateRegisterPost(); - if (!$validPost) { - $this->Flash->error(__d('Users', 'The reCaptcha could not be validated')); + if (!$this->_validateRegisterPost()) { + $this->Flash->error(__d('Users', 'Invalid reCaptcha')); return; } @@ -92,14 +90,13 @@ public function register() */ protected function _validateRegisterPost() { - if (!Configure::read('Users.Registration.reCaptcha')) { + if (!Configure::read('Users.reCaptcha.registration')) { return true; } - $validReCaptcha = $this->validateReCaptcha( + return $this->validateReCaptcha( $this->request->data('g-recaptcha-response'), $this->request->clientIp() ); - return $validReCaptcha; } /** diff --git a/src/Controller/UsersController.php b/src/Controller/UsersController.php index 45cd15827..72235a8fc 100644 --- a/src/Controller/UsersController.php +++ b/src/Controller/UsersController.php @@ -15,6 +15,7 @@ use CakeDC\Users\Controller\Component\UsersAuthComponent; use CakeDC\Users\Controller\Traits\LoginTrait; use CakeDC\Users\Controller\Traits\ProfileTrait; +use CakeDC\Users\Controller\Traits\ReCaptchaTrait; use CakeDC\Users\Controller\Traits\RegisterTrait; use CakeDC\Users\Controller\Traits\SimpleCrudTrait; use CakeDC\Users\Controller\Traits\SocialTrait; @@ -31,6 +32,7 @@ class UsersController extends AppController { use LoginTrait; use ProfileTrait; + use ReCaptchaTrait; use RegisterTrait; use SimpleCrudTrait; use SocialTrait; diff --git a/src/Locale/Users.pot b/src/Locale/Users.pot index 551c6cda2..3efabc9f5 100644 --- a/src/Locale/Users.pot +++ b/src/Locale/Users.pot @@ -243,7 +243,7 @@ msgid "Email not present" msgstr "" #: Model/Table/UsersTable.php:541 -msgid "{0}Your account validation link" +msgid "Your account validation link" msgstr "" #: Model/Table/UsersTable.php:561 diff --git a/src/Locale/es/Users.mo b/src/Locale/es/Users.mo new file mode 100644 index 000000000..421ce4ba1 Binary files /dev/null and b/src/Locale/es/Users.mo differ diff --git a/src/Locale/es/Users.po b/src/Locale/es/Users.po new file mode 100644 index 000000000..70e84f892 --- /dev/null +++ b/src/Locale/es/Users.po @@ -0,0 +1,540 @@ +# LANGUAGE translation of CakePHP Application +# Copyright 2010 - 2015, Cake Development Corporation (http://cakedc.com) +# +msgid "" +msgstr "" +"Project-Id-Version: CakeDC Users\n" +"POT-Creation-Date: 2016-03-07 20:35+0000\n" +"PO-Revision-Date: 2016-04-15 17:47+0100\n" +"Language-Team: CakeDC \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 1.8.7\n" +"Last-Translator: \n" +"Language: es_ES\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: Auth/SimpleRbacAuthorize.php:132 +msgid "" +"Missing configuration file: \"config/{0}.php\". Using default permissions" +msgstr "" +"Falta el archivo de configuración: \"config/{0}.php\". Utilizando permisos " +"predeterminados" + +#: Controller/SocialAccountsController.php:52 +msgid "Account validated successfully" +msgstr "Cuenta validada correctamente" + +#: Controller/SocialAccountsController.php:54 +msgid "Account could not be validated" +msgstr "No se pudo validar la cuenta" + +#: Controller/SocialAccountsController.php:57 +msgid "Invalid token and/or social account" +msgstr "Token o cuenta social incorrecta" + +#: Controller/SocialAccountsController.php:59 +msgid "SocialAccount already active" +msgstr "Cuenta social ya activa" + +#: Controller/SocialAccountsController.php:61 +msgid "Social Account could not be validated" +msgstr "No se pudo validar la cuenta social " + +#: Controller/SocialAccountsController.php:79 +msgid "Email sent successfully" +msgstr "Email enviado correctamente" + +#: Controller/SocialAccountsController.php:81 +msgid "Email could not be sent" +msgstr "No se puede enviar el email" + +#: Controller/SocialAccountsController.php:84 +msgid "Invalid account" +msgstr "Cuenta inválida" + +#: Controller/SocialAccountsController.php:86 +msgid "Social Account already active" +msgstr "Cuenta social ya activa" + +#: Controller/SocialAccountsController.php:88 +msgid "Email could not be resent" +msgstr "No se puede reenviar el Email" + +#: Controller/Component/RememberMeComponent.php:70 +msgid "Invalid app salt, app salt must be at least 256 bits (32 bytes) long" +msgstr "App salt inválido, debe contener al menos 256 bits (32 bytes)" + +#: Controller/Component/UsersAuthComponent.php:156 +msgid "You can't enable email validation workflow if use_email is false" +msgstr "" +"No es posible activar el flujo de trabajo para la validación de email si " +"use_mail es falso" + +#: Controller/Traits/LoginTrait.php:55 +msgid "" +"The social account is not active. Please check your email for instructions. " +"{0}" +msgstr "" +"La cuenta social está inactiva. Por favor, compruebe las instrucciones en su " +"correo. {0}" + +#: Controller/Traits/LoginTrait.php:58 Template/Users/social_email.ctp:16 +msgid "Please enter your email" +msgstr "Por favor, introduzca su email" + +#: Controller/Traits/LoginTrait.php:82 +msgid "Username or password is incorrect" +msgstr "Usuario o contraseña incorrecta" + +#: Controller/Traits/LoginTrait.php:93 +msgid "There was an error associating your social network account" +msgstr "Hubo un error al asociar su cuenta de la red social" + +#: Controller/Traits/LoginTrait.php:113 +msgid "You've successfully logged out" +msgstr "Ha cerrado sesión correctamente" + +#: Controller/Traits/PasswordManagementTrait.php:53 +msgid "Password has been changed successfully" +msgstr "Contraseña cambiada correctamente" + +#: Controller/Traits/PasswordManagementTrait.php:56;63 +msgid "Password could not be changed" +msgstr "No es posible cambiar la contraseña" + +#: Controller/Traits/PasswordManagementTrait.php:59 +#: Controller/Traits/ProfileTrait.php:46 +msgid "User was not found" +msgstr "Usuario no encontrado" + +#: Controller/Traits/PasswordManagementTrait.php:61 +msgid "The current password does not match" +msgstr "La contraseña actual no coincide" + +#: Controller/Traits/PasswordManagementTrait.php:93 +msgid "Please check your email to continue with password reset process" +msgstr "" +"Por favor, compruebe su correo para continuar con el proceso de " +"restablecimiento de contraseña" + +#: Controller/Traits/PasswordManagementTrait.php:96 +msgid "The password token could not be generated. Please try again" +msgstr "" +"No se pudo generar el token de contraseña. Por favor, inténtelo de nuevo" + +#: Controller/Traits/PasswordManagementTrait.php:101 +#: Controller/Traits/UserValidationTrait.php:94 +msgid "User {0} was not found" +msgstr "Usuario {0} no encontrado" + +#: Controller/Traits/PasswordManagementTrait.php:103 +#: Controller/Traits/UserValidationTrait.php:90;98 +msgid "Token could not be reset" +msgstr "No se puede restablecer el token" + +#: Controller/Traits/RegisterTrait.php:66 +msgid "The user could not be saved" +msgstr "No se ha podido guardar el usuario" + +#: Controller/Traits/RegisterTrait.php:82 +msgid "You have registered successfully, please log in" +msgstr "Se ha registrado correctamente, por favor ingrese" + +#: Controller/Traits/RegisterTrait.php:84 +msgid "Please validate your account before log in" +msgstr "Por favor, valide su cuenta antes de ingresar" + +#: Controller/Traits/SimpleCrudTrait.php:75;103 +msgid "The {0} has been saved" +msgstr "El {0} ha sido guardado" + +#: Controller/Traits/SimpleCrudTrait.php:78;106 +msgid "The {0} could not be saved" +msgstr "El {0} no ha podido guardarse" + +#: Controller/Traits/SimpleCrudTrait.php:125 +msgid "The {0} has been deleted" +msgstr "El {0} ha sido eliminado" + +#: Controller/Traits/SimpleCrudTrait.php:127 +msgid "The {0} could not be deleted" +msgstr "El {0} no ha podido eliminarse" + +#: Controller/Traits/UserValidationTrait.php:43 +msgid "User account validated successfully" +msgstr "Cuenta de usuario validada correctamente" + +#: Controller/Traits/UserValidationTrait.php:45 +msgid "User account could not be validated" +msgstr "No se pudo validar la cuenta de usuario" + +#: Controller/Traits/UserValidationTrait.php:48 +msgid "User already active" +msgstr "Usuario ya activo" + +#: Controller/Traits/UserValidationTrait.php:54 +msgid "Reset password token was validated successfully" +msgstr "Restablecimiento del token de contraseña validado correctamente" + +#: Controller/Traits/UserValidationTrait.php:58 +msgid "Reset password token could not be validated" +msgstr "Restablecimiento del token de contraseña no pudo validarse" + +#: Controller/Traits/UserValidationTrait.php:65 +msgid "Invalid token and/or email" +msgstr "Token y/o email inválido" + +#: Controller/Traits/UserValidationTrait.php:67 +msgid "Token already expired" +msgstr "Token ya expirado" + +#: Controller/Traits/UserValidationTrait.php:69 +msgid "Invalid validation type" +msgstr "Tipo de validación inválido" + +#: Controller/Traits/UserValidationTrait.php:88 +msgid "Token has been reset successfully. Please check your email." +msgstr "" +"Se ha restablecido el token correctamente. Por favor compruebe su email." + +#: Controller/Traits/UserValidationTrait.php:96 +msgid "User {0} is already active" +msgstr "El usuario {0} ya está activo" + +#: Model/Table/SocialAccountsTable.php:208 +msgid "{0}Your social account validation link" +msgstr "{0} Enlace de validación de su cuenta social" + +#: Model/Table/SocialAccountsTable.php:235;263 +msgid "Account already validated" +msgstr "Cuenta ya activada" + +#: Model/Table/SocialAccountsTable.php:238;266 +msgid "Account not found for the given token and email." +msgstr "Cuenta no encontrada para el token y email proporcionado" + +#: Model/Table/UsersTable.php:84 +msgid "Your password does not match your confirm password. Please try again" +msgstr "" +"Su contraseña y la comprobación no concuerdan. Por favor inténtelo de nuevo" + +#: Model/Table/UsersTable.php:178 +msgid "Username already exists" +msgstr "Nombre de usuario ya existente" + +#: Model/Table/UsersTable.php:184 +msgid "Email already exists" +msgstr "Email ya existente" + +#: Model/Table/UsersTable.php:213 +msgid "The \"tos\" property is not present" +msgstr "La propiedad \"tos\" no está presente" + +#: Model/Table/UsersTable.php:271 +msgid "Token has already expired user with no token" +msgstr "El token ha expirado usuario sin token" + +#: Model/Table/UsersTable.php:274 +msgid "User not found for the given token and email." +msgstr "Usuario no encontrado para el token y email proporcionado" + +#: Model/Table/UsersTable.php:341 +msgid "User not found" +msgstr "Usuario no encontrado" + +#: Model/Table/UsersTable.php:368 +msgid "+ {0} secs" +msgstr "+ {0} secs" + +#: Model/Table/UsersTable.php:420 +msgid "Unable to login user with reference {0}" +msgstr "No se puede iniciar sesión con el usuario con referencia {0}" + +#: Model/Table/UsersTable.php:453 +msgid "Email not present" +msgstr "No se encuentra el email" + +#: Model/Table/UsersTable.php:541 +msgid "Your account validation link" +msgstr "Enlace para validar su cuenta" + +#: Model/Table/UsersTable.php:561 +msgid "{0}Your reset password link" +msgstr "{0} Enlace para restablecer su contraseña" + +#: Model/Table/UsersTable.php:585 +msgid "The old password does not match" +msgstr "La antigua contraseña no coincide" + +#: 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 "Hola {0}" + +#: Template/Email/html/reset_password.ctp:24 +msgid "Reset your password here" +msgstr "Restablezca su contraseña aquí" + +#: 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 "" +"Si el enlace no se muestra correctamente, por favor copie la siguiente " +"dirección en su navegador web {0}" + +#: Template/Email/html/reset_password.ctp:30 +#: Template/Email/html/social_account_validation.ctp:35 +#: Template/Email/html/validation.ctp:30 +#: Template/Email/text/reset_password.ctp:24 +#: Template/Email/text/social_account_validation.ctp:26 +#: Template/Email/text/validation.ctp:24 +msgid "Thank you" +msgstr "Gracias" + +#: Template/Email/html/social_account_validation.ctp:18 +msgid "Activate your social login here" +msgstr "Active su acceso social aquí" + +#: Template/Email/html/validation.ctp:24 +msgid "Activate your account here" +msgstr "Active su cuenta aquí" + +#: 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 "Por favor copie la siguiente dirección en su navegador web {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 "" +"Por favor copie la siguiente dirección en su navegador web para activar su " +"acceso social {0}" + +#: Template/Users/add.ctp:13 Template/Users/edit.ctp:13 +#: Template/Users/index.ctp:13;26 Template/Users/view.ctp:13;77 +msgid "Actions" +msgstr "Acciones" + +#: Template/Users/add.ctp:15 Template/Users/edit.ctp:21 +#: Template/Users/view.ctp:17 +msgid "List Users" +msgstr "Listar Usuarios" + +#: Template/Users/add.ctp:16 Template/Users/edit.ctp:22 +#: Template/Users/view.ctp:19 +msgid "List Accounts" +msgstr "Listar Cuentas" + +#: Template/Users/add.ctp:22 Template/Users/register.ctp:15 +msgid "Add User" +msgstr "Añadir Usuario" + +#: Template/Users/add.ctp:32 Template/Users/change_password.ctp:13 +#: Template/Users/edit.ctp:42 Template/Users/register.ctp:26 +#: Template/Users/request_reset_password.ctp:8 +#: Template/Users/resend_token_validation.ctp:20 +#: Template/Users/social_email.ctp:19 +msgid "Submit" +msgstr "Enviar" + +#: Template/Users/change_password.ctp:5 +msgid "Please enter the new password" +msgstr "Por favor introduzca la nueva contraseña" + +#: Template/Users/change_password.ctp:7 +msgid "Current password" +msgstr "Contraseña actual" + +#: Template/Users/edit.ctp:16 Template/Users/index.ctp:40 +#: Template/Users/view.ctp:99 +msgid "Delete" +msgstr "Eliminar" + +#: Template/Users/edit.ctp:18 Template/Users/index.ctp:40 +#: Template/Users/view.ctp:16;99 +msgid "Are you sure you want to delete # {0}?" +msgstr "¿Está seguro que quiere eliminar # {0}?" + +#: Template/Users/edit.ctp:28 Template/Users/view.ctp:15 +msgid "Edit User" +msgstr "Editar Usuario" + +#: Template/Users/index.ctp:15 +msgid "New {0}" +msgstr "Nuevo {0}" + +#: Template/Users/index.ctp:37 Template/Users/view.ctp:95 +msgid "View" +msgstr "Ver" + +#: Template/Users/index.ctp:38 +msgid "Change password" +msgstr "Cambiar contraseña" + +#: Template/Users/index.ctp:39 Template/Users/view.ctp:97 +msgid "Edit" +msgstr "Editar" + +#: Template/Users/index.ctp:49 +msgid "previous" +msgstr "anterior" + +#: Template/Users/index.ctp:51 +msgid "next" +msgstr "siguiente" + +#: Template/Users/login.ctp:19 +msgid "Please enter your username and password" +msgstr "Por favor introduzca su usuario y contraseña" + +#: Template/Users/login.ctp:26 +msgid "Remember me" +msgstr "Recordarme" + +#: Template/Users/login.ctp:49 +msgid "Login" +msgstr "Ingresar" + +#: Template/Users/profile.ctp:18 +msgid "{0} {1}" +msgstr "{0} {1}" + +#: Template/Users/profile.ctp:23 +msgid "Change Password" +msgstr "Cambiar contraseña" + +#: Template/Users/profile.ctp:26 Template/Users/view.ctp:28;68 +msgid "Username" +msgstr "Usuario" + +#: Template/Users/profile.ctp:28 Template/Users/view.ctp:30 +msgid "Email" +msgstr "Email" + +#: Template/Users/profile.ctp:33 +msgid "Social Accounts" +msgstr "Cuentas sociales" + +#: Template/Users/profile.ctp:37 Template/Users/view.ctp:70 +msgid "Avatar" +msgstr "Avatar" + +#: Template/Users/profile.ctp:38 Template/Users/view.ctp:67 +msgid "Provider" +msgstr "Proveedor" + +#: Template/Users/profile.ctp:39 +msgid "Link" +msgstr "Enlace" + +#: Template/Users/register.ctp:23 +msgid "Accept TOS conditions?" +msgstr "¿Acepta las condiciones del servicios?" + +#: Template/Users/request_reset_password.ctp:5 +msgid "Please enter your email to reset your password" +msgstr "Por favor introduzca su email para restablecer su contraseña" + +#: Template/Users/resend_token_validation.ctp:15 +msgid "Resend Validation email" +msgstr "Reenviar email de validación" + +#: Template/Users/resend_token_validation.ctp:17 +msgid "Email or username" +msgstr "Email o usuario" + +#: Template/Users/view.ctp:16 +msgid "Delete User" +msgstr "Eliminar Usuario" + +#: Template/Users/view.ctp:18 +msgid "New User" +msgstr "Nuevo Usuario" + +#: Template/Users/view.ctp:26;65 +msgid "Id" +msgstr "Id" + +#: Template/Users/view.ctp:32 +msgid "First Name" +msgstr "Nombre" + +#: Template/Users/view.ctp:34 +msgid "Last Name" +msgstr "Apellidos" + +#: Template/Users/view.ctp:36;71 +msgid "Token" +msgstr "Token" + +#: Template/Users/view.ctp:38 +msgid "Api Token" +msgstr "Api Token" + +#: Template/Users/view.ctp:42;73 +msgid "Active" +msgstr "Activo" + +#: Template/Users/view.ctp:46;72 +msgid "Token Expires" +msgstr "Token caduca" + +#: Template/Users/view.ctp:48 +msgid "Activation Date" +msgstr "Fecha de activación" + +#: Template/Users/view.ctp:50 +msgid "Tos Date" +msgstr "Fecha Tos" + +#: Template/Users/view.ctp:52;75 +msgid "Created" +msgstr "Creado" + +#: Template/Users/view.ctp:54;76 +msgid "Modified" +msgstr "Modificado" + +#: Template/Users/view.ctp:61 +msgid "Related Accounts" +msgstr "Cuentas relacionadas" + +#: Template/Users/view.ctp:66 +msgid "User Id" +msgstr "Id Usuario" + +#: Template/Users/view.ctp:69 +msgid "Reference" +msgstr "Referencia" + +#: Template/Users/view.ctp:74 +msgid "Data" +msgstr "Datos" + +#: View/Helper/UserHelper.php:43 +msgid "Sign in with Facebook" +msgstr "Ingresar con Facebook" + +#: View/Helper/UserHelper.php:57 +msgid "Sign in with Twitter" +msgstr "Ingresar con Twitter" + +#: View/Helper/UserHelper.php:71 +msgid "Logout" +msgstr "Salir" + +#: View/Helper/UserHelper.php:109 +msgid "Welcome, {0}" +msgstr "Bienvenido, {0}" diff --git a/src/Model/Behavior/PasswordBehavior.php b/src/Model/Behavior/PasswordBehavior.php index 4f13207f7..0169fabdf 100644 --- a/src/Model/Behavior/PasswordBehavior.php +++ b/src/Model/Behavior/PasswordBehavior.php @@ -13,10 +13,12 @@ use CakeDC\Users\Email\EmailSender; use CakeDC\Users\Exception\UserAlreadyActiveException; +use CakeDC\Users\Exception\UserNotActiveException; use CakeDC\Users\Exception\UserNotFoundException; use CakeDC\Users\Exception\WrongPasswordException; use CakeDC\Users\Model\Behavior\Behavior; use Cake\Datasource\EntityInterface; +use Cake\Datasource\Exception\RecordNotFoundException; use Cake\Mailer\Email; use Cake\Utility\Hash; use InvalidArgumentException; @@ -66,11 +68,16 @@ public function resetToken($reference, array $options = []) } if (Hash::get($options, 'checkActive')) { if ($user->active) { - throw new UserAlreadyActiveException("User account already validated"); + throw new UserAlreadyActiveException(__d('Users', "User account already validated")); } $user->active = false; $user->activation_date = null; } + if (Hash::get($options, 'ensureActive')) { + if (!$user['active']) { + throw new UserNotActiveException(__d('Users', "User not active")); + } + } $user->updateToken($expiration); $saveResult = $this->_table->save($user); $template = !empty($options['emailTemplate']) ? $options['emailTemplate'] : 'CakeDC/Users.reset_password'; @@ -100,9 +107,13 @@ protected function _getUser($reference) */ public function changePassword(EntityInterface $user) { - $currentUser = $this->_table->get($user->id, [ - 'contain' => [] - ]); + try { + $currentUser = $this->_table->get($user->id, [ + 'contain' => [] + ]); + } catch (RecordNotFoundException $e) { + throw new UserNotFoundException(__d('Users', "User not found")); + } if (!empty($user->current_password)) { if (!$user->checkPassword($user->current_password, $currentUser->password)) { diff --git a/src/Model/Behavior/SocialBehavior.php b/src/Model/Behavior/SocialBehavior.php index f2f50ca05..319340c1f 100644 --- a/src/Model/Behavior/SocialBehavior.php +++ b/src/Model/Behavior/SocialBehavior.php @@ -68,7 +68,6 @@ public function socialLogin(array $data, array $options) $existingAccount->$user ]); } - } else { throw new AccountNotActiveException([ $existingAccount->provider, @@ -118,10 +117,10 @@ protected function _createSocialUser($data, $options = []) * @param string $validateEmail email to validate. * @param string $tokenExpiration token_expires data. * @return EntityInterface + * @todo refactor */ protected function _populateUser($data, $existingUser, $useEmail, $validateEmail, $tokenExpiration) { - $accountData['provider'] = Hash::get($data, 'provider'); $accountData['username'] = Hash::get($data, 'username'); $accountData['reference'] = Hash::get($data, 'id'); $accountData['avatar'] = Hash::get($data, 'avatar'); @@ -140,6 +139,7 @@ protected function _populateUser($data, $existingUser, $useEmail, $validateEmail } $accountData['data'] = serialize(Hash::get($data, 'raw')); $accountData['active'] = true; + $dataValidated = Hash::get($data, 'validated'); if (empty($existingUser)) { @@ -181,18 +181,20 @@ protected function _populateUser($data, $existingUser, $useEmail, $validateEmail $userData['validated'] = !empty($dataValidated); $userData['tos_date'] = date("Y-m-d H:i:s"); $userData['gender'] = Hash::get($data, 'gender'); - //$userData['timezone'] = Hash::get($data, 'timezone'); $userData['social_accounts'][] = $accountData; - $user = $this->_table->newEntity($userData, ['associated' => ['SocialAccounts']]); + + $user = $this->_table->newEntity($userData); $user = $this->_updateActive($user, false, $tokenExpiration); } else { if ($useEmail && empty($dataValidated)) { $accountData['active'] = false; } - $user = $this->_table->patchEntity($existingUser, [ - 'social_accounts' => [$accountData] - ], ['associated' => ['SocialAccounts']]); + $user = $existingUser; } + $socialAccount = $this->_table->SocialAccounts->newEntity($accountData); + //ensure provider is present in Entity + $socialAccount['provider'] = Hash::get($data, 'provider'); + $user['social_accounts'] = [$socialAccount]; return $user; } diff --git a/src/Template/Users/login.ctp b/src/Template/Users/login.ctp index 3ea2712fc..24b791185 100644 --- a/src/Template/Users/login.ctp +++ b/src/Template/Users/login.ctp @@ -20,6 +20,9 @@ use Cake\Core\Configure; Form->input('username', ['required' => true]) ?> Form->input('password', ['required' => true]) ?> User->addReCaptcha(); + } if (Configure::check('Users.RememberMe.active')) { echo $this->Form->input(Configure::read('Users.Key.Data.rememberMe'), [ 'type' => 'checkbox', @@ -28,7 +31,6 @@ use Cake\Core\Configure; ]); } ?> -

Html->link(__d('users', 'Reset Password'), ['action' => 'requestResetPassword']); } ?> -

- - - $options) : ?> - - User->socialLogin($provider); ?> - - - + User->socialLoginList()); ?> Form->button(__d('Users', 'Login')); ?> Form->end() ?> diff --git a/src/Template/Users/register.ctp b/src/Template/Users/register.ctp index f217de2f4..2c1467db3 100644 --- a/src/Template/Users/register.ctp +++ b/src/Template/Users/register.ctp @@ -24,7 +24,9 @@ use Cake\Core\Configure; if (Configure::read('Users.Tos.required')) { echo $this->Form->input('tos', ['type' => 'checkbox', 'label' => __d('Users', 'Accept TOS conditions?'), 'required' => true]); } - echo $this->User->addReCaptcha(); + if (Configure::read('Users.reCaptcha.registration')) { + echo $this->User->addReCaptcha(); + } ?> Form->button(__d('Users', 'Submit')) ?> diff --git a/src/Template/Users/social_email.ctp b/src/Template/Users/social_email.ctp index 4ac0f9071..52cc6086e 100644 --- a/src/Template/Users/social_email.ctp +++ b/src/Template/Users/social_email.ctp @@ -16,7 +16,6 @@ Form->input('email') ?> - User->addReCaptcha(); ?> Form->button(__d('Users', 'Submit')); ?> Form->end() ?> diff --git a/src/View/Helper/UserHelper.php b/src/View/Helper/UserHelper.php index 735fe4047..305ac9f12 100644 --- a/src/View/Helper/UserHelper.php +++ b/src/View/Helper/UserHelper.php @@ -33,19 +33,6 @@ class UserHelper extends Helper */ protected $_defaultConfig = []; - /** - * beforeLayout callback loads reCaptcha if enabled - * - * @param Event $event event - * @return void - */ - public function beforeLayout(Event $event) - { - if (Configure::read('Users.Registration.reCaptcha')) { - $this->addReCaptchaScript(); - } - } - /** * Social login link * @@ -58,11 +45,37 @@ public function socialLogin($name, $options = []) if (empty($options['label'])) { $options['label'] = 'Sign in with'; } - return $this->Html->link($this->Html->tag('i', '', [ - 'class' => __d('Users', 'fa fa-{0}', strtolower($name)), - ]) . __d('Users', '{0} {1}', Hash::get($options, 'label'), Inflector::camelize($name)), "/auth/$name", [ - 'escape' => false, 'class' => __d('Users', 'btn btn-social btn-{0} ' . Hash::get($options, 'class') ? :'', strtolower($name)) - ]); + $icon = $this->Html->tag('i', '', [ + 'class' => __d('Users', 'fa fa-{0}', strtolower($name)), + ]); + $providerTitle = __d('Users', '{0} {1}', Hash::get($options, 'label'), Inflector::camelize($name)); + $providerClass = __d('Users', 'btn btn-social btn-{0} ' . Hash::get($options, 'class') ?: '', strtolower($name)); + return $this->Html->link($icon . $providerTitle, "/auth/$name", [ + 'escape' => false, 'class' => $providerClass + ]); + } + + /** + * All available Social Login Icons + * + * @return array Links to Social Login Urls + */ + public function socialLoginList() + { + if (!Configure::read('Users.Social.login')) { + return []; + } + $outProviders = []; + $providers = Configure::read('OAuth.providers'); + foreach ($providers as $provider => $options) { + if (!empty($options['options']['redirectUri']) && + !empty($options['options']['clientId']) && + !empty($options['options']['clientSecret'])) { + $outProviders[] = $this->socialLogin($provider); + } + } + + return $outProviders; } /** @@ -123,7 +136,7 @@ public function welcome() } $profileUrl = Configure::read('Users.Profile.route'); - $label = __d('Users', 'Welcome, {0}', $this->Html->link($this->request->session()->read('Auth.User.first_name'), $profileUrl)); + $label = __d('Users', 'Welcome, {0}', $this->Html->link($this->request->session()->read('Auth.User.first_name') ?: $this->request->session()->read('Auth.User.username'), $profileUrl)); return $this->Html->tag('span', $label, ['class' => 'welcome']); } @@ -144,16 +157,14 @@ public function addReCaptchaScript() */ public function addReCaptcha() { - if (!Configure::read('Users.Registration.reCaptcha')) { - return false; - } - if (!Configure::read('reCaptcha.key')) { - return $this->Html->tag('p', __d('Users', 'reCaptcha is not configured! Please configure reCaptcha.key or set Users.Registration.reCaptcha to false')); + if (!Configure::read('Users.reCaptcha.key')) { + return $this->Html->tag('p', __d('Users', 'reCaptcha is not configured! Please configure Users.reCaptcha.key')); } + $this->addReCaptchaScript(); $this->Form->unlockField('g-recaptcha-response'); return $this->Html->tag('div', '', [ 'class' => 'g-recaptcha', - 'data-sitekey' => Configure::read('reCaptcha.key') + 'data-sitekey' => Configure::read('Users.reCaptcha.key') ]); } } diff --git a/tests/TestCase/Auth/SocialAuthenticateTest.php b/tests/TestCase/Auth/SocialAuthenticateTest.php index babfae221..80e40ab20 100644 --- a/tests/TestCase/Auth/SocialAuthenticateTest.php +++ b/tests/TestCase/Auth/SocialAuthenticateTest.php @@ -404,7 +404,7 @@ public function testMapUser($data, $mappedData) $result = $mapUser->invoke($this->SocialAuthenticate, 'Facebook', $data); unset($result['raw']); - $this->assertEquals($result, $mappedData); + $this->assertEquals($mappedData, $result); } /** @@ -432,7 +432,7 @@ public function providerMapper() 'first_name' => 'My first name', 'last_name' => 'My lastname.', 'email' => 'myemail@example.com', - 'avatar' => 'https://graph.facebook.com/my-facebook-id/picture?type=normal', + 'avatar' => 'https://graph.facebook.com/my-facebook-id/picture?type=large', 'gender' => 'female', 'link' => 'https://www.facebook.com/app_scoped_user_id/my-facebook-id/', 'bio' => null, diff --git a/tests/TestCase/Controller/Traits/BaseTraitTest.php b/tests/TestCase/Controller/Traits/BaseTraitTest.php index 131d58aa5..b718b6882 100644 --- a/tests/TestCase/Controller/Traits/BaseTraitTest.php +++ b/tests/TestCase/Controller/Traits/BaseTraitTest.php @@ -95,7 +95,7 @@ public function tearDown() protected function _mockRequestGet() { $this->Trait->request = $this->getMockBuilder('Cake\Network\Request') - ->setMethods(['is', 'referer']) + ->setMethods(['is', 'referer', 'data']) ->getMock(); $this->Trait->request->expects($this->any()) ->method('is') @@ -125,7 +125,7 @@ protected function _mockFlash() protected function _mockRequestPost($with = 'post') { $this->Trait->request = $this->getMockBuilder('Cake\Network\Request') - ->setMethods(['is', 'data']) + ->setMethods(['is', 'data', 'allow']) ->getMock(); $this->Trait->request->expects($this->any()) ->method('is') @@ -138,13 +138,13 @@ protected function _mockRequestPost($with = 'post') * * @return void */ - protected function _mockAuthLoggedIn() + protected function _mockAuthLoggedIn($user = []) { $this->Trait->Auth = $this->getMockBuilder('Cake\Controller\Component\AuthComponent') ->setMethods(['user', 'identify', 'setUser', 'redirectUrl']) ->disableOriginalConstructor() ->getMock(); - $user = [ + $user += [ 'id' => '00000000-0000-0000-0000-000000000001', 'password' => '12345', ]; @@ -154,7 +154,7 @@ protected function _mockAuthLoggedIn() $this->Trait->Auth->expects($this->any()) ->method('user') ->with('id') - ->will($this->returnValue('00000000-0000-0000-0000-000000000001')); + ->will($this->returnValue($user['id'])); } /** diff --git a/tests/TestCase/Controller/Traits/LoginTraitTest.php b/tests/TestCase/Controller/Traits/LoginTraitTest.php index e60b6db8d..254d50f87 100644 --- a/tests/TestCase/Controller/Traits/LoginTraitTest.php +++ b/tests/TestCase/Controller/Traits/LoginTraitTest.php @@ -230,7 +230,11 @@ public function testLoginGet() ->setMethods(['is']) ->disableOriginalConstructor() ->getMock(); - $this->Trait->request->expects($this->once()) + $this->Trait->request->expects($this->at(0)) + ->method('is') + ->with('post') + ->will($this->returnValue(false)); + $this->Trait->request->expects($this->at(1)) ->method('is') ->with('post') ->will($this->returnValue(false)); diff --git a/tests/TestCase/Controller/Traits/PasswordManagementTraitTest.php b/tests/TestCase/Controller/Traits/PasswordManagementTraitTest.php index 3c64ebb75..bbd83d76c 100644 --- a/tests/TestCase/Controller/Traits/PasswordManagementTraitTest.php +++ b/tests/TestCase/Controller/Traits/PasswordManagementTraitTest.php @@ -27,6 +27,7 @@ public function setUp() { $this->traitClassName = 'CakeDC\Users\Controller\Traits\PasswordManagementTrait'; $this->traitMockMethods = ['set', 'redirect', 'validate']; + $this->mockDefaultEmail = true; parent::setUp(); } @@ -68,6 +69,51 @@ public function testChangePasswordHappy() $this->assertTrue($hasher->check('new', $this->table->get('00000000-0000-0000-0000-000000000001')->password)); } + /** + * test + * + * @return void + */ + public function testChangePasswordWithError() + { + $this->assertEquals('12345', $this->table->get('00000000-0000-0000-0000-000000000001')->password); + $this->_mockRequestPost(); + $this->_mockAuthLoggedIn(); + $this->_mockFlash(); + $this->Trait->request->expects($this->once()) + ->method('data') + ->will($this->returnValue([ + 'password' => 'new', + 'password_confirm' => 'wrong_new', + ])); + $this->Trait->Flash->expects($this->once()) + ->method('error') + ->with('Password could not be changed'); + $this->Trait->changePassword(); + } + + /** + * test + * + * @return void + */ + public function testChangePasswordWithInvalidUser() + { + $this->_mockRequestPost(); + $this->_mockAuthLoggedIn(['id' => '12312312-0000-0000-0000-000000000002', 'password' => 'invalid-pass']); + $this->_mockFlash(); + $this->Trait->request->expects($this->once()) + ->method('data') + ->will($this->returnValue([ + 'password' => 'new', + 'password_confirm' => 'new', + ])); + $this->Trait->Flash->expects($this->once()) + ->method('error') + ->with('User was not found'); + $this->Trait->changePassword(); + } + /** * test * @@ -142,18 +188,82 @@ public function testRequestResetPasswordGet() */ public function testRequestPasswordHappy() { - $this->assertEquals('ae93ddbe32664ce7927cf0c5c5a5e59d', $this->table->get('00000000-0000-0000-0000-000000000001')->token); + $this->assertEquals('6614f65816754310a5f0553436dd89e9', $this->table->get('00000000-0000-0000-0000-000000000002')->token); $this->_mockRequestPost(); $this->_mockAuthLoggedIn(); $this->_mockFlash(); - $reference = 'user-1'; + $reference = 'user-2'; $this->Trait->request->expects($this->once()) ->method('data') ->with('reference') ->will($this->returnValue($reference)); $this->Trait->Flash->expects($this->any()) ->method('success') - ->with('Password has been changed successfully'); + ->with('Please check your email to continue with password reset process'); + $this->Trait->requestResetPassword(); + $this->assertNotEquals('xxx', $this->table->get('00000000-0000-0000-0000-000000000002')->token); + } + + /** + * test + * + * @return void + */ + public function testRequestPasswordInvalidUser() + { + $this->_mockRequestPost(); + $this->_mockAuthLoggedIn(['id' => 'invalid-id', 'password' => 'invalid-pass']); + $this->_mockFlash(); + $reference = '12312312-0000-0000-0000-000000000002'; + $this->Trait->request->expects($this->once()) + ->method('data') + ->with('reference') + ->will($this->returnValue($reference)); + $this->Trait->Flash->expects($this->any()) + ->method('error') + ->with('User 12312312-0000-0000-0000-000000000002 was not found'); + $this->Trait->requestResetPassword(); + } + + /** + * test + * + * @return void + */ + public function testRequestPasswordEmptyReference() + { + $this->_mockRequestPost(); + $this->_mockAuthLoggedIn(['id' => 'invalid-id', 'password' => 'invalid-pass']); + $this->_mockFlash(); + $reference = ''; + $this->Trait->request->expects($this->once()) + ->method('data') + ->with('reference') + ->will($this->returnValue($reference)); + $this->Trait->Flash->expects($this->any()) + ->method('error') + ->with('Token could not be reset'); + $this->Trait->requestResetPassword(); + } + + /** + * test requestResetPassword + * + * @return void + */ + public function testRequestResetPasswordUserNotActive() + { + $this->assertEquals('ae93ddbe32664ce7927cf0c5c5a5e59d', $this->table->get('00000000-0000-0000-0000-000000000001')->token); + $this->_mockRequestPost(); + $this->_mockFlash(); + $reference = 'user-1'; + $this->Trait->request->expects($this->once()) + ->method('data') + ->with('reference') + ->will($this->returnValue($reference)); + $this->Trait->Flash->expects($this->any()) + ->method('error') + ->with('The user is not active'); $this->Trait->requestResetPassword(); $this->assertNotEquals('xxx', $this->table->get('00000000-0000-0000-0000-000000000001')->token); } diff --git a/tests/TestCase/Controller/Traits/RegisterTraitTest.php b/tests/TestCase/Controller/Traits/RegisterTraitTest.php index 0ea8e721e..1454e212b 100644 --- a/tests/TestCase/Controller/Traits/RegisterTraitTest.php +++ b/tests/TestCase/Controller/Traits/RegisterTraitTest.php @@ -63,8 +63,40 @@ public function testValidateEmail() * * @return void */ - public function testRegisterHappy() + public function testRegister() { + $this->assertEquals(0, $this->table->find()->where(['username' => 'testRegistration'])->count()); + $this->_mockRequestPost(); + $this->_mockAuth(); + $this->_mockFlash(); + $this->_mockDispatchEvent(); + $this->Trait->Flash->expects($this->once()) + ->method('success') + ->with('Please validate your account before log in'); + $this->Trait->expects($this->once()) + ->method('redirect') + ->with(['action' => 'login']); + $this->Trait->request->data = [ + 'username' => 'testRegistration', + 'password' => 'password', + 'email' => 'test-registration@example.com', + 'password_confirm' => 'password', + 'tos' => 1 + ]; + + $this->Trait->register(); + + $this->assertEquals(1, $this->table->find()->where(['username' => 'testRegistration'])->count()); + } + + /** + * test + * + * @return void + */ + public function testRegisterReCaptcha() + { + Configure::write('Users.reCaptcha.registration', true); $this->assertEquals(0, $this->table->find()->where(['username' => 'testRegistration'])->count()); $this->_mockRequestPost(); $this->_mockAuth(); @@ -90,6 +122,7 @@ public function testRegisterHappy() $this->Trait->register(); $this->assertEquals(1, $this->table->find()->where(['username' => 'testRegistration'])->count()); + Configure::write('Users.reCaptcha.registration', false); } /** @@ -99,6 +132,7 @@ public function testRegisterHappy() */ public function testRegisterValidationErrors() { + Configure::write('Users.reCaptcha.registration', true); $this->assertEquals(0, $this->table->find()->where(['username' => 'testRegistration'])->count()); $this->_mockRequestPost(); $this->_mockAuth(); @@ -123,6 +157,7 @@ public function testRegisterValidationErrors() $this->Trait->register(); $this->assertEquals(0, $this->table->find()->where(['username' => 'testRegistration'])->count()); + Configure::write('Users.reCaptcha.registration', false); } /** @@ -132,6 +167,7 @@ public function testRegisterValidationErrors() */ public function testRegisterRecaptchaNotValid() { + Configure::write('Users.reCaptcha.registration', true); $this->assertEquals(0, $this->table->find()->where(['username' => 'testRegistration'])->count()); $this->_mockRequestPost(); $this->_mockAuth(); @@ -139,7 +175,7 @@ public function testRegisterRecaptchaNotValid() $this->_mockDispatchEvent(); $this->Trait->Flash->expects($this->once()) ->method('error') - ->with('The reCaptcha could not be validated'); + ->with('Invalid reCaptcha'); $this->Trait->expects($this->once()) ->method('validateRecaptcha') ->will($this->returnValue(false)); @@ -154,6 +190,7 @@ public function testRegisterRecaptchaNotValid() $this->Trait->register(); $this->assertEquals(0, $this->table->find()->where(['username' => 'testRegistration'])->count()); + Configure::write('Users.reCaptcha.registration', false); } /** diff --git a/tests/TestCase/Model/Behavior/PasswordBehaviorTest.php b/tests/TestCase/Model/Behavior/PasswordBehaviorTest.php index 238a7923d..18b4c3290 100644 --- a/tests/TestCase/Model/Behavior/PasswordBehaviorTest.php +++ b/tests/TestCase/Model/Behavior/PasswordBehaviorTest.php @@ -146,4 +146,18 @@ public function testResetTokenUserAlreadyActive() 'checkActive' => true, ]); } + + /** + * Test resetToken + * + * @expectedException CakeDC\Users\Exception\UserNotActiveException + */ + public function testResetTokenUserNotActive() + { + $user = TableRegistry::get('CakeDC/Users.Users')->findAllByUsername('user-1')->first(); + $this->Behavior->resetToken('user-1', [ + 'ensureActive' => true, + 'expiration' => 3600 + ]); + } } diff --git a/tests/TestCase/Model/Entity/UserTest.php b/tests/TestCase/Model/Entity/UserTest.php index d7028a87c..51894aa2e 100644 --- a/tests/TestCase/Model/Entity/UserTest.php +++ b/tests/TestCase/Model/Entity/UserTest.php @@ -175,9 +175,7 @@ public function testUpdateTokenAdd() $this->assertNull($this->User['token']); $this->assertNull($this->User['token_expires']); $this->User->updateToken(20); - $nowModified = new Time('now'); - $nowModified->addSecond(20); - $this->assertEquals($nowModified, $this->User['token_expires']); + $this->assertEquals('20 seconds after', $this->User['token_expires']->diffForHumans($this->now)); $this->assertNotNull($this->User['token']); } @@ -191,9 +189,7 @@ public function testUpdateTokenExistingAdd() $this->User['token'] = 'aaa'; $this->User['token_expires'] = $this->now; $this->User->updateToken(20); - $nowModified = new Time('now'); - $nowModified->addSecond(20); - $this->assertEquals($nowModified, $this->User['token_expires']); + $this->assertEquals('20 seconds after', $this->User['token_expires']->diffForHumans($this->now)); $this->assertNotEquals('aaa', $this->User['token']); } } diff --git a/tests/TestCase/View/Helper/UserHelperTest.php b/tests/TestCase/View/Helper/UserHelperTest.php index 6da81b94f..5e5038f70 100644 --- a/tests/TestCase/View/Helper/UserHelperTest.php +++ b/tests/TestCase/View/Helper/UserHelperTest.php @@ -206,11 +206,9 @@ public function testWelcomeNotLoggedInUser() */ public function testAddReCaptcha() { - $siteKey = Configure::read('reCaptcha.key'); - Configure::write('reCaptcha.key', 'testKey'); + Configure::write('Users.reCaptcha.key', 'testKey'); $result = $this->User->addReCaptcha(); $this->assertEquals('
', $result); - Configure::write('reCaptcha.key', $siteKey); } /** @@ -220,11 +218,11 @@ public function testAddReCaptcha() */ public function testAddReCaptchaEmpty() { - Configure::write('Users.Registration.reCaptcha', false); $result = $this->User->addReCaptcha(); - $this->assertFalse($result); + $expected = '

reCaptcha is not configured! Please configure Users.reCaptcha.key

'; + $this->assertEquals($expected, $result); } - + /** * Test add ReCaptcha field *