#36 Add authentication for API; generate API key

This commit is contained in:
Nabeel Shahzad 2017-12-11 22:05:22 -06:00
parent 8a6bba0d0b
commit 0f18e60fd9
12 changed files with 173 additions and 18 deletions

View File

@ -19,6 +19,18 @@ class AirportController extends AppBaseController
$this->airportRepo = $airportRepo; $this->airportRepo = $airportRepo;
} }
/**
* Do a lookup, via vaCentral, for the airport information
* @param $id
* @return AirportResource
*/
public function get($id)
{
$id = strtoupper($id);
AirportResource::withoutWrapping();
return new AirportResource($this->airportRepo->find($id));
}
/** /**
* Do a lookup, via vaCentral, for the airport information * Do a lookup, via vaCentral, for the airport information
* @param $id * @param $id

View File

@ -47,6 +47,7 @@ class Kernel extends HttpKernel
* @var array * @var array
*/ */
protected $routeMiddleware = [ protected $routeMiddleware = [
'api.auth' => \App\Http\Middleware\ApiAuth::class,
'auth' => \Illuminate\Auth\Middleware\Authenticate::class, 'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,

View File

@ -0,0 +1,67 @@
<?php
/**
* Handle the authentication for the API layer
*/
namespace App\Http\Middleware;
use Auth;
use Cache;
use Closure;
use App\Models\User;
class ApiAuth
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
// Check if Authorization header is in place
if(!$request->header('Authorization')) {
return $this->unauthorized();
}
// Try to find the user via API key. Cache this lookup
$api_key = $request->header('Authorization');
$user = Cache::remember(
config('cache.keys.USER_API_KEY.key') . $api_key,
config('cache.keys.USER_API_KEY.time'),
function () use ($api_key) {
return User::where('apikey', $api_key)->first();
}
);
if(!$user) {
return $this->unauthorized();
}
// Set the user to the request
Auth::setUser($user);
$request->merge(['user' => $user]);
$request->setUserResolver(function () use ($user) {
return $user;
});
return $next($request);
}
/**
* Return an unauthorized message
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
*/
private function unauthorized()
{
return response([
'error' => [
'code' => '401',
'http_code' => 'Unauthorized',
'message' => 'Invalid or missing API key',
],
], 401);
}
}

View File

