Moved account activation and password reset to API

This commit is contained in:
Marcin Kurczewski 2014-05-04 15:43:38 +02:00
parent 893e841a87
commit 47f7ff3490
7 changed files with 163 additions and 110 deletions

View file

@ -151,10 +151,10 @@ $userValidation =
\Chibi\Router::register(['UserController', 'activationView'], 'GET', '/activation', $userValidation); \Chibi\Router::register(['UserController', 'activationView'], 'GET', '/activation', $userValidation);
\Chibi\Router::register(['UserController', 'activationAction'], 'POST', '/activation', $userValidation); \Chibi\Router::register(['UserController', 'activationAction'], 'POST', '/activation', $userValidation);
\Chibi\Router::register(['UserController', 'activationAction'], 'GET', '/activation/{token}', $userValidation); \Chibi\Router::register(['UserController', 'activationAction'], 'GET', '/activation/{tokenText}', $userValidation);
\Chibi\Router::register(['UserController', 'passwordResetView'], 'GET', '/password-reset', $userValidation); \Chibi\Router::register(['UserController', 'passwordResetView'], 'GET', '/password-reset', $userValidation);
\Chibi\Router::register(['UserController', 'passwordResetAction'], 'POST', '/password-reset', $userValidation); \Chibi\Router::register(['UserController', 'passwordResetAction'], 'POST', '/password-reset', $userValidation);
\Chibi\Router::register(['UserController', 'passwordResetAction'], 'GET', '/password-reset/{token}', $userValidation); \Chibi\Router::register(['UserController', 'passwordResetAction'], 'GET', '/password-reset/{tokenText}', $userValidation);
\Chibi\Router::register(['UserController', 'flagAction'], 'POST', '/user/{name}/flag', $userValidation); \Chibi\Router::register(['UserController', 'flagAction'], 'POST', '/user/{name}/flag', $userValidation);
\Chibi\Router::register(['UserController', 'banAction'], 'POST', '/user/{name}/ban', $userValidation); \Chibi\Router::register(['UserController', 'banAction'], 'POST', '/user/{name}/ban', $userValidation);

View file

@ -0,0 +1,65 @@
<?php
class ActivateUserEmailJob extends AbstractJob
{
const TOKEN = 'token';
public function execute()
{
if (!$this->hasArgument(self::TOKEN))
{
$user = UserModel::findByNameOrEmail($this->getArgument(self::USER_NAME));
if (empty($user->emailUnconfirmed))
{
if (!empty($user->emailConfirmed))
throw new SimpleException('E-mail was already confirmed; activation skipped');
else
throw new SimpleException('This user has no e-mail specified; activation cannot proceed');
}
self::sendEmail($user);
return $user;
}
else
{
$tokenText = $this->getArgument(self::TOKEN);
$token = TokenModel::findByToken($tokenText);
TokenModel::checkValidity($token);
$user = $token->getUser();
$user->confirmEmail();
$token->used = true;
TokenModel::save($token);
UserModel::save($user);
LogHelper::log('{subject} just activated account', [
'subject' => TextHelper::reprUser($user)]);
return $user;
}
}
public static function sendEmail($user)
{
$regConfig = getConfig()->registration;
if (!$regConfig->confirmationEmailEnabled)
{
$user->confirmEmail();
return;
}
$mail = new Mail();
$mail->body = $regConfig->confirmationEmailBody;
$mail->subject = $regConfig->confirmationEmailSubject;
$mail->senderName = $regConfig->confirmationEmailSenderName;
$mail->senderEmail = $regConfig->confirmationEmailSenderEmail;
$mail->recipientEmail = $user->emailUnconfirmed;
return Mailer::sendMailWithTokenLink(
$user,
['UserController', 'activationAction'],
$mail);
}
}

View file

@ -24,11 +24,8 @@ class AddUserJob extends AbstractJob
Api::enablePrivilegeChecking(); Api::enablePrivilegeChecking();
LogHelper::setBuffer([]); LogHelper::setBuffer([]);
if ($firstUser and empty($user->emailConfirmed)) if ($firstUser)
{ $user->confirmEmail();
$user->emailConfirmed = $user->emailUnconfirmed;
$user->emailUnconfirmed = null;
}
//load the user after edits //load the user after edits
$user = UserModel::findById($user->id); $user = UserModel::findById($user->id);

View file

