Способи обмежити дію:
use Filament\Actions\Action;
// показати лише тим, кому можна
Action::make('refund')
->visible(fn (Order $record): bool => auth()->user()->can('refund', $record))
->action(fn (Order $record) => $record->refund());
// через політику моделі
Action::make('refund')
->authorize('refund')
->action(fn (Order $record) => $record->refund());
// показати вимкненою з поясненням з політики
Action::make('refund')
->authorize('refund')
->authorizationTooltip();
// обмежити частоту
Action::make('resendInvoice')
->rateLimit(5); // спроб на хвилину з однієї IP-адреси
Чи захищає прихована кнопка? Так, і це важлива відмінність Filament від «голого» Livewire. Дії викликаються через Livewire-запит, який можна підробити. Але перед виконанням Filament перевіряє дію на сервері: якщо visible() повертає false (або hidden() - true) чи дія disabled(), вона вважається вимкненою і не виконується. Тобто умова видимості працює і як перевірка доступу.
Порівняйте зі звичайним Livewire-компонентом: публічний метод можна викликати з консолі браузера, навіть якщо кнопки на сторінці немає, - там авторизацію треба писати в самому методі.
Готові дії в ресурсах (EditAction, DeleteAction, DeleteBulkAction) самі перевіряють відповідні методи політики (update, delete, deleteAny) - викликати authorize() для них не потрібно.
Пастки:
- умова має залежати від запису.
visible(auth()->user()->isAdmin())у таблиці перевіряє роль, але не те, чи може цей адміністратор чіпати цей запис (інший тенант, чужий відділ); - масові дії - для перевірки кожного вибраного запису потрібен
authorizeIndividualRecords('refund'), інакше перевіряється лише загальний доступ; - дія з
url()- це просто посилання: захищати треба маршрут, на який воно веде, а не кнопку; rateLimit()рахує спроби для комбінації дії, запису й сторінки - від повторних кліків захищає, але не замінює серверних обмежень на дорогі операції (листи, платежі).