Reputation: 9047
I want to create a middleware to check if the authenticated user is the owner of the item. For a single model, the code is easy and would look something like :
<?php namespace App\Http\Middleware;
use Closure;
class Owner {
public function handle($request, Closure $next)
{
$id = $request->segments()[1]; //get the id parameter
$model = Model::find($id); // find the model from DB
if(!$item)
return 'not found'; //if item not found
//check if authenticated user is owner
if($item->user_id == \Auth::id()){
return $next($request);
}else{
return 'not allowed';
}
}
}
Now let's say I have multiple models (ModelX, ModelY, ModelZ) and I don't want to rewrite the middleware handle function several times. How would one inject the referenced model in the middleware to make it accomodate multiple models from my app?
Upvotes: 4
Views: 1298
Reputation: 340
You could use route and middleware parameters:
Here’s the middleware (app/Http/Middleware/AbortIfNotOwner.php
):
<?php
namespace App\Http\Middleware;
use Closure;
class AbortIfNotOwner
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string $resourceName
* @return mixed
*/
public function handle($request, Closure $next, $resourceName)
{
$resourceId = $request->route()->parameter($resourceName);
$userId = \DB::table($resourceName)->find($resourceId)->user_id;
if ($request->user()->id != $userId) {
abort(403, 'Unauthorized action.');
}
return $next($request);
}
}
Inside app\Http\Kernel.php
:
protected $routeMiddleware = [
'owner' => 'App\Http\Middleware\AbortIfNotOwner',
];
Inside your route file (app/Http/routes.php
):
Route::group(['middleware' => ['owner:articles']], function() {
// your route
});
And optionally call it in the controller:
public function __construct()
{
$this->middleware('owner:articles', ['only' => ['edit', 'update']]);
}
Upvotes: 4
Reputation: 336
You could use middleware parameters with route/model binding. Lets assume you have a post resource.
You would define the route. The middleware gets a parameter describing the resource you want to check the ownership of.
Route::get('/post/{post}', ['middleware' => 'owner:post', 'as' => 'post', 'uses' => 'PostController@showPost']);
Then you would bind your model in RouteServiceProvider.php:
$router->bind('post', function ($value) {
return Post::where('id', $value)->firstOrFail();
});
Finally you would make the middleware. Important to know is that the binding becomes available in the request object as $request->post. Since you're passing the name of the resource to the middleware, it becomes the third parameter in the handle method.
public function handle($request, Closure $next, $resource)
{
if ($request->{$resource}->user_id == \Auth::id()) {
return $next($request);
}
return response('not allowed.', 401);
}
I suppose you could also programmatically find the resource name, but using this method your resource name and your route can be different.
Upvotes: 3