Core Concepts

Route Access

On this page

Route access stores every route of your application in system_routes and lets you mark, per role, whether the role may use it (system_routes_in).

Status: syncing routes and storing access rows works. The admin screens for routes (/systemroles/admin/roles/routes/index and /manage) do not ship with views yet. Opening them sends you back with "This screen is not available yet". Until they ship, manage route access with the sync command and code, as shown below, or publish the views and add your own.

Syncing routes#

Bash
php artisan systemroles:sync

This reads every registered route (like php artisan route:list) and stores it in system_routes:

Column Example
route_method `GET
route_url admin/invoices/{invoice}
route_name invoices.show
route_prefix admin
route_action App\Http\Controllers\InvoiceController@show
route_middleware web, auth
route_desc `GET
route_data JSON with the full scan result

A route is identified by method + URL, not by name, because many routes have no name or share one. Running the command again updates changed routes and adds new ones. It prints how many were added and updated. Routes removed from your code stay in the table.

Run it after every deploy, or set scan_on_request to true to sync whenever the Routes screen opens.

Granting route access#

Each row in system_routes_in links one role to one route:

in_route Meaning
1 Allowed
0 Denied
2 Reserved

There is one row per route per role (unique on route_id + role_id).

From code:

PHP
use BabeRuka\SystemRoles\Models\SystemRoutes;
use BabeRuka\SystemRoles\Models\SystemRoutesIn;

$route = SystemRoutes::where('route_name', 'invoices.index')->firstOrFail();

SystemRoutesIn::updateOrCreate(
    ['route_id' => $route->route_id, 'role_id' => 3],
    ['in_route' => '1']
);

Granting a role every route under a prefix:

PHP
SystemRoutes::where('route_url', 'like', 'reports/%')->each(function ($route) {
    SystemRoutesIn::updateOrCreate(
        ['route_id' => $route->route_id, 'role_id' => 3],
        ['in_route' => '1']
    );
});

Checking route access#

The package does not include a route check helper yet. This middleware checks the current route against system_routes_in:

PHP
namespace App\Http\Middleware;

use BabeRuka\SystemRoles\Models\SystemRoutes;
use BabeRuka\SystemRoles\Models\SystemRoutesIn;
use BabeRuka\SystemRoles\Services\SystemRolesService;
use Closure;
use Illuminate\Http\Request;

class RouteAccess
{
    public function handle(Request $request, Closure $next)
    {
        $route = $request->route();
        $roleId = SystemRolesService::roleIdForUser(optional($request->user())->getKey());

        if (SystemRolesService::isSuperAdminRole($roleId)) {
            return $next($request);
        }

        $stored = SystemRoutes::where('route_method', implode('|', $route->methods()))
            ->where('route_url', $route->uri())
            ->first();

        $allowed = $stored && SystemRoutesIn::where('route_id', $stored->route_id)
            ->where('role_id', $roleId)
            ->where('in_route', '1')
            ->exists();

        abort_unless($allowed, 403);

        return $next($request);
    }
}

Routes that are not synced are denied by this middleware. Decide what you want for those (deny is safer) and run systemroles:sync after adding routes.

For busy apps, cache the allowed route ids per role:

PHP
$allowedIds = cache()->remember("systemroles.routes.$roleId", 300, fn () =>
    SystemRoutesIn::where('role_id', $roleId)->where('in_route', '1')->pluck('route_id')->all()
);

Clear that cache key when you change route access.