Merge pull request #699 from nabeelio/698-Login-Pilot-Id

Enable logins using pilot ID #698
This commit is contained in:
Nabeel S 2020-05-16 11:59:03 -04:00 committed by GitHub
commit 3b1936e110
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 234 additions and 8 deletions

View 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,
];
}
}

View File

@ -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',
]);
} }
/** /**

View File

@ -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

View File

@ -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',

View File

@ -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',

View File

@ -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ì',

View File

@ -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,
]) ])

View File

@ -0,0 +1 @@
{{ $slot }}: {{ $url }}

View File

@ -0,0 +1 @@
{{ $slot }}

View File

@ -0,0 +1 @@
[{{ $slot }}]({{ $url }})

View File

@ -0,0 +1,9 @@
{!! strip_tags($header) !!}
{!! strip_tags($slot) !!}
@isset($subcopy)
{!! strip_tags($subcopy) !!}
@endisset
{!! strip_tags($footer) !!}

View 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

View File

@ -0,0 +1 @@
{{ $slot }}

View File

@ -0,0 +1 @@
{{ $slot }}

View File

@ -0,0 +1 @@
{{ $slot }}

View File

@ -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
*/ */