-
Notifications
You must be signed in to change notification settings - Fork 144
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Start of rework for validating API keys
- Loading branch information
Showing
11 changed files
with
464 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<?php | ||
|
||
return [ | ||
|
||
/* | ||
|-------------------------------------------------------------------------- | ||
| Key name | ||
|-------------------------------------------------------------------------- | ||
| | ||
| This is the name of the variable that will provide us the API key in the | ||
| header | ||
| | ||
*/ | ||
'header_key' => 'X-Authorization', | ||
|
||
/* | ||
|-------------------------------------------------------------------------- | ||
| Authentication Provider | ||
|-------------------------------------------------------------------------- | ||
| | ||
| Specify the provider that is used to authenticate users. Example: | ||
| | ||
| Chrisbjr\ApiGuard\Auth\Sentinel:class | ||
| | ||
| You can set up your own authentication provider here by creating a class | ||
| that implements the ApiGuardAuthContract interface. | ||
| | ||
*/ | ||
'auth' => null, | ||
|
||
'models' => [ | ||
|
||
'api_key' => 'Chrisbjr\ApiGuard\Models\ApiKey', | ||
|
||
], | ||
|
||
]; |
38 changes: 38 additions & 0 deletions
38
database/migrations/2016_09_13_042808_create_api_keys_table.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
<?php | ||
|
||
use Illuminate\Database\Migrations\Migration; | ||
use Illuminate\Database\Schema\Blueprint; | ||
use Illuminate\Support\Facades\Schema; | ||
|
||
class CreateApiKeysTable extends Migration | ||
{ | ||
/** | ||
* Run the migrations. | ||
* | ||
* @return void | ||
*/ | ||
public function up() | ||
{ | ||
Schema::create('api_keys', function (Blueprint $table) { | ||
$table->increments('id'); | ||
$table->nullableMorphs('apikeyable'); | ||
$table->string('key', 50); | ||
$table->string('last_ip_address', 50)->nullable(); | ||
$table->dateTime('last_used_at')->nullable(); | ||
$table->nullableTimestamps(); | ||
$table->softDeletes(); | ||
|
||
$table->index('key'); | ||
}); | ||
} | ||
|
||
/** | ||
* Reverse the migrations. | ||
* | ||
* @return void | ||
*/ | ||
public function down() | ||
{ | ||
Schema::dropIfExists('api_keys'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?php | ||
|
||
namespace Chrisbjr\ApiGuard\Auth\Contracts; | ||
|
||
use Illuminate\Http\Request; | ||
|
||
interface ApiGuardAuthContract | ||
{ | ||
/** | ||
* A method that will be triggered to indicate that a particular user/object has logged in. | ||
* | ||
* @param Request $request | ||
* @param $user | ||
* @return void | ||
*/ | ||
public function authenticate(Request $request, $user); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?php | ||
|
||
namespace Chrisbjr\ApiGuard\Auth; | ||
|
||
use Chrisbjr\ApiGuard\Auth\Contracts\ApiGuardAuthContract; | ||
use Illuminate\Http\Request; | ||
use Sentinel as SentinelAuth; | ||
|
||
/** | ||
* A sample class that shows what you can do with this interface. Here, we use | ||
* this class to trigger the "login" method provided by Sentinel. The "login" | ||
* method from Sentinel logs that the user has logged in and populates the | ||
* "last_login" field in the database. | ||
* | ||
* @package Chrisbjr\ApiGuard\Auth | ||
*/ | ||
class Sentinel implements ApiGuardAuthContract | ||
{ | ||
/** | ||
* @param Request $request | ||
* @param $user | ||
*/ | ||
public function authenticate(Request $request, $user) | ||
{ | ||
SentinelAuth::login($user); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
<?php | ||
|
||
namespace Chrisbjr\ApiGuard\Console\Commands; | ||
|
||
use Chrisbjr\ApiGuard\Models\ApiKey; | ||
use Illuminate\Console\Command; | ||
|
||
class GenerateApiKey extends Command | ||
{ | ||
/** | ||
* The name and signature of the console command. | ||
* | ||
* @var string | ||
*/ | ||
protected $signature = 'api-key:generate | ||
{--id= : ID of the model you want to bind to this API key} | ||
{--type= : The class name of the model you want to bind to this API key}'; | ||
|
||
/** | ||
* The console command description. | ||
* | ||
* @var string | ||
*/ | ||
protected $description = 'Generate an API key'; | ||
|
||
/** | ||
* Create a new command instance. | ||
* | ||
*/ | ||
public function __construct() | ||
{ | ||
parent::__construct(); | ||
} | ||
|
||
/** | ||
* Execute the console command. | ||
* | ||
* @return mixed | ||
*/ | ||
public function handle() | ||
{ | ||
$apiKeyableId = $this->option('id'); | ||
$apiKeyableType = $this->option('type'); | ||
|
||
$apiKey = new ApiKey([ | ||
'key' => ApiKey::generateKey(), | ||
'apikeyable_id' => $apiKeyableId, | ||
'apikeyable_type' => $apiKeyableType, | ||
]); | ||
|
||
$apiKey->save(); | ||
|
||
$this->info('An API key was created with the following key: ' . $apiKey->key); | ||
|
||
return; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php | ||
|
||
namespace Chrisbjr\ApiGuard\Http\Controllers; | ||
|
||
use EllipseSynergie\ApiResponse\Laravel\Response; | ||
use Illuminate\Routing\Controller; | ||
use League\Fractal\Manager; | ||
|
||
class ApiController extends Controller | ||
{ | ||
/** | ||
* @var Response | ||
*/ | ||
protected $response; | ||
|
||
public function __construct() | ||
{ | ||
$fractal = new Manager(); | ||
|
||
if (isset($_GET['include'])) { | ||
$fractal->parseIncludes($_GET['include']); | ||
} | ||
|
||
$this->response = new Response($fractal); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
<?php | ||
|
||
namespace Chrisbjr\ApiGuard\Http\Middleware; | ||
|
||
use Carbon\Carbon; | ||
use Chrisbjr\ApiGuard\Models\Device; | ||
use Closure; | ||
|
||
class AuthenticateApiKey | ||
{ | ||
/** | ||
* Handle an incoming request. | ||
* | ||
* @param \Illuminate\Http\Request $request | ||
* @param Closure $next | ||
* @param string|null $guard | ||
* @return mixed | ||
*/ | ||
public function handle($request, Closure $next, $guard = null) | ||
{ | ||
$apiKeyValue = $request->header(config('apiguard.header_key', 'X-Authorization')); | ||
|
||
$apiKey = app(config('apiguard.models.api_key', 'Chrisbjr\ApiGuard\Models\ApiKey'))->where('key', $apiKeyValue) | ||
->first(); | ||
|
||
if (empty($apiKey)) { | ||
return $this->unauthorizedResponse(); | ||
} | ||
|
||
// Update this api key's last_used_at and last_ip_address | ||
$apiKey->update([ | ||
'last_used_at' => Carbon::now(), | ||
'last_ip_address' => $request->ip(), | ||
]); | ||
|
||
$user = $apiKey->apikeyable; | ||
|
||
if (! empty(config('apiguard.auth'))) { | ||
$apiGuardAuth = app(config('apiguard.auth')); | ||
$apiGuardAuth->authenticate($user); | ||
} | ||
|
||
// Bind the user or object to the request | ||
// By doing this, we can now get the specified user through the request object in the controller using: | ||
// $request->user() | ||
$request->setUserResolver(function () use ($user) { | ||
return $user; | ||
}); | ||
|
||
// Attach the apikey object to the request | ||
$request->apiKey = $apiKey; | ||
|
||
return $next($request); | ||
} | ||
|
||
protected function unauthorizedResponse() | ||
{ | ||
return response([ | ||
'error' => [ | ||
'code' => '401', | ||
'http_code' => 'GEN-UNAUTHORIZED', | ||
'message' => 'Unauthorized.', | ||
], | ||
], 401); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<?php | ||
|
||
namespace Chrisbjr\ApiGuard\Http\Requests; | ||
|
||
use EllipseSynergie\ApiResponse\Laravel\Response; | ||
use Illuminate\Contracts\Validation\Validator; | ||
use Illuminate\Foundation\Http\FormRequest; | ||
use League\Fractal\Manager; | ||
|
||
class ApiFormRequest extends FormRequest | ||
{ | ||
public function expectsJson() | ||
{ | ||
return true; | ||
} | ||
|
||
/** | ||
* Format the errors from the given Validator instance. | ||
* | ||
* @param \Illuminate\Contracts\Validation\Validator $validator | ||
* @return array | ||
*/ | ||
protected function formatErrors(Validator $validator) | ||
{ | ||
return $validator->getMessageBag()->toArray(); | ||
} | ||
|
||
public function response(array $errors) | ||
{ | ||
$response = new Response(new Manager()); | ||
|
||
return $response->errorUnprocessable($errors); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
<?php | ||
|
||
namespace Chrisbjr\ApiGuard\Models; | ||
|
||
use Carbon\Carbon; | ||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Eloquent\SoftDeletes; | ||
use Request; | ||
|
||
class ApiKey extends Model | ||
{ | ||
use SoftDeletes; | ||
|
||
protected $fillable = [ | ||
'key', | ||
'apikeyable_id', | ||
'apikeyable_type', | ||
'last_ip_address', | ||
'last_used_at', | ||
]; | ||
|
||
/** | ||
* @return \Illuminate\Database\Eloquent\Relations\MorphTo | ||
*/ | ||
public function apikeyable() | ||
{ | ||
return $this->morphTo(); | ||
} | ||
|
||
/** | ||
* @param $apikeyable | ||
* | ||
* @return ApiKey | ||
*/ | ||
public static function make($apikeyable) | ||
{ | ||
$apiKey = new ApiKey([ | ||
'key' => self::generateKey(), | ||
'apikeyable_id' => $apikeyable->id, | ||
'apikeyable_type' => get_class($apikeyable), | ||
'last_ip_address' => Request::ip(), | ||
'last_used_at' => Carbon::now(), | ||
]); | ||
|
||
$apiKey->save(); | ||
|
||
return $apiKey; | ||
} | ||
|
||
/** | ||
* A sure method to generate a unique API key | ||
* | ||
* @return string | ||
*/ | ||
public static function generateKey() | ||
{ | ||
do { | ||
$salt = sha1(time() . mt_rand()); | ||
$newKey = substr($salt, 0, 40); | ||
} // Already in the DB? Fail. Try again | ||
while (self::keyExists($newKey)); | ||
|
||
return $newKey; | ||
} | ||
|
||
/** | ||
* Checks whether a key exists in the database or not | ||
* | ||
* @param $key | ||
* @return bool | ||
*/ | ||
private static function keyExists($key) | ||
{ | ||
$apiKeyCount = self::where('key', '=', $key)->limit(1)->count(); | ||
|
||
if ($apiKeyCount > 0) return true; | ||
|
||
return false; | ||
} | ||
} |
Oops, something went wrong.