Merge pull request #622 from MyIgel/controllers
AuthController (login, logout), use templating, replaced gettext, input validation
This commit is contained in:
commit
d4d4b409b6
|
@ -14,7 +14,6 @@ To report bugs use [engelsystem/issues](https://github.com/engelsystem/engelsyst
|
||||||
* PHP >= 7.1
|
* PHP >= 7.1
|
||||||
* Required modules:
|
* Required modules:
|
||||||
* dom
|
* dom
|
||||||
* gettext
|
|
||||||
* json
|
* json
|
||||||
* mbstring
|
* mbstring
|
||||||
* PDO
|
* PDO
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.1.0",
|
"php": ">=7.1.0",
|
||||||
"ext-gettext": "*",
|
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"ext-libxml": "*",
|
"ext-libxml": "*",
|
||||||
"ext-mbstring": "*",
|
"ext-mbstring": "*",
|
||||||
|
@ -24,6 +23,7 @@
|
||||||
"ext-xml": "*",
|
"ext-xml": "*",
|
||||||
"doctrine/dbal": "^2.9",
|
"doctrine/dbal": "^2.9",
|
||||||
"erusev/parsedown": "^1.7",
|
"erusev/parsedown": "^1.7",
|
||||||
|
"gettext/gettext": "^4.6",
|
||||||
"illuminate/container": "5.8.*",
|
"illuminate/container": "5.8.*",
|
||||||
"illuminate/database": "5.8.*",
|
"illuminate/database": "5.8.*",
|
||||||
"illuminate/support": "5.8.*",
|
"illuminate/support": "5.8.*",
|
||||||
|
@ -32,6 +32,7 @@
|
||||||
"psr/container": "^1.0",
|
"psr/container": "^1.0",
|
||||||
"psr/http-server-middleware": "^1.0",
|
"psr/http-server-middleware": "^1.0",
|
||||||
"psr/log": "^1.1",
|
"psr/log": "^1.1",
|
||||||
|
"respect/validation": "^1.1",
|
||||||
"swiftmailer/swiftmailer": "^6.2",
|
"swiftmailer/swiftmailer": "^6.2",
|
||||||
"symfony/http-foundation": "^4.3",
|
"symfony/http-foundation": "^4.3",
|
||||||
"symfony/psr-http-message-bridge": "^1.2",
|
"symfony/psr-http-message-bridge": "^1.2",
|
||||||
|
|
|
@ -17,7 +17,7 @@ return [
|
||||||
\Engelsystem\Database\DatabaseServiceProvider::class,
|
\Engelsystem\Database\DatabaseServiceProvider::class,
|
||||||
\Engelsystem\Http\RequestServiceProvider::class,
|
\Engelsystem\Http\RequestServiceProvider::class,
|
||||||
\Engelsystem\Http\SessionServiceProvider::class,
|
\Engelsystem\Http\SessionServiceProvider::class,
|
||||||
\Engelsystem\Helpers\TranslationServiceProvider::class,
|
\Engelsystem\Helpers\Translation\TranslationServiceProvider::class,
|
||||||
\Engelsystem\Http\ResponseServiceProvider::class,
|
\Engelsystem\Http\ResponseServiceProvider::class,
|
||||||
\Engelsystem\Http\Psr7ServiceProvider::class,
|
\Engelsystem\Http\Psr7ServiceProvider::class,
|
||||||
\Engelsystem\Helpers\AuthenticatorServiceProvider::class,
|
\Engelsystem\Helpers\AuthenticatorServiceProvider::class,
|
||||||
|
@ -25,6 +25,7 @@ return [
|
||||||
\Engelsystem\Middleware\RouteDispatcherServiceProvider::class,
|
\Engelsystem\Middleware\RouteDispatcherServiceProvider::class,
|
||||||
\Engelsystem\Middleware\RequestHandlerServiceProvider::class,
|
\Engelsystem\Middleware\RequestHandlerServiceProvider::class,
|
||||||
\Engelsystem\Middleware\SessionHandlerServiceProvider::class,
|
\Engelsystem\Middleware\SessionHandlerServiceProvider::class,
|
||||||
|
\Engelsystem\Http\Validation\ValidationServiceProvider::class,
|
||||||
|
|
||||||
// Additional services
|
// Additional services
|
||||||
\Engelsystem\Mail\MailerServiceProvider::class,
|
\Engelsystem\Mail\MailerServiceProvider::class,
|
||||||
|
|
|
@ -99,13 +99,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,
|
||||||
|
@ -141,12 +138,12 @@ return [
|
||||||
|
|
||||||
// Available locales in /locale/
|
// Available locales in /locale/
|
||||||
'locales' => [
|
'locales' => [
|
||||||
'de_DE.UTF-8' => 'Deutsch',
|
'de_DE' => 'Deutsch',
|
||||||
'en_US.UTF-8' => 'English',
|
'en_US' => 'English',
|
||||||
],
|
],
|
||||||
|
|
||||||
// The default locale to use
|
// The default locale to use
|
||||||
'default_locale' => env('DEFAULT_LOCALE', 'en_US.UTF-8'),
|
'default_locale' => env('DEFAULT_LOCALE', 'en_US'),
|
||||||
|
|
||||||
// Available T-Shirt sizes, set value to null if not available
|
// Available T-Shirt sizes, set value to null if not available
|
||||||
'tshirt_sizes' => [
|
'tshirt_sizes' => [
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -35,8 +35,8 @@ RUN rm -f /app/import/* /app/config/config.php
|
||||||
# Build the PHP container
|
# Build the PHP container
|
||||||
FROM php:7-fpm-alpine
|
FROM php:7-fpm-alpine
|
||||||
WORKDIR /var/www
|
WORKDIR /var/www
|
||||||
RUN apk add --no-cache icu-dev gettext-dev && \
|
RUN apk add --no-cache icu-dev && \
|
||||||
docker-php-ext-install intl gettext pdo_mysql
|
docker-php-ext-install intl pdo_mysql
|
||||||
COPY --from=data /app/ /var/www
|
COPY --from=data /app/ /var/www
|
||||||
RUN chown -R www-data:www-data /var/www/import/ /var/www/storage/ && \
|
RUN chown -R www-data:www-data /var/www/import/ /var/www/storage/ && \
|
||||||
rm -r /var/www/html
|
rm -r /var/www/html
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Migrations;
|
||||||
|
|
||||||
|
use Engelsystem\Database\Migration\Migration;
|
||||||
|
|
||||||
|
class FixUserLanguages extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migration
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
$connection = $this->schema->getConnection();
|
||||||
|
$connection
|
||||||
|
->table('users_settings')
|
||||||
|
->update([
|
||||||
|
'language' => $connection->raw('REPLACE(language, ".UTF-8", "")')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migration
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
$connection = $this->schema->getConnection();
|
||||||
|
$connection
|
||||||
|
->table('users_settings')
|
||||||
|
->update([
|
||||||
|
'language' => $connection->raw('CONCAT(language, ".UTF-8")')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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'));
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Engelsystem\Helpers\Translation\Translator;
|
||||||
use Engelsystem\Mail\EngelsystemMailer;
|
use Engelsystem\Mail\EngelsystemMailer;
|
||||||
use Engelsystem\Models\User\User;
|
use Engelsystem\Models\User\User;
|
||||||
use Psr\Log\LogLevel;
|
use Psr\Log\LogLevel;
|
||||||
|
@ -17,7 +18,7 @@ function engelsystem_email_to_user($recipientUser, $title, $message, $notIfItsMe
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var \Engelsystem\Helpers\Translator $translator */
|
/** @var Translator $translator */
|
||||||
$translator = app()->get('translator');
|
$translator = app()->get('translator');
|
||||||
$locale = $translator->getLocale();
|
$locale = $translator->getLocale();
|
||||||
|
|
||||||
|
|
|
@ -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([
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -2,7 +2,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Engelsystem\n"
|
"Project-Id-Version: Engelsystem\n"
|
||||||
"POT-Creation-Date: 2019-04-28 15:23+0200\n"
|
"POT-Creation-Date: 2019-04-28 15:23+0200\n"
|
||||||
"PO-Revision-Date: 2019-06-12 16:07+0200\n"
|
"PO-Revision-Date: 2019-06-13 11:54+0200\n"
|
||||||
"Last-Translator: msquare <msquare@notrademark.de>\n"
|
"Last-Translator: msquare <msquare@notrademark.de>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"Language: de_DE\n"
|
"Language: de_DE\n"
|
||||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Generator: Poedit 1.8.11\n"
|
"X-Generator: Poedit 1.8.11\n"
|
||||||
"X-Poedit-KeywordsList: __;_e;translate;translatePlural;gettext;gettext_noop\n"
|
"X-Poedit-KeywordsList: __;_e;translate;translatePlural\n"
|
||||||
"X-Poedit-Basepath: ../../../..\n"
|
"X-Poedit-Basepath: ../../../..\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Poedit-SourceCharset: UTF-8\n"
|
"X-Poedit-SourceCharset: UTF-8\n"
|
||||||
|
@ -1529,19 +1529,20 @@ msgstr "Nachname"
|
||||||
msgid "Entry required!"
|
msgid "Entry required!"
|
||||||
msgstr "Pflichtfeld!"
|
msgstr "Pflichtfeld!"
|
||||||
|
|
||||||
#: includes/pages/guest_login.php:414
|
#~ msgid "auth.no-password"
|
||||||
msgid "Please enter a 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
|
#~ 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 +2358,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
|
||||||
|
@ -2762,3 +2763,9 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Diese Seite existiert nicht oder Du hast keinen Zugriff. Melde Dich an um "
|
"Diese Seite existiert nicht oder Du hast keinen Zugriff. Melde Dich an um "
|
||||||
"Zugriff zu erhalten!"
|
"Zugriff zu erhalten!"
|
||||||
|
|
||||||
|
msgid "validation.password.required"
|
||||||
|
msgstr "Bitte gib ein Passwort an."
|
||||||
|
|
||||||
|
msgid "validation.login.required"
|
||||||
|
msgstr "Bitte gib einen Loginnamen an."
|
Binary file not shown.
|
@ -0,0 +1,32 @@
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Engelsystem 2.0\n"
|
||||||
|
"POT-Creation-Date: 2017-12-29 19:01+0100\n"
|
||||||
|
"PO-Revision-Date: 2019-06-04 23:41+0200\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."
|
||||||
|
|
||||||
|
msgid "validation.password.required"
|
||||||
|
msgstr "The password is required."
|
||||||
|
|
||||||
|
msgid "validation.login.required"
|
||||||
|
msgstr "The login name is required."
|
Binary file not shown.
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
|
|
@ -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,14 @@
|
||||||
|
|
||||||
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 Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||||
|
|
||||||
class AuthController extends BaseController
|
class AuthController extends BaseController
|
||||||
|
@ -17,17 +23,91 @@ 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(): Response
|
||||||
|
{
|
||||||
|
return $this->showLogin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $showRecovery
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
protected function showLogin($showRecovery = false): Response
|
||||||
|
{
|
||||||
|
$errors = Collection::make(Arr::flatten($this->session->get('errors', [])));
|
||||||
|
$this->session->remove('errors');
|
||||||
|
|
||||||
|
return $this->response->withView(
|
||||||
|
'pages/login',
|
||||||
|
['errors' => $errors, 'show_password_recovery' => $showRecovery]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Posted login form
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function postLogin(Request $request): Response
|
||||||
|
{
|
||||||
|
$data = $this->validate($request, [
|
||||||
|
'login' => 'required',
|
||||||
|
'password' => 'required',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$user = $this->auth->authenticate($data['login'], $data['password']);
|
||||||
|
|
||||||
|
if (!$user instanceof User) {
|
||||||
|
$this->session->set('errors', $this->session->get('errors', []) + ['auth.not-found']);
|
||||||
|
|
||||||
|
return $this->showLogin(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$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();
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,12 @@
|
||||||
|
|
||||||
namespace Engelsystem\Controllers;
|
namespace Engelsystem\Controllers;
|
||||||
|
|
||||||
|
use Engelsystem\Http\Validation\ValidatesRequest;
|
||||||
|
|
||||||
abstract class BaseController
|
abstract class BaseController
|
||||||
{
|
{
|
||||||
|
use ValidatesRequest;
|
||||||
|
|
||||||
/** @var string[]|string[][] A list of Permissions required to access the controller or certain pages */
|
/** @var string[]|string[][] A list of Permissions required to access the controller or certain pages */
|
||||||
protected $permissions = [];
|
protected $permissions = [];
|
||||||
|
|
||||||
|
|
|
@ -9,13 +9,13 @@ class MetricsEngine implements EngineInterface
|
||||||
/**
|
/**
|
||||||
* Render metrics
|
* Render metrics
|
||||||
*
|
*
|
||||||
* @example $data = ['foo' => [['labels' => ['foo'=>'bar'], 'value'=>42]], 'bar'=>123]
|
|
||||||
*
|
|
||||||
* @param string $path
|
* @param string $path
|
||||||
* @param mixed[] $data
|
* @param mixed[] $data
|
||||||
* @return string
|
* @return string
|
||||||
|
*
|
||||||
|
* @example $data = ['foo' => [['labels' => ['foo'=>'bar'], 'value'=>42]], 'bar'=>123]
|
||||||
*/
|
*/
|
||||||
public function get($path, $data = []): string
|
public function get(string $path, array $data = []): string
|
||||||
{
|
{
|
||||||
$return = [];
|
$return = [];
|
||||||
foreach ($data as $name => $list) {
|
foreach ($data as $name => $list) {
|
||||||
|
@ -52,7 +52,7 @@ class MetricsEngine implements EngineInterface
|
||||||
* @param string $path
|
* @param string $path
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function canRender($path): bool
|
public function canRender(string $path): bool
|
||||||
{
|
{
|
||||||
return $path == '/metrics';
|
return $path == '/metrics';
|
||||||
}
|
}
|
||||||
|
@ -60,8 +60,8 @@ class MetricsEngine implements EngineInterface
|
||||||
/**
|
/**
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param array|mixed $row
|
* @param array|mixed $row
|
||||||
* @see https://prometheus.io/docs/instrumenting/exposition_formats/
|
|
||||||
* @return string
|
* @return string
|
||||||
|
* @see https://prometheus.io/docs/instrumenting/exposition_formats/
|
||||||
*/
|
*/
|
||||||
protected function formatData($name, $row): string
|
protected function formatData($name, $row): string
|
||||||
{
|
{
|
||||||
|
@ -135,4 +135,12 @@ class MetricsEngine implements EngineInterface
|
||||||
$value
|
$value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does nothing as shared data will onyly result in unexpected behaviour
|
||||||
|
*
|
||||||
|
* @param string|mixed[] $key
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
public function share($key, $value = null) { }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Helpers\Translation;
|
||||||
|
|
||||||
|
use Gettext\Translator;
|
||||||
|
|
||||||
|
class GettextTranslator extends Translator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $domain
|
||||||
|
* @param string $context
|
||||||
|
* @param string $original
|
||||||
|
* @return string
|
||||||
|
* @throws TranslationNotFound
|
||||||
|
*/
|
||||||
|
public function dpgettext($domain, $context, $original)
|
||||||
|
{
|
||||||
|
$this->assertHasTranslation($domain, $context, $original);
|
||||||
|
|
||||||
|
return parent::dpgettext($domain, $context, $original);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $domain
|
||||||
|
* @param string $context
|
||||||
|
* @param string $original
|
||||||
|
* @param string $plural
|
||||||
|
* @param string $value
|
||||||
|
* @return string
|
||||||
|
* @throws TranslationNotFound
|
||||||
|
*/
|
||||||
|
public function dnpgettext($domain, $context, $original, $plural, $value)
|
||||||
|
{
|
||||||
|
$this->assertHasTranslation($domain, $context, $original);
|
||||||
|
|
||||||
|
return parent::dnpgettext($domain, $context, $original, $plural, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $domain
|
||||||
|
* @param string $context
|
||||||
|
* @param string $original
|
||||||
|
* @throws TranslationNotFound
|
||||||
|
*/
|
||||||
|
protected function assertHasTranslation($domain, $context, $original)
|
||||||
|
{
|
||||||
|
if ($this->getTranslation($domain, $context, $original)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new TranslationNotFound(implode('/', [$domain, $context, $original]));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Helpers\Translation;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class TranslationNotFound extends Exception
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Helpers\Translation;
|
||||||
|
|
||||||
|
use Engelsystem\Config\Config;
|
||||||
|
use Engelsystem\Container\ServiceProvider;
|
||||||
|
use Gettext\Translations;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Session;
|
||||||
|
|
||||||
|
class TranslationServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/** @var GettextTranslator */
|
||||||
|
protected $translators = [];
|
||||||
|
|
||||||
|
public function register(): void
|
||||||
|
{
|
||||||
|
/** @var Config $config */
|
||||||
|
$config = $this->app->get('config');
|
||||||
|
/** @var Session $session */
|
||||||
|
$session = $this->app->get('session');
|
||||||
|
|
||||||
|
$locales = $config->get('locales');
|
||||||
|
$locale = $config->get('default_locale');
|
||||||
|
$fallbackLocale = $config->get('fallback_locale', 'en_US');
|
||||||
|
|
||||||
|
$sessionLocale = $session->get('locale', $locale);
|
||||||
|
if (isset($locales[$sessionLocale])) {
|
||||||
|
$locale = $sessionLocale;
|
||||||
|
}
|
||||||
|
|
||||||
|
$session->set('locale', $locale);
|
||||||
|
|
||||||
|
$translator = $this->app->make(
|
||||||
|
Translator::class,
|
||||||
|
[
|
||||||
|
'locale' => $locale,
|
||||||
|
'locales' => $locales,
|
||||||
|
'fallbackLocale' => $fallbackLocale,
|
||||||
|
'getTranslatorCallback' => [$this, 'getTranslator'],
|
||||||
|
'localeChangeCallback' => [$this, 'setLocale'],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$this->app->instance(Translator::class, $translator);
|
||||||
|
$this->app->instance('translator', $translator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $locale
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
public function setLocale(string $locale): void
|
||||||
|
{
|
||||||
|
$locale .= '.UTF-8';
|
||||||
|
// Set the users locale
|
||||||
|
putenv('LC_ALL=' . $locale);
|
||||||
|
setlocale(LC_ALL, $locale);
|
||||||
|
|
||||||
|
// Reset numeric formatting to allow output of floats
|
||||||
|
putenv('LC_NUMERIC=C');
|
||||||
|
setlocale(LC_NUMERIC, 'C');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $locale
|
||||||
|
* @return GettextTranslator
|
||||||
|
*/
|
||||||
|
public function getTranslator(string $locale): GettextTranslator
|
||||||
|
{
|
||||||
|
if (!isset($this->translators[$locale])) {
|
||||||
|
$file = $this->app->get('path.lang') . '/' . $locale . '/default.mo';
|
||||||
|
|
||||||
|
/** @var GettextTranslator $translator */
|
||||||
|
$translator = $this->app->make(GettextTranslator::class);
|
||||||
|
|
||||||
|
/** @var Translations $translations */
|
||||||
|
$translations = $this->app->make(Translations::class);
|
||||||
|
$translations->addFromMoFile($file);
|
||||||
|
|
||||||
|
$translator->loadTranslations($translations);
|
||||||
|
|
||||||
|
$this->translators[$locale] = $translator;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->translators[$locale];
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Engelsystem\Helpers;
|
namespace Engelsystem\Helpers\Translation;
|
||||||
|
|
||||||
class Translator
|
class Translator
|
||||||
{
|
{
|
||||||
|
@ -10,6 +10,12 @@ class Translator
|
||||||
/** @var string */
|
/** @var string */
|
||||||
protected $locale;
|
protected $locale;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
protected $fallbackLocale;
|
||||||
|
|
||||||
|
/** @var callable */
|
||||||
|
protected $getTranslatorCallback;
|
||||||
|
|
||||||
/** @var callable */
|
/** @var callable */
|
||||||
protected $localeChangeCallback;
|
protected $localeChangeCallback;
|
||||||
|
|
||||||
|
@ -17,15 +23,24 @@ class Translator
|
||||||
* Translator constructor.
|
* Translator constructor.
|
||||||
*
|
*
|
||||||
* @param string $locale
|
* @param string $locale
|
||||||
|
* @param string $fallbackLocale
|
||||||
|
* @param callable $getTranslatorCallback
|
||||||
* @param string[] $locales
|
* @param string[] $locales
|
||||||
* @param callable $localeChangeCallback
|
* @param callable $localeChangeCallback
|
||||||
*/
|
*/
|
||||||
public function __construct(string $locale, array $locales = [], callable $localeChangeCallback = null)
|
public function __construct(
|
||||||
{
|
string $locale,
|
||||||
|
string $fallbackLocale,
|
||||||
|
callable $getTranslatorCallback,
|
||||||
|
array $locales = [],
|
||||||
|
callable $localeChangeCallback = null
|
||||||
|
) {
|
||||||
$this->localeChangeCallback = $localeChangeCallback;
|
$this->localeChangeCallback = $localeChangeCallback;
|
||||||
|
$this->getTranslatorCallback = $getTranslatorCallback;
|
||||||
|
|
||||||
$this->setLocale($locale);
|
$this->setLocale($locale);
|
||||||
$this->setLocales($locales);
|
$this->fallbackLocale = $fallbackLocale;
|
||||||
|
$this->locales = $locales;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,9 +52,7 @@ class Translator
|
||||||
*/
|
*/
|
||||||
public function translate(string $key, array $replace = []): string
|
public function translate(string $key, array $replace = []): string
|
||||||
{
|
{
|
||||||
$translated = $this->translateGettext($key);
|
return $this->translateText('gettext', [$key], $replace);
|
||||||
|
|
||||||
return $this->replaceText($translated, $replace);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -53,7 +66,29 @@ class Translator
|
||||||
*/
|
*/
|
||||||
public function translatePlural(string $key, string $pluralKey, int $number, array $replace = []): string
|
public function translatePlural(string $key, string $pluralKey, int $number, array $replace = []): string
|
||||||
{
|
{
|
||||||
$translated = $this->translateGettextPlural($key, $pluralKey, $number);
|
return $this->translateText('ngettext', [$key, $pluralKey, $number], $replace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $type
|
||||||
|
* @param array $parameters
|
||||||
|
* @param array $replace
|
||||||
|
* @return mixed|string
|
||||||
|
*/
|
||||||
|
protected function translateText(string $type, array $parameters, array $replace = [])
|
||||||
|
{
|
||||||
|
$translated = $parameters[0];
|
||||||
|
|
||||||
|
foreach ([$this->locale, $this->fallbackLocale] as $lang) {
|
||||||
|
/** @var GettextTranslator $translator */
|
||||||
|
$translator = call_user_func($this->getTranslatorCallback, $lang);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$translated = call_user_func_array([$translator, $type], $parameters);
|
||||||
|
break;
|
||||||
|
} catch (TranslationNotFound $e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $this->replaceText($translated, $replace);
|
return $this->replaceText($translated, $replace);
|
||||||
}
|
}
|
||||||
|
@ -74,32 +109,6 @@ class Translator
|
||||||
return call_user_func_array('sprintf', array_merge([$key], $replace));
|
return call_user_func_array('sprintf', array_merge([$key], $replace));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Translate the key via gettext
|
|
||||||
*
|
|
||||||
* @param string $key
|
|
||||||
* @return string
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
protected function translateGettext(string $key): string
|
|
||||||
{
|
|
||||||
return gettext($key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Translate the key via gettext
|
|
||||||
*
|
|
||||||
* @param string $key
|
|
||||||
* @param string $keyPlural
|
|
||||||
* @param int $number
|
|
||||||
* @return string
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
protected function translateGettextPlural(string $key, string $keyPlural, int $number): string
|
|
||||||
{
|
|
||||||
return ngettext($key, $keyPlural, $number);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
|
@ -1,63 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Engelsystem\Helpers;
|
|
||||||
|
|
||||||
use Engelsystem\Config\Config;
|
|
||||||
use Engelsystem\Container\ServiceProvider;
|
|
||||||
use Symfony\Component\HttpFoundation\Session\Session;
|
|
||||||
|
|
||||||
class TranslationServiceProvider extends ServiceProvider
|
|
||||||
{
|
|
||||||
public function register()
|
|
||||||
{
|
|
||||||
/** @var Config $config */
|
|
||||||
$config = $this->app->get('config');
|
|
||||||
/** @var Session $session */
|
|
||||||
$session = $this->app->get('session');
|
|
||||||
|
|
||||||
$locales = $config->get('locales');
|
|
||||||
$locale = $config->get('default_locale');
|
|
||||||
|
|
||||||
$sessionLocale = $session->get('locale', $locale);
|
|
||||||
if (isset($locales[$sessionLocale])) {
|
|
||||||
$locale = $sessionLocale;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->initGettext();
|
|
||||||
$session->set('locale', $locale);
|
|
||||||
|
|
||||||
$translator = $this->app->make(
|
|
||||||
Translator::class,
|
|
||||||
['locale' => $locale, 'locales' => $locales, 'localeChangeCallback' => [$this, 'setLocale']]
|
|
||||||
);
|
|
||||||
$this->app->instance(Translator::class, $translator);
|
|
||||||
$this->app->instance('translator', $translator);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $textDomain
|
|
||||||
* @param string $encoding
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
protected function initGettext($textDomain = 'default', $encoding = 'UTF-8')
|
|
||||||
{
|
|
||||||
bindtextdomain($textDomain, $this->app->get('path.lang'));
|
|
||||||
bind_textdomain_codeset($textDomain, $encoding);
|
|
||||||
textdomain($textDomain);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $locale
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
public function setLocale($locale)
|
|
||||||
{
|
|
||||||
// Set the users locale
|
|
||||||
putenv('LC_ALL=' . $locale);
|
|
||||||
setlocale(LC_ALL, $locale);
|
|
||||||
|
|
||||||
// Reset numeric formatting to allow output of floats
|
|
||||||
putenv('LC_NUMERIC=C');
|
|
||||||
setlocale(LC_NUMERIC, 'C');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Http\Exceptions;
|
||||||
|
|
||||||
|
use Engelsystem\Http\Validation\Validator;
|
||||||
|
use RuntimeException;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class ValidationException extends RuntimeException
|
||||||
|
{
|
||||||
|
/** @var Validator */
|
||||||
|
protected $validator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Validator $validator
|
||||||
|
* @param string $message
|
||||||
|
* @param int $code
|
||||||
|
* @param Throwable|null $previous
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
Validator $validator,
|
||||||
|
string $message = '',
|
||||||
|
int $code = 0,
|
||||||
|
Throwable $previous = null
|
||||||
|
) {
|
||||||
|
$this->validator = $validator;
|
||||||
|
parent::__construct($message, $code, $previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Validator
|
||||||
|
*/
|
||||||
|
public function getValidator(): Validator
|
||||||
|
{
|
||||||
|
return $this->validator;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Http\Validation\Rules;
|
||||||
|
|
||||||
|
use Respect\Validation\Rules\In as RespectIn;
|
||||||
|
|
||||||
|
class In extends RespectIn
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param mixed $haystack
|
||||||
|
* @param bool $compareIdentical
|
||||||
|
*/
|
||||||
|
public function __construct($haystack, $compareIdentical = false)
|
||||||
|
{
|
||||||
|
if (!is_array($haystack)) {
|
||||||
|
$haystack = explode(',', $haystack);
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct($haystack, $compareIdentical);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Http\Validation\Rules;
|
||||||
|
|
||||||
|
class NotIn extends In
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param mixed $input
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function validate($input)
|
||||||
|
{
|
||||||
|
return !parent::validate($input);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Http\Validation;
|
||||||
|
|
||||||
|
use Engelsystem\Http\Exceptions\ValidationException;
|
||||||
|
use Engelsystem\Http\Request;
|
||||||
|
|
||||||
|
trait ValidatesRequest
|
||||||
|
{
|
||||||
|
/** @var Validator */
|
||||||
|
protected $validator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @param array $rules
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function validate(Request $request, array $rules)
|
||||||
|
{
|
||||||
|
if (!$this->validator->validate(
|
||||||
|
(array)$request->getParsedBody(),
|
||||||
|
$rules
|
||||||
|
)) {
|
||||||
|
throw new ValidationException($this->validator);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->validator->getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Validator $validator
|
||||||
|
*/
|
||||||
|
public function setValidator(Validator $validator)
|
||||||
|
{
|
||||||
|
$this->validator = $validator;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Http\Validation;
|
||||||
|
|
||||||
|
use Engelsystem\Application;
|
||||||
|
use Engelsystem\Container\ServiceProvider;
|
||||||
|
use Engelsystem\Controllers\BaseController;
|
||||||
|
|
||||||
|
class ValidationServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
public function register()
|
||||||
|
{
|
||||||
|
$validator = $this->app->make(Validator::class);
|
||||||
|
$this->app->instance(Validator::class, $validator);
|
||||||
|
$this->app->instance('validator', $validator);
|
||||||
|
|
||||||
|
$this->app->afterResolving(function ($object, Application $app) {
|
||||||
|
if (!$object instanceof BaseController) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$object->setValidator($app->get(Validator::class));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Http\Validation;
|
||||||
|
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Respect\Validation\Exceptions\ComponentException;
|
||||||
|
use Respect\Validation\Validator as RespectValidator;
|
||||||
|
|
||||||
|
class Validator
|
||||||
|
{
|
||||||
|
/** @var string[] */
|
||||||
|
protected $errors = [];
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
protected $data = [];
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
protected $mapping = [
|
||||||
|
'accepted' => 'TrueVal',
|
||||||
|
'int' => 'IntVal',
|
||||||
|
'required' => 'NotEmpty',
|
||||||
|
];
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
protected $nestedRules = ['optional', 'not'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $data
|
||||||
|
* @param array $rules
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function validate($data, $rules)
|
||||||
|
{
|
||||||
|
$this->errors = [];
|
||||||
|
$this->data = [];
|
||||||
|
|
||||||
|
foreach ($rules as $key => $values) {
|
||||||
|
$v = new RespectValidator();
|
||||||
|
$v->with('\\Engelsystem\\Http\\Validation\\Rules', true);
|
||||||
|
|
||||||
|
$value = isset($data[$key]) ? $data[$key] : null;
|
||||||
|
$values = explode('|', $values);
|
||||||
|
|
||||||
|
$packing = [];
|
||||||
|
foreach ($this->nestedRules as $rule) {
|
||||||
|
if (in_array($rule, $values)) {
|
||||||
|
$packing[] = $rule;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$values = array_diff($values, $this->nestedRules);
|
||||||
|
foreach ($values as $parameters) {
|
||||||
|
$parameters = explode(':', $parameters);
|
||||||
|
$rule = array_shift($parameters);
|
||||||
|
$rule = Str::camel($rule);
|
||||||
|
$rule = $this->map($rule);
|
||||||
|
|
||||||
|
// To allow rules nesting
|
||||||
|
$w = $v;
|
||||||
|
try {
|
||||||
|
foreach (array_reverse(array_merge($packing, [$rule])) as $rule) {
|
||||||
|
if (!in_array($rule, $this->nestedRules)) {
|
||||||
|
call_user_func_array([$w, $rule], $parameters);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$w = call_user_func_array([new RespectValidator(), $rule], [$w]);
|
||||||
|
}
|
||||||
|
} catch (ComponentException $e) {
|
||||||
|
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($w->validate($value)) {
|
||||||
|
$this->data[$key] = $value;
|
||||||
|
} else {
|
||||||
|
$this->errors[$key][] = implode('.', ['validation', $key, $this->mapBack($rule)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$v->removeRules();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return empty($this->errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $rule
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function map($rule)
|
||||||
|
{
|
||||||
|
return $this->mapping[$rule] ?? $rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $rule
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function mapBack($rule)
|
||||||
|
{
|
||||||
|
$mapping = array_flip($this->mapping);
|
||||||
|
|
||||||
|
return $mapping[$rule] ?? $rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getData(): array
|
||||||
|
{
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function getErrors(): array
|
||||||
|
{
|
||||||
|
return $this->errors;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,10 @@
|
||||||
namespace Engelsystem\Middleware;
|
namespace Engelsystem\Middleware;
|
||||||
|
|
||||||
use Engelsystem\Http\Exceptions\HttpException;
|
use Engelsystem\Http\Exceptions\HttpException;
|
||||||
|
use Engelsystem\Http\Exceptions\ValidationException;
|
||||||
|
use Engelsystem\Http\Request;
|
||||||
use Engelsystem\Http\Response;
|
use Engelsystem\Http\Response;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
use Psr\Http\Server\MiddlewareInterface;
|
use Psr\Http\Server\MiddlewareInterface;
|
||||||
|
@ -18,6 +21,16 @@ class ErrorHandler implements MiddlewareInterface
|
||||||
/** @var string */
|
/** @var string */
|
||||||
protected $viewPrefix = 'errors/';
|
protected $viewPrefix = 'errors/';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of inputs that are not saved from form input
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $formIgnore = [
|
||||||
|
'password',
|
||||||
|
'password_confirmation',
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param TwigLoader $loader
|
* @param TwigLoader $loader
|
||||||
*/
|
*/
|
||||||
|
@ -43,6 +56,21 @@ class ErrorHandler implements MiddlewareInterface
|
||||||
$response = $handler->handle($request);
|
$response = $handler->handle($request);
|
||||||
} catch (HttpException $e) {
|
} catch (HttpException $e) {
|
||||||
$response = $this->createResponse($e->getMessage(), $e->getStatusCode(), $e->getHeaders());
|
$response = $this->createResponse($e->getMessage(), $e->getStatusCode(), $e->getHeaders());
|
||||||
|
} catch (ValidationException $e) {
|
||||||
|
$response = $this->createResponse('', 302, ['Location' => $this->getPreviousUrl($request)]);
|
||||||
|
|
||||||
|
if ($request instanceof Request) {
|
||||||
|
$session = $request->getSession();
|
||||||
|
$session->set(
|
||||||
|
'errors',
|
||||||
|
array_merge_recursive(
|
||||||
|
$session->get('errors', []),
|
||||||
|
['validation' => $e->getValidator()->getErrors()]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$session->set('form-data', Arr::except($request->request->all(), $this->formIgnore));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$statusCode = $response->getStatusCode();
|
$statusCode = $response->getStatusCode();
|
||||||
|
@ -106,4 +134,17 @@ class ErrorHandler implements MiddlewareInterface
|
||||||
{
|
{
|
||||||
return response($content, $status, $headers);
|
return response($content, $status, $headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getPreviousUrl(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
if ($header = $request->getHeader('referer')) {
|
||||||
|
return array_pop($header);
|
||||||
|
}
|
||||||
|
|
||||||
|
return '/';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
namespace Engelsystem\Middleware;
|
namespace Engelsystem\Middleware;
|
||||||
|
|
||||||
use Engelsystem\Helpers\Authenticator;
|
use Engelsystem\Helpers\Authenticator;
|
||||||
use Engelsystem\Helpers\Translator;
|
use Engelsystem\Helpers\Translation\Translator;
|
||||||
use Engelsystem\Http\Request;
|
use Engelsystem\Http\Request;
|
||||||
use Engelsystem\Http\Response;
|
use Engelsystem\Http\Response;
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
|
@ -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();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace Engelsystem\Middleware;
|
namespace Engelsystem\Middleware;
|
||||||
|
|
||||||
use Engelsystem\Helpers\Translator;
|
use Engelsystem\Helpers\Translation\Translator;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
use Psr\Http\Server\MiddlewareInterface;
|
use Psr\Http\Server\MiddlewareInterface;
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Renderer;
|
||||||
|
|
||||||
|
abstract class Engine implements EngineInterface
|
||||||
|
{
|
||||||
|
/** @var array */
|
||||||
|
protected $sharedData = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed[]|string $key
|
||||||
|
* @param null $value
|
||||||
|
*/
|
||||||
|
public function share($key, $value = null)
|
||||||
|
{
|
||||||
|
if (!is_array($key)) {
|
||||||
|
$key = [$key => $value];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->sharedData = array_replace_recursive($this->sharedData, $key);
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,11 +11,17 @@ interface EngineInterface
|
||||||
* @param mixed[] $data
|
* @param mixed[] $data
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function get($path, $data = []);
|
public function get(string $path, array $data = []): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $path
|
* @param string $path
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function canRender($path);
|
public function canRender(string $path): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|mixed[] $key
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
public function share($key, $value = null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace Engelsystem\Renderer;
|
namespace Engelsystem\Renderer;
|
||||||
|
|
||||||
class HtmlEngine implements EngineInterface
|
class HtmlEngine extends Engine
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Render a template
|
* Render a template
|
||||||
|
@ -11,9 +11,11 @@ class HtmlEngine implements EngineInterface
|
||||||
* @param mixed[] $data
|
* @param mixed[] $data
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function get($path, $data = [])
|
public function get(string $path, array $data = []): string
|
||||||
{
|
{
|
||||||
|
$data = array_replace_recursive($this->sharedData, $data);
|
||||||
$template = file_get_contents($path);
|
$template = file_get_contents($path);
|
||||||
|
|
||||||
if (is_array($data)) {
|
if (is_array($data)) {
|
||||||
foreach ($data as $name => $content) {
|
foreach ($data as $name => $content) {
|
||||||
$template = str_replace('%' . $name . '%', $content, $template);
|
$template = str_replace('%' . $name . '%', $content, $template);
|
||||||
|
@ -27,7 +29,7 @@ class HtmlEngine implements EngineInterface
|
||||||
* @param string $path
|
* @param string $path
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function canRender($path)
|
public function canRender(string $path): bool
|
||||||
{
|
{
|
||||||
return mb_strpos($path, '.htm') !== false && file_exists($path);
|
return mb_strpos($path, '.htm') !== false && file_exists($path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace Engelsystem\Renderer\Twig\Extensions;
|
namespace Engelsystem\Renderer\Twig\Extensions;
|
||||||
|
|
||||||
use Engelsystem\Helpers\Translator;
|
use Engelsystem\Helpers\Translation\Translator;
|
||||||
use Twig_Extension as TwigExtension;
|
use Twig_Extension as TwigExtension;
|
||||||
use Twig_Extensions_TokenParser_Trans as TranslationTokenParser;
|
use Twig_Extensions_TokenParser_Trans as TranslationTokenParser;
|
||||||
use Twig_Filter as TwigFilter;
|
use Twig_Filter as TwigFilter;
|
||||||
|
|
|
@ -7,7 +7,7 @@ use Twig_Error_Loader as LoaderError;
|
||||||
use Twig_Error_Runtime as RuntimeError;
|
use Twig_Error_Runtime as RuntimeError;
|
||||||
use Twig_Error_Syntax as SyntaxError;
|
use Twig_Error_Syntax as SyntaxError;
|
||||||
|
|
||||||
class TwigEngine implements EngineInterface
|
class TwigEngine extends Engine
|
||||||
{
|
{
|
||||||
/** @var Twig */
|
/** @var Twig */
|
||||||
protected $twig;
|
protected $twig;
|
||||||
|
@ -25,8 +25,10 @@ class TwigEngine implements EngineInterface
|
||||||
* @return string
|
* @return string
|
||||||
* @throws LoaderError|RuntimeError|SyntaxError
|
* @throws LoaderError|RuntimeError|SyntaxError
|
||||||
*/
|
*/
|
||||||
public function get($path, $data = [])
|
public function get(string $path, array $data = []): string
|
||||||
{
|
{
|
||||||
|
$data = array_replace_recursive($this->sharedData, $data);
|
||||||
|
|
||||||
return $this->twig->render($path, $data);
|
return $this->twig->render($path, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +36,7 @@ class TwigEngine implements EngineInterface
|
||||||
* @param string $path
|
* @param string $path
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function canRender($path)
|
public function canRender(string $path): bool
|
||||||
{
|
{
|
||||||
return $this->twig->getLoader()->exists($path);
|
return $this->twig->getLoader()->exists($path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
use Engelsystem\Application;
|
use Engelsystem\Application;
|
||||||
use Engelsystem\Config\Config;
|
use Engelsystem\Config\Config;
|
||||||
use Engelsystem\Helpers\Authenticator;
|
use Engelsystem\Helpers\Authenticator;
|
||||||
use Engelsystem\Helpers\Translator;
|
use Engelsystem\Helpers\Translation\Translator;
|
||||||
use Engelsystem\Http\Request;
|
use Engelsystem\Http\Request;
|
||||||
use Engelsystem\Http\Response;
|
use Engelsystem\Http\Response;
|
||||||
use Engelsystem\Http\UrlGeneratorInterface;
|
use Engelsystem\Http\UrlGeneratorInterface;
|
||||||
|
|
|
@ -3,40 +3,166 @@
|
||||||
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\Exceptions\ValidationException;
|
||||||
|
use Engelsystem\Http\Request;
|
||||||
use Engelsystem\Http\Response;
|
use Engelsystem\Http\Response;
|
||||||
use Engelsystem\Http\UrlGeneratorInterface;
|
use Engelsystem\Http\UrlGeneratorInterface;
|
||||||
|
use Engelsystem\Http\Validation\Validator;
|
||||||
|
use Engelsystem\Models\User\Settings;
|
||||||
|
use Engelsystem\Models\User\User;
|
||||||
|
use Engelsystem\Test\Unit\HasDatabase;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Session;
|
||||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
||||||
|
|
||||||
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
|
||||||
|
* @covers \Engelsystem\Controllers\AuthController::showLogin
|
||||||
*/
|
*/
|
||||||
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();
|
||||||
|
|
||||||
|
$session->expects($this->once())
|
||||||
|
->method('get')
|
||||||
|
->with('errors', [])
|
||||||
|
->willReturn(['foo' => 'bar']);
|
||||||
|
$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
|
||||||
|
*/
|
||||||
|
public function testPostLogin()
|
||||||
|
{
|
||||||
|
$this->initDatabase();
|
||||||
|
|
||||||
|
$request = new Request();
|
||||||
|
/** @var Response|MockObject $response */
|
||||||
|
$response = $this->createMock(Response::class);
|
||||||
|
/** @var UrlGeneratorInterface|MockObject $url */
|
||||||
|
/** @var Authenticator|MockObject $auth */
|
||||||
|
list(, , $url, $auth) = $this->getMocks();
|
||||||
|
$session = new Session(new MockArraySessionStorage());
|
||||||
|
/** @var Validator|MockObject $validator */
|
||||||
|
$validator = new Validator();
|
||||||
|
|
||||||
|
$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->once())
|
||||||
|
->method('withView')
|
||||||
|
->with('pages/login', ['errors' => Collection::make(['auth.not-found']), 'show_password_recovery' => true])
|
||||||
|
->willReturn($response);
|
||||||
|
$response->expects($this->once())
|
||||||
|
->method('redirectTo')
|
||||||
|
->with('news')
|
||||||
|
->willReturn($response);
|
||||||
|
|
||||||
|
// No credentials
|
||||||
|
$controller = new AuthController($response, $session, $url, $auth);
|
||||||
|
$controller->setValidator($validator);
|
||||||
|
try {
|
||||||
|
$controller->postLogin($request);
|
||||||
|
$this->fail('Login without credentials possible');
|
||||||
|
} catch (ValidationException $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Missing password
|
||||||
|
$request = new Request([], ['login' => 'foo']);
|
||||||
|
try {
|
||||||
|
$controller->postLogin($request);
|
||||||
|
$this->fail('Login without password possible');
|
||||||
|
} catch (ValidationException $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// No user found
|
||||||
|
$request = new Request([], ['login' => 'foo', 'password' => 'bar']);
|
||||||
|
$controller->postLogin($request);
|
||||||
|
$this->assertEquals([], $session->all());
|
||||||
|
|
||||||
|
// Authenticated user
|
||||||
|
$controller->postLogin($request);
|
||||||
|
|
||||||
|
$this->assertNotNull($user->last_login_at);
|
||||||
|
$this->assertEquals(['user_id' => 42, 'locale' => 'de_DE'], $session->all());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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())
|
$session->expects($this->once())
|
||||||
->method('invalidate');
|
->method('invalidate');
|
||||||
|
|
||||||
$response->expects($this->once())
|
|
||||||
->method('redirectTo')
|
|
||||||
->with('https://foo.bar/');
|
|
||||||
|
|
||||||
$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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,5 +21,7 @@ class BaseControllerTest extends TestCase
|
||||||
'dolor',
|
'dolor',
|
||||||
],
|
],
|
||||||
], $controller->getPermissions());
|
], $controller->getPermissions());
|
||||||
|
|
||||||
|
$this->assertTrue(method_exists($controller, 'setValidator'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,4 +66,15 @@ class MetricsEngineTest extends TestCase
|
||||||
$this->assertFalse($engine->canRender('/metrics.foo'));
|
$this->assertFalse($engine->canRender('/metrics.foo'));
|
||||||
$this->assertTrue($engine->canRender('/metrics'));
|
$this->assertTrue($engine->canRender('/metrics'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\Metrics\MetricsEngine::share
|
||||||
|
*/
|
||||||
|
public function testShare()
|
||||||
|
{
|
||||||
|
$engine = new MetricsEngine();
|
||||||
|
|
||||||
|
$engine->share('foo', 42);
|
||||||
|
$this->assertEquals('', $engine->get('/metrics'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() { }
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,3 @@
|
||||||
|
# Testing content
|
||||||
|
msgid "foo.bar"
|
||||||
|
msgstr "Foo Bar!"
|
|
@ -0,0 +1,67 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Test\Unit\Helpers\Translation;
|
||||||
|
|
||||||
|
use Engelsystem\Helpers\Translation\GettextTranslator;
|
||||||
|
use Engelsystem\Helpers\Translation\TranslationNotFound;
|
||||||
|
use Engelsystem\Test\Unit\ServiceProviderTest;
|
||||||
|
use Gettext\Translation;
|
||||||
|
use Gettext\Translations;
|
||||||
|
|
||||||
|
class GettextTranslatorTest extends ServiceProviderTest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Helpers\Translation\GettextTranslator::assertHasTranslation()
|
||||||
|
*/
|
||||||
|
public function testNoTranslation()
|
||||||
|
{
|
||||||
|
$translations = $this->getTranslations();
|
||||||
|
|
||||||
|
$translator = new GettextTranslator();
|
||||||
|
$translator->loadTranslations($translations);
|
||||||
|
|
||||||
|
$this->assertEquals('Translation!', $translator->gettext('test.value'));
|
||||||
|
|
||||||
|
$this->expectException(TranslationNotFound::class);
|
||||||
|
$this->expectExceptionMessage('//foo.bar');
|
||||||
|
|
||||||
|
$translator->gettext('foo.bar');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Helpers\Translation\GettextTranslator::dpgettext()
|
||||||
|
*/
|
||||||
|
public function testDpgettext()
|
||||||
|
{
|
||||||
|
$translations = $this->getTranslations();
|
||||||
|
|
||||||
|
$translator = new GettextTranslator();
|
||||||
|
$translator->loadTranslations($translations);
|
||||||
|
|
||||||
|
$this->assertEquals('Translation!', $translator->dpgettext(null, null, 'test.value'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Helpers\Translation\GettextTranslator::dnpgettext()
|
||||||
|
*/
|
||||||
|
public function testDnpgettext()
|
||||||
|
{
|
||||||
|
$translations = $this->getTranslations();
|
||||||
|
|
||||||
|
$translator = new GettextTranslator();
|
||||||
|
$translator->loadTranslations($translations);
|
||||||
|
|
||||||
|
$this->assertEquals('Translations!', $translator->dnpgettext(null, null, 'test.value', 'test.values', 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTranslations(): Translations
|
||||||
|
{
|
||||||
|
$translations = new Translations();
|
||||||
|
$translations[] =
|
||||||
|
(new Translation(null, 'test.value', 'test.values'))
|
||||||
|
->setTranslation('Translation!')
|
||||||
|
->setPluralTranslations(['Translations!']);
|
||||||
|
|
||||||
|
return $translations;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Engelsystem\Test\Unit\Helpers;
|
namespace Engelsystem\Test\Unit\Helpers\Translation;
|
||||||
|
|
||||||
use Engelsystem\Config\Config;
|
use Engelsystem\Config\Config;
|
||||||
use Engelsystem\Helpers\TranslationServiceProvider;
|
use Engelsystem\Helpers\Translation\TranslationServiceProvider;
|
||||||
use Engelsystem\Helpers\Translator;
|
use Engelsystem\Helpers\Translation\Translator;
|
||||||
use Engelsystem\Test\Unit\ServiceProviderTest;
|
use Engelsystem\Test\Unit\ServiceProviderTest;
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
use Symfony\Component\HttpFoundation\Session\Session;
|
use Symfony\Component\HttpFoundation\Session\Session;
|
||||||
|
@ -12,13 +12,16 @@ use Symfony\Component\HttpFoundation\Session\Session;
|
||||||
class TranslationServiceProviderTest extends ServiceProviderTest
|
class TranslationServiceProviderTest extends ServiceProviderTest
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @covers \Engelsystem\Helpers\TranslationServiceProvider::register()
|
* @covers \Engelsystem\Helpers\Translation\TranslationServiceProvider::register()
|
||||||
*/
|
*/
|
||||||
public function testRegister()
|
public function testRegister(): void
|
||||||
{
|
{
|
||||||
|
$defaultLocale = 'fo_OO';
|
||||||
|
$locale = 'te_ST.WTF-9';
|
||||||
|
$locales = ['fo_OO' => 'Foo', 'fo_OO.BAR' => 'Foo (Bar)', 'te_ST.WTF-9' => 'WTF\'s Testing?'];
|
||||||
|
$config = new Config(['locales' => $locales, 'default_locale' => $defaultLocale]);
|
||||||
|
|
||||||
$app = $this->getApp(['make', 'instance', 'get']);
|
$app = $this->getApp(['make', 'instance', 'get']);
|
||||||
/** @var Config|MockObject $config */
|
|
||||||
$config = $this->createMock(Config::class);
|
|
||||||
/** @var Session|MockObject $session */
|
/** @var Session|MockObject $session */
|
||||||
$session = $this->createMock(Session::class);
|
$session = $this->createMock(Session::class);
|
||||||
/** @var Translator|MockObject $translator */
|
/** @var Translator|MockObject $translator */
|
||||||
|
@ -27,31 +30,14 @@ class TranslationServiceProviderTest extends ServiceProviderTest
|
||||||
/** @var TranslationServiceProvider|MockObject $serviceProvider */
|
/** @var TranslationServiceProvider|MockObject $serviceProvider */
|
||||||
$serviceProvider = $this->getMockBuilder(TranslationServiceProvider::class)
|
$serviceProvider = $this->getMockBuilder(TranslationServiceProvider::class)
|
||||||
->setConstructorArgs([$app])
|
->setConstructorArgs([$app])
|
||||||
->setMethods(['initGettext', 'setLocale'])
|
->setMethods(['setLocale'])
|
||||||
->getMock();
|
->getMock();
|
||||||
|
|
||||||
$serviceProvider->expects($this->once())
|
|
||||||
->method('initGettext');
|
|
||||||
|
|
||||||
$app->expects($this->exactly(2))
|
$app->expects($this->exactly(2))
|
||||||
->method('get')
|
->method('get')
|
||||||
->withConsecutive(['config'], ['session'])
|
->withConsecutive(['config'], ['session'])
|
||||||
->willReturnOnConsecutiveCalls($config, $session);
|
->willReturnOnConsecutiveCalls($config, $session);
|
||||||
|
|
||||||
$defaultLocale = 'fo_OO';
|
|
||||||
$locale = 'te_ST.WTF-9';
|
|
||||||
$locales = ['fo_OO' => 'Foo', 'fo_OO.BAR' => 'Foo (Bar)', 'te_ST.WTF-9' => 'WTF\'s Testing?'];
|
|
||||||
$config->expects($this->exactly(2))
|
|
||||||
->method('get')
|
|
||||||
->withConsecutive(
|
|
||||||
['locales'],
|
|
||||||
['default_locale']
|
|
||||||
)
|
|
||||||
->willReturnOnConsecutiveCalls(
|
|
||||||
$locales,
|
|
||||||
$defaultLocale
|
|
||||||
);
|
|
||||||
|
|
||||||
$session->expects($this->once())
|
$session->expects($this->once())
|
||||||
->method('get')
|
->method('get')
|
||||||
->with('locale', $defaultLocale)
|
->with('locale', $defaultLocale)
|
||||||
|
@ -67,6 +53,8 @@ class TranslationServiceProviderTest extends ServiceProviderTest
|
||||||
[
|
[
|
||||||
'locale' => $locale,
|
'locale' => $locale,
|
||||||
'locales' => $locales,
|
'locales' => $locales,
|
||||||
|
'fallbackLocale' => 'en_US',
|
||||||
|
'getTranslatorCallback' => [$serviceProvider, 'getTranslator'],
|
||||||
'localeChangeCallback' => [$serviceProvider, 'setLocale'],
|
'localeChangeCallback' => [$serviceProvider, 'setLocale'],
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -81,4 +69,22 @@ class TranslationServiceProviderTest extends ServiceProviderTest
|
||||||
|
|
||||||
$serviceProvider->register();
|
$serviceProvider->register();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Helpers\Translation\TranslationServiceProvider::getTranslator()
|
||||||
|
*/
|
||||||
|
public function testGetTranslator(): void
|
||||||
|
{
|
||||||
|
$app = $this->getApp(['get']);
|
||||||
|
$serviceProvider = new TranslationServiceProvider($app);
|
||||||
|
|
||||||
|
$this->setExpects($app, 'get', ['path.lang'], __DIR__ . '/Assets');
|
||||||
|
|
||||||
|
// Get translator
|
||||||
|
$translator = $serviceProvider->getTranslator('fo_OO');
|
||||||
|
$this->assertEquals('Foo Bar!', $translator->gettext('foo.bar'));
|
||||||
|
|
||||||
|
// Retry from cache
|
||||||
|
$serviceProvider->getTranslator('fo_OO');
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Test\Unit\Helpers\Translation;
|
||||||
|
|
||||||
|
use Engelsystem\Helpers\Translation\GettextTranslator;
|
||||||
|
use Engelsystem\Helpers\Translation\TranslationNotFound;
|
||||||
|
use Engelsystem\Helpers\Translation\Translator;
|
||||||
|
use Engelsystem\Test\Unit\ServiceProviderTest;
|
||||||
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
class TranslatorTest extends ServiceProviderTest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Helpers\Translation\Translator::__construct
|
||||||
|
* @covers \Engelsystem\Helpers\Translation\Translator::getLocale
|
||||||
|
* @covers \Engelsystem\Helpers\Translation\Translator::getLocales
|
||||||
|
* @covers \Engelsystem\Helpers\Translation\Translator::hasLocale
|
||||||
|
* @covers \Engelsystem\Helpers\Translation\Translator::setLocale
|
||||||
|
* @covers \Engelsystem\Helpers\Translation\Translator::setLocales
|
||||||
|
*/
|
||||||
|
public function testInit()
|
||||||
|
{
|
||||||
|
$locales = ['te_ST' => 'Tests', 'fo_OO' => 'SomeFOO'];
|
||||||
|
$locale = 'te_ST';
|
||||||
|
|
||||||
|
/** @var callable|MockObject $localeChange */
|
||||||
|
$localeChange = $this->getMockBuilder(stdClass::class)
|
||||||
|
->setMethods(['__invoke'])
|
||||||
|
->getMock();
|
||||||
|
$localeChange->expects($this->exactly(2))
|
||||||
|
->method('__invoke')
|
||||||
|
->withConsecutive(['te_ST'], ['fo_OO']);
|
||||||
|
|
||||||
|
$translator = new Translator($locale, 'fo_OO', function () { }, $locales, $localeChange);
|
||||||
|
|
||||||
|
$this->assertEquals($locales, $translator->getLocales());
|
||||||
|
$this->assertEquals($locale, $translator->getLocale());
|
||||||
|
|
||||||
|
$translator->setLocale('fo_OO');
|
||||||
|
$this->assertEquals('fo_OO', $translator->getLocale());
|
||||||
|
|
||||||
|
$newLocales = ['lo_RM' => 'Lorem', 'ip_SU-M' => 'Ipsum'];
|
||||||
|
$translator->setLocales($newLocales);
|
||||||
|
$this->assertEquals($newLocales, $translator->getLocales());
|
||||||
|
|
||||||
|
$this->assertTrue($translator->hasLocale('ip_SU-M'));
|
||||||
|
$this->assertFalse($translator->hasLocale('te_ST'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Helpers\Translation\Translator::translate
|
||||||
|
*/
|
||||||
|
public function testTranslate()
|
||||||
|
{
|
||||||
|
/** @var Translator|MockObject $translator */
|
||||||
|
$translator = $this->getMockBuilder(Translator::class)
|
||||||
|
->setConstructorArgs(['de_DE', 'en_US', function () { }, ['de_DE' => 'Deutsch']])
|
||||||
|
->setMethods(['translateText'])
|
||||||
|
->getMock();
|
||||||
|
$translator->expects($this->exactly(2))
|
||||||
|
->method('translateText')
|
||||||
|
->withConsecutive(['gettext', ['Hello!'], []], ['gettext', ['My favourite number is %u!'], [3]])
|
||||||
|
->willReturnOnConsecutiveCalls('Hallo!', 'Meine Lieblingszahl ist die 3!');
|
||||||
|
|
||||||
|
$return = $translator->translate('Hello!');
|
||||||
|
$this->assertEquals('Hallo!', $return);
|
||||||
|
|
||||||
|
$return = $translator->translate('My favourite number is %u!', [3]);
|
||||||
|
$this->assertEquals('Meine Lieblingszahl ist die 3!', $return);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Helpers\Translation\Translator::translatePlural
|
||||||
|
*/
|
||||||
|
public function testTranslatePlural()
|
||||||
|
{
|
||||||
|
/** @var Translator|MockObject $translator */
|
||||||
|
$translator = $this->getMockBuilder(Translator::class)
|
||||||
|
->setConstructorArgs(['de_DE', 'en_US', function () { }, ['de_DE' => 'Deutsch']])
|
||||||
|
->setMethods(['translateText'])
|
||||||
|
->getMock();
|
||||||
|
$translator->expects($this->once())
|
||||||
|
->method('translateText')
|
||||||
|
->with('ngettext', ['%s apple', '%s apples', 2], [2])
|
||||||
|
->willReturn('2 Äpfel');
|
||||||
|
|
||||||
|
$return = $translator->translatePlural('%s apple', '%s apples', 2, [2]);
|
||||||
|
$this->assertEquals('2 Äpfel', $return);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Helpers\Translation\Translator::translatePlural
|
||||||
|
* @covers \Engelsystem\Helpers\Translation\Translator::translateText
|
||||||
|
* @covers \Engelsystem\Helpers\Translation\Translator::replaceText
|
||||||
|
*/
|
||||||
|
public function testReplaceText()
|
||||||
|
{
|
||||||
|
/** @var GettextTranslator|MockObject $gtt */
|
||||||
|
$gtt = $this->createMock(GettextTranslator::class);
|
||||||
|
/** @var callable|MockObject $getTranslator */
|
||||||
|
$getTranslator = $this->getMockBuilder(stdClass::class)
|
||||||
|
->setMethods(['__invoke'])
|
||||||
|
->getMock();
|
||||||
|
$getTranslator->expects($this->exactly(5))
|
||||||
|
->method('__invoke')
|
||||||
|
->withConsecutive(['te_ST'], ['fo_OO'], ['te_ST'], ['fo_OO'], ['te_ST'])
|
||||||
|
->willReturn($gtt);
|
||||||
|
|
||||||
|
$i = 0;
|
||||||
|
$gtt->expects($this->exactly(4))
|
||||||
|
->method('gettext')
|
||||||
|
->willReturnCallback(function () use (&$i) {
|
||||||
|
$i++;
|
||||||
|
if ($i != 4) {
|
||||||
|
throw new TranslationNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Lorem %s???';
|
||||||
|
});
|
||||||
|
$this->setExpects($gtt, 'ngettext', ['foo.barf'], 'Lorem %s!');
|
||||||
|
|
||||||
|
$translator = new Translator('te_ST', 'fo_OO', $getTranslator, ['te_ST' => 'Test', 'fo_OO' => 'Foo']);
|
||||||
|
|
||||||
|
// No translation
|
||||||
|
$this->assertEquals('foo.bar', $translator->translate('foo.bar'));
|
||||||
|
|
||||||
|
// Fallback translation
|
||||||
|
$this->assertEquals('Lorem test2???', $translator->translate('foo.batz', ['test2']));
|
||||||
|
|
||||||
|
// Successful translation
|
||||||
|
$this->assertEquals('Lorem test3!', $translator->translatePlural('foo.barf', 'foo.bar2', 3, ['test3']));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,90 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Engelsystem\Test\Unit\Helpers;
|
|
||||||
|
|
||||||
use Engelsystem\Helpers\Translator;
|
|
||||||
use Engelsystem\Test\Unit\ServiceProviderTest;
|
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
|
||||||
use stdClass;
|
|
||||||
|
|
||||||
class TranslatorTest extends ServiceProviderTest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @covers \Engelsystem\Helpers\Translator::__construct
|
|
||||||
* @covers \Engelsystem\Helpers\Translator::getLocale
|
|
||||||
* @covers \Engelsystem\Helpers\Translator::getLocales
|
|
||||||
* @covers \Engelsystem\Helpers\Translator::hasLocale
|
|
||||||
* @covers \Engelsystem\Helpers\Translator::setLocale
|
|
||||||
* @covers \Engelsystem\Helpers\Translator::setLocales
|
|
||||||
*/
|
|
||||||
public function testInit()
|
|
||||||
{
|
|
||||||
$locales = ['te_ST.ER-01' => 'Tests', 'fo_OO' => 'SomeFOO'];
|
|
||||||
$locale = 'te_ST.ER-01';
|
|
||||||
|
|
||||||
/** @var callable|MockObject $callable */
|
|
||||||
$callable = $this->getMockBuilder(stdClass::class)
|
|
||||||
->setMethods(['__invoke'])
|
|
||||||
->getMock();
|
|
||||||
$callable->expects($this->exactly(2))
|
|
||||||
->method('__invoke')
|
|
||||||
->withConsecutive(['te_ST.ER-01'], ['fo_OO']);
|
|
||||||
|
|
||||||
$translator = new Translator($locale, $locales, $callable);
|
|
||||||
|
|
||||||
$this->assertEquals($locales, $translator->getLocales());
|
|
||||||
$this->assertEquals($locale, $translator->getLocale());
|
|
||||||
|
|
||||||
$translator->setLocale('fo_OO');
|
|
||||||
$this->assertEquals('fo_OO', $translator->getLocale());
|
|
||||||
|
|
||||||
$newLocales = ['lo_RM' => 'Lorem', 'ip_SU-M' => 'Ipsum'];
|
|
||||||
$translator->setLocales($newLocales);
|
|
||||||
$this->assertEquals($newLocales, $translator->getLocales());
|
|
||||||
|
|
||||||
$this->assertTrue($translator->hasLocale('ip_SU-M'));
|
|
||||||
$this->assertFalse($translator->hasLocale('te_ST.ER-01'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers \Engelsystem\Helpers\Translator::replaceText
|
|
||||||
* @covers \Engelsystem\Helpers\Translator::translate
|
|
||||||
*/
|
|
||||||
public function testTranslate()
|
|
||||||
{
|
|
||||||
/** @var Translator|MockObject $translator */
|
|
||||||
$translator = $this->getMockBuilder(Translator::class)
|
|
||||||
->setConstructorArgs(['de_DE.UTF-8', ['de_DE.UTF-8' => 'Deutsch']])
|
|
||||||
->setMethods(['translateGettext'])
|
|
||||||
->getMock();
|
|
||||||
$translator->expects($this->exactly(2))
|
|
||||||
->method('translateGettext')
|
|
||||||
->withConsecutive(['Hello!'], ['My favourite number is %u!'])
|
|
||||||
->willReturnOnConsecutiveCalls('Hallo!', 'Meine Lieblingszahl ist die %u!');
|
|
||||||
|
|
||||||
$return = $translator->translate('Hello!');
|
|
||||||
$this->assertEquals('Hallo!', $return);
|
|
||||||
|
|
||||||
$return = $translator->translate('My favourite number is %u!', [3]);
|
|
||||||
$this->assertEquals('Meine Lieblingszahl ist die 3!', $return);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers \Engelsystem\Helpers\Translator::translatePlural
|
|
||||||
*/
|
|
||||||
public function testTranslatePlural()
|
|
||||||
{
|
|
||||||
/** @var Translator|MockObject $translator */
|
|
||||||
$translator = $this->getMockBuilder(Translator::class)
|
|
||||||
->setConstructorArgs(['de_DE.UTF-8', ['de_DE.UTF-8' => 'Deutsch']])
|
|
||||||
->setMethods(['translateGettextPlural'])
|
|
||||||
->getMock();
|
|
||||||
$translator->expects($this->once())
|
|
||||||
->method('translateGettextPlural')
|
|
||||||
->with('%s apple', '%s apples', 2)
|
|
||||||
->willReturn('2 Äpfel');
|
|
||||||
|
|
||||||
$return = $translator->translatePlural('%s apple', '%s apples', 2, [2]);
|
|
||||||
$this->assertEquals('2 Äpfel', $return);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,7 +6,7 @@ use Engelsystem\Application;
|
||||||
use Engelsystem\Config\Config;
|
use Engelsystem\Config\Config;
|
||||||
use Engelsystem\Container\Container;
|
use Engelsystem\Container\Container;
|
||||||
use Engelsystem\Helpers\Authenticator;
|
use Engelsystem\Helpers\Authenticator;
|
||||||
use Engelsystem\Helpers\Translator;
|
use Engelsystem\Helpers\Translation\Translator;
|
||||||
use Engelsystem\Http\Request;
|
use Engelsystem\Http\Request;
|
||||||
use Engelsystem\Http\Response;
|
use Engelsystem\Http\Response;
|
||||||
use Engelsystem\Http\UrlGeneratorInterface;
|
use Engelsystem\Http\UrlGeneratorInterface;
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Test\Unit\Http\Exceptions;
|
||||||
|
|
||||||
|
use Engelsystem\Http\Exceptions\ValidationException;
|
||||||
|
use Engelsystem\Http\Validation\Validator;
|
||||||
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class ValidationExceptionTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Http\Exceptions\ValidationException::__construct
|
||||||
|
* @covers \Engelsystem\Http\Exceptions\ValidationException::getValidator
|
||||||
|
*/
|
||||||
|
public function testConstruct()
|
||||||
|
{
|
||||||
|
/** @var Validator|MockObject $validator */
|
||||||
|
$validator = $this->createMock(Validator::class);
|
||||||
|
|
||||||
|
$exception = new ValidationException($validator);
|
||||||
|
|
||||||
|
$this->assertEquals($validator, $exception->getValidator());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Test\Unit\Http\Validation\Rules;
|
||||||
|
|
||||||
|
use Engelsystem\Http\Validation\Rules\In;
|
||||||
|
use Engelsystem\Test\Unit\TestCase;
|
||||||
|
|
||||||
|
class InTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Http\Validation\Rules\In::__construct
|
||||||
|
*/
|
||||||
|
public function testConstruct()
|
||||||
|
{
|
||||||
|
$rule = new In('foo,bar');
|
||||||
|
|
||||||
|
$this->assertEquals(['foo', 'bar'], $rule->haystack);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Test\Unit\Http\Validation\Rules;
|
||||||
|
|
||||||
|
use Engelsystem\Http\Validation\Rules\NotIn;
|
||||||
|
use Engelsystem\Test\Unit\TestCase;
|
||||||
|
|
||||||
|
class NotInTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Http\Validation\Rules\NotIn::validate
|
||||||
|
*/
|
||||||
|
public function testConstruct()
|
||||||
|
{
|
||||||
|
$rule = new NotIn('foo,bar');
|
||||||
|
|
||||||
|
$this->assertTrue($rule->validate('lorem'));
|
||||||
|
$this->assertFalse($rule->validate('foo'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Test\Unit\Http\Validation\Stub;
|
||||||
|
|
||||||
|
use Engelsystem\Controllers\BaseController;
|
||||||
|
use Engelsystem\Http\Request;
|
||||||
|
|
||||||
|
class ValidatesRequestImplementation extends BaseController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @param array $rules
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function validateData(Request $request, array $rules)
|
||||||
|
{
|
||||||
|
return $this->validate($request, $rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasValidator()
|
||||||
|
{
|
||||||
|
return !is_null($this->validator);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Test\Unit\Http\Validation;
|
||||||
|
|
||||||
|
use Engelsystem\Http\Exceptions\ValidationException;
|
||||||
|
use Engelsystem\Http\Request;
|
||||||
|
use Engelsystem\Http\Validation\Validator;
|
||||||
|
use Engelsystem\Test\Unit\Http\Validation\Stub\ValidatesRequestImplementation;
|
||||||
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class ValidatesRequestTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Http\Validation\ValidatesRequest::validate
|
||||||
|
* @covers \Engelsystem\Http\Validation\ValidatesRequest::setValidator
|
||||||
|
*/
|
||||||
|
public function testValidate()
|
||||||
|
{
|
||||||
|
/** @var Validator|MockObject $validator */
|
||||||
|
$validator = $this->createMock(Validator::class);
|
||||||
|
$validator->expects($this->exactly(2))
|
||||||
|
->method('validate')
|
||||||
|
->withConsecutive(
|
||||||
|
[['foo' => 'bar'], ['foo' => 'required']],
|
||||||
|
[[], ['foo' => 'required']]
|
||||||
|
)
|
||||||
|
->willReturnOnConsecutiveCalls(
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
$validator->expects($this->once())
|
||||||
|
->method('getData')
|
||||||
|
->willReturn(['foo' => 'bar']);
|
||||||
|
|
||||||
|
$implementation = new ValidatesRequestImplementation();
|
||||||
|
$implementation->setValidator($validator);
|
||||||
|
|
||||||
|
$return = $implementation->validateData(new Request([], ['foo' => 'bar']), ['foo' => 'required']);
|
||||||
|
|
||||||
|
$this->assertEquals(['foo' => 'bar'], $return);
|
||||||
|
|
||||||
|
$this->expectException(ValidationException::class);
|
||||||
|
$implementation->validateData(new Request([], []), ['foo' => 'required']);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Test\Unit\Http\Validation;
|
||||||
|
|
||||||
|
use Engelsystem\Application;
|
||||||
|
use Engelsystem\Http\Validation\ValidationServiceProvider;
|
||||||
|
use Engelsystem\Http\Validation\Validator;
|
||||||
|
use Engelsystem\Test\Unit\Http\Validation\Stub\ValidatesRequestImplementation;
|
||||||
|
use Engelsystem\Test\Unit\ServiceProviderTest;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
class ValidationServiceProviderTest extends ServiceProviderTest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Http\Validation\ValidationServiceProvider::register
|
||||||
|
*/
|
||||||
|
public function testRegister()
|
||||||
|
{
|
||||||
|
$app = new Application();
|
||||||
|
|
||||||
|
$serviceProvider = new ValidationServiceProvider($app);
|
||||||
|
$serviceProvider->register();
|
||||||
|
|
||||||
|
$this->assertTrue($app->has(Validator::class));
|
||||||
|
$this->assertTrue($app->has('validator'));
|
||||||
|
|
||||||
|
/** @var ValidatesRequestImplementation $validatesRequest */
|
||||||
|
$validatesRequest = $app->make(ValidatesRequestImplementation::class);
|
||||||
|
$this->assertTrue($validatesRequest->hasValidator());
|
||||||
|
|
||||||
|
// Test afterResolving early return
|
||||||
|
$app->make(stdClass::class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Test\Unit\Http\Validation;
|
||||||
|
|
||||||
|
use Engelsystem\Http\Validation\Validator;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class ValidatorTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Http\Validation\Validator::validate
|
||||||
|
* @covers \Engelsystem\Http\Validation\Validator::getData
|
||||||
|
* @covers \Engelsystem\Http\Validation\Validator::getErrors
|
||||||
|
*/
|
||||||
|
public function testValidate()
|
||||||
|
{
|
||||||
|
$val = new Validator();
|
||||||
|
|
||||||
|
$this->assertTrue($val->validate(
|
||||||
|
['foo' => 'bar', 'lorem' => 'on', 'dolor' => 'bla'],
|
||||||
|
['lorem' => 'accepted']
|
||||||
|
));
|
||||||
|
$this->assertEquals(['lorem' => 'on'], $val->getData());
|
||||||
|
|
||||||
|
$this->assertFalse($val->validate(
|
||||||
|
[],
|
||||||
|
['lorem' => 'required|min:3']
|
||||||
|
));
|
||||||
|
$this->assertEquals(
|
||||||
|
['lorem' => ['validation.lorem.required', 'validation.lorem.min']],
|
||||||
|
$val->getErrors()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Http\Validation\Validator::validate
|
||||||
|
*/
|
||||||
|
public function testValidateChaining()
|
||||||
|
{
|
||||||
|
$val = new Validator();
|
||||||
|
|
||||||
|
$this->assertTrue($val->validate(
|
||||||
|
['lorem' => 10],
|
||||||
|
['lorem' => 'required|min:3|max:10']
|
||||||
|
));
|
||||||
|
$this->assertTrue($val->validate(
|
||||||
|
['lorem' => 3],
|
||||||
|
['lorem' => 'required|min:3|max:10']
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->assertFalse($val->validate(
|
||||||
|
['lorem' => 2],
|
||||||
|
['lorem' => 'required|min:3|max:10']
|
||||||
|
));
|
||||||
|
$this->assertFalse($val->validate(
|
||||||
|
['lorem' => 42],
|
||||||
|
['lorem' => 'required|min:3|max:10']
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Http\Validation\Validator::validate
|
||||||
|
*/
|
||||||
|
public function testValidateNotImplemented()
|
||||||
|
{
|
||||||
|
$val = new Validator();
|
||||||
|
|
||||||
|
$this->expectException(InvalidArgumentException::class);
|
||||||
|
|
||||||
|
$val->validate(
|
||||||
|
['lorem' => 'bar'],
|
||||||
|
['foo' => 'never_implemented']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Http\Validation\Validator::map
|
||||||
|
* @covers \Engelsystem\Http\Validation\Validator::mapBack
|
||||||
|
*/
|
||||||
|
public function testValidateMapping()
|
||||||
|
{
|
||||||
|
$val = new Validator();
|
||||||
|
|
||||||
|
$this->assertTrue($val->validate(
|
||||||
|
['foo' => 'bar'],
|
||||||
|
['foo' => 'required']
|
||||||
|
));
|
||||||
|
$this->assertTrue($val->validate(
|
||||||
|
['foo' => '0'],
|
||||||
|
['foo' => 'int']
|
||||||
|
));
|
||||||
|
$this->assertTrue($val->validate(
|
||||||
|
['foo' => 'on'],
|
||||||
|
['foo' => 'accepted']
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->assertFalse($val->validate(
|
||||||
|
[],
|
||||||
|
['lorem' => 'required']
|
||||||
|
));
|
||||||
|
$this->assertEquals(
|
||||||
|
['lorem' => ['validation.lorem.required']],
|
||||||
|
$val->getErrors()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Http\Validation\Validator::validate
|
||||||
|
*/
|
||||||
|
public function testValidateNesting()
|
||||||
|
{
|
||||||
|
$val = new Validator();
|
||||||
|
|
||||||
|
$this->assertTrue($val->validate(
|
||||||
|
[],
|
||||||
|
['foo' => 'not|required']
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->assertTrue($val->validate(
|
||||||
|
['foo' => 'foo'],
|
||||||
|
['foo' => 'not|int']
|
||||||
|
));
|
||||||
|
$this->assertFalse($val->validate(
|
||||||
|
['foo' => 1],
|
||||||
|
['foo' => 'not|int']
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->assertTrue($val->validate(
|
||||||
|
[],
|
||||||
|
['foo' => 'optional|int']
|
||||||
|
));
|
||||||
|
$this->assertTrue($val->validate(
|
||||||
|
['foo' => '33'],
|
||||||
|
['foo' => 'optional|int']
|
||||||
|
));
|
||||||
|
$this->assertFalse($val->validate(
|
||||||
|
['foo' => 'T'],
|
||||||
|
['foo' => 'optional|int']
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,14 +2,23 @@
|
||||||
|
|
||||||
namespace Engelsystem\Test\Unit\Middleware;
|
namespace Engelsystem\Test\Unit\Middleware;
|
||||||
|
|
||||||
|
use Engelsystem\Application;
|
||||||
use Engelsystem\Http\Exceptions\HttpException;
|
use Engelsystem\Http\Exceptions\HttpException;
|
||||||
|
use Engelsystem\Http\Exceptions\ValidationException;
|
||||||
|
use Engelsystem\Http\Psr7ServiceProvider;
|
||||||
|
use Engelsystem\Http\Request;
|
||||||
use Engelsystem\Http\Response;
|
use Engelsystem\Http\Response;
|
||||||
|
use Engelsystem\Http\ResponseServiceProvider;
|
||||||
|
use Engelsystem\Http\Validation\Validator;
|
||||||
use Engelsystem\Middleware\ErrorHandler;
|
use Engelsystem\Middleware\ErrorHandler;
|
||||||
use Engelsystem\Test\Unit\Middleware\Stub\ReturnResponseMiddlewareHandler;
|
use Engelsystem\Test\Unit\Middleware\Stub\ReturnResponseMiddlewareHandler;
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Psr\Http\Server\RequestHandlerInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Session;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
||||||
use Twig_LoaderInterface as TwigLoader;
|
use Twig_LoaderInterface as TwigLoader;
|
||||||
|
|
||||||
class ErrorHandlerTest extends TestCase
|
class ErrorHandlerTest extends TestCase
|
||||||
|
@ -104,7 +113,7 @@ class ErrorHandlerTest extends TestCase
|
||||||
/**
|
/**
|
||||||
* @covers \Engelsystem\Middleware\ErrorHandler::process
|
* @covers \Engelsystem\Middleware\ErrorHandler::process
|
||||||
*/
|
*/
|
||||||
public function testProcessException()
|
public function testProcessHttpException()
|
||||||
{
|
{
|
||||||
/** @var ServerRequestInterface|MockObject $request */
|
/** @var ServerRequestInterface|MockObject $request */
|
||||||
$request = $this->createMock(ServerRequestInterface::class);
|
$request = $this->createMock(ServerRequestInterface::class);
|
||||||
|
@ -144,6 +153,67 @@ class ErrorHandlerTest extends TestCase
|
||||||
$this->assertEquals($psrResponse, $return);
|
$this->assertEquals($psrResponse, $return);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Middleware\ErrorHandler::process
|
||||||
|
* @covers \Engelsystem\Middleware\ErrorHandler::getPreviousUrl
|
||||||
|
*/
|
||||||
|
public function testProcessValidationException()
|
||||||
|
{
|
||||||
|
/** @var TwigLoader|MockObject $twigLoader */
|
||||||
|
$twigLoader = $this->createMock(TwigLoader::class);
|
||||||
|
$handler = $this->getMockForAbstractClass(RequestHandlerInterface::class);
|
||||||
|
$validator = $this->createMock(Validator::class);
|
||||||
|
|
||||||
|
$handler->expects($this->exactly(2))
|
||||||
|
->method('handle')
|
||||||
|
->willReturnCallback(function () use ($validator) {
|
||||||
|
throw new ValidationException($validator);
|
||||||
|
});
|
||||||
|
|
||||||
|
$validator->expects($this->exactly(2))
|
||||||
|
->method('getErrors')
|
||||||
|
->willReturn(['foo' => ['validation.foo.numeric']]);
|
||||||
|
|
||||||
|
$session = new Session(new MockArraySessionStorage());
|
||||||
|
$session->set('errors', ['validation' => ['foo' => ['validation.foo.required']]]);
|
||||||
|
$request = Request::create(
|
||||||
|
'/foo/bar',
|
||||||
|
'POST',
|
||||||
|
['foo' => 'bar', 'password' => 'Test123', 'password_confirmation' => 'Test1234']
|
||||||
|
);
|
||||||
|
$request->setSession($session);
|
||||||
|
|
||||||
|
/** @var Application $app */
|
||||||
|
$app = app();
|
||||||
|
(new ResponseServiceProvider($app))->register();
|
||||||
|
(new Psr7ServiceProvider($app))->register();
|
||||||
|
|
||||||
|
$errorHandler = new ErrorHandler($twigLoader);
|
||||||
|
|
||||||
|
$return = $errorHandler->process($request, $handler);
|
||||||
|
|
||||||
|
$this->assertEquals(302, $return->getStatusCode());
|
||||||
|
$this->assertEquals('/', $return->getHeaderLine('location'));
|
||||||
|
$this->assertEquals([
|
||||||
|
'errors' => [
|
||||||
|
'validation' => [
|
||||||
|
'foo' => [
|
||||||
|
'validation.foo.required',
|
||||||
|
'validation.foo.numeric',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'form-data' => [
|
||||||
|
'foo' => 'bar',
|
||||||
|
],
|
||||||
|
], $session->all());
|
||||||
|
|
||||||
|
$request = $request->withAddedHeader('referer', '/foo/batz');
|
||||||
|
$return = $errorHandler->process($request, $handler);
|
||||||
|
|
||||||
|
$this->assertEquals('/foo/batz', $return->getHeaderLine('location'));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers \Engelsystem\Middleware\ErrorHandler::process
|
* @covers \Engelsystem\Middleware\ErrorHandler::process
|
||||||
*/
|
*/
|
||||||
|
@ -153,7 +223,7 @@ class ErrorHandlerTest extends TestCase
|
||||||
$request = $this->createMock(ServerRequestInterface::class);
|
$request = $this->createMock(ServerRequestInterface::class);
|
||||||
/** @var TwigLoader|MockObject $twigLoader */
|
/** @var TwigLoader|MockObject $twigLoader */
|
||||||
$twigLoader = $this->createMock(TwigLoader::class);
|
$twigLoader = $this->createMock(TwigLoader::class);
|
||||||
$response = new Response('<!DOCTYPE html><html><body><h1>Hi!</h1></body></html>', 500);
|
$response = new Response('<!DOCTYPE html><html lang="en"><body><h1>Hi!</h1></body></html>', 500);
|
||||||
$returnResponseHandler = new ReturnResponseMiddlewareHandler($response);
|
$returnResponseHandler = new ReturnResponseMiddlewareHandler($response);
|
||||||
|
|
||||||
/** @var ErrorHandler|MockObject $errorHandler */
|
/** @var ErrorHandler|MockObject $errorHandler */
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
namespace Engelsystem\Test\Unit\Middleware;
|
namespace Engelsystem\Test\Unit\Middleware;
|
||||||
|
|
||||||
use Engelsystem\Helpers\Authenticator;
|
use Engelsystem\Helpers\Authenticator;
|
||||||
use Engelsystem\Helpers\Translator;
|
use Engelsystem\Helpers\Translation\Translator;
|
||||||
use Engelsystem\Http\Request;
|
use Engelsystem\Http\Request;
|
||||||
use Engelsystem\Middleware\LegacyMiddleware;
|
use Engelsystem\Middleware\LegacyMiddleware;
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace Engelsystem\Test\Unit\Middleware;
|
namespace Engelsystem\Test\Unit\Middleware;
|
||||||
|
|
||||||
use Engelsystem\Helpers\Translator;
|
use Engelsystem\Helpers\Translation\Translator;
|
||||||
use Engelsystem\Middleware\SetLocale;
|
use Engelsystem\Middleware\SetLocale;
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Test\Unit\Renderer;
|
||||||
|
|
||||||
|
use Engelsystem\Test\Unit\Renderer\Stub\EngineImplementation;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class EngineTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Renderer\Engine::share
|
||||||
|
*/
|
||||||
|
public function testShare()
|
||||||
|
{
|
||||||
|
$engine = new EngineImplementation();
|
||||||
|
$engine->share(['foo' => ['bar' => 'baz', 'lorem' => 'ipsum']]);
|
||||||
|
$engine->share(['foo' => ['lorem' => 'dolor']]);
|
||||||
|
$engine->share('key', 'value');
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
['foo' => ['bar' => 'baz', 'lorem' => 'dolor'], 'key' => 'value'],
|
||||||
|
$engine->getSharedData()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,11 +16,12 @@ class HtmlEngineTest extends TestCase
|
||||||
public function testGet()
|
public function testGet()
|
||||||
{
|
{
|
||||||
$engine = new HtmlEngine();
|
$engine = new HtmlEngine();
|
||||||
|
$engine->share('shared_data', 'tester');
|
||||||
|
|
||||||
$file = $this->createTempFile('<div>%main_content%</div>');
|
$file = $this->createTempFile('<div>%main_content% is a %shared_data%</div>');
|
||||||
|
|
||||||
$data = $engine->get($file, ['main_content' => 'Lorem ipsum dolor sit']);
|
$data = $engine->get($file, ['main_content' => 'Lorem ipsum dolor sit']);
|
||||||
$this->assertEquals('<div>Lorem ipsum dolor sit</div>', $data);
|
$this->assertEquals('<div>Lorem ipsum dolor sit is a tester</div>', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Engelsystem\Test\Unit\Renderer\Stub;
|
||||||
|
|
||||||
|
use Engelsystem\Renderer\Engine;
|
||||||
|
|
||||||
|
class EngineImplementation extends Engine
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function get(string $path, array $data = []): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function canRender(string $path): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getSharedData(): array
|
||||||
|
{
|
||||||
|
return $this->sharedData;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace Engelsystem\Test\Unit\Renderer\Twig\Extensions;
|
namespace Engelsystem\Test\Unit\Renderer\Twig\Extensions;
|
||||||
|
|
||||||
use Engelsystem\Helpers\Translator;
|
use Engelsystem\Helpers\Translation\Translator;
|
||||||
use Engelsystem\Renderer\Twig\Extensions\Translation;
|
use Engelsystem\Renderer\Twig\Extensions\Translation;
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
use Twig_Extensions_TokenParser_Trans as TranslationTokenParser;
|
use Twig_Extensions_TokenParser_Trans as TranslationTokenParser;
|
||||||
|
|
|
@ -20,16 +20,16 @@ class TwigEngineTest extends TestCase
|
||||||
$twig = $this->createMock(Twig::class);
|
$twig = $this->createMock(Twig::class);
|
||||||
|
|
||||||
$path = 'foo.twig';
|
$path = 'foo.twig';
|
||||||
$data = ['lorem' => 'ipsum'];
|
|
||||||
|
|
||||||
$twig->expects($this->once())
|
$twig->expects($this->once())
|
||||||
->method('render')
|
->method('render')
|
||||||
->with($path, $data)
|
->with($path, ['lorem' => 'ipsum', 'shared' => 'data'])
|
||||||
->willReturn('LoremIpsum!');
|
->willReturn('LoremIpsum data!');
|
||||||
|
|
||||||
$engine = new TwigEngine($twig);
|
$engine = new TwigEngine($twig);
|
||||||
$return = $engine->get($path, $data);
|
$engine->share('shared', 'data');
|
||||||
$this->assertEquals('LoremIpsum!', $return);
|
|
||||||
|
$return = $engine->get($path, ['lorem' => 'ipsum']);
|
||||||
|
$this->assertEquals('LoremIpsum data!', $return);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue