Питання на співбесіді: Authorization
Найпопулярніші питання з реальних Laravel/PHP співбесід для всіх рівнів
3 питання
Права описують у Gate або Policy, а перевіряють у трьох місцях: контролері, шаблоні й формі запиту.
У контролері - authorize(), який кидає 403 сам:
public function update(Request $request, Post $post)
{
$this->authorize('update', $post);
// сюди дійде лише той, кому можна
}
Або через фасад, коли потрібна саме перевірка, а не зупинка:
if (Gate::allows('update', $post)) {
// ...
}
if ($request->user()->cannot('update', $post)) {
abort(403);
}
У Blade - директиви, які ховають те, чого не можна:
@can('update', $post)
<a href="{{ route('posts.edit', $post) }}">Редагувати</a>
@endcan
@cannot('update', $post)
<span>Тільки перегляд</span>
@endcannot
У маршруті - middleware can:
Route::put('/posts/{post}', [PostController::class, 'update'])
->middleware('can:update,post');
Важливо: @can у шаблоні лише ховає кнопку. Це зручність для користувача, а не захист - без перевірки в контролері запит усе одно можна надіслати вручну. Перевірка на сервері обовʼязкова завжди, навіть коли кнопки не видно.
- Gate - замикання для простих, не прив'язаних до моделі перевірок.
- Policy - клас, що групує правила авторизації навколо конкретної моделі.
// Gate
Gate::define('view-admin', fn (User $u) => $u->is_admin);
// Policy
class PostPolicy
{
public function update(User $user, Post $post): bool
{
return $user->id === $post->user_id;
}
}
Застосування:
$this->authorize('update', $post); // у контролері
@can('update', $post) ... @endcan // у Blade
$user->can('update', $post); // будь-де
Політики автоматично відкривають 403 при відмові.
Починається все з простого $user->is_admin, далі зʼявляється редактор, потім модератор - і умови розповзаються по коду.
Крок перший - роль як enum:
enum Role: string
{
case Admin = 'admin';
case Editor = 'editor';
case Author = 'author';
}
Це вже краще за рядки, але перевірки виду $user->role === Role::Editor розкидані по контролерах ламаються, щойно права ролі змінюються.
Крок другий - права, а не ролі. Код питає «чи можна публікувати», а не «чи ти редактор»:
Gate::define('publish', fn (User $user) => $user->hasPermission('publish'));
Роль стає лише набором прав, і зміна набору не потребує правок у коді.
Policy для дій над моделлю:
class PostPolicy
{
public function update(User $user, Post $post): bool
{
return $user->hasPermission('posts.update')
|| $post->author_id === $user->id;
}
}
before() для суперкористувача - щоб не дублювати перевірку в кожному методі:
public function before(User $user): ?bool
{
return $user->isAdmin() ? true : null;
}
Повертати треба саме null, а не false: false заборонить дію остаточно й не дасть іншим методам відпрацювати.
Коли брати пакет. spatie/laravel-permission дає ролі, права й кеш перевірок з коробки. Він доречний, коли набір прав змінюють з адмінки; якщо ролей три й вони зашиті в код, enum із Policy простіший.
Що не забути: перевірки прав кешуються не самі - на кожен запит це кілька звернень до бази, тому права користувача варто завантажувати разом із ним.