An experiment in model-based access control for Laravel applications.
This was to address a shortcoming or two I found in Laravel policies and gates, though that is no longer the case.
This package allows record-level access control based on custom authorization rules.
This handles cases where you may have many records in a table, but each entity has its own rules for access control. For example, you may have a table of documents, and it's not a simple scenario where a user can edit their own documents, and view all others. Maybe the access of each document has to be set separately, e.g. document X can be edited by Bob & Jane, and viewed by the Staff group; whereas document Y can only be viewed by the Finance team.
Here's a demo of how you can use it:
use Illuminate\Database\Eloquent\Model;
use Tanerkay\ModelAcl\Traits\ModelBasedAccessControl;
class Report extends Model
{
use ModelBasedAccessControl;
// ...
}
$post->can('view');composer require tanerkay/laravel-model-aclYou can publish the migration with:
php artisan vendor:publish --provider="Tanerkay\ModelAcl\ModelAclServiceProvider" --tag="model-acl-migrations"php artisan migrateYou can optionally publish the config file with:
php artisan vendor:publish --provider="Tanerkay\ModelAcl\ModelAclServiceProvider" --tag="model-acl-config"The addAccessControl() method can be used to add rules to individual models.
public function addAccessControl(
string|array $abilities,
object|array $ruleDefinitions,
?string $description = null
): voidIt accepts:
- an ability name or list of abilities that can be used in calls to
can(). - an array of rule definitions, each rule definition is an array containing a class string
'class'and an array of arguments'arguments'. - an optional description of the rule, which can be stored in the database alongside the rule definition.
e.g. Restrict a certain post to moderators and admins.
use Tanerkay\ModelAcl\Rules\HasRole;
$post->addAccessControl('view', [
[
'class' => HasRole::class,
'arguments' => ['admin', 'moderator'],
]
]);
// throws exception if $user->hasRole(['admin', 'moderator']) doesn't return true
$post->can('view');The HasRole class assumes your User model has a method hasRole() that accepts a string or an array of strings. You can customize the name of the method using the env key MODEL_ACL_AUTHENTICATABLE_HAS_ROLE.
For other rules or logic, you can construct your own Rule class which implements \Tanerkay\ModelAcl\Contracts\RuleContract or which extends the abstract class \Tanerkay\ModelAcl\Rules\Rule.
e.g.
use Illuminate\Support\Arr;
use Tanerkay\ModelAcl\Rules\Rule;
use Tanerkay\ModelAcl\Exceptions\AuthorizationException;
class IpWhitelistRule extends Rule
{
public function authorize(?Authenticatable $user, $options): void
{
$ip = request()->ip();
$whitelist = Arr::wrap($options['ip_whitelist']);
if (!in_array($ip, $whitelist)) {
throw new AuthorizationException('IP address not in whitelist.');
}
}
}$site = Site::query()->create();
$site->addAccessControl('show', [
[
'class' => IpWhitelistRule::class,
'options' => [
'ip_whitelist' => ['127.0.0.1'],
],
]
]);
// throws exception if request()->ip() is not in the whitelist
$site->can('show');composer test- Spatie for making awesome packages, this package leverages
spatie/laravel-package-toolsand is itself derived fromspatie/laravel-activitylog.