Rebuild password reset
This commit is contained in:
parent
8f8130634e
commit
dd03662968
|
@ -13,6 +13,12 @@ $route->get('/login', 'AuthController@login');
|
||||||
$route->post('/login', 'AuthController@postLogin');
|
$route->post('/login', 'AuthController@postLogin');
|
||||||
$route->get('/logout', 'AuthController@logout');
|
$route->get('/logout', 'AuthController@logout');
|
||||||
|
|
||||||
|
// Password recovery
|
||||||
|
$route->get('/password/reset', 'PasswordResetController@reset');
|
||||||
|
$route->post('/password/reset', 'PasswordResetController@postReset');
|
||||||
|
$route->get('/password/reset/{token:.+}', 'PasswordResetController@resetPassword');
|
||||||
|
$route->post('/password/reset/{token:.+}', 'PasswordResetController@postResetPassword');
|
||||||
|
|
||||||
// Stats
|
// Stats
|
||||||
$route->get('/metrics', 'Metrics\\Controller@metrics');
|
$route->get('/metrics', 'Metrics\\Controller@metrics');
|
||||||
$route->get('/stats', 'Metrics\\Controller@stats');
|
$route->get('/stats', 'Metrics\\Controller@stats');
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Engelsystem\Database\DB;
|
use Engelsystem\Database\DB;
|
||||||
use Engelsystem\Models\User\PasswordReset;
|
|
||||||
use Engelsystem\Models\User\State;
|
use Engelsystem\Models\User\State;
|
||||||
use Engelsystem\Models\User\User;
|
use Engelsystem\Models\User\User;
|
||||||
use Engelsystem\ShiftCalendarRenderer;
|
use Engelsystem\ShiftCalendarRenderer;
|
||||||
|
@ -311,120 +310,6 @@ function users_list_controller()
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Second step of password recovery: set a new password using the token link from email
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function user_password_recovery_set_new_controller()
|
|
||||||
{
|
|
||||||
$request = request();
|
|
||||||
$passwordReset = PasswordReset::whereToken($request->input('token'))->first();
|
|
||||||
if (!$passwordReset) {
|
|
||||||
error(__('Token is not correct.'));
|
|
||||||
redirect(page_link_to('login'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($request->hasPostData('submit')) {
|
|
||||||
$valid = true;
|
|
||||||
|
|
||||||
if (
|
|
||||||
$request->has('password')
|
|
||||||
&& strlen($request->postData('password')) >= config('min_password_length')
|
|
||||||
) {
|
|
||||||
if ($request->postData('password') != $request->postData('password2')) {
|
|
||||||
$valid = false;
|
|
||||||
error(__('Your passwords don\'t match.'));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$valid = false;
|
|
||||||
error(__('Your password is to short (please use at least 6 characters).'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($valid) {
|
|
||||||
auth()->setPassword($passwordReset->user, $request->postData('password'));
|
|
||||||
success(__('Password saved.'));
|
|
||||||
$passwordReset->delete();
|
|
||||||
redirect(page_link_to('login'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return User_password_set_view();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* First step of password recovery: display a form that asks for your email and send email with recovery link
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function user_password_recovery_start_controller()
|
|
||||||
{
|
|
||||||
$request = request();
|
|
||||||
if ($request->hasPostData('submit')) {
|
|
||||||
$valid = true;
|
|
||||||
|
|
||||||
$user_source = null;
|
|
||||||
if ($request->has('email') && strlen(strip_request_item('email')) > 0) {
|
|
||||||
$email = strip_request_item('email');
|
|
||||||
if (check_email($email)) {
|
|
||||||
/** @var User $user_source */
|
|
||||||
$user_source = User::whereEmail($email)->first();
|
|
||||||
if (!$user_source) {
|
|
||||||
$valid = false;
|
|
||||||
error(__('E-mail address is not correct.'));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$valid = false;
|
|
||||||
error(__('E-mail address is not correct.'));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$valid = false;
|
|
||||||
error(__('Please enter your e-mail.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($valid) {
|
|
||||||
$token = User_generate_password_recovery_token($user_source);
|
|
||||||
engelsystem_email_to_user(
|
|
||||||
$user_source,
|
|
||||||
__('Password recovery'),
|
|
||||||
sprintf(
|
|
||||||
__('Please visit %s to recover your password.'),
|
|
||||||
page_link_to('user_password_recovery', ['token' => $token])
|
|
||||||
)
|
|
||||||
);
|
|
||||||
success(__('We sent an email containing your password recovery link.'));
|
|
||||||
redirect(page_link_to('login'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return User_password_recovery_view();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User password recovery in 2 steps.
|
|
||||||
* (By email)
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function user_password_recovery_controller()
|
|
||||||
{
|
|
||||||
if (request()->has('token')) {
|
|
||||||
return user_password_recovery_set_new_controller();
|
|
||||||
}
|
|
||||||
|
|
||||||
return user_password_recovery_start_controller();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Menu title for password recovery.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function user_password_recovery_title()
|
|
||||||
{
|
|
||||||
return __('Password recovery');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a user from param user_id.
|
* Loads a user from param user_id.
|
||||||
*
|
*
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Engelsystem\Database\DB;
|
use Engelsystem\Database\DB;
|
||||||
use Engelsystem\Models\User\PasswordReset;
|
|
||||||
use Engelsystem\Models\User\User;
|
use Engelsystem\Models\User\User;
|
||||||
use Engelsystem\ValidationResult;
|
use Engelsystem\ValidationResult;
|
||||||
use Illuminate\Database\Query\JoinClause;
|
use Illuminate\Database\Query\JoinClause;
|
||||||
|
@ -227,24 +226,6 @@ function User_reset_api_key($user, $log = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a new password recovery token for given user.
|
|
||||||
*
|
|
||||||
* @param User $user
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function User_generate_password_recovery_token($user)
|
|
||||||
{
|
|
||||||
$reset = PasswordReset::findOrNew($user->id);
|
|
||||||
$reset->user_id = $user->id;
|
|
||||||
$reset->token = md5($user->name . time() . rand());
|
|
||||||
$reset->save();
|
|
||||||
|
|
||||||
engelsystem_log('Password recovery for ' . User_Nick_render($user, true) . ' started.');
|
|
||||||
|
|
||||||
return $reset->token;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param User $user
|
* @param User $user
|
||||||
* @return float
|
* @return float
|
||||||
|
|
|
@ -759,41 +759,6 @@ function User_view_state_admin($freeloader, $user_source)
|
||||||
return $state;
|
return $state;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* View for password recovery step 1: E-Mail
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function User_password_recovery_view()
|
|
||||||
{
|
|
||||||
return page_with_title(user_password_recovery_title(), [
|
|
||||||
msg(),
|
|
||||||
__('We will send you an e-mail with a password recovery link. Please use the email address you used for registration.'),
|
|
||||||
form([
|
|
||||||
form_text('email', __('E-Mail'), ''),
|
|
||||||
form_submit('submit', __('Recover'))
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* View for password recovery step 2: New password
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function User_password_set_view()
|
|
||||||
{
|
|
||||||
return page_with_title(user_password_recovery_title(), [
|
|
||||||
msg(),
|
|
||||||
__('Please enter a new password.'),
|
|
||||||
form([
|
|
||||||
form_password('password', __('Password')),
|
|
||||||
form_password('password2', __('Confirm password')),
|
|
||||||
form_submit('submit', __('Save'))
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array[] $user_angeltypes
|
* @param array[] $user_angeltypes
|
||||||
* @return string
|
* @return string
|
||||||
|
|
Binary file not shown.
|
@ -619,9 +619,9 @@ msgid "Please visit %s to recover your password."
|
||||||
msgstr "Bitte besuche %s, um Dein Passwort zurückzusetzen"
|
msgstr "Bitte besuche %s, um Dein Passwort zurückzusetzen"
|
||||||
|
|
||||||
#: includes/controller/users_controller.php:394
|
#: includes/controller/users_controller.php:394
|
||||||
msgid "We sent an email containing your password recovery link."
|
msgid "We sent you an email containing your password recovery link."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Wir haben eine eMail mit einem Link zum Passwort-zurücksetzen geschickt."
|
"Wir haben dir eine eMail mit einem Link zum Passwort-zurücksetzen geschickt."
|
||||||
|
|
||||||
#: includes/helper/email_helper.php:41
|
#: includes/helper/email_helper.php:41
|
||||||
#, php-format
|
#, php-format
|
||||||
|
@ -2769,3 +2769,21 @@ msgstr "Bitte gib ein Passwort an."
|
||||||
|
|
||||||
msgid "validation.login.required"
|
msgid "validation.login.required"
|
||||||
msgstr "Bitte gib einen Loginnamen an."
|
msgstr "Bitte gib einen Loginnamen an."
|
||||||
|
|
||||||
|
msgid "form.submit"
|
||||||
|
msgstr "Absenden"
|
||||||
|
|
||||||
|
msgid "validation.email.required"
|
||||||
|
msgstr "Bitte gib eine E-Mail-Adresse an."
|
||||||
|
|
||||||
|
msgid "validation.email.email"
|
||||||
|
msgstr "Die E-Mail-Adresse ist nicht gültig."
|
||||||
|
|
||||||
|
msgid "validation.password.min"
|
||||||
|
msgstr "Dein angegebenes Passwort ist zu kurz."
|
||||||
|
|
||||||
|
msgid "validation.password.confirmed"
|
||||||
|
msgstr "Deine Passwörter stimmen nicht überein."
|
||||||
|
|
||||||
|
msgid "validation.password_confirmation.required"
|
||||||
|
msgstr "Du musst dein Passwort bestätigen."
|
||||||
|
|
Binary file not shown.
|
@ -30,3 +30,21 @@ msgstr "The password is required."
|
||||||
|
|
||||||
msgid "validation.login.required"
|
msgid "validation.login.required"
|
||||||
msgstr "The login name is required."
|
msgstr "The login name is required."
|
||||||
|
|
||||||
|
msgid "form.submit"
|
||||||
|
msgstr "Submit"
|
||||||
|
|
||||||
|
msgid "validation.email.required"
|
||||||
|
msgstr "The email address is required."
|
||||||
|
|
||||||
|
msgid "validation.email.email"
|
||||||
|
msgstr "This email address is not valid."
|
||||||
|
|
||||||
|
msgid "validation.password.min"
|
||||||
|
msgstr "Your password is too short."
|
||||||
|
|
||||||
|
msgid "validation.password.confirmed"
|
||||||
|
msgstr "Your passwords are not equal."
|
||||||
|
|
||||||
|
msgid "validation.password_confirmation.required"
|
||||||
|
msgstr "You have to confirm your password."
|
||||||
|
|
Binary file not shown.
|
@ -551,7 +551,7 @@ msgid "Please visit %s to recover your password."
|
||||||
msgstr "Por favor visite %s para recuperar sua senha"
|
msgstr "Por favor visite %s para recuperar sua senha"
|
||||||
|
|
||||||
#: includes/controller/users_controller.php:271
|
#: includes/controller/users_controller.php:271
|
||||||
msgid "We sent an email containing your password recovery link."
|
msgid "We sent you an email containing your password recovery link."
|
||||||
msgstr "Nós enviamos um email com o link para recuperação da sua senha."
|
msgstr "Nós enviamos um email com o link para recuperação da sua senha."
|
||||||
|
|
||||||
#: includes/helper/email_helper.php:12
|
#: includes/helper/email_helper.php:12
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{ __('Hi %s,', [username]) }}
|
{% block title %}{{ __('Hi %s,', [username]) }}{% endblock %}
|
||||||
|
|
||||||
{{ __('here is a message for you from the %s:', [config('app_name')]) }}
|
{% block introduction %}{{ __('here is a message for you from the %s:', [config('app_name')]) }}{% endblock %}
|
||||||
{{ message|raw }}
|
{% block message %}{{ message|raw }}{% endblock %}
|
||||||
|
|
||||||
{{ __('This email is autogenerated and has not been signed. You got this email because you are registered in the %s.', [config('app_name')]) }}
|
{% block footer %}{{ __('This email is autogenerated and has not been signed. You got this email because you are registered in the %s.', [config('app_name')]) }}{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
{% extends "emails/mail.twig" %}
|
||||||
|
|
||||||
|
{% block message %}{{ __('Please visit %s to recover your password.', [url('/password/reset/') ~ reset.token]) }}{% endblock %}
|
|
@ -0,0 +1,18 @@
|
||||||
|
{% macro input(name, label, type, required) %}
|
||||||
|
<div class="form-group">
|
||||||
|
{% if label %}
|
||||||
|
<label for="{{ name }}">{{ label }}</label>
|
||||||
|
{% endif %}
|
||||||
|
<input type="{{ type|default('text') }}" class="form-control" id="{{ name }}" name="{{ name }}"
|
||||||
|
{%- if required|default(false) %} required="required"{% endif -%}
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro hidden(name, value) %}
|
||||||
|
<input type="hidden" id="{{ name }}" name="{{ name }}" value="{{ value }}">
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro submit(label) %}
|
||||||
|
<button type="submit" class="btn btn-default">{{ label|default(__('form.submit')) }}</button>
|
||||||
|
{% endmacro %}
|
|
@ -62,7 +62,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<a href="{{ url('user-password-recovery') }}" class="">
|
<a href="{{ url('/password/reset') }}" class="">
|
||||||
{{ __('I forgot my password') }}
|
{{ __('I forgot my password') }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
{% extends "pages/password/reset.twig" %}
|
||||||
|
{% import 'macros/base.twig' as m %}
|
||||||
|
{% import 'macros/form.twig' as f %}
|
||||||
|
|
||||||
|
{% block row_content %}
|
||||||
|
<div class="col-md-8">
|
||||||
|
<form action="" enctype="multipart/form-data" method="post">
|
||||||
|
{{ csrf() }}
|
||||||
|
|
||||||
|
{{ f.input('password', __('Password'), 'password', true) }}
|
||||||
|
{{ f.input('password_confirmation', __('Confirm password'), 'password', true) }}
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
{{ f.submit(__('Save')) }}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{% extends "pages/password/reset.twig" %}
|
||||||
|
{% import 'macros/base.twig' as m %}
|
||||||
|
|
||||||
|
{% block row_content %}
|
||||||
|
<div class="col-md-12">
|
||||||
|
{% if type == 'email' %}
|
||||||
|
{{ m.alert(__('We sent you an email containing your password recovery link.'), 'info') }}
|
||||||
|
{% elseif type == 'reset' %}
|
||||||
|
{{ m.alert(__('Password saved.'), 'success') }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,32 @@
|
||||||
|
{% extends 'layouts/app.twig' %}
|
||||||
|
{% import 'macros/base.twig' as m %}
|
||||||
|
{% import 'macros/form.twig' as f %}
|
||||||
|
|
||||||
|
{% block title %}{{ __('Password recovery') }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<h1>{{ __('Password recovery') }}</h1>
|
||||||
|
|
||||||
|
{% for message in errors|default([]) %}
|
||||||
|
{{ m.alert(__(message), 'danger') }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
{% block row_content %}
|
||||||
|
<div class="col-md-8">
|
||||||
|
<form action="" enctype="multipart/form-data" method="post">
|
||||||
|
{{ csrf() }}
|
||||||
|
|
||||||
|
{{ __('We will send you an e-mail with a password recovery link. Please use the email address you used for registration.') }}
|
||||||
|
{{ f.input('email', __('E-Mail'), 'email', true) }}
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
{{ f.submit(__('Recover')) }}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,167 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Controllers;
|
||||||
|
|
||||||
|
use Engelsystem\Http\Exceptions\HttpNotFound;
|
||||||
|
use Engelsystem\Http\Request;
|
||||||
|
use Engelsystem\Http\Response;
|
||||||
|
use Engelsystem\Mail\EngelsystemMailer;
|
||||||
|
use Engelsystem\Models\User\PasswordReset;
|
||||||
|
use Engelsystem\Models\User\User;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||||
|
|
||||||
|
class PasswordResetController extends BaseController
|
||||||
|
{
|
||||||
|
/** @var LoggerInterface */
|
||||||
|
protected $log;
|
||||||
|
|
||||||
|
/** @var EngelsystemMailer */
|
||||||
|
protected $mail;
|
||||||
|
|
||||||
|
/** @var Response */
|
||||||
|
protected $response;
|
||||||
|
|
||||||
|
/** @var SessionInterface */
|
||||||
|
protected $session;
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
protected $permissions = [
|
||||||
|
'reset' => 'login',
|
||||||
|
'postReset' => 'login',
|
||||||
|
'resetPassword' => 'login',
|
||||||
|
'postResetPassword' => 'login',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Response $response
|
||||||
|
* @param SessionInterface $session
|
||||||
|
* @param EngelsystemMailer $mail
|
||||||
|
* @param LoggerInterface $log
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
Response $response,
|
||||||
|
SessionInterface $session,
|
||||||
|
EngelsystemMailer $mail,
|
||||||
|
LoggerInterface $log
|
||||||
|
) {
|
||||||
|
$this->log = $log;
|
||||||
|
$this->mail = $mail;
|
||||||
|
$this->response = $response;
|
||||||
|
$this->session = $session;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function reset(): Response
|
||||||
|
{
|
||||||
|
return $this->showView('pages/password/reset');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function postReset(Request $request): Response
|
||||||
|
{
|
||||||
|
$data = $this->validate($request, [
|
||||||
|
'email' => 'required|email',
|
||||||
|
]);
|
||||||
|
|
||||||
|
/** @var User $user */
|
||||||
|
$user = User::whereEmail($data['email'])->first();
|
||||||
|
if ($user) {
|
||||||
|
$reset = PasswordReset::findOrNew($user->id);
|
||||||
|
$reset->user_id = $user->id;
|
||||||
|
$reset->token = md5(random_bytes(64));
|
||||||
|
$reset->save();
|
||||||
|
|
||||||
|
$this->log->info(
|
||||||
|
sprintf('Password recovery for %s (%u)', $user->name, $user->id),
|
||||||
|
['user' => $user->toJson()]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->mail->sendViewTranslated(
|
||||||
|
$user,
|
||||||
|
'Password recovery',
|
||||||
|
'emails/password-reset',
|
||||||
|
['username' => $user->name, 'reset' => $reset]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->showView('pages/password/reset-success', ['type' => 'email']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function resetPassword(Request $request): Response
|
||||||
|
{
|
||||||
|
$this->requireToken($request);
|
||||||
|
|
||||||
|
return $this->showView('pages/password/reset-form');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function postResetPassword(Request $request): Response
|
||||||
|
{
|
||||||
|
$reset = $this->requireToken($request);
|
||||||
|
|
||||||
|
$data = $this->validate($request, [
|
||||||
|
'password' => 'required|min:' . config('min_password_length'),
|
||||||
|
'password_confirmation' => 'required',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($data['password'] !== $data['password_confirmation']) {
|
||||||
|
$this->session->set('errors',
|
||||||
|
array_merge($this->session->get('errors', []), ['validation.password.confirmed']));
|
||||||
|
|
||||||
|
return $this->showView('pages/password/reset-form');
|
||||||
|
}
|
||||||
|
|
||||||
|
auth()->setPassword($reset->user, $data['password']);
|
||||||
|
$reset->delete();
|
||||||
|
|
||||||
|
return $this->showView('pages/password/reset-success', ['type' => 'reset']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $view
|
||||||
|
* @param array $data
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
protected function showView($view = 'pages/password/reset', $data = []): Response
|
||||||
|
{
|
||||||
|
$errors = Collection::make(Arr::flatten($this->session->get('errors', [])));
|
||||||
|
$this->session->remove('errors');
|
||||||
|
|
||||||
|
return $this->response->withView(
|
||||||
|
$view,
|
||||||
|
array_merge_recursive(['errors' => $errors], $data)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @return PasswordReset
|
||||||
|
*/
|
||||||
|
protected function requireToken(Request $request): PasswordReset
|
||||||
|
{
|
||||||
|
$token = $request->getAttribute('token');
|
||||||
|
/** @var PasswordReset|null $reset */
|
||||||
|
$reset = PasswordReset::whereToken($token)->first();
|
||||||
|
|
||||||
|
if (!$reset) {
|
||||||
|
throw new HttpNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $reset;
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,7 +26,6 @@ class LegacyMiddleware implements MiddlewareInterface
|
||||||
'shifts_json_export',
|
'shifts_json_export',
|
||||||
'users',
|
'users',
|
||||||
'user_driver_licenses',
|
'user_driver_licenses',
|
||||||
'user_password_recovery',
|
|
||||||
'user_worklog',
|
'user_worklog',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -112,11 +111,6 @@ class LegacyMiddleware implements MiddlewareInterface
|
||||||
case 'shifts_json_export':
|
case 'shifts_json_export':
|
||||||
require_once realpath(__DIR__ . '/../../includes/controller/shifts_controller.php');
|
require_once realpath(__DIR__ . '/../../includes/controller/shifts_controller.php');
|
||||||
shifts_json_export_controller();
|
shifts_json_export_controller();
|
||||||
case 'user_password_recovery':
|
|
||||||
require_once realpath(__DIR__ . '/../../includes/controller/users_controller.php');
|
|
||||||
$title = user_password_recovery_title();
|
|
||||||
$content = user_password_recovery_controller();
|
|
||||||
return [$title, $content];
|
|
||||||
case 'public_dashboard':
|
case 'public_dashboard':
|
||||||
return public_dashboard_controller();
|
return public_dashboard_controller();
|
||||||
case 'angeltypes':
|
case 'angeltypes':
|
||||||
|
|
|
@ -0,0 +1,266 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Test\Unit\Controllers;
|
||||||
|
|
||||||
|
use Engelsystem\Config\Config;
|
||||||
|
use Engelsystem\Controllers\PasswordResetController;
|
||||||
|
use Engelsystem\Helpers\Authenticator;
|
||||||
|
use Engelsystem\Http\Exceptions\HttpNotFound;
|
||||||
|
use Engelsystem\Http\Exceptions\ValidationException;
|
||||||
|
use Engelsystem\Http\Request;
|
||||||
|
use Engelsystem\Http\Response;
|
||||||
|
use Engelsystem\Http\Validation\Validator;
|
||||||
|
use Engelsystem\Mail\EngelsystemMailer;
|
||||||
|
use Engelsystem\Models\User\PasswordReset;
|
||||||
|
use Engelsystem\Models\User\User;
|
||||||
|
use Engelsystem\Renderer\Renderer;
|
||||||
|
use Engelsystem\Test\Unit\HasDatabase;
|
||||||
|
use Engelsystem\Test\Unit\TestCase;
|
||||||
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
|
use Psr\Log\Test\TestLogger;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Session;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
||||||
|
|
||||||
|
class PasswordResetControllerTest extends TestCase
|
||||||
|
{
|
||||||
|
use HasDatabase;
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
protected $args = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\PasswordResetController::reset
|
||||||
|
* @covers \Engelsystem\Controllers\PasswordResetController::__construct
|
||||||
|
*/
|
||||||
|
public function testReset(): void
|
||||||
|
{
|
||||||
|
$controller = $this->getController('pages/password/reset');
|
||||||
|
$response = $controller->reset();
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\PasswordResetController::postReset
|
||||||
|
*/
|
||||||
|
public function testPostReset(): void
|
||||||
|
{
|
||||||
|
$this->initDatabase();
|
||||||
|
$request = new Request([], ['email' => 'foo@bar.batz']);
|
||||||
|
$user = $this->createUser();
|
||||||
|
|
||||||
|
$controller = $this->getController(
|
||||||
|
'pages/password/reset-success',
|
||||||
|
['type' => 'email', 'errors' => collect()]
|
||||||
|
);
|
||||||
|
/** @var TestLogger $log */
|
||||||
|
$log = $this->args['log'];
|
||||||
|
/** @var EngelsystemMailer|MockObject $mailer */
|
||||||
|
$mailer = $this->args['mailer'];
|
||||||
|
$this->setExpects($mailer, 'sendViewTranslated');
|
||||||
|
|
||||||
|
$controller->postReset($request);
|
||||||
|
|
||||||
|
$this->assertNotEmpty(PasswordReset::find($user->id)->first());
|
||||||
|
$this->assertTrue($log->hasInfoThatContains($user->name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\PasswordResetController::postReset
|
||||||
|
*/
|
||||||
|
public function testPostResetInvalidRequest(): void
|
||||||
|
{
|
||||||
|
$request = new Request();
|
||||||
|
|
||||||
|
$controller = $this->getController();
|
||||||
|
|
||||||
|
$this->expectException(ValidationException::class);
|
||||||
|
$controller->postReset($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\PasswordResetController::postReset
|
||||||
|
*/
|
||||||
|
public function testPostResetNoUser(): void
|
||||||
|
{
|
||||||
|
$this->initDatabase();
|
||||||
|
$request = new Request([], ['email' => 'foo@bar.batz']);
|
||||||
|
|
||||||
|
$controller = $this->getController(
|
||||||
|
'pages/password/reset-success',
|
||||||
|
['type' => 'email', 'errors' => collect()]
|
||||||
|
);
|
||||||
|
|
||||||
|
$controller->postReset($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\PasswordResetController::resetPassword
|
||||||
|
* @covers \Engelsystem\Controllers\PasswordResetController::requireToken
|
||||||
|
*/
|
||||||
|
public function testResetPassword(): void
|
||||||
|
{
|
||||||
|
$this->initDatabase();
|
||||||
|
|
||||||
|
$user = $this->createUser();
|
||||||
|
$token = $this->createToken($user);
|
||||||
|
$request = new Request([], [], ['token' => $token->token]);
|
||||||
|
|
||||||
|
$controller = $this->getController('pages/password/reset-form');
|
||||||
|
|
||||||
|
$controller->resetPassword($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\PasswordResetController::resetPassword
|
||||||
|
* @covers \Engelsystem\Controllers\PasswordResetController::requireToken
|
||||||
|
*/
|
||||||
|
public function testResetPasswordNoToken(): void
|
||||||
|
{
|
||||||
|
$this->initDatabase();
|
||||||
|
$controller = $this->getController();
|
||||||
|
|
||||||
|
$this->expectException(HttpNotFound::class);
|
||||||
|
$controller->resetPassword(new Request());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\PasswordResetController::postResetPassword
|
||||||
|
*/
|
||||||
|
public function testPostResetPassword(): void
|
||||||
|
{
|
||||||
|
$this->initDatabase();
|
||||||
|
|
||||||
|
$this->app->instance('config', new Config(['min_password_length' => 3]));
|
||||||
|
$user = $this->createUser();
|
||||||
|
$token = $this->createToken($user);
|
||||||
|
$password = 'SomeRandomPasswordForAmazingSecurity';
|
||||||
|
$request = new Request(
|
||||||
|
[],
|
||||||
|
['password' => $password, 'password_confirmation' => $password],
|
||||||
|
['token' => $token->token]
|
||||||
|
);
|
||||||
|
|
||||||
|
$controller = $this->getController(
|
||||||
|
'pages/password/reset-success',
|
||||||
|
['type' => 'reset', 'errors' => collect()]
|
||||||
|
);
|
||||||
|
|
||||||
|
$auth = new Authenticator($request, $this->args['session'], $user);
|
||||||
|
$this->app->instance('authenticator', $auth);
|
||||||
|
|
||||||
|
$response = $controller->postResetPassword($request);
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
$this->assertEmpty(PasswordReset::find($user->id));
|
||||||
|
$this->assertNotNull(auth()->authenticate($user->name, $password));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\PasswordResetController::postResetPassword
|
||||||
|
* @covers \Engelsystem\Controllers\PasswordResetController::showView
|
||||||
|
*/
|
||||||
|
public function testPostResetPasswordNotMatching(): void
|
||||||
|
{
|
||||||
|
$this->initDatabase();
|
||||||
|
|
||||||
|
$this->app->instance('config', new Config(['min_password_length' => 3]));
|
||||||
|
$user = $this->createUser();
|
||||||
|
$token = $this->createToken($user);
|
||||||
|
$password = 'SomeRandomPasswordForAmazingSecurity';
|
||||||
|
$request = new Request(
|
||||||
|
[],
|
||||||
|
['password' => $password, 'password_confirmation' => $password . 'OrNot'],
|
||||||
|
['token' => $token->token]
|
||||||
|
);
|
||||||
|
|
||||||
|
$controller = $this->getController(
|
||||||
|
'pages/password/reset-form',
|
||||||
|
['errors' => collect(['some.other.error', 'validation.password.confirmed'])]
|
||||||
|
);
|
||||||
|
/** @var Session $session */
|
||||||
|
$session = $this->args['session'];
|
||||||
|
$session->set('errors', ['foo' => ['bar' => 'some.other.error']]);
|
||||||
|
|
||||||
|
$controller->postResetPassword($request);
|
||||||
|
$this->assertEmpty($session->get('errors'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function getControllerArgs(): array
|
||||||
|
{
|
||||||
|
$response = new Response();
|
||||||
|
$session = new Session(new MockArraySessionStorage());
|
||||||
|
/** @var EngelsystemMailer|MockObject $mailer */
|
||||||
|
$mailer = $this->createMock(EngelsystemMailer::class);
|
||||||
|
$log = new TestLogger();
|
||||||
|
$renderer = $this->createMock(Renderer::class);
|
||||||
|
$response->setRenderer($renderer);
|
||||||
|
|
||||||
|
return $this->args = [
|
||||||
|
'response' => $response,
|
||||||
|
'session' => $session,
|
||||||
|
'mailer' => $mailer,
|
||||||
|
'log' => $log,
|
||||||
|
'renderer' => $renderer
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $view
|
||||||
|
* @param array $data
|
||||||
|
* @return PasswordResetController
|
||||||
|
*/
|
||||||
|
protected function getController(?string $view = null, ?array $data = null): PasswordResetController
|
||||||
|
{
|
||||||
|
/** @var Response $response */
|
||||||
|
/** @var Session $session */
|
||||||
|
/** @var EngelsystemMailer|MockObject $mailer */
|
||||||
|
/** @var TestLogger $log */
|
||||||
|
/** @var Renderer|MockObject $renderer */
|
||||||
|
list($response, $session, $mailer, $log, $renderer) = array_values($this->getControllerArgs());
|
||||||
|
$controller = new PasswordResetController($response, $session, $mailer, $log);
|
||||||
|
$controller->setValidator(new Validator());
|
||||||
|
|
||||||
|
if ($view) {
|
||||||
|
$args = [$view];
|
||||||
|
if ($data) {
|
||||||
|
$args[] = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setExpects($renderer, 'render', $args, 'Foo');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return User
|
||||||
|
*/
|
||||||
|
protected function createUser(): User
|
||||||
|
{
|
||||||
|
$user = new User([
|
||||||
|
'name' => 'foo',
|
||||||
|
'password' => '',
|
||||||
|
'email' => 'foo@bar.batz',
|
||||||
|
'api_key' => '',
|
||||||
|
]);
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
* @return PasswordReset
|
||||||
|
*/
|
||||||
|
protected function createToken(User $user): PasswordReset
|
||||||
|
{
|
||||||
|
$reset = new PasswordReset(['user_id' => $user->id, 'token' => 'SomeTestToken123']);
|
||||||
|
$reset->save();
|
||||||
|
|
||||||
|
return $reset;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue