-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from givebutter/keyable-scope
Keyable scope
- Loading branch information
Showing
12 changed files
with
437 additions
and
13 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
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,60 @@ | ||
<?php | ||
|
||
namespace Givebutter\LaravelKeyable\Http\Middleware; | ||
|
||
use Closure; | ||
use Illuminate\Support\Arr; | ||
use Illuminate\Support\Reflector; | ||
use Illuminate\Contracts\Routing\UrlRoutable; | ||
use Illuminate\Database\Eloquent\ModelNotFoundException; | ||
|
||
class EnforceKeyableScope | ||
{ | ||
/** | ||
* 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) | ||
{ | ||
$route = $request->route(); | ||
|
||
if (empty($route->parameterNames())) { | ||
return $next($request); | ||
} | ||
|
||
$parameterName = $route->parameterNames()[0]; | ||
$parameterValue = $route->originalParameters()[$parameterName]; | ||
$parameter = Arr::first($route->signatureParameters(UrlRoutable::class)); | ||
$instance = app(Reflector::getParameterClassName($parameter)); | ||
|
||
$childRouteBindingMethod = $route->allowsTrashedBindings() | ||
? 'resolveSoftDeletableChildRouteBinding' | ||
: 'resolveChildRouteBinding'; | ||
|
||
if (! $request->keyable->{$childRouteBindingMethod}( | ||
$parameterName, | ||
$parameterValue, | ||
$route->bindingFieldFor($parameterName) | ||
)) { | ||
throw (new ModelNotFoundException)->setModel(get_class($instance), [$parameterValue]); | ||
} | ||
|
||
return $next($request); | ||
} | ||
|
||
protected static function getParameterName($name, $parameters) | ||
{ | ||
if (array_key_exists($name, $parameters)) { | ||
return $name; | ||
} | ||
|
||
if (array_key_exists($snakedName = Str::snake($name), $parameters)) { | ||
return $snakedName; | ||
} | ||
} | ||
} |
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
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 Givebutter\Tests\Feature; | ||
|
||
use Givebutter\Tests\TestCase; | ||
use Givebutter\Tests\Support\Account; | ||
use Illuminate\Support\Facades\Route; | ||
|
||
class AuthenticateApiKey extends TestCase | ||
{ | ||
/** @test */ | ||
public function request_with_api_key_responds_ok() | ||
{ | ||
Route::get("/api/posts", function () { | ||
return response('All good', 200); | ||
})->middleware(['api', 'auth.apikey']); | ||
|
||
$account = Account::create(); | ||
|
||
$this->withHeaders([ | ||
'Authorization' => 'Bearer ' . $account->createApiKey()->key, | ||
])->get("/api/posts")->assertOk(); | ||
} | ||
|
||
/** @test */ | ||
public function request_without_api_key_responds_unauthorized() | ||
{ | ||
Route::get("/api/posts", function () { | ||
return response('All good', 200); | ||
})->middleware(['api', 'auth.apikey']); | ||
|
||
$this->get("/api/posts")->assertUnauthorized(); | ||
} | ||
} |
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,116 @@ | ||
<?php | ||
|
||
namespace Givebutter\Tests\Feature; | ||
|
||
use Illuminate\Http\Request; | ||
use Givebutter\Tests\TestCase; | ||
use Givebutter\Tests\Support\Post; | ||
use Givebutter\Tests\Support\Account; | ||
use Illuminate\Support\Facades\Route; | ||
use Givebutter\Tests\Support\PostsController; | ||
use Givebutter\Tests\Support\CommentsController; | ||
|
||
class EnforceKeyableScope extends TestCase | ||
{ | ||
/** @test */ | ||
public function request_with_parameter_must_be_owned_by_keyable() | ||
{ | ||
Route::get("/api/posts/{post}", function (Request $request, Post $post) { | ||
return response('All good', 200); | ||
})->middleware(['api', 'auth.apikey'])->keyableScoped(); | ||
|
||
$account = Account::create(); | ||
$post = $account->posts()->create(); | ||
|
||
$this->withHeaders([ | ||
'Authorization' => 'Bearer ' . $account->createApiKey()->key, | ||
])->get("/api/posts/{$post->id}")->assertOk(); | ||
} | ||
|
||
/** @test */ | ||
public function request_with_model_not_owned_by_keyable_throws_model_not_found() | ||
{ | ||
Route::get("/api/posts/{post}", function (Request $request, Post $post) { | ||
return response('All good', 200); | ||
})->middleware([ 'api', 'auth.apikey'])->keyableScoped(); | ||
|
||
$account = Account::create(); | ||
$account2 = Account::create(); | ||
$post = $account2->posts()->create(); | ||
|
||
$this->withHeaders([ | ||
'Authorization' => 'Bearer ' . $account->createApiKey()->key, | ||
])->get("/api/posts/{$post->id}")->assertNotFound(); | ||
} | ||
|
||
/** @test */ | ||
public function works_with_resource_routes() | ||
{ | ||
Route::prefix('api')->middleware(['api', 'auth.apikey'])->group(function () { | ||
Route::apiResource('posts', PostsController::class) | ||
->only('show') | ||
->keyableScoped(); | ||
}); | ||
|
||
/* | ||
| -------------------------------- | ||
| PASSING | ||
| -------------------------------- | ||
*/ | ||
$account = Account::create(); | ||
$post = $account->posts()->create(); | ||
|
||
$this->withHeaders([ | ||
'Authorization' => 'Bearer ' . $account->createApiKey()->key, | ||
])->get("/api/posts/{$post->id}")->assertOk(); | ||
|
||
/* | ||
| -------------------------------- | ||
| FAILING | ||
| -------------------------------- | ||
*/ | ||
$account2 = Account::create(); | ||
$post = $account2->posts()->create(); | ||
|
||
$this->withHeaders([ | ||
'Authorization' => 'Bearer ' . $account->createApiKey()->key, | ||
])->get("/api/posts/{$post->id}")->assertNotFound(); | ||
} | ||
|
||
/** @test */ | ||
public function can_use_scoped_with_keyableScoped() | ||
{ | ||
Route::middleware(['api', 'auth.apikey'])->group(function () { | ||
Route::apiResource('posts.comments', CommentsController::class) | ||
->only('show') | ||
->scoped() | ||
->keyableScoped(); | ||
}); | ||
|
||
/* | ||
| -------------------------------- | ||
| PASSING | ||
| -------------------------------- | ||
*/ | ||
$account = Account::create(); | ||
$post = $account->posts()->create(); | ||
$comment = $post->comments()->create(); | ||
|
||
$this->withHeaders([ | ||
'Authorization' => 'Bearer ' . $account->createApiKey()->key, | ||
])->get("posts/{$post->id}/comments/{$comment->id}")->assertOk(); | ||
|
||
/* | ||
| -------------------------------- | ||
| FAILING | ||
| -------------------------------- | ||
*/ | ||
$account2 = Account::create(); | ||
$post2 = $account2->posts()->create(); | ||
$comment2 = $post2->comments()->create(); | ||
|
||
$this->withHeaders([ | ||
'Authorization' => 'Bearer ' . $account->createApiKey()->key, | ||
])->get("posts/{$post->id}/comments/{$comment2->id}")->assertNotFound(); | ||
} | ||
} |
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,16 @@ | ||
<?php | ||
|
||
namespace Givebutter\Tests\Support; | ||
|
||
use Givebutter\LaravelKeyable\Keyable; | ||
use Illuminate\Database\Eloquent\Model; | ||
|
||
class Account extends Model | ||
{ | ||
use Keyable; | ||
|
||
public function posts() | ||
{ | ||
return $this->hasMany(Post::class); | ||
} | ||
} |
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,14 @@ | ||
<?php | ||
|
||
namespace Givebutter\Tests\Support; | ||
|
||
use Givebutter\Tests\Support\Post; | ||
use Illuminate\Database\Eloquent\Model; | ||
|
||
class Comment extends Model | ||
{ | ||
public function post() | ||
{ | ||
return $this->belongsTo(Post::class); | ||
} | ||
} |
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,14 @@ | ||
<?php | ||
|
||
namespace Givebutter\Tests\Support; | ||
|
||
use Illuminate\Http\Request; | ||
use Givebutter\Tests\Support\Post; | ||
|
||
class CommentsController | ||
{ | ||
public function show(Request $request, Post $post, Comment $comment) | ||
{ | ||
return response('All good', 200); | ||
} | ||
} |
Oops, something went wrong.