Schema-based authorization and permissions for Laravel, with database-scoped ability checks.
patrickhanna/laravel-warrant is a Laravel package for schema-based authorization and permissions for laravel, with database-scoped ability checks..
It currently has 9 GitHub stars and 8 downloads on Packagist (latest version v3.3.0).
Install it with composer require patrickhanna/laravel-warrant.
Discover more Laravel packages by patrickhanna
or browse all Laravel packages to compare alternatives.
Last updated
Row-level permissions & authorization for Laravel — one rule, compiled straight to SQL.
Read the documentation → laravel-warrant.dev
[!WARNING] Laravel Warrant is in beta and still being tested. It's usable, but the API may change between releases — pin your version and check the changelog before upgrading. Please report any issues you run into.
[!NOTE] Not an official Laravel package. Laravel Warrant is an independent, open-source project. It is not affiliated with, maintained by, or endorsed by the Laravel team.
Schema-based authorization for Laravel that compiles a small, human-readable rule language directly into SQL — so "what can this user do?" and "which rows can this user touch?" are answered by the database in a single query, not by loading records into memory.
if is_self or (is_manager and same_department)
they can view, update
they cannot delete
That block is a real, complete Warrant rule. Warrant turns it into a WHERE
clause.
composer require patrickhanna/laravel-warrant
The service provider auto-registers. Publish the config to edit it in place:
php artisan vendor:publish --tag=warrant-config
Requirements: PHP 8.2+, Laravel 11 or 12. The SQL Warrant generates is supported on PostgreSQL, MySQL/MariaDB, and SQLite.
Full docs live at laravel-warrant.dev:
WHERE clause the database evaluates.if … they can/cannot …
language that non-authors can follow.$user->can(), @can, Gate::authorize,
and the can: route middleware resolve Warrant abilities, while abilities no
schema declares fall through to your existing policies.See Why Warrant for the full rationale and a comparison with the policy/query approach.
1. The model uses the trait and names its schema:
use Illuminate\Database\Eloquent\Model;
use Warrant\HasWarrantSchema;
class Timesheet extends Model
{
use HasWarrantSchema;
public function warrantSchema(): string
{
return \App\Warrant\TimesheetSchema::class;
}
}
2. The schema declares the vocabulary — abilities and conditions:
namespace App\Warrant;
use App\Models\Timesheet;
use Illuminate\Contracts\Database\Query\Builder;
use Warrant\Ability;
use Warrant\GlobalCondition;
use Warrant\Schema\Conditions\GlobalConditionContext;
use Warrant\Schema\Conditions\TargetedConditionContext;
use Warrant\Schema\WarrantSchema;
use Warrant\TargetedCondition;
class TimesheetSchema extends WarrantSchema
{
public const model = Timesheet::class;
#[Ability] public const VIEW = 'view';
#[Ability] public const UPDATE = 'update';
#[Ability] public const DELETE = 'delete';
#[Ability] public const APPROVE = 'approve';
// Targeted: narrows WHICH timesheet rows the user matches.
#[TargetedCondition]
public function isSelf(TargetedConditionContext $c): Builder
{
return $c->query->whereRaw('timesheets.user_id = ?', [$c->user->getAuthIdentifier()]);
}
#[TargetedCondition]
public function inDepartment(TargetedConditionContext $c): Builder
{
return $c->query->whereIn('timesheets.department_id', $c->arguments);
}
// Global: a plain yes/no about the user, independent of any row.
#[GlobalCondition]
public function isAdmin(GlobalConditionContext $c): bool
{
return (bool) $c->user->is_admin;
}
}
3. The resolver hands rules (as data) to Warrant for the current user:
namespace App\Warrant;
use Warrant\RuleResolutionContext;
use Warrant\RuleResolver;
use Warrant\RuleSyntaxTree\WarrantRuleSet;
class DatabaseRuleResolver implements RuleResolver
{
public function resolve(RuleResolutionContext $context): WarrantRuleSet
{
// Load the raw rule string + any binding values for this user/resource.
[$syntax, $bindings] = MyRuleStore::for(
user: $context->user,
resource: $context->schemaKey, // 'timesheets'
);
return WarrantRuleSet::fromSyntax($context->schemaKey, $syntax, $bindings);
}
}
The rules themselves are just text:
if is_self they can view, update, delete
if in_department(?, ?) they can view, approve
if is_admin they can *
4. Register the resolver and schema in config/warrant.php:
'rule_resolver' => App\Warrant\DatabaseRuleResolver::class,
'schemas' => [App\Warrant\TimesheetSchema::class],
5. Check access — every call is a single SQL query:
// Which timesheets can the current user update?
$editable = Timesheet::query()->userHasAbility('update')->get();
// Can this user approve this specific timesheet?
if (Timesheet::userHasAbilities('approve', $timesheet)) { /* ... */ }
// Attach the per-row ability list, e.g. for rendering buttons.
$rows = Timesheet::query()->selectUserAbilities()->get();
$rows->first()->abilities; // e.g. ['view', 'update']
// Or go through Laravel's Gate — Warrant resolves these too:
$user->can('approve', $timesheet);
The Quick start walks through this end to end.
Laravel Warrant is open-source software licensed under the MIT license.