@ -96,6 +96,16 @@ class User extends Authenticatable
]; ];
/**
* Returns a 40 character API key that a user can use
* @return string
*/
public static function generateApiKey()
{
$key = sha1(time() . mt_rand());
return $key;
}
public function pilot_id() public function pilot_id()
{ {
return $this->airline->icao.str_pad($this->id, 3, '0', STR_PAD_LEFT); return $this->airline->icao.str_pad($this->id, 3, '0', STR_PAD_LEFT);

View File

@ -68,7 +68,7 @@ class RouteServiceProvider extends ServiceProvider
Route::group([ Route::group([
'middleware' => [ 'middleware' => [
'api', 'api',
//\App\Http\Middleware\MeasureExecutionTime::class 'api.auth',
], ],
'namespace' => $this->namespace."\\API", 'namespace' => $this->namespace."\\API",
'prefix' => 'api', 'prefix' => 'api',

View File

@ -67,23 +67,26 @@ class PilotService extends BaseService
public function createPilot(array $data) public function createPilot(array $data)
{ {
$user = User::create(['name' => $data['name'], $user = User::create([
'name' => $data['name'],
'email' => $data['email'], 'email' => $data['email'],
'apikey' => User::generateApiKey(),
'airline_id' => $data['airline'], 'airline_id' => $data['airline'],
'home_airport_id' => $data['home_airport'], 'home_airport_id' => $data['home_airport'],
'curr_airport_id' => $data['home_airport'], 'curr_airport_id' => $data['home_airport'],
'password' => Hash::make($data['password'])]); 'password' => Hash::make($data['password'])
]);
# Attach the user roles # Attach the user roles
$role = Role::where('name', 'user')->first(); $role = Role::where('name', 'user')->first();
$user->attachRole($role); $user->attachRole($role);
# Let's check their rank # Let's check their rank
$this->calculatePilotRank($user); $this->calculatePilotRank($user);
event(new UserRegistered($user));
# TODO: Send out an email # TODO: Send out an email
event(new UserRegistered($user));
# Looking good, let's return their information
return $user; return $user;
} }
} }

View File

@ -8,12 +8,16 @@ return [
'keys' => [ 'keys' => [
'AIRPORT_VACENTRAL_LOOKUP' => [ 'AIRPORT_VACENTRAL_LOOKUP' => [
'key' => 'airports:lookup:', 'key' => 'airports:lookup:',
'time' => 1800, 'time' => 60 * 30,
], ],
'RANKS_PILOT_LIST' => [ 'RANKS_PILOT_LIST' => [
'key' => 'ranks:pilot_list', 'key' => 'ranks:pilot_list',
'time' => 600, 'time' => 60 * 10,
] ],
'USER_API_KEY' => [
'key' => 'user:apikey',
'time' => 60 * 5, // 5 min
],
], ],
'stores' => [ 'stores' => [

View File

@ -18,6 +18,7 @@ class CreateUsersTable extends Migration
$table->string('name')->nullable(); $table->string('name')->nullable();
$table->string('email')->unique(); $table->string('email')->unique();
$table->string('password'); $table->string('password');
$table->string('apikey', 40)->nullable();
$table->integer('airline_id')->nullable()->unsigned(); $table->integer('airline_id')->nullable()->unsigned();
$table->integer('rank_id')->nullable()->unsigned(); $table->integer('rank_id')->nullable()->unsigned();
$table->string('home_airport_id', 5)->nullable(); $table->string('home_airport_id', 5)->nullable();
@ -33,6 +34,7 @@ class CreateUsersTable extends Migration
$table->softDeletes(); $table->softDeletes();
$table->index('email'); $table->index('email');
$table->index('apikey');
}); });
// Create table for storing roles // Create table for storing roles

View File

@ -17,6 +17,7 @@ Route::group([], function () {
Route::match(['get'], 'status', 'BaseController@status'); Route::match(['get'], 'status', 'BaseController@status');
Route::match(['get'], 'airports/{id}', 'AirportController@get');
Route::match(['get'], 'airports/{id}/lookup', 'AirportController@lookup'); Route::match(['get'], 'airports/{id}/lookup', 'AirportController@lookup');
Route::match(['get'], 'flight/{id}', 'FlightController@get'); Route::match(['get'], 'flight/{id}', 'FlightController@get');

54
tests/ApiTest.php Normal file
View File

@ -0,0 +1,54 @@
<?php
/**
* Test API calls and authentication, etc
*/
class ApiTest extends TestCase
{
protected static $headers = [
'Authorization' => 'testapikey'
];
public function setUp()
{
parent::setUp();
$this->addData('base');
}
/**
* Ensure authentication against the API works
*/
public function testApiAuthentication()
{
$uri = '/api/airports/kjfk';
// Missing auth header
$this->get($uri)->assertStatus(401);
// Test invalid API key
$this->withHeaders(['Authorization' => 'invalidKey'])->get($uri)
->assertStatus(401);
// Test upper/lower case of Authorization header, etc
$this->withHeaders(self::$headers)->get($uri)
->assertStatus(200)
->assertJson(['icao' => 'KJFK'], true);
$this->withHeaders(['AUTHORIZATION' => 'testapikey'])->get($uri)
->assertStatus(200)
->assertJson(['icao' => 'KJFK'], true);
}
/**
* Make sure the airport data is returned
*/
public function testAirportRequest()
{
$this->withHeaders(self::$headers)->get('/api/airports/KJFK')
->assertStatus(200)
->assertJson(['icao' => 'KJFK'], true);
$this->withHeaders(self::$headers)->get('/api/airports/UNK')
->assertStatus(404);
}
}

View File

@ -1,19 +1,19 @@
airports: airports:
- id: 1 - id: KAUS
iata: AUS iata: AUS
icao: KAUS icao: KAUS
name: Austin-Bergstrom name: Austin-Bergstrom
location: Austin, Texas, USA location: Austin, Texas, USA
lat: 30.1945278 lat: 30.1945278
lon: -97.6698889 lon: -97.6698889
- id: 2 - id: KJFK
iata: JFK iata: JFK
icao: KJFK icao: KJFK
name: John F Kennedy name: John F Kennedy
location: New York, New York, USA location: New York, New York, USA
lat: 40.6399257 lat: 40.6399257
lon: -73.7786950 lon: -73.7786950
- id: 3 - id: EGLL
iata: LHR iata: LHR
icao: EGLL icao: EGLL
name: London Heathrow name: London Heathrow

View File

@ -13,6 +13,7 @@ users:
name: Admin User name: Admin User
email: admin@phpvms.net email: admin@phpvms.net
password: admin password: admin
apikey: testapikey
rank_id: 1 rank_id: 1
created_at: now created_at: now
updated_at: now updated_at: now
@ -42,14 +43,14 @@ ranks:
auto_promote: 0 auto_promote: 0
airports: airports:
- id: 1 - id: KAUS
icao: KAUS icao: KAUS
name: Austin-Bergstrom name: Austin-Bergstrom
location: Austin, Texas, USA location: Austin, Texas, USA
lat: 30.1945278 lat: 30.1945278
lon: -97.6698889 lon: -97.6698889
fuel_jeta_cost: 100 fuel_jeta_cost: 100
- id: 2 - id: KJFK
icao: KJFK icao: KJFK
name: John F Kennedy name: John F Kennedy
location: New York, New York, USA location: New York, New York, USA