Working on password recovery
This commit is contained in:
parent
f30d2b4011
commit
9d3d85f739
8 changed files with 212 additions and 12 deletions
|
@ -4,7 +4,91 @@
|
|||
namespace App\Controllers\Auth;
|
||||
|
||||
use App\Controllers\Controller;
|
||||
use App\Web\Mail;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class PasswordRecoveryController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @return Response
|
||||
* @throws \Twig\Error\LoaderError
|
||||
* @throws \Twig\Error\RuntimeError
|
||||
* @throws \Twig\Error\SyntaxError
|
||||
*/
|
||||
public function recover(Request $request, Response $response): Response
|
||||
{
|
||||
return view()->render($response, 'auth/recover_mail.twig');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @return Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function recoverMail(Request $request, Response $response): Response
|
||||
{
|
||||
if ($this->session->get('logged', false)) {
|
||||
return redirect($response, route('home'));
|
||||
}
|
||||
|
||||
$user = $this->database->query('SELECT `id`, `username` FROM `users` WHERE `email` = ? LIMIT 1', param($request, 'email'))->fetch();
|
||||
|
||||
if (!isset($user->id)) {
|
||||
$this->session->alert(lang('recover_email_sent'), 'success');
|
||||
return redirect($response, route('recover'));
|
||||
}
|
||||
|
||||
$resetToken = bin2hex(random_bytes(16));
|
||||
|
||||
$this->database->query('UPDATE `users` SET `reset_token`=? WHERE `id` = ?', [
|
||||
$resetToken,
|
||||
$user->id,
|
||||
]);
|
||||
|
||||
Mail::make()
|
||||
->from('no-reply@'.str_ireplace('www.', '', parse_url($this->config['base_url'], PHP_URL_HOST)), $this->config['app_name'])
|
||||
->to(param($request, 'email'))
|
||||
->subject(lang('mail.recover_password', [$this->config['app_name']]))
|
||||
->message(lang('mail.recover_text', [
|
||||
$user->username,
|
||||
route('recover.password', ['resetToken' => $resetToken]),
|
||||
]))
|
||||
->send();
|
||||
|
||||
$this->session->alert(lang('recover_email_sent'), 'success');
|
||||
return redirect($response, route('recover'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @param string $resetToken
|
||||
* @return Response
|
||||
* @throws \Twig\Error\LoaderError
|
||||
* @throws \Twig\Error\RuntimeError
|
||||
* @throws \Twig\Error\SyntaxError
|
||||
*/
|
||||
public function recoverPasswordForm(Request $request, Response $response, string $resetToken): Response
|
||||
{
|
||||
return view()->render($response, 'auth/recover_password.twig', [
|
||||
'reset_token' => $resetToken
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @param string $resetToken
|
||||
* @return Response
|
||||
*/
|
||||
public function recoverPassword(Request $request, Response $response, string $resetToken): Response
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
// Auth routes
|
||||
use App\Controllers\AdminController;
|
||||
use App\Controllers\Auth\PasswordRecoveryController;
|
||||
use App\Controllers\Auth\RegisterController;
|
||||
use App\Controllers\ClientController;
|
||||
use App\Controllers\DashboardController;
|
||||
|
@ -66,6 +67,10 @@ $app->get('/', [DashboardController::class, 'redirects'])->setName('root');
|
|||
$app->get('/register', [RegisterController::class, 'registerForm'])->setName('register.show');
|
||||
$app->post('/register', [RegisterController::class, 'register'])->setName('register');
|
||||
$app->get('/activate/{activateToken}', [RegisterController::class, 'activateUser'])->setName('activate');
|
||||
$app->get('/recover', [PasswordRecoveryController::class, 'recover'])->setName('recover');
|
||||
$app->post('/recover/mail', [PasswordRecoveryController::class, 'recoverMail'])->setName('recover.mail');
|
||||
$app->get('/recover/password/{resetToken}', [PasswordRecoveryController::class, 'recoverPasswordForm'])->setName('recover.password.view');
|
||||
$app->post('/recover/password/{resetToken}', [PasswordRecoveryController::class, 'recoverPassword'])->setName('recover.password');
|
||||
$app->get('/login', [LoginController::class, 'show'])->setName('login.show');
|
||||
$app->post('/login', [LoginController::class, 'login'])->setName('login');
|
||||
$app->map(['GET', 'POST'], '/logout', [LoginController::class, 'logout'])->setName('logout');
|
||||
|
|
|
@ -118,7 +118,15 @@ return [
|
|||
'password_recovery' => 'Recover password',
|
||||
'no_account' => 'Don\'t have an account?',
|
||||
'register' => 'Register',
|
||||
'register_success' => 'The account has been created, a confirmation email has been sent.',
|
||||
'default_user_quota' => 'Default User Quota',
|
||||
'invalid_quota' => 'Invalid values as default user quota.',
|
||||
'mail.activate_text' => "Hi %s!\nthank you for creating your account on %s (%s), click on the following link to activate it:\n\n%s"
|
||||
'mail.activate_text' => "Hi %s!\nthank you for creating your account on %s (%s), click on the following link to activate it:\n\n%s",
|
||||
'mail.activate_account' => '%s - Account Activation',
|
||||
'mail.recover_text' => "Hi %s,\na password reset has been requested for your account. To complete the procedure click on the following link:\n\n%s\n\nIf it wasn't you who requested the password reset, simply ignore this email.",
|
||||
'mail.recover_password' => '%s - Password Recovery',
|
||||
'recover_email_sent' => 'If present, a recovery email was sent to the specified account.',
|
||||
'account_activated' => 'Account activated, now you can login!',
|
||||
'quota_enabled' => 'Enable user quota',
|
||||
'password_repeat' => 'Repeat Password',
|
||||
];
|
||||
|
|
|
@ -36,17 +36,16 @@
|
|||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<label for="username" class="sr-only">{{ lang('login.username') }}</label>
|
||||
<input type="text" id="username" class="form-control" placeholder="{{ lang('login.username') }}" name="username" required autofocus>
|
||||
<input type="text" id="username" class="form-control first" placeholder="{{ lang('login.username') }}" name="username" required autofocus>
|
||||
<label for="password" class="sr-only">{{ lang('password') }}</label>
|
||||
<input type="password" id="password" class="form-control" placeholder="{{ lang('password') }}" name="password" required>
|
||||
<input type="password" id="password" class="form-control last" placeholder="{{ lang('password') }}" name="password" required>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" name="remember" class="form-check-input float-left" id="remember">
|
||||
<label class="form-check-label" for="remember">{{ lang('remember_me') }}</label>
|
||||
</div>
|
||||
<a href="#" class="">{{ lang('password_recovery') }}</a>
|
||||
<a href="{{ route('recover') }}" class="">{{ lang('password_recovery') }}</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-2">
|
||||
|
|
52
resources/templates/auth/recover_mail.twig
Normal file
52
resources/templates/auth/recover_mail.twig
Normal file
|
@ -0,0 +1,52 @@
|
|||
{% extends 'base.twig' %}
|
||||
|
||||
{% block title %}{{ lang('password_recovery') }}{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<style>
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
height: 100%;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
-ms-flex-align: center;
|
||||
-ms-flex-pack: center;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
justify-content: center;
|
||||
padding-bottom: 40px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<form class="form-signin" method="post" action="{{ route('recover.mail') }}">
|
||||
<div class="row text-center">
|
||||
<div class="col-md-12">
|
||||
<h1 class="h3 mb-3 font-weight-normal">{{ config.app_name }}</h1>
|
||||
{% include 'comp/alert.twig' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<label for="email" class="sr-only">{{ lang('email') }}</label>
|
||||
<input type="email" id="email" class="form-control" placeholder="mail@example.com" name="email" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-2">
|
||||
<div class="col-md-12">
|
||||
<button class="btn btn-lg btn-primary btn-block" type="submit">{{ lang('password_recovery') }}</button>
|
||||
<div class="text-center mt-2">
|
||||
<a href="{{ route('login.show') }}">{{ lang('cancel') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
52
resources/templates/auth/recover_password.twig
Normal file
52
resources/templates/auth/recover_password.twig
Normal file
|
@ -0,0 +1,52 @@
|
|||
{% extends 'base.twig' %}
|
||||
|
||||
{% block title %}{{ lang('password_recovery') }}{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<style>
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
height: 100%;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
-ms-flex-align: center;
|
||||
-ms-flex-pack: center;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
justify-content: center;
|
||||
padding-bottom: 40px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<form class="form-signin" method="post" action="{{ route('recover.mail') }}">
|
||||
<div class="row text-center">
|
||||
<div class="col-md-12">
|
||||
<h1 class="h3 mb-3 font-weight-normal">{{ config.app_name }}</h1>
|
||||
{% include 'comp/alert.twig' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<input type="hidden" name="reset_token" value="{{ reset_token }}">
|
||||
<label for="password" class="sr-only">{{ lang('password') }}</label>
|
||||
<input type="password" id="password" class="form-control first" placeholder="{{ lang('password') }}" name="password" required>
|
||||
<label for="password_repeat" class="sr-only">{{ lang('password_repeat') }}</label>
|
||||
<input type="password" id="password_repeat" class="form-control last" placeholder="{{ lang('password_repeat') }}" name="password_repeat" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-2">
|
||||
<div class="col-md-12">
|
||||
<button class="btn btn-lg btn-primary btn-block" type="submit">{{ lang('password_recovery') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,6 +1,6 @@
|
|||
{% extends 'base.twig' %}
|
||||
|
||||
{% block title %}{{ lang('login') }}{% endblock %}
|
||||
{% block title %}{{ lang('register') }}{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<style>
|
||||
|
@ -36,11 +36,11 @@
|
|||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<label for="username" class="sr-only">{{ lang('username') }}</label>
|
||||
<input type="text" id="username" class="form-control" placeholder="{{ lang('username') }}" name="username" required autofocus>
|
||||
<input type="text" id="username" class="form-control first" placeholder="{{ lang('username') }}" name="username" required autofocus>
|
||||
<label for="email" class="sr-only">E-Mail</label>
|
||||
<input type="email" id="email" class="form-control" placeholder="mail@example.com" name="email" required>
|
||||
<input type="email" id="email" class="form-control middle" placeholder="mail@example.com" name="email" required>
|
||||
<label for="password" class="sr-only">{{ lang('password') }}</label>
|
||||
<input type="password" id="password" class="form-control" placeholder="{{ lang('password') }}" name="password" required>
|
||||
<input type="password" id="password" class="form-control last" placeholder="{{ lang('password') }}" name="password" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-2">
|
||||
|
|
|
@ -28,18 +28,18 @@ body {
|
|||
z-index: 2;
|
||||
}
|
||||
|
||||
.form-signin input[type="text"] {
|
||||
.form-signin input.first {
|
||||
margin-bottom: -1px;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.form-signin input[type="email"] {
|
||||
.form-signin input.middle {
|
||||
margin-bottom: -1px;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.form-signin input[type="password"] {
|
||||
.form-signin input.last {
|
||||
margin-bottom: .50rem;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
|
|
Loading…
Add table
Reference in a new issue