Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Авторизация с Bouncer

Аутентификация отвечает на вопрос «кто это», а авторизация — «что ему можно». Например, редактировать статью может только её автор. В AdonisJS права проверяет пакет Bouncer: правила описываются функциями, а проверка в контроллере занимает одну строку. В Laravel то же самое делают Gate и политики.

Установка

node ace add @adonisjs/bouncer

Команда создаст файлы app/abilities/main.ts и app/policies/main.ts и подключит middleware, которое добавляет объект bouncer в контекст запроса. Для примера считаем, что у статьи есть столбец user_id и свойство userId — автор статьи.

Способность

Способность (ability) — функция, которая получает пользователя и запись и возвращает true или false:

// app/abilities/main.ts
import { Bouncer } from '@adonisjs/bouncer'
import User from '#models/user'
import Post from '#models/post'
 
export const editPost = Bouncer.ability((user: User, post: Post) => {
  return user.id === post.userId
})

Текущего пользователя Bouncer передаёт сам, вы передаёте только статью.

Проверка в контроллере

// app/controllers/posts_controller.ts
import type { HttpContext } from '@adonisjs/core/http'
import Post from '#models/post'
import { editPost } from '#abilities/main'
 
export default class PostsController {
  async edit({ params, bouncer, view }: HttpContext) {
    const post = await Post.findOrFail(params.id)
    await bouncer.authorize(editPost, post)
    return view.render('pages/posts/edit', { post })
  }
}

Если способность вернула false, authorize выбросит исключение и посетитель получит ответ 403. Когда нужно не прерывать запрос, а просто узнать ответ, используйте await bouncer.allows(editPost, post) или denies.

Проверка в шаблоне

Кнопку «Редактировать» стоит показывать только тем, кому это разрешено:

{{-- resources/views/pages/posts/show.edge --}}
@can('editPost', post)
  <a href="{{ route('posts.edit', { id: post.id }) }}">Редактировать</a>
@end

Тег @can принимает имя способности строкой. Для обратной проверки есть @cannot.

Политики

Когда правил для одной модели становится много, их собирают в политику — класс с методом на каждое действие:

node ace make:policy post
// app/policies/post_policy.ts
import { BasePolicy } from '@adonisjs/bouncer'
import User from '#models/user'
import Post from '#models/post'
 
export default class PostPolicy extends BasePolicy {
  edit(user: User, post: Post) {
    return user.id === post.userId
  }
 
  delete(user: User, post: Post) {
    return user.id === post.userId
  }
}
// app/controllers/posts_controller.ts
import PostPolicy from '#policies/post_policy'
 
await bouncer.with(PostPolicy).authorize('delete', post)

В шаблоне политику указывают через точку: @can('PostPolicy.edit', post).

Частая ошибка

Кнопку спрятали тегом @can, а проверку в контроллере не написали. Тогда любой пользователь откроет /posts/5/edit вручную и изменит чужую статью. Шаблон только скрывает элементы, защищает же проверка в контроллере — она нужна всегда.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.