|
|
@@ -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();
|
|
|
@@ -272,6 +335,122 @@ class FreshRSS_user_Controller extends Minz_ActionController {
|
|
|
return $ok;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 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.
|
|
|
*
|