Core Concepts

Controller (Class) Access

On this page

Controller access lets an administrator allow or deny a role on a whole controller, without code changes. Manage it at Admin → Classes (/systemroles/admin/roles/classes/index).

How controllers get listed#

SystemRoles scans the folder set in controller_directory (default app/Http/Controllers, including sub-folders). For each PHP file it reads the namespace and the first class name and stores them in system_classes.

  • A controller is identified by namespace + class name, so App\Http\Controllers\UsersController and App\Http\Controllers\Admin\UsersController are two separate entries.
  • The scan runs when the Classes screen opens (if scan_on_request is true), when you run php artisan systemroles:sync --classes, and when you click Add Class Access.
  • Scanning only adds and updates. Controllers you delete from the code stay in the table until you remove them.

The Classes screen#

Column Meaning
Name Class name
Filename File name
Namespace Full namespace
Description Generated description
# of Class Access How many roles have an access row for it
Action Manage Class Access

Add Class Access#

The Add Class Access button (in a confirmation dialog) rescans the controllers and creates a row in system_classes_in for every role on every controller, set to "revoke" (0). Existing rows are kept. Use it once after installing, so every controller appears with a complete set of roles.

Manage Class Access#

For one controller, the screen lists every role with two choices:

  • Grant Access (in_role = 1)
  • Revoke Access (in_role = 0)

Save to store the choices in system_classes_in.

Checking controller access in code#

The check lives on SystemClassesController:

PHP
use BabeRuka\SystemRoles\Http\Controllers\SystemRoles\SystemClassesController;
use BabeRuka\SystemRoles\Services\SystemRolesService;

$roleId = SystemRolesService::roleIdForUser(auth()->id());
$classes = new SystemClassesController();

// Returns 1 (allowed) or 0 (denied). Super admin always gets 1.
$classes->checkSystemClassesInit($roleId, \App\Http\Controllers\InvoiceController::class);

// Same check, but aborts with 403 when denied.
$classes->checkAccess($roleId, \App\Http\Controllers\InvoiceController::class);

The class name can be:

  • fully qualified (App\Http\Controllers\Admin\UsersController): matched on namespace and name. Recommended.
  • short (UsersController): matched on name only. If two controllers share the name, the first match is used, so prefer the full name.

A controller that is not in system_classes, or has no row for the role, is denied.

chechAccess() (misspelt) still works as an alias of checkAccess() for older code. Use checkAccess() in new code.

Enforcing controller access on every request#

A middleware can check the controller behind the current route:

PHP
namespace App\Http\Middleware;

use BabeRuka\SystemRoles\Http\Controllers\SystemRoles\SystemClassesController;
use BabeRuka\SystemRoles\Services\SystemRolesService;
use Closure;
use Illuminate\Http\Request;

class ControllerAccess
{
    public function handle(Request $request, Closure $next)
    {
        $controller = $request->route()?->getControllerClass();

        // Closures and package routes are not checked here.
        if (! $controller || str_starts_with($controller, 'BabeRuka\\SystemRoles\\')) {
            return $next($request);
        }

        $roleId = SystemRolesService::roleIdForUser(optional($request->user())->getKey());

        if ((new SystemClassesController())->checkSystemClassesInit($roleId, $controller) !== 1) {
            abort(403, 'You do not have access to this area.');
        }

        return $next($request);
    }
}

Apply it to a route group, not globally, so public pages such as login keep working:

PHP
Route::middleware(['auth', 'controller.access'])->group(function () {
    Route::resource('invoices', InvoiceController::class);
});

When to use controller access#

Good for:

  • locking whole areas (Reports, Billing, Settings) per role;
  • letting an administrator open or close an area quickly.

Less suited for:

  • different rights inside one controller (view vs delete). Use permissions for that.

You can combine both: controller access as a coarse gate, permissions for actions inside.