Версія v1.6.5 Попередні версії: документація на GitHub
Авторизація
На цій сторінці
Доступ до панелі й до даних перевіряється на двох рівнях:
- Хто може увійти в панель: gate
viewLarsadmin. - Що може робити користувач з моделями: політики Laravel, плюс обмеження видимих рядків.
Доступ до панелі
Усі маршрути за екраном входу проходять middleware Authenticate (користувач залогінений на guard
панелі) і Authorize (виконується перевірка нижче). Інакше відповідь 403 (для JSON-запитів без
X-Inertia це JSON {"message": "..."}).
Відповіді 401 (не залогінений) і 403 для JSON-запитів містять перекладене повідомлення
(larsadmin::messages.unauthenticated / larsadmin::messages.forbidden) мовою панелі.
Authorize викликає Larsadmin::check($request). Провайдер застосунку
(App\Providers\LarsadminServiceProvider) налаштовує перевірку так:
(config('larsadmin.allow_local', true) && app()->environment('local'))
|| Gate::forUser($request->user(Larsadmin::guard()))->check('viewLarsadmin')
Gate viewLarsadmin
Базовий провайдер оголошує gate, який закритий для всіх. Відкрийте його, перевизначивши метод
gate() у app/Providers/LarsadminServiceProvider.php:
use Illuminate\Support\Facades\Gate;
use Maxcxam\Larsadmin\LarsadminApplicationServiceProvider;
class LarsadminServiceProvider extends LarsadminApplicationServiceProvider
{
protected function gate(): void
{
Gate::define('viewLarsadmin', function ($user) {
return in_array($user->email, [
'[email protected]',
]);
});
}
}
Можна ввести будь-яку власну логіку: роль, дозвіл, колонку is_admin.
allow_local
У APP_ENV=local і з larsadmin.allow_local = true (за замовчуванням) gate не перевіряється:
у панель потрапляє кожен автентифікований користувач. Це зручно для розробки (так працює демо-застосунок, на якому розробляється пакет),
але небезпечно на спільних чи публічних серверах.
LARSADMIN_ALLOW_LOCAL=false # завжди вимагати gate
Власна перевірка доступу
Замість gate можна зареєструвати довільне замикання, перевизначивши метод authorization() у
app/Providers/LarsadminServiceProvider.php:
use Illuminate\Http\Request;
use Maxcxam\Larsadmin\Larsadmin;
protected function authorization(): void
{
Larsadmin::auth(fn (Request $request) => $request->user()?->isAdmin() === true);
}
Таке замикання повністю замінює перевірку з базового провайдера (allow_local і gate тоді не діють).
Без провайдера застосунку діє лише allow_local у local; в інших середовищах доступу немає.
Окремий guard
LARSADMIN_GUARD (або guard у конфігу) задає guard автентифікації панелі. Той самий guard
використовується для gate і для request->user().
Політики моделей
Entity перевіряє політику моделі ($model) для таких ability:
| Дія | Ability | Аргумент |
|---|---|---|
| Навігація, список, доступ до Entity | viewAny |
клас моделі |
Кнопка й сторінка створення, store |
create |
клас моделі |
| Перегляд запису | view |
запис |
Редагування, update |
update |
запис |
| Видалення | delete |
запис |
Приклад app/Policies/TagPolicy.php: теги можна переглядати й змінювати, але не видаляти.
class TagPolicy
{
public function viewAny(User $user): bool { return true; }
public function view(User $user, Tag $tag): bool { return true; }
public function create(User $user): bool { return true; }
public function update(User $user, Tag $tag): bool { return true; }
/** Tags may never be deleted from the panel. */
public function delete(User $user, Tag $tag): bool { return false; }
}
Кнопки в інтерфейсі показуються за цими ж перевірками, але сервер завжди перевіряє їх повторно.
Політики працюють «fail closed»
Якщо для моделі зареєстровано політику, рішення повністю делегується gate Laravel:
- відсутній метод політики означає заборону;
- хуки
before()політики (іGate::before) діють, як звично.
Це стосується й прав на attach/detach у зв'язках (див. Зв'язки), крім того, що
для них при відсутньому методі ability береться update.
Моделі без політики: allow_without_policy
Якщо політики немає, діє larsadmin.allow_without_policy:
| Значення | Результат |
|---|---|
true (за замовчуванням) |
усе дозволено |
false |
усе заборонено |
Для продакшну рекомендується false і явні політики для кожної керованої моделі.
Обмеження рядків
Політика відповідає «чи можна взагалі», а не «які саме рядки». Рядки обмежують статичні хуки Entity (Сутності):
class OwnPost extends Entity
{
public static string $model = \App\Models\Post::class;
public static function indexQuery(Request $request, Builder $query): Builder
{
return $query->where('user_id', $request->user()->getKey());
}
}
indexQuery діє на список та панелі зв'язків. detailQuery (за замовчуванням дорівнює indexQuery)
діє на перегляд, редагування, оновлення, видалення й дії над вибраними записами. Запис поза
областю дає 404. relatableQuery визначає, що можна вибрати у зв'язках, і діє також при валідації.
Дії над записами додатково перевіряють update для кожного запису (Дії).
Глобальний пошук
Глобальний пошук (Ctrl/⌘+K) дотримується тих самих правил, що й список Entity:
- Entity шукається, лише якщо має
$globallySearchable = trueі користувач маєviewAny. Інакше вона мовчки пропускається й не згадується у відповіді. - Рядки беруться лише з
indexQuery(), також коли пошук іде через Laravel Scout: рушій пропонує лише ключі, а записи завантажуються з бази в межах області користувача. - Колонки полів, прихованих
canSee, не шукаються (SQL-шлях). На Scout-шляху збіг визначає індекс рушія, тож поле, прихованеcanSee, але присутнє вtoSearchableArray(), усе ще може дати збіг. - Назва й підзаголовок не показують полів, прихованих
canSee, і пов'язаних записів поза областю пов'язаної Entity. - Кожен результат перевіряється на
view; записи без дозволу відкидаються. - Результати не кешуються; ендпоінт має лімітер
larsadmin-search.
Scout-індекс не знає про canSee: не кладіть чутливі поля в toSearchableArray() або вимкніть Scout
для Entity через usesScout(): false. Див. Глобальний пошук.
Журнал аудиту
Журнал аудиту має власний gate viewLarsadminAudit, якого пакет не визначає: доки застосунок його не
визначить, журнал не бачить ніхто (fail closed). allow_local на нього не діє.
protected function gate(): void
{
Gate::define('viewLarsadmin', fn ($user) => in_array($user->email, ['[email protected]']));
Gate::define('viewLarsadminAudit', fn ($user) => $user->is_admin);
}
- Сторінка «Журнал аудиту» показує записи лише тих Entity, на яких користувач має
viewAny. - Історія запису додатково вимагає, щоб запис був у межах
detailQuery()і користувач мав на ньогоview. - Зміни полів, прихованих від глядача через
canSee, не показуються. - Сторінка «Журнал аудиту» не обмежується за рядками: вона не перевіряє
indexQuery()іviewдля кожного запису журналу, тож власник gate бачить усі записи журналу кожної Entity, на якій маєviewAny. Gate це межа довіри: у multi-tenant застосунках давайте його лише тим, хто може бачити дані всіх орендарів.
Див. Журнал аудиту.
Сповіщення та фонові дії
- Дзвіночок показує лише сповіщення типу
larsadmin, адресовані поточному користувачу guard-а панелі. Чуже або неіснуюче сповіщення відповідає 404. Окремого gate немає: досить доступу до панелі. - Фонову дію обробник черги виконує від імені користувача, що її запустив, і повторює всі перевірки
(fail closed):
viewLarsadmin,viewAnyна Entity,canSee()дії,authorizedToRunStandalone()абоauthorizedToRun()для кожного запису,indexQuery()/detailQuery(). Права, відкликані між запуском і виконанням, діють: записи пропускаються або запуск завершується помилкою. Перед кожним пакетом користувач перечитується, а доступ до панелі перевіряється знову.session()у обробнику це окрема порожня сесія запуску, тож скоупінг, що спирається на сесію, не бачить даних інших запусків. - Запуски бачить і скасовує лише їхній власник. Адміністратори чужих запусків не бачать; файл
ActionResponse::file()завантажує лише власник. - Realtime-канали панелі (
private-larsa_inbox.*) авторизує лишеPOST {path}/broadcasting/authпанелі;/broadcasting/authзастосунку їх не авторизує.
Див. Сповіщення та фонові дії.
Контрольний список
- Закрийте gate
viewLarsadminна реальні умови, вимкнітьallow_localпоза розробкою. - Створіть політики для керованих моделей і розгляньте
allow_without_policy => false. - Додайте
indexQuery, якщо користувач бачить лише частину рядків. - Для Entity з
$globallySearchableі Laravel Scout перевірте, щоtoSearchableArray()не містить полів, прихованихcanSee. - Якщо журнал аудиту ввімкнено, дайте gate
viewLarsadminAuditлише тим, кому можна бачити історію всіх записів доступних їм Entity. - Для фонових дій перевірте, що
canSee(),canRun(),fields(), політики йindexQuery()не залежать від сесії чи заголовків запиту: в обробнику черги сесія порожня, а заголовків немає. Скоупінг за орендарем із сесії в черзі не спрацює (поверне порожнє значення). - Не використовуйте префікс
larsa_(LARSADMIN_CHANNEL_PREFIX) для broadcast-каналів застосунку. - Не запускайте публічні сервери з
APP_ENV=local.