Relationship model policies and authorisation on Select Form fields

Hello! I'm relatively new to Filament and Livewire in general. I'm trying to set up a secure way to limit the usage of select form fields when the user does not have permissions to access certain actions on the related model. These are controlled by Laravel Policies. I'm wondering about a few things: - Does the ->hidden() method on form fields just a visual, or does it prevent sending the whole field to the front end? Is it a secure way to prevent access to a certain resource on the form? - If I decide to use ->disabled() on the select, same question as the above. Is it a secure way to prevent querying the related data, or can the user re-enable the field and it's functionality with some devtools magic? - Is there a better way to achieve related model authorisation on select fields?
Solution:
Thank you both for the suggestions! I'm not trying to filter data depending on roles, the user either has access to it, or doesn't have access to it, so I don't have to worry about that. I took some time and read trough the docs more thoroughly and it seems even there the ->hidden() and ->disabled() options are used for access control in an example. So I will assume that if the policies are set up correctly it will block the user from saving or getting hidden/disabled fields....
Jump to solution
4 Replies
Razuro
Razuro3w ago
My current implementation looks like this:
Section::make('Location Information')->schema([

Select::make('event_venue_id')
->relationship('eventVenue', 'name')
->createOptionForm(
auth()->user()->can('create', EventVenue::class)
? EventVenue::getForm() : null
)
->editOptionForm(fn($record) => auth()->user()->can('update', $record->eventVenue)
? EventVenue::getForm() : null
)
->searchable()
->preload(),

TextInput::make('participation_limit')
->integer()
->minValue(0),

])->hidden(auth()->user()->cannot('viewAny', EventVenue::class))
->columns(2)->columnSpan(1),
Section::make('Location Information')->schema([

Select::make('event_venue_id')
->relationship('eventVenue', 'name')
->createOptionForm(
auth()->user()->can('create', EventVenue::class)
? EventVenue::getForm() : null
)
->editOptionForm(fn($record) => auth()->user()->can('update', $record->eventVenue)
? EventVenue::getForm() : null
)
->searchable()
->preload(),

TextInput::make('participation_limit')
->integer()
->minValue(0),

])->hidden(auth()->user()->cannot('viewAny', EventVenue::class))
->columns(2)->columnSpan(1),
Relationships:
// EventVenue.php

public function events(): HasMany
{
return $this->hasMany(Event::class);
}
// EventVenue.php

public function events(): HasMany
{
return $this->hasMany(Event::class);
}
// Event.php

public function eventVenue(): belongsTo
{
return $this->belongsTo(EventVenue::class);
}
// Event.php

public function eventVenue(): belongsTo
{
return $this->belongsTo(EventVenue::class);
}
gringomutt
gringomutt3w ago
In general i think you could work some HTML magic and might have some problems. If you are using panel builder and form builder i would grab de request on the Create/Edit pages and validate the data there and make sure they are not trying to inject any data. Also from i am able to see in your code you are running a validation that will return true or false. But does not stop data input. In short i would probably block at the relationship and data entry/edit point. Good luck
icolatsi22
icolatsi223w ago
The way I've done it in the past is, I use a custom query for the select. So the select gets different options per role. Maybe you can create an $allowedOptions array
$allowedOptions = [
'admin' => [allowed options used in the Select 'where' query],
'client' => [allowed options used in the Select 'where' query],
];
$allowedOptions = [
'admin' => [allowed options used in the Select 'where' query],
'client' => [allowed options used in the Select 'where' query],
];
(Problem is this is hard-coded. You can create a model to store these so you can edit them) something like this...
->options(function () {
$user_role = auth()->user()->getRoleNames()->toArray()[0];
$allowed = $allowedOptions[$user_role];
// Event::whereIn('xxx', $allowed)...
}),
->options(function () {
$user_role = auth()->user()->getRoleNames()->toArray()[0];
$allowed = $allowedOptions[$user_role];
// Event::whereIn('xxx', $allowed)...
}),
If you have multiple roles, change the way you build the $allowed
Solution
Razuro
Razuro3w ago
Thank you both for the suggestions! I'm not trying to filter data depending on roles, the user either has access to it, or doesn't have access to it, so I don't have to worry about that. I took some time and read trough the docs more thoroughly and it seems even there the ->hidden() and ->disabled() options are used for access control in an example. So I will assume that if the policies are set up correctly it will block the user from saving or getting hidden/disabled fields. If anyone has this questions, there is more information here: https://filamentphp.com/docs/3.x/forms/fields/getting-started#disabling-a-field