Enable logins using pilot ID #698
This commit is contained in:
parent
00505a1607
commit
b9aeda1cba
43
app/Exceptions/PilotIdNotFound.php
Normal file
43
app/Exceptions/PilotIdNotFound.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
class PilotIdNotFound extends AbstractHttpException
|
||||||
|
{
|
||||||
|
private $pilot_id;
|
||||||
|
|
||||||
|
public function __construct($pilot_id)
|
||||||
|
{
|
||||||
|
$this->pilot_id = $pilot_id;
|
||||||
|
parent::__construct(
|
||||||
|
404,
|
||||||
|
'Pilot '.$pilot_id.' not found'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the RFC 7807 error type (without the URL root)
|
||||||
|
*/
|
||||||
|
public function getErrorType(): string
|
||||||
|
{
|
||||||
|
return 'pilot-id-not-found';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the detailed error string
|
||||||
|
*/
|
||||||
|
public function getErrorDetails(): string
|
||||||
|
{
|
||||||
|
return $this->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array with the error details, merged with the RFC7807 response
|
||||||
|
*/
|
||||||
|
public function getErrorMetadata(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'pilot_id' => $this->pilot_id,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -3,36 +3,98 @@
|
|||||||
namespace App\Http\Controllers\Auth;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
use App\Contracts\Controller;
|
use App\Contracts\Controller;
|
||||||
|
use App\Exceptions\PilotIdNotFound;
|
||||||
use App\Models\Enums\UserState;
|
use App\Models\Enums\UserState;
|
||||||
|
use App\Services\UserService;
|
||||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
/**
|
|
||||||
* Class LoginController
|
|
||||||
*/
|
|
||||||
class LoginController extends Controller
|
class LoginController extends Controller
|
||||||
{
|
{
|
||||||
use AuthenticatesUsers;
|
use AuthenticatesUsers;
|
||||||
|
|
||||||
protected $redirectTo = '/dashboard';
|
protected $redirectTo = '/dashboard';
|
||||||
|
|
||||||
|
/** @var UserService */
|
||||||
|
private $userSvc;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $loginFieldValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LoginController constructor.
|
* LoginController constructor.
|
||||||
|
*
|
||||||
|
* @param UserService $userSvc
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct(UserService $userSvc)
|
||||||
{
|
{
|
||||||
$this->redirectTo = config('phpvms.login_redirect');
|
$this->redirectTo = config('phpvms.login_redirect');
|
||||||
$this->middleware('guest', ['except' => 'logout']);
|
$this->middleware('guest', ['except' => 'logout']);
|
||||||
|
$this->userSvc = $userSvc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
* Get the needed authorization credentials from the request.
|
||||||
|
* Overriding the value from the trait
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function showLoginForm()
|
protected function credentials(Request $request)
|
||||||
{
|
{
|
||||||
return view('auth/login');
|
return [
|
||||||
|
'email' => $this->loginFieldValue,
|
||||||
|
'password' => $request->input('password'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the user login request.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function validateLogin(Request $request)
|
||||||
|
{
|
||||||
|
$id_field = $request->input('email');
|
||||||
|
$validations = ['required', 'string'];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Trying to login by email or not?
|
||||||
|
*
|
||||||
|
* If not, run a validation rule which attempts to split the user by their VA and ID
|
||||||
|
* Then inject that user's email into the request
|
||||||
|
*/
|
||||||
|
if (strpos($id_field, '@') !== false) {
|
||||||
|
$validations[] = 'email';
|
||||||
|
$this->loginFieldValue = $request->input('email');
|
||||||
|
} else {
|
||||||
|
$validations[] = function ($attr, $value, $fail) use ($request) {
|
||||||
|
try {
|
||||||
|
$user = $this->userSvc->findUserByPilotId($value);
|
||||||
|
} catch (PilotIdNotFound $ex) {
|
||||||
|
Log::warning('Error logging in, pilot_id not found, id='.$value);
|
||||||
|
$fail('Pilot not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$request->email = $user->email;
|
||||||
|
$this->loginFieldValue = $user->email;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
$request->validate([
|
||||||
|
'email' => $validations,
|
||||||
|
'password' => 'required|string',
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -6,6 +6,7 @@ use App\Contracts\Service;
|
|||||||
use App\Events\UserRegistered;
|
use App\Events\UserRegistered;
|
||||||
use App\Events\UserStateChanged;
|
use App\Events\UserStateChanged;
|
||||||
use App\Events\UserStatsChanged;
|
use App\Events\UserStatsChanged;
|
||||||
|
use App\Exceptions\PilotIdNotFound;
|
||||||
use App\Exceptions\UserPilotIdExists;
|
use App\Exceptions\UserPilotIdExists;
|
||||||
use App\Models\Enums\PirepState;
|
use App\Models\Enums\PirepState;
|
||||||
use App\Models\Enums\UserState;
|
use App\Models\Enums\UserState;
|
||||||
@ -14,6 +15,7 @@ use App\Models\Rank;
|
|||||||
use App\Models\Role;
|
use App\Models\Role;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Repositories\AircraftRepository;
|
use App\Repositories\AircraftRepository;
|
||||||
|
use App\Repositories\AirlineRepository;
|
||||||
use App\Repositories\SubfleetRepository;
|
use App\Repositories\SubfleetRepository;
|
||||||
use App\Repositories\UserRepository;
|
use App\Repositories\UserRepository;
|
||||||
use App\Support\Units\Time;
|
use App\Support\Units\Time;
|
||||||
@ -25,6 +27,7 @@ use function is_array;
|
|||||||
class UserService extends Service
|
class UserService extends Service
|
||||||
{
|
{
|
||||||
private $aircraftRepo;
|
private $aircraftRepo;
|
||||||
|
private $airlineRepo;
|
||||||
private $subfleetRepo;
|
private $subfleetRepo;
|
||||||
private $userRepo;
|
private $userRepo;
|
||||||
|
|
||||||
@ -32,15 +35,18 @@ class UserService extends Service
|
|||||||
* UserService constructor.
|
* UserService constructor.
|
||||||
*
|
*
|
||||||
* @param AircraftRepository $aircraftRepo
|
* @param AircraftRepository $aircraftRepo
|
||||||
|
* @param AirlineRepository $airlineRepo
|
||||||
* @param SubfleetRepository $subfleetRepo
|
* @param SubfleetRepository $subfleetRepo
|
||||||
* @param UserRepository $userRepo
|
* @param UserRepository $userRepo
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
AircraftRepository $aircraftRepo,
|
AircraftRepository $aircraftRepo,
|
||||||
|
AirlineRepository $airlineRepo,
|
||||||
SubfleetRepository $subfleetRepo,
|
SubfleetRepository $subfleetRepo,
|
||||||
UserRepository $userRepo
|
UserRepository $userRepo
|
||||||
) {
|
) {
|
||||||
$this->aircraftRepo = $aircraftRepo;
|
$this->aircraftRepo = $aircraftRepo;
|
||||||
|
$this->airlineRepo = $airlineRepo;
|
||||||
$this->subfleetRepo = $subfleetRepo;
|
$this->subfleetRepo = $subfleetRepo;
|
||||||
$this->userRepo = $userRepo;
|
$this->userRepo = $userRepo;
|
||||||
}
|
}
|
||||||
@ -178,6 +184,42 @@ class UserService extends Service
|
|||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split a given pilot ID into an airline and ID portions
|
||||||
|
*
|
||||||
|
* @param string $pilot_id
|
||||||
|
*/
|
||||||
|
public function findUserByPilotId(string $pilot_id)
|
||||||
|
{
|
||||||
|
$airlines = $this->airlineRepo->all(['id', 'icao', 'iata']);
|
||||||
|
|
||||||
|
$ident_str = null;
|
||||||
|
$pilot_id = strtoupper($pilot_id);
|
||||||
|
foreach ($airlines as $airline) {
|
||||||
|
if (strpos($pilot_id, $airline->icao) !== false) {
|
||||||
|
$ident_str = $airline->icao;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($pilot_id, $airline->iata) !== false) {
|
||||||
|
$ident_str = $airline->iata;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($ident_str)) {
|
||||||
|
throw new PilotIdNotFound($pilot_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$parsed_pilot_id = str_replace($ident_str, '', $pilot_id);
|
||||||
|
$user = User::where(['airline_id' => $airline->id, 'pilot_id' => $parsed_pilot_id])->first();
|
||||||
|
if (empty($user)) {
|
||||||
|
throw new PilotIdNotFound($pilot_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the subfleets this user is allowed access to,
|
* Return the subfleets this user is allowed access to,
|
||||||
* based on their current rank
|
* based on their current rank
|
||||||
|
@ -10,6 +10,7 @@ return [
|
|||||||
'newestpilots' => 'Newest Pilots',
|
'newestpilots' => 'Newest Pilots',
|
||||||
'profile' => 'Profile',
|
'profile' => 'Profile',
|
||||||
'email' => 'Email',
|
'email' => 'Email',
|
||||||
|
'pilot_id' => 'Pilot ID',
|
||||||
'register' => 'Register',
|
'register' => 'Register',
|
||||||
'login' => 'Log In',
|
'login' => 'Log In',
|
||||||
'logout' => 'Log Out',
|
'logout' => 'Log Out',
|
||||||
@ -48,6 +49,7 @@ return [
|
|||||||
'inactive' => 'Inactive',
|
'inactive' => 'Inactive',
|
||||||
'yes' => 'Yes',
|
'yes' => 'Yes',
|
||||||
'no' => 'No',
|
'no' => 'No',
|
||||||
|
'or' => 'or',
|
||||||
'days' => [
|
'days' => [
|
||||||
'mon' => 'Monday',
|
'mon' => 'Monday',
|
||||||
'tues' => 'Tuesday',
|
'tues' => 'Tuesday',
|
||||||
|
@ -10,6 +10,7 @@ return [
|
|||||||
'newestpilots' => 'Nuevos pilotos',
|
'newestpilots' => 'Nuevos pilotos',
|
||||||
'profile' => 'Perfil',
|
'profile' => 'Perfil',
|
||||||
'email' => 'Email',
|
'email' => 'Email',
|
||||||
|
'pilot_id' => 'Pilot ID',
|
||||||
'register' => 'Registrarse',
|
'register' => 'Registrarse',
|
||||||
'login' => 'Iniciar sesión',
|
'login' => 'Iniciar sesión',
|
||||||
'logout' => 'Cerrar sesión',
|
'logout' => 'Cerrar sesión',
|
||||||
@ -48,6 +49,7 @@ return [
|
|||||||
'inactive' => 'Inactivo',
|
'inactive' => 'Inactivo',
|
||||||
'yes' => 'Sí',
|
'yes' => 'Sí',
|
||||||
'no' => 'No',
|
'no' => 'No',
|
||||||
|
'or' => 'o',
|
||||||
'days' => [
|
'days' => [
|
||||||
'mon' => 'lunes',
|
'mon' => 'lunes',
|
||||||
'tues' => 'martes',
|
'tues' => 'martes',
|
||||||
|
@ -10,6 +10,7 @@ return [
|
|||||||
'newestpilots' => 'Ultimi Piloti',
|
'newestpilots' => 'Ultimi Piloti',
|
||||||
'profile' => 'Profilo',
|
'profile' => 'Profilo',
|
||||||
'email' => 'Email',
|
'email' => 'Email',
|
||||||
|
'pilot_id' => 'Pilot ID',
|
||||||
'login' => 'Accesso',
|
'login' => 'Accesso',
|
||||||
'logout' => 'Uscita',
|
'logout' => 'Uscita',
|
||||||
'register' => 'Registrazione',
|
'register' => 'Registrazione',
|
||||||
@ -48,6 +49,7 @@ return [
|
|||||||
'inactive' => 'Inattivo',
|
'inactive' => 'Inattivo',
|
||||||
'yes' => 'Sì',
|
'yes' => 'Sì',
|
||||||
'no' => 'No',
|
'no' => 'No',
|
||||||
|
'or' => 'o',
|
||||||
'days' => [
|
'days' => [
|
||||||
'mon' => 'Lunedì',
|
'mon' => 'Lunedì',
|
||||||
'tues' => 'Martedì',
|
'tues' => 'Martedì',
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
{{
|
{{
|
||||||
Form::text('email', old('email'), [
|
Form::text('email', old('email'), [
|
||||||
'id' => 'email',
|
'id' => 'email',
|
||||||
'placeholder' => __('common.email'),
|
'placeholder' => __('common.email').' '.__('common.or').' '.__('common.pilot_id'),
|
||||||
'class' => 'form-control',
|
'class' => 'form-control',
|
||||||
'required' => true,
|
'required' => true,
|
||||||
])
|
])
|
||||||
|
1
resources/views/vendor/mail/text/button.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/button.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{ $slot }}: {{ $url }}
|
1
resources/views/vendor/mail/text/footer.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/footer.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{ $slot }}
|
1
resources/views/vendor/mail/text/header.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/header.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{{ $slot }}]({{ $url }})
|
9
resources/views/vendor/mail/text/layout.blade.php
vendored
Normal file
9
resources/views/vendor/mail/text/layout.blade.php
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{!! strip_tags($header) !!}
|
||||||
|
|
||||||
|
{!! strip_tags($slot) !!}
|
||||||
|
@isset($subcopy)
|
||||||
|
|
||||||
|
{!! strip_tags($subcopy) !!}
|
||||||
|
@endisset
|
||||||
|
|
||||||
|
{!! strip_tags($footer) !!}
|
27
resources/views/vendor/mail/text/message.blade.php
vendored
Normal file
27
resources/views/vendor/mail/text/message.blade.php
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
@component('mail::layout')
|
||||||
|
{{-- Header --}}
|
||||||
|
@slot('header')
|
||||||
|
@component('mail::header', ['url' => config('app.url')])
|
||||||
|
{{ config('app.name') }}
|
||||||
|
@endcomponent
|
||||||
|
@endslot
|
||||||
|
|
||||||
|
{{-- Body --}}
|
||||||
|
{{ $slot }}
|
||||||
|
|
||||||
|
{{-- Subcopy --}}
|
||||||
|
@isset($subcopy)
|
||||||
|
@slot('subcopy')
|
||||||
|
@component('mail::subcopy')
|
||||||
|
{{ $subcopy }}
|
||||||
|
@endcomponent
|
||||||
|
@endslot
|
||||||
|
@endisset
|
||||||
|
|
||||||
|
{{-- Footer --}}
|
||||||
|
@slot('footer')
|
||||||
|
@component('mail::footer')
|
||||||
|
© {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.')
|
||||||
|
@endcomponent
|
||||||
|
@endslot
|
||||||
|
@endcomponent
|
1
resources/views/vendor/mail/text/panel.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/panel.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{ $slot }}
|
1
resources/views/vendor/mail/text/subcopy.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/subcopy.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{ $slot }}
|
1
resources/views/vendor/mail/text/table.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/table.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{ $slot }}
|
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Exceptions\PilotIdNotFound;
|
||||||
use App\Exceptions\UserPilotIdExists;
|
use App\Exceptions\UserPilotIdExists;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Repositories\SettingRepository;
|
use App\Repositories\SettingRepository;
|
||||||
@ -8,7 +9,10 @@ use Illuminate\Support\Facades\Hash;
|
|||||||
|
|
||||||
class UserTest extends TestCase
|
class UserTest extends TestCase
|
||||||
{
|
{
|
||||||
|
/** @var SettingRepository */
|
||||||
protected $settingsRepo;
|
protected $settingsRepo;
|
||||||
|
|
||||||
|
/** @var UserService */
|
||||||
protected $userSvc;
|
protected $userSvc;
|
||||||
|
|
||||||
public function setUp(): void
|
public function setUp(): void
|
||||||
@ -204,6 +208,33 @@ class UserTest extends TestCase
|
|||||||
$this->userSvc->changePilotId($user1, 2);
|
$this->userSvc->changePilotId($user1, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure that the splitting of the user ID works
|
||||||
|
*/
|
||||||
|
public function testUserPilotIdSplit(): void
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User $user */
|
||||||
|
$user = factory(App\Models\User::class)->create();
|
||||||
|
$found_user = $this->userSvc->findUserByPilotId($user->ident);
|
||||||
|
$this->assertEquals($user->id, $found_user->id);
|
||||||
|
|
||||||
|
// Look for them with the IATA code
|
||||||
|
$found_user = $this->userSvc->findUserByPilotId($user->airline->iata.$user->id);
|
||||||
|
$this->assertEquals($user->id, $found_user->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pilot ID not found
|
||||||
|
*/
|
||||||
|
public function testUserPilotIdSplitInvalidId(): void
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User $user */
|
||||||
|
$user = factory(App\Models\User::class)->create();
|
||||||
|
|
||||||
|
$this->expectException(PilotIdNotFound::class);
|
||||||
|
$this->userSvc->findUserByPilotId($user->airline->iata);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test the pilot ID being added when a new user is created
|
* Test the pilot ID being added when a new user is created
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user