DEV Community

Abdullah Sowailem
Abdullah Sowailem

Posted on

FieldGuard Dynamic Field-Level Security for Laravel

FieldGuard

FieldGuard is a lightweight, non-intrusive Laravel package for field-level security on Eloquent models. It allows you to control which users can view or modify specific attributes using dynamic, database-driven rules.

YouTube Video

Features

  • Database-Driven Rules: Create and manage security rules in the database for runtime configuration.
  • Non-Intrusive: No traits, base classes, or attributes required for your models.
  • Automatic Enforcement: Optionally enforce security via model events (retrieved, saving) without any code changes.
  • Read & Write Protection: Separate rules for viewing and updating fields.
  • Data Masking: Automatically mask sensitive fields (e.g., ***-***-***).
  • Middleware Integration: Automatically filter unauthorized fields from incoming requests.
  • Caching: Performance-optimized with Laravel cache support for database rules.
  • No-Code Friendly: Centralized control without altering your core models.

Installation

composer require sowailem/fieldguard
Enter fullscreen mode Exit fullscreen mode

Run the migrations to create the rules table:

php artisan migrate
Enter fullscreen mode Exit fullscreen mode

Usage

1. Creating Security Rules

All field-level permissions are managed through the field_guard_rules table. You can use the FieldGuardRule model to create rules.

use Sowailem\FieldGuard\Models\FieldGuardRule;

FieldGuardRule::create([
    'model_class' => 'App\Models\User',
    'field_name' => 'salary',
    'read_policy' => ['roles' => ['admin', 'hr'], 'allow_self' => true],
    'write_policy' => ['roles' => ['admin']],
    'mask' => 'PROTECTED',
    'is_active' => true,
]);
Enter fullscreen mode Exit fullscreen mode

Rule Structure

  • model_class: Fully qualified class name of the Eloquent model.
  • field_name: The name of the attribute to secure.
  • read_policy: (Optional) Permission for viewing the field. Can be a string or JSON object.
  • write_policy: (Optional) Permission for creating or updating the field. Can be a string or JSON object.
  • mask: (Optional) Value to return if read permission is denied (instead of removing the field).
  • is_active: Boolean to enable/disable the rule (defaults to true).

Policy Types

Policies can be:

  • Gate Name: A Laravel Gate name (e.g., 'view-salary').
  • 'self': Allows access if the user's ID matches the model's primary key.
  • 'role:name': Requires the user to have a specific role (expects a hasRole($role) method on the User model).
  • 'true' / 'false': Always allow or always deny.
  • JSON Array: For complex logic (e.g., ['roles' => ['admin'], 'allow_self' => true, 'gate' => 'extra-check']).

2. Enforcing Security

Automatic Enforcement (Global)

You can enable automatic enforcement for all models by setting automatic_enforcement to true in your config/fieldguard.php. This uses Eloquent events to filter fields automatically.

'automatic_enforcement' => true,
Enter fullscreen mode Exit fullscreen mode
  • Read: Automatically hides or masks fields when a model is retrieved from the database.
  • Write: Automatically prevents unauthorized fields from being saved (reverts to original values).

Manual Enforcement (Read)

Use the FieldGuard facade to filter model attributes. This is useful in controllers or API resources.

use Sowailem\FieldGuard\Facades\FieldGuard;

public function view(User $user)
{
    // Returns the model attributes as an array, filtered by security rules
    return FieldGuard::apply($user, auth()->user());
}
Enter fullscreen mode Exit fullscreen mode

API Resource Integration

public function toArray($request)
{
    return FieldGuard::apply($this->resource, $request->user());
}
Enter fullscreen mode Exit fullscreen mode

Automatic Enforcement (Middleware - Write)

Register the middleware in your bootstrap/app.php (Laravel 11+) or app/Http/Kernel.php:

// Laravel 11+ example in bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'fieldguard' => \Sowailem\FieldGuard\Middleware\EnforceFieldSecurityMiddleware::class,
    ]);
})
Enter fullscreen mode Exit fullscreen mode

Apply it to routes to filter request data before it reaches your controller:

Route::put('/users/{user}', [UserController::class, 'update'])
    ->middleware('fieldguard:App\Models\User');
Enter fullscreen mode Exit fullscreen mode

The middleware will automatically remove unauthorized fields from $request->all() before they reach your controller logic.

3. Caching

Database rules are cached for performance. By default, they are cached using the tag fieldguard:rules (configurable in config/fieldguard.php). When you update rules via the provided FieldGuardRuleRepository, the cache is automatically cleared.

You can also manually clear the cache:

php artisan fieldguard:clear-cache
Enter fullscreen mode Exit fullscreen mode

4. Custom Policy Resolvers

If you need custom logic for interpreting database policies (e.g., integrating with a specific RBAC system), implement the PolicyResolver interface and register it in config/fieldguard.php.

namespace App\Security;

use Sowailem\FieldGuard\Contracts\PolicyResolver;
use Illuminate\Database\Eloquent\Model;

class MyCustomResolver implements PolicyResolver
{
    public function resolve(array $policy, Model $model, $user): bool
    {
        // Your custom logic here
        return true;
    }
}
Enter fullscreen mode Exit fullscreen mode

Register it in config/fieldguard.php:

'resolver' => \App\Security\MyCustomResolver::class,
Enter fullscreen mode Exit fullscreen mode

5. Seeding Initial Rules

The package includes a seeder example to help you bootstrap rules. You can publish and run the seeder or use the provided example:

php artisan db:seed --class="Sowailem\FieldGuard\Database\Seeders\FieldGuardRuleSeeder"
Enter fullscreen mode Exit fullscreen mode

6. Administrative API

FieldGuard comes with built-in RESTful API endpoints for managing security rules.

  • GET /field-guard/rules – List all rules (supports pagination and filtering)
  • POST /field-guard/rules – Create a new rule
  • GET /field-guard/rules/{id} – View a specific rule
  • PUT/PATCH /field-guard/rules/{id} – Update an existing rule
  • DELETE /field-guard/rules/{id} – Delete a rule

Configuration

You can customize the API prefix and middleware in config/fieldguard.php:

'api' => [
    'enabled' => true,
    'prefix' => 'field-guard',
    'middleware' => ['api', 'auth:sanctum'],
],
Enter fullscreen mode Exit fullscreen mode

Authorization

The API uses a gate named manage-field-guard to authorize requests. Ensure you define this gate in your AuthServiceProvider or AppServiceProvider:

use Illuminate\Support\Facades\Gate;

Gate::define('manage-field-guard', function ($user) {
    return $user->isAdmin(); // Your authorization logic
});
Enter fullscreen mode Exit fullscreen mode

Publishing Routes

If you want to customize the routes, you can publish them:

php artisan vendor:publish --tag="fieldguard-routes"
Enter fullscreen mode Exit fullscreen mode

Then disable the automatic loading in config/fieldguard.php and manually register them in your routes/api.php.

Testing

vendor/bin/phpunit
Enter fullscreen mode Exit fullscreen mode

License

The MIT License (MIT). Please see License File for more information.

Top comments (0)