@ -16,20 +16,17 @@ class EditUserEmailJob extends AbstractUserJob
if ($oldEmail == $newEmail) if ($oldEmail == $newEmail)
return $user; return $user;
$user->emailUnconfirmed = $newEmail;
$user->emailConfirmed = null;
if (Auth::getCurrentUser()->id == $user->id) if (Auth::getCurrentUser()->id == $user->id)
{ {
$user->emailUnconfirmed = $newEmail;
$user->emailConfirmed = null;
if (!empty($newEmail)) if (!empty($newEmail))
{ ActivateUserEmailJob::sendEmail($user);
$this->sendEmail($user);
}
} }
else else
{ {
$user->emailUnconfirmed = null; $user->confirmEmail();
$user->emailConfirmed = $newEmail;
} }
UserModel::save($user); UserModel::save($user);
@ -50,29 +47,4 @@ class EditUserEmailJob extends AbstractUserJob
Access::getIdentity($this->user), Access::getIdentity($this->user),
]; ];
} }
//todo: change to private once finished refactors to UserController
public function sendEmail($user)
{
$regConfig = getConfig()->registration;
if (!$regConfig->confirmationEmailEnabled)
{
$user->emailUnconfirmed = null;
$user->emailConfirmed = $user->emailUnconfirmed;
return;
}
$mail = new Mail();
$mail->body = $regConfig->confirmationEmailBody;
$mail->subject = $regConfig->confirmationEmailSubject;
$mail->senderName = $regConfig->confirmationEmailSenderName;
$mail->senderEmail = $regConfig->confirmationEmailSenderEmail;
$mail->recipientEmail = $user->emailUnconfirmed;
return Mailer::sendMailWithTokenLink(
$user,
['UserController', 'activationAction'],
$mail);
}
} }

View file

@ -0,0 +1,64 @@
<?php
class PasswordResetJob extends AbstractJob
{
const TOKEN = 'token';
const NEW_PASSWORD = 'new-password';
public function execute()
{
if (!$this->hasArgument(self::TOKEN))
{
$user = UserModel::findByNameOrEmail($this->getArgument(self::USER_NAME));
if (empty($user->emailConfirmed))
throw new SimpleException('This user has no e-mail confirmed; password reset cannot proceed');
self::sendEmail($user);
return $user;
}
else
{
$tokenText = $this->getArgument(self::TOKEN);
$token = TokenModel::findByToken($tokenText);
TokenModel::checkValidity($token);
$alphabet = array_merge(range('A', 'Z'), range('a', 'z'), range('0', '9'));
$newPassword = join('', array_map(function($x) use ($alphabet)
{
return $alphabet[$x];
}, array_rand($alphabet, 8)));
$user = $token->getUser();
$user->passHash = UserModel::hashPassword($newPassword, $user->passSalt);
$token->used = true;
TokenModel::save($token);
UserModel::save($user);
LogHelper::log('{subject} just reset password', [
'subject' => TextHelper::reprUser($user)]);
$x = new StdClass;
$x->user = $user;
$x->newPassword = $newPassword;
return $x;
}
}
public static function sendEmail($user)
{
$regConfig = getConfig()->registration;
$mail = new Mail();
$mail->body = $regConfig->passwordResetEmailBody;
$mail->subject = $regConfig->passwordResetEmailSubject;
$mail->senderName = $regConfig->passwordResetEmailSenderName;
$mail->senderEmail = $regConfig->passwordResetEmailSenderEmail;
$mail->recipientEmail = $user->emailConfirmed;
return Mailer::sendMailWithTokenLink(
$user,
['UserController', 'passwordResetAction'],
$mail);
}
}

View file

