Aureus ERP

How Aureus ERP Handles Roles and Permissions: Beyond Filament Shield

Aureus ERP is a modular ERP built on Laravel and Filament. The moment many roles share one panel, a single question decides if it is usable: who can see which records? 

Standard permission tooling answers only half of that. This post covers the other half, and how the Aureus ERP Security module solved it.

Key takeaways

The problem: “can view” is not the same as “can view this”

Most Laravel permission setups, including Filament Shield on top of Spatie laravel-permission, model access as a yes/no flag per action:

view_sales_order, create_sales_order, update_sales_order, delete_sales_order …

That works for features. If a user has view_sales_order, Shield lets them open the screen. The catch is that it lets them see every sales order in the company.

An ERP needs something finer. A salesperson should see only their own orders. A team lead should see their Team’s. An admin should see everything.

So the requirement was record-level access control, layered on the normal permission check, and applied across every module without rewriting each screen.

What Filament Shield doesn’t support

To be precise about the gap in default Filament Shield:

However, we did not want to drop Shield or Spatie, since roles still do their job. We needed to extend them with a scope.

The core idea: a permission type

The whole design rests on one small enum. Every user carries a resource_permission that says how wide their access is:

This is the piece Shield lacks: a scope axis on top of the yes/no permission. A role grants what you can do. The permission type decides whose records you can do it to.

Solution part 1: filtering every query with an Eloquent global scope

The scope is useless unless every list and dropdown respects it. An Eloquent global scope reads the user’s resource_permission and filters accordingly:

Note the touch on INDIVIDUAL: you also see records you follow in Chatter. Collaboration still works even under the tightest scope.

To switch this on for a Filament resource, we drop in one trait that overrides the base query:

That is the whole integration cost per screen. There is no per-resource access logic to maintain.

Solution part 2: one place to resolve “who can I see?”

Each scope needs a different set of allowed users. A small Bouncer service (via a bouncer() helper) resolves and caches that set, so it is not recomputed on every query:

Models opt in with a flexible HasPermissionScope trait. It knows how each model stores ownership, whether an owner column (creator_id), an assignment column (user_id), or a pivot table.

Solution part 3: policy checks for a single record

Query scoping hides records from lists. We also need to authorize an action on one record. That policy layer uses a complementary HasScopedPermissions trait:

Same three-way logic of global, group, and individual. Now it answers “may this user touch this record?” inside the policies.

Challenges and how we solved them

We also extended Spatie’s PermissionRegistrar to wire a gate before hook and team-aware checks. Spatie stays the foundation while fitting the ERP’s needs.

Benefits of record-level permissions in Aureus ERP

Frequently asked questions

Does Filament Shield support record-level permissions?

No. Filament Shield and Spatie laravel-permission grant permissions globally per action. They decide if a user can view a resource, not which records. Aureus ERP adds that layer with a permission type and an Eloquent global scope.

What is the difference between role permissions and record-level permissions?

Role permissions define actions like view, create, update, and delete. Record-level permissions define which records those actions apply to, such as your own, your team’s, or all records.

How does Aureus ERP filter records per user?

Each user has a resource_permission value of Global, Group, or Individual. A global Eloquent scope automatically filters queries based on that access level.

Final thoughts

Filament Shield manages feature permissions, while Aureus ERP extends it with record-level permissions. This ensures users see only the data they’re allowed to access.

A global query scope, policy checks, and a cached Bouncer provide consistent, team-aware access across every module with minimal boilerplate.

Want to see how Aureus ERP can fit your team’s access rules? Tell us about your company and we will walk you through it.

Further reading: Spatie laravel-permission documentation, Filament Shield plugin, Laravel Eloquent query scopes

Exit mobile version