dev-resources.site
for different kinds of informations.
Using Laravel Policy with middleware to protect routes
So my goal was to only allow the logged in user to view or make changes to data that belongs to him. How do I know which data belongs to him? In the model LanguagePack
I save his user id. Any other data like the WordList
model has a relation to language pack.
A user accesses the data via an url like this:
/languagepack/wordlist/1
So I don't want him to be able to change the id to 2 if that language pack wasn't created by him and then see and edit that data.
To do this I created a policy class that looks like this:
<?php
namespace App\Policies;
use App\Models\LanguagePack;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class LanguagePackPolicy
{
use HandlesAuthorization;
public function view(User $user, LanguagePack $languagePack)
{
return $user->id === $languagePack->userid;
}
public function update(User $user, LanguagePack $languagePack)
{
return $user->id === $languagePack->userid;
}
public function delete(User $user, LanguagePack $languagePack)
{
return $user->id === $languagePack->user_id;
}
}
Then I created a middleware for getting the language pack model from the route and running the policy check by authorizing actions. If the action is allowed, it will continue the request, otherwise it throws a 403 error and aborts the request.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class AuthorizeLanguagePack
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
$languagePack = $request->route('languagePack');
if ($languagePack) {
$user = $request->user();
if ($user->can('view', $languagePack) ||
$user->can('update', $languagePack) ||
$user->can('delete', $languagePack)) {
return $next($request);
}
abort(403, 'Unauthorized');
}
return $next($request);
}
}
The middleware has to be registered in the file src/app/Http/Kernel.php
like this:
protected $routeMiddleware = [
//add to the list of route middlewares
'authorize.languagepack' => \App\Http\Middleware\AuthorizeLanguagePack::class,
];
And finally we add the middleware key authorize.languagepack
to the routes in web.php
:
Route::middleware(['auth', 'authorize.languagepack'])->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index'])->name('home');
Route::get('languagepack/create', [LanguageInfoController::class, 'create']);
Route::get('languagepack/edit/{languagePack}', [LanguageInfoController::class, 'edit']);
Route::get('languagepack/wordlist/{languagePack}', [WordlistController::class, 'edit']);
})
There are probably other ways to achieve the same result. Let me know in the comments if you know of a better way...
Featured ones: