diff options
Diffstat (limited to 'app/Controllers/userController.php')
| -rw-r--r-- | app/Controllers/userController.php | 195 |
1 files changed, 187 insertions, 8 deletions
diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index c1c27a4ab..9e909a3b5 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -33,12 +33,23 @@ class FreshRSS_user_Controller extends Minz_ActionController { return false; } - public static function updateUser($user, $passwordPlain, $apiPasswordPlain, $userConfigUpdated = array()) { + public static function updateUser($user, $email, $passwordPlain, $apiPasswordPlain, $userConfigUpdated = array()) { $userConfig = get_user_configuration($user); if ($userConfig === null) { return false; } + if ($email !== null && $userConfig->mail_login !== $email) { + $userConfig->mail_login = $email; + + if (FreshRSS_Context::$system_conf->force_email_validation) { + $salt = FreshRSS_Context::$system_conf->salt; + $userConfig->email_validation_token = sha1($salt . uniqid(mt_rand(), true)); + $mailer = new FreshRSS_User_Mailer(); + $mailer->send_email_need_validation($user, $userConfig); + } + } + if ($passwordPlain != '') { $passwordHash = self::hashPassword($passwordPlain); $userConfig->passwordHash = $passwordHash; @@ -84,7 +95,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { $apiPasswordPlain = Minz_Request::param('apiPasswordPlain', '', true); $username = Minz_Request::param('username'); - $ok = self::updateUser($username, $passwordPlain, $apiPasswordPlain, array( + $ok = self::updateUser($username, null, $passwordPlain, $apiPasswordPlain, array( 'token' => Minz_Request::param('token', null), )); @@ -111,25 +122,58 @@ class FreshRSS_user_Controller extends Minz_ActionController { Minz_Error::error(403); } + $email_not_verified = FreshRSS_Context::$user_conf->email_validation_token !== ''; + if ($email_not_verified) { + $this->view->_layout('simple'); + $this->view->disable_aside = true; + } + Minz_View::prependTitle(_t('conf.profile.title') . ' · '); Minz_View::appendScript(Minz_Url::display('/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js'))); if (Minz_Request::isPost()) { + $system_conf = FreshRSS_Context::$system_conf; + $user_config = FreshRSS_Context::$user_conf; + $old_email = $user_config->mail_login; + + $email = trim(Minz_Request::param('email', '')); $passwordPlain = Minz_Request::param('newPasswordPlain', '', true); Minz_Request::_param('newPasswordPlain'); //Discard plain-text password ASAP $_POST['newPasswordPlain'] = ''; $apiPasswordPlain = Minz_Request::param('apiPasswordPlain', '', true); - $ok = self::updateUser(Minz_Session::param('currentUser'), $passwordPlain, $apiPasswordPlain, array( + if ($system_conf->force_email_validation && empty($email)) { + Minz_Request::bad( + _t('user.email.feedback.required'), + array('c' => 'user', 'a' => 'profile') + ); + } + + if (!empty($email) && !validateEmailAddress($email)) { + Minz_Request::bad( + _t('user.email.feedback.invalid'), + array('c' => 'user', 'a' => 'profile') + ); + } + + $ok = self::updateUser( + Minz_Session::param('currentUser'), + $email, + $passwordPlain, + $apiPasswordPlain, + array( 'token' => Minz_Request::param('token', null), - )); + ) + ); Minz_Session::_param('passwordHash', FreshRSS_Context::$user_conf->passwordHash); if ($ok) { - if ($passwordPlain == '') { + if ($system_conf->force_email_validation && $email !== $old_email) { + Minz_Request::good(_t('feedback.profile.updated'), array('c' => 'user', 'a' => 'validateEmail')); + } elseif ($passwordPlain == '') { Minz_Request::good(_t('feedback.profile.updated'), array('c' => 'user', 'a' => 'profile')); } else { Minz_Request::good(_t('feedback.profile.updated'), array('c' => 'index', 'a' => 'index')); @@ -151,6 +195,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { Minz_View::prependTitle(_t('admin.user.title') . ' · '); + $this->view->show_email_field = FreshRSS_Context::$system_conf->force_email_validation; $this->view->current_user = Minz_Request::param('u'); $this->view->nb_articles = 0; @@ -165,7 +210,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { } } - public static function createUser($new_user_name, $passwordPlain, $apiPasswordPlain, $userConfig = array(), $insertDefaultFeeds = true) { + public static function createUser($new_user_name, $email, $passwordPlain, $apiPasswordPlain, $userConfig = array(), $insertDefaultFeeds = true) { if (!is_array($userConfig)) { $userConfig = array(); } @@ -193,7 +238,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { if ($ok) { $userDAO = new FreshRSS_UserDAO(); $ok &= $userDAO->createUser($new_user_name, $userConfig['language'], $insertDefaultFeeds); - $ok &= self::updateUser($new_user_name, $passwordPlain, $apiPasswordPlain); + $ok &= self::updateUser($new_user_name, $email, $passwordPlain, $apiPasswordPlain); } return $ok; } @@ -204,6 +249,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { * Request parameters are: * - new_user_language * - new_user_name + * - new_user_email * - new_user_passwordPlain * - r (i.e. a redirection url, optional) * @@ -216,11 +262,28 @@ class FreshRSS_user_Controller extends Minz_ActionController { } if (Minz_Request::isPost()) { + $system_conf = FreshRSS_Context::$system_conf; + $new_user_name = Minz_Request::param('new_user_name'); + $email = Minz_Request::param('new_user_email', ''); $passwordPlain = Minz_Request::param('new_user_passwordPlain', '', true); $new_user_language = Minz_Request::param('new_user_language', FreshRSS_Context::$user_conf->language); - $ok = self::createUser($new_user_name, $passwordPlain, '', array('language' => $new_user_language)); + if ($system_conf->force_email_validation && empty($email)) { + Minz_Request::bad( + _t('user.email.feedback.required'), + array('c' => 'auth', 'a' => 'register') + ); + } + + if (!empty($email) && !validateEmailAddress($email)) { + Minz_Request::bad( + _t('user.email.feedback.invalid'), + array('c' => 'auth', 'a' => 'register') + ); + } + + $ok = self::createUser($new_user_name, $email, $passwordPlain, '', array('language' => $new_user_language)); Minz_Request::_param('new_user_passwordPlain'); //Discard plain-text password ASAP $_POST['new_user_passwordPlain'] = ''; invalidateHttpCache(); @@ -273,6 +336,122 @@ class FreshRSS_user_Controller extends Minz_ActionController { } /** + * This action validates an email address, based on the token sent by email. + * It also serves the main page when user is blocked. + * + * Request parameters are: + * - username + * - token + * + * This route works with GET requests since the URL is provided by email. + * The security risks (e.g. forged URL by an attacker) are not very high so + * it's ok. + * + * It returns 404 error if `force_email_validation` is disabled or if the + * user doesn't exist. + * + * It returns 403 if user isn't logged in and `username` param isn't passed. + */ + public function validateEmailAction() { + if (!FreshRSS_Context::$system_conf->force_email_validation) { + Minz_Error::error(404); + } + + Minz_View::prependTitle(_t('user.email.validation.title') . ' · '); + $this->view->_layout('simple'); + + $username = Minz_Request::param('username'); + $token = Minz_Request::param('token'); + + if ($username) { + $user_config = get_user_configuration($username); + } elseif (FreshRSS_Auth::hasAccess()) { + $user_config = FreshRSS_Context::$user_conf; + } else { + Minz_Error::error(403); + } + + if (!FreshRSS_UserDAO::exists($username) || $user_config === null) { + Minz_Error::error(404); + } + + if ($user_config->email_validation_token === '') { + Minz_Request::good( + _t('user.email.validation.feedback.unnecessary'), + array('c' => 'index', 'a' => 'index') + ); + } + + if ($token) { + if ($user_config->email_validation_token !== $token) { + Minz_Request::bad( + _t('user.email.validation.feedback.wrong_token'), + array('c' => 'user', 'a' => 'validateEmail') + ); + } + + $user_config->email_validation_token = ''; + if ($user_config->save()) { + Minz_Request::good( + _t('user.email.validation.feedback.ok'), + array('c' => 'index', 'a' => 'index') + ); + } else { + Minz_Request::bad( + _t('user.email.validation.feedback.error'), + array('c' => 'user', 'a' => 'validateEmail') + ); + } + } + } + + /** + * This action resends a validation email to the current user. + * + * It only acts on POST requests but doesn't require any param (except the + * CSRF token). + * + * It returns 403 error if the user is not logged in or 404 if request is + * not POST. Else it redirects silently to the index if user has already + * validated its email, or to the user#validateEmail route. + */ + public function sendValidationEmailAction() { + if (!FreshRSS_Auth::hasAccess()) { + Minz_Error::error(403); + } + + if (!Minz_Request::isPost()) { + Minz_Error::error(404); + } + + $username = Minz_Session::param('currentUser', '_'); + $user_config = FreshRSS_Context::$user_conf; + + if ($user_config->email_validation_token === '') { + Minz_Request::forward(array( + 'c' => 'index', + 'a' => 'index', + ), true); + } + + $mailer = new FreshRSS_User_Mailer(); + $ok = $mailer->send_email_need_validation($username, $user_config); + + $redirect_url = array('c' => 'user', 'a' => 'validateEmail'); + if ($ok) { + Minz_Request::good( + _t('user.email.validation.feedback.email_sent'), + $redirect_url + ); + } else { + Minz_Request::bad( + _t('user.email.validation.feedback.email_failed'), + $redirect_url + ); + } + } + + /** * This action delete an existing user. * * Request parameter is: |
