Security

Access control

Access control

Laravolt v7 treats access control as a platform concern: routes, policies, menus, and UI actions should all describe the same permission model.

Permissions are not only about hiding buttons. They define who is allowed to change business state.

The model

A typical Laravolt application uses:

  • users
  • roles
  • permissions
  • Laravel policies
  • menu visibility metadata
  • middleware or controller authorization checks

Keep the permission vocabulary explicit. A permission like product.update is easier to understand, test, and generate than a vague name like manage product.

Naming permissions

Use a resource-action pattern:

Plain Text
product.read
product.create
product.update
product.delete
purchase-order.approve
report.export
user.invite

This makes permissions searchable and easy to map to policies, menu items, and action buttons.

Authorize routes and controllers

Protect the route or controller before thinking about the UI.

PHP
Route::resource('products', ProductController::class)
->middleware('auth');

Then authorize inside the controller using Laravel policies:

PHP
public function edit(Product $product): View
{
$this->authorize('update', $product);
return view('products.edit', compact('product'));
}

For one-off workflow actions, use named policy methods:

PHP
public function approve(PurchaseOrder $purchaseOrder): RedirectResponse
{
$this->authorize('approve', $purchaseOrder);
// approve the purchase order...
return back()->with('success', 'Purchase order approved.');
}

Protect menu entries

Menus should not show destinations the current user cannot open.

PHP
app('laravolt.menu.builder')->register(function ($menu) {
$menu->add('Products', route('products.index'))
->data('icon', 'box')
->data('permissions', 'product.read');
});

For module configuration, keep permission metadata near the route:

PHP
'Products' => [
'menu' => [
'All products' => [
'route' => 'products.index',
'icon' => 'box',
'permissions' => 'product.read',
],
],
],

Menu visibility is a convenience, not the security boundary. Always protect the route/controller too.

Protect buttons and row actions

Buttons should mirror the same policy checks used by the backend.

Blade
@can('update', $product)
<x-volt-button href="{{ route('products.edit', $product) }}" variant="secondary">
Edit
</x-volt-button>
@endcan
@can('delete', $product)
<x-volt-button variant="danger">
Delete
</x-volt-button>
@endcan

This keeps the UI honest: users only see actions that make sense for their role and the record state.

Role and permission changes

Laravolt v7 invalidates access after role or permission updates so permission changes take effect immediately. This protects teams from stale sessions where a user keeps access after a role was removed.

Operational note

When changing roles in production, communicate the expected session impact. Users may need to sign in again after their permissions are updated.

Testing access control

Every important permission should have at least one positive and one negative test.

PHP
it('allows product managers to edit products', function () {
$role = app(config('laravolt.epicentrum.models.role'))->create(['name' => 'product-manager']);
$role->syncPermission(['product.update']);
$user = User::factory()->create();
$user->assignRole($role);
$this->actingAs($user)
->get(route('products.edit', Product::factory()->create()))
->assertOk();
});
it('blocks users without product update permission', function () {
$user = User::factory()->create();
$this->actingAs($user)
->get(route('products.edit', Product::factory()->create()))
->assertForbidden();
});

Laravolt roles expose syncPermission([...]), while users using the platform role concern expose assignRole($role) and hasPermission($permission).

AI-ready security rules

When using coding agents on Laravolt projects, include these rules in the task prompt:

Plain Text
Do not add a route, controller action, menu item, or destructive button
without adding the corresponding policy/permission check.
Use resource-action permission names.
Add tests for allowed and denied access.

This keeps generated code aligned with the platform security model.

Checklist

  • [ ] Permission names follow resource.action.
  • [ ] Routes/controllers authorize access.
  • [ ] Menu entries declare permission metadata.
  • [ ] Buttons and row actions use @can or equivalent checks.
  • [ ] Role/permission updates are tested for session invalidation where relevant.
  • [ ] Positive and negative access tests exist for critical workflows.
Previous
AI Task Patterns