#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;
}
/**
* 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
* @param $id

View File

@ -47,6 +47,7 @@ class Kernel extends HttpKernel
* @var array
*/
protected $routeMiddleware = [
'api.auth' => \App\Http\Middleware\ApiAuth::class,
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::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()
{
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([
'middleware' => [
'api',
//\App\Http\Middleware\MeasureExecutionTime::class
'api.auth',
],
'namespace' => $this->namespace."\\API",
'prefix' => 'api',

View File

@ -67,23 +67,26 @@ class PilotService extends BaseService
public function createPilot(array $data)
{
$user = User::create(['name' => $data['name'],
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'apikey' => User::generateApiKey(),
'airline_id' => $data['airline'],
'home_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
$role = Role::where('name', 'user')->first();
$user->attachRole($role);
# Let's check their rank
$this->calculatePilotRank($user);
event(new UserRegistered($user));
# TODO: Send out an email
event(new UserRegistered($user));
# Looking good, let's return their information
return $user;
}
}

View File

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

View File

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

View File

@ -17,6 +17,7 @@ Route::group([], function () {
Route::match(['get'], 'status', 'BaseController@status');
Route::match(['get'], 'airports/{id}', 'AirportController@get');
Route::match(['get'], 'airports/{id}/lookup', 'AirportController@lookup');
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:
- id: 1
- id: KAUS
iata: AUS
icao: KAUS
name: Austin-Bergstrom
location: Austin, Texas, USA
lat: 30.1945278
lon: -97.6698889
- id: 2
- id: KJFK
iata: JFK
icao: KJFK
name: John F Kennedy
location: New York, New York, USA
lat: 40.6399257
lon: -73.7786950
- id: 3
- id: EGLL
iata: LHR
icao: EGLL
name: London Heathrow

View File

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