Implemented AuthController for login
* Moved /login functionality to AuthController * Refactored password handling logic to use the Authenticator
This commit is contained in:
parent
fd4303f336
commit
bcce2625a8
|
@ -95,13 +95,10 @@ return [
|
||||||
// Number of hours that an angel has to sign out own shifts
|
// Number of hours that an angel has to sign out own shifts
|
||||||
'last_unsubscribe' => 3,
|
'last_unsubscribe' => 3,
|
||||||
|
|
||||||
// Define the algorithm to use for `crypt()` of passwords
|
// Define the algorithm to use for `password_verify()`
|
||||||
// If the user uses an old algorithm the password will be converted to the new format
|
// If the user uses an old algorithm the password will be converted to the new format
|
||||||
// MD5 '$1'
|
// See https://secure.php.net/manual/en/password.constants.php for a complete list
|
||||||
// Blowfish '$2y$13'
|
'password_algorithm' => PASSWORD_DEFAULT,
|
||||||
// SHA-256 '$5$rounds=5000'
|
|
||||||
// SHA-512 '$6$rounds=5000'
|
|
||||||
'crypt_alg' => '$6$rounds=5000',
|
|
||||||
|
|
||||||
// The minimum length for passwords
|
// The minimum length for passwords
|
||||||
'min_password_length' => 8,
|
'min_password_length' => 8,
|
||||||
|
|
|
@ -9,6 +9,8 @@ $route->get('/', 'HomeController@index');
|
||||||
$route->get('/credits', 'CreditsController@index');
|
$route->get('/credits', 'CreditsController@index');
|
||||||
|
|
||||||
// Authentication
|
// Authentication
|
||||||
|
$route->get('/login', 'AuthController@login');
|
||||||
|
$route->post('/login', 'AuthController@postLogin');
|
||||||
$route->get('/logout', 'AuthController@logout');
|
$route->get('/logout', 'AuthController@logout');
|
||||||
|
|
||||||
// Stats
|
// Stats
|
||||||
|
|
|
@ -28,7 +28,7 @@ class CreateUsersTables extends Migration
|
||||||
|
|
||||||
$table->string('name', 24)->unique();
|
$table->string('name', 24)->unique();
|
||||||
$table->string('email', 254)->unique();
|
$table->string('email', 254)->unique();
|
||||||
$table->string('password', 128);
|
$table->string('password', 255);
|
||||||
$table->string('api_key', 32);
|
$table->string('api_key', 32);
|
||||||
|
|
||||||
$table->dateTime('last_login_at')->nullable();
|
$table->dateTime('last_login_at')->nullable();
|
||||||
|
|
|
@ -47,6 +47,7 @@ function users_controller()
|
||||||
function user_delete_controller()
|
function user_delete_controller()
|
||||||
{
|
{
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
$auth = auth();
|
||||||
$request = request();
|
$request = request();
|
||||||
|
|
||||||
if ($request->has('user_id')) {
|
if ($request->has('user_id')) {
|
||||||
|
@ -68,12 +69,10 @@ function user_delete_controller()
|
||||||
if ($request->hasPostData('submit')) {
|
if ($request->hasPostData('submit')) {
|
||||||
$valid = true;
|
$valid = true;
|
||||||
|
|
||||||
if (
|
if (!(
|
||||||
!(
|
|
||||||
$request->has('password')
|
$request->has('password')
|
||||||
&& verify_password($request->postData('password'), $user->password, $user->id)
|
&& $auth->verifyPassword($user, $request->postData('password'))
|
||||||
)
|
)) {
|
||||||
) {
|
|
||||||
$valid = false;
|
$valid = false;
|
||||||
error(__('Your password is incorrect. Please try it again.'));
|
error(__('Your password is incorrect. Please try it again.'));
|
||||||
}
|
}
|
||||||
|
@ -341,7 +340,7 @@ function user_password_recovery_set_new_controller()
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($valid) {
|
if ($valid) {
|
||||||
set_password($passwordReset->user->id, $request->postData('password'));
|
auth()->setPassword($passwordReset->user, $request->postData('password'));
|
||||||
success(__('Password saved.'));
|
success(__('Password saved.'));
|
||||||
$passwordReset->delete();
|
$passwordReset->delete();
|
||||||
redirect(page_link_to('login'));
|
redirect(page_link_to('login'));
|
||||||
|
|
|
@ -291,8 +291,8 @@ function admin_user()
|
||||||
$request->postData('new_pw') != ''
|
$request->postData('new_pw') != ''
|
||||||
&& $request->postData('new_pw') == $request->postData('new_pw2')
|
&& $request->postData('new_pw') == $request->postData('new_pw2')
|
||||||
) {
|
) {
|
||||||
set_password($user_id, $request->postData('new_pw'));
|
|
||||||
$user_source = User::find($user_id);
|
$user_source = User::find($user_id);
|
||||||
|
auth()->setPassword($user_source, $request->postData('new_pw'));
|
||||||
engelsystem_log('Set new password for ' . User_Nick_render($user_source, true));
|
engelsystem_log('Set new password for ' . User_Nick_render($user_source, true));
|
||||||
$html .= success('Passwort neu gesetzt.', true);
|
$html .= success('Passwort neu gesetzt.', true);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -8,14 +8,6 @@ use Engelsystem\Models\User\Settings;
|
||||||
use Engelsystem\Models\User\State;
|
use Engelsystem\Models\User\State;
|
||||||
use Engelsystem\Models\User\User;
|
use Engelsystem\Models\User\User;
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function login_title()
|
|
||||||
{
|
|
||||||
return __('Login');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
@ -226,7 +218,7 @@ function guest_register()
|
||||||
|
|
||||||
// Assign user-group and set password
|
// Assign user-group and set password
|
||||||
DB::insert('INSERT INTO `UserGroups` (`uid`, `group_id`) VALUES (?, -20)', [$user->id]);
|
DB::insert('INSERT INTO `UserGroups` (`uid`, `group_id`) VALUES (?, -20)', [$user->id]);
|
||||||
set_password($user->id, $request->postData('password'));
|
auth()->setPassword($user, $request->postData('password'));
|
||||||
|
|
||||||
// Assign angel-types
|
// Assign angel-types
|
||||||
$user_angel_types_info = [];
|
$user_angel_types_info = [];
|
||||||
|
@ -369,112 +361,3 @@ function entry_required()
|
||||||
{
|
{
|
||||||
return '<span class="text-info glyphicon glyphicon-warning-sign"></span>';
|
return '<span class="text-info glyphicon glyphicon-warning-sign"></span>';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function guest_login()
|
|
||||||
{
|
|
||||||
$nick = '';
|
|
||||||
$request = request();
|
|
||||||
$session = session();
|
|
||||||
$valid = true;
|
|
||||||
|
|
||||||
$session->remove('uid');
|
|
||||||
|
|
||||||
if ($request->hasPostData('submit')) {
|
|
||||||
if ($request->has('nick') && !empty($request->input('nick'))) {
|
|
||||||
$nickValidation = User_validate_Nick($request->input('nick'));
|
|
||||||
$nick = $nickValidation->getValue();
|
|
||||||
$login_user = User::whereName($nickValidation->getValue())->first();
|
|
||||||
if ($login_user) {
|
|
||||||
if ($request->has('password')) {
|
|
||||||
if (!verify_password($request->postData('password'), $login_user->password, $login_user->id)) {
|
|
||||||
$valid = false;
|
|
||||||
error(__('Your password is incorrect. Please try it again.'));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$valid = false;
|
|
||||||
error(__('Please enter a password.'));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$valid = false;
|
|
||||||
error(__('No user was found with that Nickname. Please try again. If you are still having problems, ask a Dispatcher.'));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$valid = false;
|
|
||||||
error(__('Please enter a nickname.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($valid && $login_user) {
|
|
||||||
$session->set('uid', $login_user->id);
|
|
||||||
$session->set('locale', $login_user->settings->language);
|
|
||||||
|
|
||||||
redirect(page_link_to(config('home_site')));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return page([
|
|
||||||
div('col-md-12', [
|
|
||||||
div('row', [
|
|
||||||
EventConfig_countdown_page()
|
|
||||||
]),
|
|
||||||
div('row', [
|
|
||||||
div('col-sm-6 col-sm-offset-3 col-md-4 col-md-offset-4', [
|
|
||||||
div('panel panel-primary first', [
|
|
||||||
div('panel-heading', [
|
|
||||||
'<span class="icon-icon_angel"></span> ' . __('Login')
|
|
||||||
]),
|
|
||||||
div('panel-body', [
|
|
||||||
msg(),
|
|
||||||
form([
|
|
||||||
form_text_placeholder('nick', __('Nick'), $nick),
|
|
||||||
form_password_placeholder('password', __('Password')),
|
|
||||||
form_submit('submit', __('Login')),
|
|
||||||
!$valid ? buttons([
|
|
||||||
button(page_link_to('user_password_recovery'), __('I forgot my password'))
|
|
||||||
]) : ''
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
div('panel-footer', [
|
|
||||||
glyph('info-sign') . __('Please note: You have to activate cookies!')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
div('row', [
|
|
||||||
div('col-sm-6 text-center', [
|
|
||||||
heading(register_title(), 2),
|
|
||||||
get_register_hint()
|
|
||||||
]),
|
|
||||||
div('col-sm-6 text-center', [
|
|
||||||
heading(__('What can I do?'), 2),
|
|
||||||
'<p>' . __('Please read about the jobs you can do to help us.') . '</p>',
|
|
||||||
buttons([
|
|
||||||
button(
|
|
||||||
page_link_to('angeltypes', ['action' => 'about']),
|
|
||||||
__('Teams/Job description') . ' »'
|
|
||||||
)
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function get_register_hint()
|
|
||||||
{
|
|
||||||
if (auth()->can('register') && config('registration_enabled')) {
|
|
||||||
return join('', [
|
|
||||||
'<p>' . __('Please sign up, if you want to help us!') . '</p>',
|
|
||||||
buttons([
|
|
||||||
button(page_link_to('register'), register_title() . ' »')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return error(__('Registration is disabled.'), true);
|
|
||||||
}
|
|
||||||
|
|
|
@ -101,9 +101,10 @@ function user_settings_main($user_source, $enable_tshirt_size, $tshirt_sizes)
|
||||||
function user_settings_password($user_source)
|
function user_settings_password($user_source)
|
||||||
{
|
{
|
||||||
$request = request();
|
$request = request();
|
||||||
|
$auth = auth();
|
||||||
if (
|
if (
|
||||||
!$request->has('password')
|
!$request->has('password')
|
||||||
|| !verify_password($request->postData('password'), $user_source->password, $user_source->id)
|
|| !$auth->verifyPassword($user_source, $request->postData('password'))
|
||||||
) {
|
) {
|
||||||
error(__('-> not OK. Please try again.'));
|
error(__('-> not OK. Please try again.'));
|
||||||
} elseif (strlen($request->postData('new_password')) < config('min_password_length')) {
|
} elseif (strlen($request->postData('new_password')) < config('min_password_length')) {
|
||||||
|
@ -111,7 +112,7 @@ function user_settings_password($user_source)
|
||||||
} elseif ($request->postData('new_password') != $request->postData('new_password2')) {
|
} elseif ($request->postData('new_password') != $request->postData('new_password2')) {
|
||||||
error(__('Your passwords don\'t match.'));
|
error(__('Your passwords don\'t match.'));
|
||||||
} else {
|
} else {
|
||||||
set_password($user_source->id, $request->postData('new_password'));
|
$auth->setPassword($user_source, $request->postData('new_password'));
|
||||||
success(__('Password saved.'));
|
success(__('Password saved.'));
|
||||||
}
|
}
|
||||||
redirect(page_link_to('user_settings'));
|
redirect(page_link_to('user_settings'));
|
||||||
|
|
|
@ -1,74 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Engelsystem\Database\DB;
|
use Engelsystem\Database\DB;
|
||||||
use Engelsystem\Models\User\User;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* generate a salt (random string) of arbitrary length suitable for the use with crypt()
|
|
||||||
*
|
|
||||||
* @param int $length
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function generate_salt($length = 16)
|
|
||||||
{
|
|
||||||
$alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
||||||
$salt = '';
|
|
||||||
for ($i = 0; $i < $length; $i++) {
|
|
||||||
$salt .= $alphabet[rand(0, strlen($alphabet) - 1)];
|
|
||||||
}
|
|
||||||
return $salt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set the password of a user
|
|
||||||
*
|
|
||||||
* @param int $uid
|
|
||||||
* @param string $password
|
|
||||||
*/
|
|
||||||
function set_password($uid, $password)
|
|
||||||
{
|
|
||||||
$user = User::find($uid);
|
|
||||||
$user->password = crypt($password, config('crypt_alg') . '$' . generate_salt(16) . '$');
|
|
||||||
$user->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* verify a password given a precomputed salt.
|
|
||||||
* if $uid is given and $salt is an old-style salt (plain md5), we convert it automatically
|
|
||||||
*
|
|
||||||
* @param string $password
|
|
||||||
* @param string $salt
|
|
||||||
* @param int $uid
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
function verify_password($password, $salt, $uid = null)
|
|
||||||
{
|
|
||||||
$crypt_alg = config('crypt_alg');
|
|
||||||
$correct = false;
|
|
||||||
if (substr($salt, 0, 1) == '$') {
|
|
||||||
// new-style crypt()
|
|
||||||
$correct = crypt($password, $salt) == $salt;
|
|
||||||
} elseif (substr($salt, 0, 7) == '{crypt}') {
|
|
||||||
// old-style crypt() with DES and static salt - not used anymore
|
|
||||||
$correct = crypt($password, '77') == $salt;
|
|
||||||
} elseif (strlen($salt) == 32) {
|
|
||||||
// old-style md5 without salt - not used anymore
|
|
||||||
$correct = md5($password) == $salt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($correct && substr($salt, 0, strlen($crypt_alg)) != $crypt_alg && intval($uid)) {
|
|
||||||
// this password is stored in another format than we want it to be.
|
|
||||||
// let's update it!
|
|
||||||
// we duplicate the query from the above set_password() function to have the extra safety of checking
|
|
||||||
// the old hash
|
|
||||||
$user = User::find($uid);
|
|
||||||
if ($user->password == $salt) {
|
|
||||||
$user->password = crypt($password, $crypt_alg . '$' . generate_salt() . '$');
|
|
||||||
$user->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $correct;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int $user_id
|
* @param int $user_id
|
||||||
|
|
|
@ -578,7 +578,7 @@ function AngelTypes_about_view($angeltypes, $user_logged_in)
|
||||||
$buttons[] = button(page_link_to('register'), register_title());
|
$buttons[] = button(page_link_to('register'), register_title());
|
||||||
}
|
}
|
||||||
|
|
||||||
$buttons[] = button(page_link_to('login'), login_title());
|
$buttons[] = button(page_link_to('login'), __('Login'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$faqUrl = config('faq_url');
|
$faqUrl = config('faq_url');
|
||||||
|
|
|
@ -126,7 +126,7 @@ function User_registration_success_view($event_welcome_message)
|
||||||
div('col-md-4', [
|
div('col-md-4', [
|
||||||
'<h2>' . __('Login') . '</h2>',
|
'<h2>' . __('Login') . '</h2>',
|
||||||
form([
|
form([
|
||||||
form_text('nick', __('Nick'), ''),
|
form_text('login', __('Nick'), ''),
|
||||||
form_password('password', __('Password')),
|
form_password('password', __('Password')),
|
||||||
form_submit('submit', __('Login')),
|
form_submit('submit', __('Login')),
|
||||||
buttons([
|
buttons([
|
||||||
|
|
|
@ -1530,18 +1530,21 @@ msgid "Entry required!"
|
||||||
msgstr "Pflichtfeld!"
|
msgstr "Pflichtfeld!"
|
||||||
|
|
||||||
#: includes/pages/guest_login.php:414
|
#: includes/pages/guest_login.php:414
|
||||||
msgid "Please enter a password."
|
msgid "auth.no-password"
|
||||||
msgstr "Gib bitte ein Passwort ein."
|
msgstr "Gib bitte ein Passwort ein."
|
||||||
|
|
||||||
#: includes/pages/guest_login.php:418
|
#: includes/pages/guest_login.php:418
|
||||||
msgid ""
|
msgid "auth.not-found"
|
||||||
"No user was found with that Nickname. Please try again. If you are still "
|
|
||||||
"having problems, ask a Dispatcher."
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Es wurde kein Engel mit diesem Namen gefunden. Probiere es bitte noch "
|
"Es wurde kein Engel gefunden. Probiere es bitte noch einmal. Wenn das Problem "
|
||||||
"einmal. Wenn das Problem weiterhin besteht, frage einen Dispatcher."
|
"weiterhin besteht, melde dich im Himmel."
|
||||||
|
|
||||||
#: includes/pages/guest_login.php:451 includes/view/User_view.php:130
|
#: includes/pages/guest_login.php:451 includes/view/User_view.php:130
|
||||||
|
msgid "auth.no-nickname"
|
||||||
|
msgstr "Gib bitte einen Nick an."
|
||||||
|
|
||||||
|
#: includes/pages/guest_login.php:481
|
||||||
|
#: includes/view/User_view.php:122
|
||||||
msgid "I forgot my password"
|
msgid "I forgot my password"
|
||||||
msgstr "Passwort vergessen"
|
msgstr "Passwort vergessen"
|
||||||
|
|
||||||
|
@ -2357,7 +2360,7 @@ msgid ""
|
||||||
"I have my own car with me and am willing to use it for the event (You'll get "
|
"I have my own car with me and am willing to use it for the event (You'll get "
|
||||||
"reimbursed for fuel)"
|
"reimbursed for fuel)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Ich habe mein eigenes Auto dabei und möchte würde es zum Fahren für das "
|
"Ich habe mein eigenes Auto dabei und möchte es zum Fahren für das "
|
||||||
"Event verwenden (Du wirst für Spritkosten entschädigt)"
|
"Event verwenden (Du wirst für Spritkosten entschädigt)"
|
||||||
|
|
||||||
#: includes/view/UserDriverLicenses_view.php:30
|
#: includes/view/UserDriverLicenses_view.php:30
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,26 @@
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Engelsystem 2.0\n"
|
||||||
|
"POT-Creation-Date: 2017-12-29 19:01+0100\n"
|
||||||
|
"PO-Revision-Date: 2018-11-27 00:28+0100\n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Generator: Poedit 1.8.11\n"
|
||||||
|
"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
|
||||||
|
"X-Poedit-Basepath: .\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Poedit-SourceCharset: UTF-8\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language: en_US\n"
|
||||||
|
"X-Poedit-SearchPath-0: .\n"
|
||||||
|
|
||||||
|
msgid "auth.no-nickname"
|
||||||
|
msgstr "Please enter a nickname."
|
||||||
|
|
||||||
|
msgid "auth.no-password"
|
||||||
|
msgstr "Please enter a password."
|
||||||
|
|
||||||
|
msgid "auth.not-found"
|
||||||
|
msgstr "No user was found. Please try again. If you are still having problems, ask Heaven."
|
Binary file not shown.
|
@ -2,7 +2,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Engelsystem 2.0\n"
|
"Project-Id-Version: Engelsystem 2.0\n"
|
||||||
"POT-Creation-Date: 2017-04-25 05:23+0200\n"
|
"POT-Creation-Date: 2017-04-25 05:23+0200\n"
|
||||||
"PO-Revision-Date: 2018-10-05 15:35+0200\n"
|
"PO-Revision-Date: 2018-11-27 00:29+0100\n"
|
||||||
"Last-Translator: samba <samba@autistici.org>\n"
|
"Last-Translator: samba <samba@autistici.org>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"Language: pt_BR\n"
|
"Language: pt_BR\n"
|
||||||
|
@ -1420,19 +1420,17 @@ msgid "Entry required!"
|
||||||
msgstr "Campo necessário!"
|
msgstr "Campo necessário!"
|
||||||
|
|
||||||
#: includes/pages/guest_login.php:319
|
#: includes/pages/guest_login.php:319
|
||||||
msgid "Please enter a password."
|
msgid "auth.no-password"
|
||||||
msgstr "Por favor digite uma senha."
|
msgstr "Por favor digite uma senha."
|
||||||
|
|
||||||
#: includes/pages/guest_login.php:323
|
#: includes/pages/guest_login.php:323
|
||||||
msgid ""
|
msgid "auth.not-found"
|
||||||
"No user was found with that Nickname. Please try again. If you are still "
|
|
||||||
"having problems, ask a Dispatcher."
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Nenhum usuário com esse apelido foi encontrado. Por favor tente novamente. \n"
|
"Nenhum usuário foi encontrado. Por favor tente novamente. \n"
|
||||||
"Se você continuar com problemas, pergunte a um Dispatcher."
|
"Se você continuar com problemas, pergunte a um Dispatcher."
|
||||||
|
|
||||||
#: includes/pages/guest_login.php:327
|
#: includes/pages/guest_login.php:327
|
||||||
msgid "Please enter a nickname."
|
msgid "auth.no-nickname"
|
||||||
msgstr "Por favor digite um apelido."
|
msgstr "Por favor digite um apelido."
|
||||||
|
|
||||||
#: includes/pages/guest_login.php:358 includes/view/User_view.php:101
|
#: includes/pages/guest_login.php:358 includes/view/User_view.php:101
|
Binary file not shown.
|
@ -0,0 +1,5 @@
|
||||||
|
{% extends "errors/default.twig" %}
|
||||||
|
|
||||||
|
{% block title %}{{ __("405: Method not allowed") }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content_headline_text %}{{ __("405: Method not allowed") }}{% endblock %}
|
|
@ -0,0 +1,11 @@
|
||||||
|
{% macro angel() %}
|
||||||
|
<span class="icon-icon_angel"></span>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro glyphicon(glyph) %}
|
||||||
|
<span class="glyphicon glyphicon-{{ glyph }}"></span>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro alert(message, type) %}
|
||||||
|
<div class="alert alert-{{ type|default('info') }}">{{ message }}</div>
|
||||||
|
{% endmacro %}
|
|
@ -0,0 +1,104 @@
|
||||||
|
{% extends "layouts/app.twig" %}
|
||||||
|
{% import 'macros/base.twig' as m %}
|
||||||
|
|
||||||
|
{% block title %}{{ __('Login') }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12 text-center">
|
||||||
|
<h2>{{ __('Welcome to the %s!', [config('name') ~ m.angel() ~ (config('app_name')|upper) ])|raw }}</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
{% for name,date in {
|
||||||
|
(__('Buildup starts')): config('buildup_start'),
|
||||||
|
(__('Event starts')): config('event_start'),
|
||||||
|
(__('Event ends')): config('event_end'),
|
||||||
|
(__('Teardown ends')): config('teardown_end')
|
||||||
|
} if date %}
|
||||||
|
{% if date > date() %}
|
||||||
|
<div class="col-sm-3 text-center hidden-xs">
|
||||||
|
<h4>{{ name }}</h4>
|
||||||
|
<span class="moment-countdown text-big" data-timestamp="{{ date.getTimestamp }}">%c</span>
|
||||||
|
<small>{{ date.format(__('Y-m-d')) }}</small>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6 col-sm-offset-3 col-md-4 col-md-offset-4">
|
||||||
|
<div class="panel panel-primary first">
|
||||||
|
|
||||||
|
<div class="panel-heading">{{ m.angel }} {{ __('Login') }}</div>
|
||||||
|
|
||||||
|
<div class="panel-body">
|
||||||
|
{% for message in errors|default([]) %}
|
||||||
|
{{ m.alert(__(message), 'danger') }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<form action="" enctype="multipart/form-data" method="post">
|
||||||
|
{{ csrf() }}
|
||||||
|
<div class="form-group">
|
||||||
|
<input class="form-control" id="form_nick"
|
||||||
|
type="text" name="login" value="" placeholder="{{ __('Nick') }}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<input class="form-control" id="form_password"
|
||||||
|
type="password" name="password" value="" placeholder="{{ __('Password') }}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button class="btn btn-primary" type="submit" name="submit">
|
||||||
|
{{ __('Login') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{% if show_password_recovery|default(false) %}
|
||||||
|
<a href="{{ url('user-password-recovery') }}" class="btn btn-default ">
|
||||||
|
{{ __('I forgot my password') }}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel-footer">
|
||||||
|
{{ m.glyphicon('info-sign') }} {{ __('Please note: You have to activate cookies!') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6 text-center">
|
||||||
|
<h2>{{ __('Register') }}</h2>
|
||||||
|
{% if has_permission_to('register') and config('registration_enabled') %}
|
||||||
|
<p>{{ __('Please sign up, if you want to help us!') }}</p>
|
||||||
|
<div class="form-group">
|
||||||
|
<a href="{{ url('register') }}" class="btn btn-default">{{ __('Register') }} »</a>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
{{ m.alert(__('Registration is disabled.'), 'danger') }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-6 text-center">
|
||||||
|
<h2>{{ __('What can I do?') }}</h2>
|
||||||
|
<p>{{ __('Please read about the jobs you can do to help us.') }}</p>
|
||||||
|
<div class="form-group">
|
||||||
|
<a href="{{ url('angeltypes', {'action': 'about'}) }}" class="btn btn-default">
|
||||||
|
{{ __('Teams/Job description') }} »
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -2,8 +2,12 @@
|
||||||
|
|
||||||
namespace Engelsystem\Controllers;
|
namespace Engelsystem\Controllers;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Engelsystem\Helpers\Authenticator;
|
||||||
|
use Engelsystem\Http\Request;
|
||||||
use Engelsystem\Http\Response;
|
use Engelsystem\Http\Response;
|
||||||
use Engelsystem\Http\UrlGeneratorInterface;
|
use Engelsystem\Http\UrlGeneratorInterface;
|
||||||
|
use Engelsystem\Models\User\User;
|
||||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||||
|
|
||||||
class AuthController extends BaseController
|
class AuthController extends BaseController
|
||||||
|
@ -17,20 +21,100 @@ class AuthController extends BaseController
|
||||||
/** @var UrlGeneratorInterface */
|
/** @var UrlGeneratorInterface */
|
||||||
protected $url;
|
protected $url;
|
||||||
|
|
||||||
public function __construct(Response $response, SessionInterface $session, UrlGeneratorInterface $url)
|
/** @var Authenticator */
|
||||||
{
|
protected $auth;
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
protected $permissions = [
|
||||||
|
'login' => 'login',
|
||||||
|
'postLogin' => 'login',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Response $response
|
||||||
|
* @param SessionInterface $session
|
||||||
|
* @param UrlGeneratorInterface $url
|
||||||
|
* @param Authenticator $auth
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
Response $response,
|
||||||
|
SessionInterface $session,
|
||||||
|
UrlGeneratorInterface $url,
|
||||||
|
Authenticator $auth
|
||||||
|
) {
|
||||||
$this->response = $response;
|
$this->response = $response;
|
||||||
$this->session = $session;
|
$this->session = $session;
|
||||||
$this->url = $url;
|
$this->url = $url;
|
||||||
|
$this->auth = $auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Response
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function logout()
|
public function login()
|
||||||
|
{
|
||||||
|
return $this->response->withView('pages/login');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Posted login form
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function postLogin(Request $request): Response
|
||||||
|
{
|
||||||
|
$return = $this->authenticateUser($request->get('login', ''), $request->get('password', ''));
|
||||||
|
if (!$return instanceof User) {
|
||||||
|
return $this->response->withView(
|
||||||
|
'pages/login',
|
||||||
|
['errors' => [$return], 'show_password_recovery' => true]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $return;
|
||||||
|
|
||||||
|
$this->session->invalidate();
|
||||||
|
$this->session->set('user_id', $user->id);
|
||||||
|
$this->session->set('locale', $user->settings->language);
|
||||||
|
|
||||||
|
$user->last_login_at = new Carbon();
|
||||||
|
$user->save(['touch' => false]);
|
||||||
|
|
||||||
|
return $this->response->redirectTo('news');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function logout(): Response
|
||||||
{
|
{
|
||||||
$this->session->invalidate();
|
$this->session->invalidate();
|
||||||
|
|
||||||
return $this->response->redirectTo($this->url->to('/'));
|
return $this->response->redirectTo($this->url->to('/'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify the user and password
|
||||||
|
*
|
||||||
|
* @param $login
|
||||||
|
* @param $password
|
||||||
|
* @return User|string
|
||||||
|
*/
|
||||||
|
protected function authenticateUser(string $login, string $password)
|
||||||
|
{
|
||||||
|
if (!$login) {
|
||||||
|
return 'auth.no-nickname';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$password) {
|
||||||
|
return 'auth.no-password';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$user = $this->auth->authenticate($login, $password)) {
|
||||||
|
return 'auth.not-found';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,9 @@ class Authenticator
|
||||||
/** @var string[] */
|
/** @var string[] */
|
||||||
protected $permissions;
|
protected $permissions;
|
||||||
|
|
||||||
|
/** @var int */
|
||||||
|
protected $passwordAlgorithm = PASSWORD_DEFAULT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ServerRequestInterface $request
|
* @param ServerRequestInterface $request
|
||||||
* @param Session $session
|
* @param Session $session
|
||||||
|
@ -48,7 +51,7 @@ class Authenticator
|
||||||
return $this->user;
|
return $this->user;
|
||||||
}
|
}
|
||||||
|
|
||||||
$userId = $this->session->get('uid');
|
$userId = $this->session->get('user_id');
|
||||||
if (!$userId) {
|
if (!$userId) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -104,17 +107,15 @@ class Authenticator
|
||||||
$abilities = (array)$abilities;
|
$abilities = (array)$abilities;
|
||||||
|
|
||||||
if (empty($this->permissions)) {
|
if (empty($this->permissions)) {
|
||||||
$userId = $this->user ? $this->user->id : $this->session->get('uid');
|
$user = $this->user();
|
||||||
|
|
||||||
if ($userId) {
|
if ($user) {
|
||||||
if ($user = $this->user()) {
|
|
||||||
$this->permissions = $this->getPermissionsByUser($user);
|
$this->permissions = $this->getPermissionsByUser($user);
|
||||||
|
|
||||||
$user->last_login_at = new Carbon();
|
$user->last_login_at = new Carbon();
|
||||||
$user->save();
|
$user->save();
|
||||||
} else {
|
} elseif ($this->session->get('user_id')) {
|
||||||
$this->session->remove('uid');
|
$this->session->remove('user_id');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($this->permissions)) {
|
if (empty($this->permissions)) {
|
||||||
|
@ -131,6 +132,78 @@ class Authenticator
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $login
|
||||||
|
* @param string $password
|
||||||
|
* @return User|null
|
||||||
|
*/
|
||||||
|
public function authenticate(string $login, string $password)
|
||||||
|
{
|
||||||
|
/** @var User $user */
|
||||||
|
$user = $this->userRepository->whereName($login)->first();
|
||||||
|
if (!$user) {
|
||||||
|
$user = $this->userRepository->whereEmail($login)->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$user) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->verifyPassword($user, $password)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
* @param string $password
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function verifyPassword(User $user, string $password)
|
||||||
|
{
|
||||||
|
$algorithm = $this->passwordAlgorithm;
|
||||||
|
|
||||||
|
if (!password_verify($password, $user->password)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password_needs_rehash($user->password, $algorithm)) {
|
||||||
|
$this->setPassword($user, $password);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param UserRepository $user
|
||||||
|
* @param string $password
|
||||||
|
*/
|
||||||
|
public function setPassword(User $user, string $password)
|
||||||
|
{
|
||||||
|
$algorithm = $this->passwordAlgorithm;
|
||||||
|
|
||||||
|
$user->password = password_hash($password, $algorithm);
|
||||||
|
$user->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getPasswordAlgorithm()
|
||||||
|
{
|
||||||
|
return $this->passwordAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $passwordAlgorithm
|
||||||
|
*/
|
||||||
|
public function setPasswordAlgorithm(int $passwordAlgorithm)
|
||||||
|
{
|
||||||
|
$this->passwordAlgorithm = $passwordAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param User $user
|
* @param User $user
|
||||||
* @return array
|
* @return array
|
||||||
|
|
|
@ -2,14 +2,18 @@
|
||||||
|
|
||||||
namespace Engelsystem\Helpers;
|
namespace Engelsystem\Helpers;
|
||||||
|
|
||||||
|
use Engelsystem\Config\Config;
|
||||||
use Engelsystem\Container\ServiceProvider;
|
use Engelsystem\Container\ServiceProvider;
|
||||||
|
|
||||||
class AuthenticatorServiceProvider extends ServiceProvider
|
class AuthenticatorServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
public function register()
|
public function register()
|
||||||
{
|
{
|
||||||
|
/** @var Config $config */
|
||||||
|
$config = $this->app->get('config');
|
||||||
/** @var Authenticator $authenticator */
|
/** @var Authenticator $authenticator */
|
||||||
$authenticator = $this->app->make(Authenticator::class);
|
$authenticator = $this->app->make(Authenticator::class);
|
||||||
|
$authenticator->setPasswordAlgorithm($config->get('password_algorithm'));
|
||||||
|
|
||||||
$this->app->instance(Authenticator::class, $authenticator);
|
$this->app->instance(Authenticator::class, $authenticator);
|
||||||
$this->app->instance('authenticator', $authenticator);
|
$this->app->instance('authenticator', $authenticator);
|
||||||
|
|
|
@ -19,7 +19,6 @@ class LegacyMiddleware implements MiddlewareInterface
|
||||||
'angeltypes',
|
'angeltypes',
|
||||||
'atom',
|
'atom',
|
||||||
'ical',
|
'ical',
|
||||||
'login',
|
|
||||||
'public_dashboard',
|
'public_dashboard',
|
||||||
'rooms',
|
'rooms',
|
||||||
'shift_entries',
|
'shift_entries',
|
||||||
|
@ -175,10 +174,6 @@ class LegacyMiddleware implements MiddlewareInterface
|
||||||
$title = settings_title();
|
$title = settings_title();
|
||||||
$content = user_settings();
|
$content = user_settings();
|
||||||
return [$title, $content];
|
return [$title, $content];
|
||||||
case 'login':
|
|
||||||
$title = login_title();
|
|
||||||
$content = guest_login();
|
|
||||||
return [$title, $content];
|
|
||||||
case 'register':
|
case 'register':
|
||||||
$title = register_title();
|
$title = register_title();
|
||||||
$content = guest_register();
|
$content = guest_register();
|
||||||
|
|
|
@ -3,40 +3,154 @@
|
||||||
namespace Engelsystem\Test\Unit\Controllers;
|
namespace Engelsystem\Test\Unit\Controllers;
|
||||||
|
|
||||||
use Engelsystem\Controllers\AuthController;
|
use Engelsystem\Controllers\AuthController;
|
||||||
|
use Engelsystem\Helpers\Authenticator;
|
||||||
|
use Engelsystem\Http\Request;
|
||||||
use Engelsystem\Http\Response;
|
use Engelsystem\Http\Response;
|
||||||
use Engelsystem\Http\UrlGeneratorInterface;
|
use Engelsystem\Http\UrlGeneratorInterface;
|
||||||
|
use Engelsystem\Models\User\Settings;
|
||||||
|
use Engelsystem\Models\User\User;
|
||||||
|
use Engelsystem\Test\Unit\HasDatabase;
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||||
|
|
||||||
class AuthControllerTest extends TestCase
|
class AuthControllerTest extends TestCase
|
||||||
{
|
{
|
||||||
|
use HasDatabase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers \Engelsystem\Controllers\AuthController::__construct
|
* @covers \Engelsystem\Controllers\AuthController::__construct
|
||||||
* @covers \Engelsystem\Controllers\AuthController::logout
|
* @covers \Engelsystem\Controllers\AuthController::login
|
||||||
*/
|
*/
|
||||||
public function testLogout()
|
public function testLogin()
|
||||||
{
|
{
|
||||||
/** @var Response|MockObject $response */
|
/** @var Response|MockObject $response */
|
||||||
$response = $this->createMock(Response::class);
|
$response = $this->createMock(Response::class);
|
||||||
/** @var SessionInterface|MockObject $session */
|
/** @var SessionInterface|MockObject $session */
|
||||||
$session = $this->getMockForAbstractClass(SessionInterface::class);
|
|
||||||
/** @var UrlGeneratorInterface|MockObject $url */
|
/** @var UrlGeneratorInterface|MockObject $url */
|
||||||
$url = $this->getMockForAbstractClass(UrlGeneratorInterface::class);
|
/** @var Authenticator|MockObject $auth */
|
||||||
|
list(, $session, $url, $auth) = $this->getMocks();
|
||||||
|
|
||||||
|
$response->expects($this->once())
|
||||||
|
->method('withView')
|
||||||
|
->with('pages/login')
|
||||||
|
->willReturn($response);
|
||||||
|
|
||||||
|
$controller = new AuthController($response, $session, $url, $auth);
|
||||||
|
$controller->login();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\AuthController::postLogin
|
||||||
|
* @covers \Engelsystem\Controllers\AuthController::authenticateUser
|
||||||
|
*/
|
||||||
|
public function testPostLogin()
|
||||||
|
{
|
||||||
|
$this->initDatabase();
|
||||||
|
|
||||||
|
$request = new Request();
|
||||||
|
/** @var Response|MockObject $response */
|
||||||
|
$response = $this->createMock(Response::class);
|
||||||
|
/** @var SessionInterface|MockObject $session */
|
||||||
|
/** @var UrlGeneratorInterface|MockObject $url */
|
||||||
|
/** @var Authenticator|MockObject $auth */
|
||||||
|
list(, $session, $url, $auth) = $this->getMocks();
|
||||||
|
|
||||||
|
$user = new User([
|
||||||
|
'name' => 'foo',
|
||||||
|
'password' => '',
|
||||||
|
'email' => '',
|
||||||
|
'api_key' => '',
|
||||||
|
'last_login_at' => null,
|
||||||
|
]);
|
||||||
|
$user->forceFill(['id' => 42,]);
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
$settings = new Settings(['language' => 'de_DE', 'theme' => '']);
|
||||||
|
$settings->user()
|
||||||
|
->associate($user)
|
||||||
|
->save();
|
||||||
|
|
||||||
|
$auth->expects($this->exactly(2))
|
||||||
|
->method('authenticate')
|
||||||
|
->with('foo', 'bar')
|
||||||
|
->willReturnOnConsecutiveCalls(null, $user);
|
||||||
|
|
||||||
|
$response->expects($this->exactly(3))
|
||||||
|
->method('withView')
|
||||||
|
->withConsecutive(
|
||||||
|
['pages/login', ['errors' => ['auth.no-nickname'], 'show_password_recovery' => true]],
|
||||||
|
['pages/login', ['errors' => ['auth.no-password'], 'show_password_recovery' => true]],
|
||||||
|
['pages/login', ['errors' => ['auth.not-found'], 'show_password_recovery' => true]])
|
||||||
|
->willReturn($response);
|
||||||
|
$response->expects($this->once())
|
||||||
|
->method('redirectTo')
|
||||||
|
->with('news')
|
||||||
|
->willReturn($response);
|
||||||
|
|
||||||
$session->expects($this->once())
|
$session->expects($this->once())
|
||||||
->method('invalidate');
|
->method('invalidate');
|
||||||
|
|
||||||
$response->expects($this->once())
|
$session->expects($this->exactly(2))
|
||||||
->method('redirectTo')
|
->method('set')
|
||||||
->with('https://foo.bar/');
|
->withConsecutive(
|
||||||
|
['user_id', 42],
|
||||||
|
['locale', 'de_DE']
|
||||||
|
);
|
||||||
|
|
||||||
|
$controller = new AuthController($response, $session, $url, $auth);
|
||||||
|
$controller->postLogin($request);
|
||||||
|
|
||||||
|
$request = new Request(['login' => 'foo']);
|
||||||
|
$controller->postLogin($request);
|
||||||
|
|
||||||
|
$request = new Request(['login' => 'foo', 'password' => 'bar']);
|
||||||
|
// No user found
|
||||||
|
$controller->postLogin($request);
|
||||||
|
// Authenticated user
|
||||||
|
$controller->postLogin($request);
|
||||||
|
|
||||||
|
$this->assertNotNull($user->last_login_at);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\AuthController::logout
|
||||||
|
*/
|
||||||
|
public function testLogout()
|
||||||
|
{
|
||||||
|
/** @var Response $response */
|
||||||
|
/** @var SessionInterface|MockObject $session */
|
||||||
|
/** @var UrlGeneratorInterface|MockObject $url */
|
||||||
|
/** @var Authenticator|MockObject $auth */
|
||||||
|
list($response, $session, $url, $auth) = $this->getMocks();
|
||||||
|
|
||||||
|
$session->expects($this->once())
|
||||||
|
->method('invalidate');
|
||||||
|
|
||||||
$url->expects($this->once())
|
$url->expects($this->once())
|
||||||
->method('to')
|
->method('to')
|
||||||
->with('/')
|
->with('/')
|
||||||
->willReturn('https://foo.bar/');
|
->willReturn('https://foo.bar/');
|
||||||
|
|
||||||
$controller = new AuthController($response, $session, $url);
|
$controller = new AuthController($response, $session, $url, $auth);
|
||||||
$controller->logout();
|
$return = $controller->logout();
|
||||||
|
|
||||||
|
$this->assertEquals(['https://foo.bar/'], $return->getHeader('location'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function getMocks()
|
||||||
|
{
|
||||||
|
$response = new Response();
|
||||||
|
/** @var SessionInterface|MockObject $session */
|
||||||
|
$session = $this->getMockForAbstractClass(SessionInterface::class);
|
||||||
|
/** @var UrlGeneratorInterface|MockObject $url */
|
||||||
|
$url = $this->getMockForAbstractClass(UrlGeneratorInterface::class);
|
||||||
|
/** @var Authenticator|MockObject $auth */
|
||||||
|
$auth = $this->createMock(Authenticator::class);
|
||||||
|
|
||||||
|
return [$response, $session, $url, $auth];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,4 @@ class ControllerImplementation extends BaseController
|
||||||
'dolor',
|
'dolor',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $permissions
|
|
||||||
*/
|
|
||||||
public function setPermissions(array $permissions)
|
|
||||||
{
|
|
||||||
$this->permissions = $permissions;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
namespace Engelsystem\Test\Unit\Helpers;
|
namespace Engelsystem\Test\Unit\Helpers;
|
||||||
|
|
||||||
use Engelsystem\Application;
|
use Engelsystem\Application;
|
||||||
|
use Engelsystem\Config\Config;
|
||||||
use Engelsystem\Helpers\Authenticator;
|
use Engelsystem\Helpers\Authenticator;
|
||||||
use Engelsystem\Helpers\AuthenticatorServiceProvider;
|
use Engelsystem\Helpers\AuthenticatorServiceProvider;
|
||||||
use Engelsystem\Http\Request;
|
use Engelsystem\Http\Request;
|
||||||
|
@ -19,11 +20,19 @@ class AuthenticatorServiceProviderTest extends ServiceProviderTest
|
||||||
$app = new Application();
|
$app = new Application();
|
||||||
$app->bind(ServerRequestInterface::class, Request::class);
|
$app->bind(ServerRequestInterface::class, Request::class);
|
||||||
|
|
||||||
|
$config = new Config();
|
||||||
|
$config->set('password_algorithm', PASSWORD_DEFAULT);
|
||||||
|
$app->instance('config', $config);
|
||||||
|
|
||||||
$serviceProvider = new AuthenticatorServiceProvider($app);
|
$serviceProvider = new AuthenticatorServiceProvider($app);
|
||||||
$serviceProvider->register();
|
$serviceProvider->register();
|
||||||
|
|
||||||
$this->assertInstanceOf(Authenticator::class, $app->get(Authenticator::class));
|
$this->assertInstanceOf(Authenticator::class, $app->get(Authenticator::class));
|
||||||
$this->assertInstanceOf(Authenticator::class, $app->get('authenticator'));
|
$this->assertInstanceOf(Authenticator::class, $app->get('authenticator'));
|
||||||
$this->assertInstanceOf(Authenticator::class, $app->get('auth'));
|
$this->assertInstanceOf(Authenticator::class, $app->get('auth'));
|
||||||
|
|
||||||
|
/** @var Authenticator $auth */
|
||||||
|
$auth = $app->get(Authenticator::class);
|
||||||
|
$this->assertEquals(PASSWORD_DEFAULT, $auth->getPasswordAlgorithm());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ namespace Engelsystem\Test\Unit\Helpers;
|
||||||
|
|
||||||
use Engelsystem\Helpers\Authenticator;
|
use Engelsystem\Helpers\Authenticator;
|
||||||
use Engelsystem\Models\User\User;
|
use Engelsystem\Models\User\User;
|
||||||
|
use Engelsystem\Test\Unit\HasDatabase;
|
||||||
use Engelsystem\Test\Unit\Helpers\Stub\UserModelImplementation;
|
use Engelsystem\Test\Unit\Helpers\Stub\UserModelImplementation;
|
||||||
use Engelsystem\Test\Unit\ServiceProviderTest;
|
use Engelsystem\Test\Unit\ServiceProviderTest;
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
|
@ -12,6 +13,8 @@ use Symfony\Component\HttpFoundation\Session\Session;
|
||||||
|
|
||||||
class AuthenticatorTest extends ServiceProviderTest
|
class AuthenticatorTest extends ServiceProviderTest
|
||||||
{
|
{
|
||||||
|
use HasDatabase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers \Engelsystem\Helpers\Authenticator::__construct(
|
* @covers \Engelsystem\Helpers\Authenticator::__construct(
|
||||||
* @covers \Engelsystem\Helpers\Authenticator::user
|
* @covers \Engelsystem\Helpers\Authenticator::user
|
||||||
|
@ -29,7 +32,7 @@ class AuthenticatorTest extends ServiceProviderTest
|
||||||
|
|
||||||
$session->expects($this->exactly(3))
|
$session->expects($this->exactly(3))
|
||||||
->method('get')
|
->method('get')
|
||||||
->with('uid')
|
->with('user_id')
|
||||||
->willReturnOnConsecutiveCalls(
|
->willReturnOnConsecutiveCalls(
|
||||||
null,
|
null,
|
||||||
42,
|
42,
|
||||||
|
@ -114,16 +117,13 @@ class AuthenticatorTest extends ServiceProviderTest
|
||||||
/** @var User|MockObject $user */
|
/** @var User|MockObject $user */
|
||||||
$user = $this->createMock(User::class);
|
$user = $this->createMock(User::class);
|
||||||
|
|
||||||
$user->expects($this->once())
|
$session->expects($this->once())
|
||||||
->method('save');
|
|
||||||
|
|
||||||
$session->expects($this->exactly(2))
|
|
||||||
->method('get')
|
->method('get')
|
||||||
->with('uid')
|
->with('user_id')
|
||||||
->willReturn(42);
|
->willReturn(42);
|
||||||
$session->expects($this->once())
|
$session->expects($this->once())
|
||||||
->method('remove')
|
->method('remove')
|
||||||
->with('uid');
|
->with('user_id');
|
||||||
|
|
||||||
/** @var Authenticator|MockObject $auth */
|
/** @var Authenticator|MockObject $auth */
|
||||||
$auth = $this->getMockBuilder(Authenticator::class)
|
$auth = $this->getMockBuilder(Authenticator::class)
|
||||||
|
@ -151,4 +151,115 @@ class AuthenticatorTest extends ServiceProviderTest
|
||||||
// Permissions cached
|
// Permissions cached
|
||||||
$this->assertTrue($auth->can('bar'));
|
$this->assertTrue($auth->can('bar'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Helpers\Authenticator::authenticate
|
||||||
|
*/
|
||||||
|
public function testAuthenticate()
|
||||||
|
{
|
||||||
|
$this->initDatabase();
|
||||||
|
|
||||||
|
/** @var ServerRequestInterface|MockObject $request */
|
||||||
|
$request = $this->getMockForAbstractClass(ServerRequestInterface::class);
|
||||||
|
/** @var Session|MockObject $session */
|
||||||
|
$session = $this->createMock(Session::class);
|
||||||
|
$userRepository = new User();
|
||||||
|
|
||||||
|
(new User([
|
||||||
|
'name' => 'lorem',
|
||||||
|
'password' => password_hash('testing', PASSWORD_DEFAULT),
|
||||||
|
'email' => 'lorem@foo.bar',
|
||||||
|
'api_key' => '',
|
||||||
|
]))->save();
|
||||||
|
(new User([
|
||||||
|
'name' => 'ipsum',
|
||||||
|
'password' => '',
|
||||||
|
'email' => 'ipsum@foo.bar',
|
||||||
|
'api_key' => '',
|
||||||
|
]))->save();
|
||||||
|
|
||||||
|
$auth = new Authenticator($request, $session, $userRepository);
|
||||||
|
$this->assertNull($auth->authenticate('not-existing', 'foo'));
|
||||||
|
$this->assertNull($auth->authenticate('ipsum', 'wrong-password'));
|
||||||
|
$this->assertInstanceOf(User::class, $auth->authenticate('lorem', 'testing'));
|
||||||
|
$this->assertInstanceOf(User::class, $auth->authenticate('lorem@foo.bar', 'testing'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Helpers\Authenticator::verifyPassword
|
||||||
|
*/
|
||||||
|
public function testVerifyPassword()
|
||||||
|
{
|
||||||
|
$this->initDatabase();
|
||||||
|
$password = password_hash('testing', PASSWORD_ARGON2I);
|
||||||
|
$user = new User([
|
||||||
|
'name' => 'lorem',
|
||||||
|
'password' => $password,
|
||||||
|
'email' => 'lorem@foo.bar',
|
||||||
|
'api_key' => '',
|
||||||
|
]);
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
/** @var Authenticator|MockObject $auth */
|
||||||
|
$auth = $this->getMockBuilder(Authenticator::class)
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->setMethods(['setPassword'])
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$auth->expects($this->once())
|
||||||
|
->method('setPassword')
|
||||||
|
->with($user, 'testing');
|
||||||
|
$auth->setPasswordAlgorithm(PASSWORD_BCRYPT);
|
||||||
|
|
||||||
|
$this->assertFalse($auth->verifyPassword($user, 'randomStuff'));
|
||||||
|
$this->assertTrue($auth->verifyPassword($user, 'testing'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Helpers\Authenticator::setPassword
|
||||||
|
*/
|
||||||
|
public function testSetPassword()
|
||||||
|
{
|
||||||
|
$this->initDatabase();
|
||||||
|
$user = new User([
|
||||||
|
'name' => 'ipsum',
|
||||||
|
'password' => '',
|
||||||
|
'email' => 'ipsum@foo.bar',
|
||||||
|
'api_key' => '',
|
||||||
|
]);
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
$auth = $this->getAuthenticator();
|
||||||
|
$auth->setPasswordAlgorithm(PASSWORD_ARGON2I);
|
||||||
|
|
||||||
|
$auth->setPassword($user, 'FooBar');
|
||||||
|
$this->assertTrue($user->isClean());
|
||||||
|
|
||||||
|
$this->assertTrue(password_verify('FooBar', $user->password));
|
||||||
|
$this->assertFalse(password_needs_rehash($user->password, PASSWORD_ARGON2I));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Helpers\Authenticator::setPasswordAlgorithm
|
||||||
|
* @covers \Engelsystem\Helpers\Authenticator::getPasswordAlgorithm
|
||||||
|
*/
|
||||||
|
public function testPasswordAlgorithm()
|
||||||
|
{
|
||||||
|
$auth = $this->getAuthenticator();
|
||||||
|
|
||||||
|
$auth->setPasswordAlgorithm(PASSWORD_ARGON2I);
|
||||||
|
$this->assertEquals(PASSWORD_ARGON2I, $auth->getPasswordAlgorithm());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Authenticator
|
||||||
|
*/
|
||||||
|
protected function getAuthenticator()
|
||||||
|
{
|
||||||
|
return new class extends Authenticator
|
||||||
|
{
|
||||||
|
/** @noinspection PhpMissingParentConstructorInspection */
|
||||||
|
public function __construct() { }
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ class UrlGeneratorServiceProviderTest extends ServiceProviderTest
|
||||||
$urlGenerator = $this->getMockBuilder(UrlGenerator::class)
|
$urlGenerator = $this->getMockBuilder(UrlGenerator::class)
|
||||||
->getMock();
|
->getMock();
|
||||||
|
|
||||||
$app = $this->getApp();
|
$app = $this->getApp(['make', 'instance', 'bind']);
|
||||||
|
|
||||||
$this->setExpects($app, 'make', [UrlGenerator::class], $urlGenerator);
|
$this->setExpects($app, 'make', [UrlGenerator::class], $urlGenerator);
|
||||||
$app->expects($this->exactly(2))
|
$app->expects($this->exactly(2))
|
||||||
|
@ -29,6 +29,9 @@ class UrlGeneratorServiceProviderTest extends ServiceProviderTest
|
||||||
['http.urlGenerator', $urlGenerator],
|
['http.urlGenerator', $urlGenerator],
|
||||||
[UrlGeneratorInterface::class, $urlGenerator]
|
[UrlGeneratorInterface::class, $urlGenerator]
|
||||||
);
|
);
|
||||||
|
$app->expects($this->once())
|
||||||
|
->method('bind')
|
||||||
|
->with(UrlGeneratorInterface::class, UrlGenerator::class);
|
||||||
|
|
||||||
$serviceProvider = new UrlGeneratorServiceProvider($app);
|
$serviceProvider = new UrlGeneratorServiceProvider($app);
|
||||||
$serviceProvider->register();
|
$serviceProvider->register();
|
||||||
|
|
Loading…
Reference in New Issue