Права в Orchid устроены просто и потому обманчиво: canSee() убирает кнопку, permission() закрывает экран, и всё выглядит защищённым. Между тем разница между «элемент не отображается» и «действие невозможно выполнить» здесь принципиальна, и на ней регулярно строят дырявые админки.
Разберём механизм и, главное, где проходит настоящая граница доступа.
Система прав в Orchid
Orchid предоставляет встроенную систему ролей и разрешений (RBAC):
- Roles — группы разрешений (Администратор, Модератор, Менеджер)
- Permissions — конкретные действия (создание статей, управление пользователями)
Orchid Permissions vs Laravel Gates: Orchid использует свою систему, но она совместима с Laravel. Можно комбинировать оба подхода.
Регистрация разрешений
// app/Orchid/PlatformProvider.php
namespace App\Orchid;
use Orchid\Platform\Dashboard;
use Orchid\Platform\ItemPermission;
use Orchid\Platform\OrchidServiceProvider;
class PlatformProvider extends OrchidServiceProvider
{
public function permissions(): array
{
return [
ItemPermission::group('Система')
->addPermission('platform.systems.users', 'Пользователи')
->addPermission('platform.systems.roles', 'Роли'),
ItemPermission::group('Контент')
->addPermission('platform.posts.view', 'Просмотр статей')
->addPermission('platform.posts.create', 'Создание статей')
->addPermission('platform.posts.edit', 'Редактирование статей')
->addPermission('platform.posts.delete', 'Удаление статей'),
ItemPermission::group('Заказы')
->addPermission('platform.orders.view', 'Просмотр заказов')
->addPermission('platform.orders.manage', 'Управление заказами')
->addPermission('platform.orders.export', 'Экспорт заказов'),
ItemPermission::group('Настройки')
->addPermission('platform.settings', 'Настройки сайта'),
];
}
}Создание ролей
Через UI
Перейдите в /admin/roles и создайте роли:
- Администратор — все разрешения
- Контент-менеджер — platform.posts.*
- Менеджер заказов — platform.orders.*
Через Seeder
// database/seeders/RolesSeeder.php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Orchid\Platform\Models\Role;
class RolesSeeder extends Seeder
{
public function run(): void
{
// Администратор
Role::updateOrCreate(
['slug' => 'admin'],
[
'name' => 'Администратор',
'permissions' => [
'platform.index' => true,
'platform.systems.users' => true,
'platform.systems.roles' => true,
'platform.posts.view' => true,
'platform.posts.create' => true,
'platform.posts.edit' => true,
'platform.posts.delete' => true,
'platform.orders.view' => true,
'platform.orders.manage' => true,
'platform.orders.export' => true,
'platform.settings' => true,
],
]
);
// Контент-менеджер
Role::updateOrCreate(
['slug' => 'content-manager'],
[
'name' => 'Контент-менеджер',
'permissions' => [
'platform.index' => true,
'platform.posts.view' => true,
'platform.posts.create' => true,
'platform.posts.edit' => true,
],
]
);
// Менеджер заказов
Role::updateOrCreate(
['slug' => 'order-manager'],
[
'name' => 'Менеджер заказов',
'permissions' => [
'platform.index' => true,
'platform.orders.view' => true,
'platform.orders.manage' => true,
],
]
);
}
}Проверка прав в Screen
Ограничение всего экрана
// app/Orchid/Screens/PostListScreen.php
class PostListScreen extends Screen
{
/**
* Требуемые разрешения для доступа
*/
public function permission(): ?iterable
{
return [
'platform.posts.view',
];
}
// ...
}Множественные разрешения
public function permission(): ?iterable
{
return [
'platform.posts.view',
'platform.posts.edit', // Нужны ОБА разрешения
];
}
// ИЛИ любое из (через OR в middleware)Скрытие элементов по правам
canSee() — это про интерфейс, а не про доступ. Он убирает кнопку из разметки. Метод-обработчик, который эта кнопка вызывала, остаётся доступным: он привязан к экрану, и отправить запрос на его выполнение можно напрямую, минуя интерфейс.
То есть пользователь без права на удаление не видит кнопку «Удалить» — и при этом может выполнить удаление, если знает имя метода. Знать его несложно: имена методов предсказуемы, а разметка соседних экранов подсказывает соглашения.
Проверка прав обязана быть внутри метода:
public function remove(Order $order)
{
abort_unless(Auth::user()->hasAccess('platform.orders.delete'), 403);
$order->delete();
Toast::info('Заказ удалён');
}canSee() при этом остаётся нужным — он избавляет оператора от кнопок, которыми тот не может воспользоваться. Но это удобство, а не защита.
Кнопки в commandBar
public function commandBar(): iterable
{
return [
Link::make('Создать')
->icon('plus')
->route('platform.post.edit')
->canSee(auth()->user()->hasAccess('platform.posts.create')),
Button::make('Удалить')
->icon('trash')
->method('delete')
->canSee(auth()->user()->hasAccess('platform.posts.delete')),
Button::make('Экспорт')
->icon('cloud-download')
->method('export')
->canSee(auth()->user()->hasAnyAccess([
'platform.orders.export',
'platform.admin',
])),
];
}Колонки в таблице
public function layout(): iterable
{
return [
Layout::table('posts', [
TD::make('title', 'Заголовок'),
TD::make('actions')
->canSee(auth()->user()->hasAccess('platform.posts.edit'))
->render(fn($post) => Link::make('Редактировать')
->route('platform.post.edit', $post)),
]),
];
}Поля в форме
public function layout(): iterable
{
return [
Layout::rows([
Input::make('post.title')
->title('Заголовок'),
// Только админы могут менять автора
Select::make('post.author_id')
->fromModel(User::class, 'name')
->title('Автор')
->canSee(auth()->user()->hasAccess('platform.admin')),
// Только с правом публикации
Switcher::make('post.is_published')
->title('Опубликовать')
->canSee(auth()->user()->hasAccess('platform.posts.publish')),
]),
];
}Меню по правам
// app/Orchid/PlatformProvider.php
public function menu(): array
{
return [
Menu::make('Статьи')
->icon('docs')
->route('platform.post.list')
->permission('platform.posts.view'),
Menu::make('Заказы')
->icon('bag')
->route('platform.orders.list')
->permission('platform.orders.view'),
Menu::make('Пользователи')
->icon('people')
->route('platform.systems.users')
->permission('platform.systems.users'),
Menu::make('Настройки')
->icon('settings')
->permission('platform.settings')
->list([
Menu::make('Общие')
->route('platform.settings.general'),
Menu::make('SEO')
->route('platform.settings.seo'),
]),
];
}Интеграция с Laravel Gates
// app/Providers/AuthServiceProvider.php
use Illuminate\Support\Facades\Gate;
public function boot(): void
{
// Orchid permissions как Gates
Gate::before(function ($user, $ability) {
if ($user->hasAccess($ability)) {
return true;
}
});
// Дополнительные Gates
Gate::define('edit-post', function ($user, $post) {
return $user->id === $post->author_id
|| $user->hasAccess('platform.posts.edit-any');
});
}// Использование в Screen
public function layout(): iterable
{
return [
Layout::rows([
Input::make('post.title')
->canSee(Gate::allows('edit-post', $this->post)),
]),
];
}Проверка в методах Screen
public function delete(Post $post): void
{
// Проверка прав
if (!auth()->user()->hasAccess('platform.posts.delete')) {
Toast::error('Недостаточно прав');
return;
}
// Или через Gate
$this->authorize('delete', $post);
$post->delete();
Toast::info('Статья удалена');
}Middleware для роутов
// routes/platform.php
use Tabuna\Breadcrumbs\Trail;
// Группа с проверкой прав
Route::middleware(['platform.access:platform.orders.view'])
->group(function () {
Route::screen('orders', OrderListScreen::class)
->name('platform.orders.list');
Route::screen('orders/{order}', OrderEditScreen::class)
->name('platform.orders.edit')
->middleware('platform.access:platform.orders.manage');
});Не забывайте о backend! canSee() только скрывает UI. Всегда проверяйте права в методах screen (delete, save и т.д.) — злоумышленник может отправить запрос напрямую.
Команды для управления
# Создать админа
php artisan orchid:admin admin admin@example.com password
# Список пользователей
php artisan orchid:table
# Сбросить кэш разрешений
php artisan cache:clearИтоги
| Метод | Назначение |
|---|---|
permission() | Ограничить доступ ко всему Screen |
canSee() | Скрыть элемент по условию |
hasAccess() | Проверить одно разрешение |
hasAnyAccess() | Проверить любое из разрешений |
Правила работы с правами.
Проверка в методе — обязательна, скрытие в интерфейсе — желательно. Именно в таком порядке приоритета. Экран без canSee() неудобен; метод без проверки прав — уязвим.
Права описывают действия, а не роли. platform.orders.delete, а не isManager(). Роль — это набор прав, который меняется по решению бизнеса; проверка роли в коде означает, что каждое такое решение требует правки кода и выкладки.
Проверяйте право на конкретный объект, а не только на действие. «Может редактировать заказы» и «может редактировать этот заказ» — разные вопросы. Идентификатор объекта приходит из запроса, и менеджер, имеющий доступ к своим заказам, при отсутствии второй проверки открывает чужие простой подменой числа в адресе.
Регистрируйте все разрешения в одном месте и группируйте по разделам — иначе через год никто не сможет ответить, что означает конкретная галочка в настройках роли.
Не забывайте про массовые действия и экспорт. Кнопка «Выгрузить в Excel» на экране со списком часто остаётся без проверки прав, хотя выгружает ровно те же данные, доступ к которым вы ограничивали в других местах.
Способ проверить админку за десять минут. Заведите тестовую учётную запись с минимальными правами, войдите под ней и попробуйте выполнить действия, которых в интерфейсе не видно, — отправив запрос на соответствующий метод вручную (инструменты разработчика в браузере позволяют повторить запрос с изменёнными параметрами). Это самая быстрая проверка того, что права реально работают, а не только прячут кнопки. Проходить её стоит после каждого крупного изменения в админке.
Комментарии
Система комментариев скоро будет подключена. А пока вы можете написать мне в Telegram или на email.