@ -223,51 +223,30 @@ class UserController
Assets::setSubTitle('account activation'); Assets::setSubTitle('account activation');
} }
public function activationAction($token) public function activationAction($tokenText)
{ {
$context = getContext(); $context = getContext();
$context->viewName = 'message'; $context->viewName = 'message';
Assets::setSubTitle('account activation'); Assets::setSubTitle('account activation');
$name = InputHelper::get('name');
if (empty($token)) if (empty($tokenText))
{ {
$name = InputHelper::get('name'); Api::run(new ActivateUserEmailJob(), [ ActivateUserEmailJob::USER_NAME => $name ]);
$user = UserModel::findByNameOrEmail($name);
if (empty($user->emailUnconfirmed))
{
if (!empty($user->emailConfirmed))
throw new SimpleException('E-mail was already confirmed; activation skipped');
else
throw new SimpleException('This user has no e-mail specified; activation cannot proceed');
}
EditUserEmailJob::sendEmail($user);
Messenger::message('Activation e-mail resent.'); Messenger::message('Activation e-mail resent.');
} }
else else
{ {
$dbToken = TokenModel::findByToken($token); $user = Api::run(new ActivateUserEmailJob(), [ ActivateUserEmailJob::TOKEN => $tokenText ]);
TokenModel::checkValidity($dbToken);
$dbUser = $dbToken->getUser();
if (empty($dbUser->emailConfirmed))
{
$dbUser->emailConfirmed = $dbUser->emailUnconfirmed;
$dbUser->emailUnconfirmed = null;
}
$dbToken->used = true;
TokenModel::save($dbToken);
UserModel::save($dbUser);
LogHelper::log('{subject} just activated account', ['subject' => TextHelper::reprUser($dbUser)]);
$message = 'Activation completed successfully.'; $message = 'Activation completed successfully.';
if (getConfig()->registration->staffActivation) if (getConfig()->registration->staffActivation)
$message .= ' However, your account still must be confirmed by staff.'; $message .= ' However, your account still must be confirmed by staff.';
Messenger::message($message); Messenger::message($message);
if (!getConfig()->registration->staffActivation) if (!getConfig()->registration->staffActivation)
{ Auth::setCurrentUser($user);
Auth::setCurrentUser($dbUser);
}
} }
} }
@ -278,64 +257,31 @@ class UserController
Assets::setSubTitle('password reset'); Assets::setSubTitle('password reset');
} }
public function passwordResetAction($token) public function passwordResetAction($tokenText)
{ {
$context = getContext(); $context = getContext();
$context->viewName = 'message'; $context->viewName = 'message';
Assets::setSubTitle('password reset'); Assets::setSubTitle('password reset');
$name = InputHelper::get('name');
if (empty($token)) if (empty($tokenText))
{ {
$name = InputHelper::get('name'); Api::run(new PasswordResetJob(), [ PasswordResetJob::USER_NAME => $name ]);
$user = UserModel::findByNameOrEmail($name);
if (empty($user->emailConfirmed))
throw new SimpleException('This user has no e-mail confirmed; password reset cannot proceed');
self::sendPasswordResetConfirmation($user);
Messenger::message('E-mail sent. Follow instructions to reset password.'); Messenger::message('E-mail sent. Follow instructions to reset password.');
} }
else else
{ {
$dbToken = TokenModel::findByToken($token); $ret = Api::run(new PasswordResetJob(), [ PasswordResetJob::TOKEN => $tokenText ]);
TokenModel::checkValidity($dbToken);
$alphabet = array_merge(range('A', 'Z'), range('a', 'z'), range('0', '9')); Messenger::message(sprintf(
$randomPassword = join('', array_map(function($x) use ($alphabet) 'Password reset successful. Your new password is **%s**.',
{ $ret->newPassword));
return $alphabet[$x];
}, array_rand($alphabet, 8)));
$dbUser = $dbToken->getUser(); Auth::setCurrentUser($ret->user);
$dbUser->passHash = UserModel::hashPassword($randomPassword, $dbUser->passSalt);
$dbToken->used = true;
TokenModel::save($dbToken);
UserModel::save($dbUser);
LogHelper::log('{subject} just reset password', ['subject' => TextHelper::reprUser($dbUser)]);
$message = 'Password reset successful. Your new password is **' . $randomPassword . '**.';
Messenger::message($message);
Auth::setCurrentUser($dbUser);
} }
} }
private static function sendPasswordResetConfirmation($user)
{
$regConfig = getConfig()->registration;
$mail = new Mail();
$mail->body = $regConfig->passwordResetEmailBody;
$mail->subject = $regConfig->passwordResetEmailSubject;
$mail->senderName = $regConfig->passwordResetEmailSenderName;
$mail->senderEmail = $regConfig->passwordResetEmailSenderEmail;
$mail->recipientEmail = $user->emailConfirmed;
return Mailer::sendMailWithTokenLink(
$user,
['UserController', 'passwordResetAction'],
$mail);
}
private function requirePasswordConfirmation() private function requirePasswordConfirmation()
{ {
$user = getContext()->transport->user; $user = getContext()->transport->user;

View file

@ -112,6 +112,15 @@ class UserEntity extends AbstractEntity
$this->setSetting(UserModel::SETTING_ENDLESS_SCROLLING, $enabled ? 1 : 0); $this->setSetting(UserModel::SETTING_ENDLESS_SCROLLING, $enabled ? 1 : 0);
} }
public function confirmEmail()
{
if (!empty($this->emailConfirmed))
return;
$this->emailConfirmed = $user->emailUnconfirmed;
$this->emailUnconfirmed = null;
}
public function hasFavorited($post) public function hasFavorited($post)
{ {
$stmt = new Sql\SelectStatement(); $stmt = new Sql\SelectStatement();