Best way to configure allowed groups to a route

Hi,

I am currently using laravel for a project, but this framework is too heavy for my needs, and slim seems to fit for now on. I’m trying to estimate the migration time, but I don’t know how to migrate something:

In laravel, I have:

  • A Menu class, where I define allowed groups for a specific menu entry. Each menu entry has a key.
  • A Menu that shows only what the connected user can see, based on his groups
  • A “require-group” middleware that checks, for a given route, that the user is allowed
  • A set of routes, using this middleware And passing as a parameter the menu key. Example:
Route::get('/dashboards', ['middleware' => 'require-group:realtime.dashboard', function () { return view('dashboard'); }]);

The point is that Laravel allows to pass a string parameter to the middleware with the syntax middleware:parameter, while I can’t find any equivalent for Slim.

Moreover, this pattern is not optimal as I also have routes that are not displayed in the menu.
I also want to avoid to complexify the creation of a new page by splitting the configurations in many files, and the best approach for me seems to be something like this in routing:

$app->get('/dashboard', function ($request, $response, $args) { /* ... */})->requireGroups('administrator');

And for my menu (pseudo code as I am not yet familiar with Slim):

if( getRouteFor($url)->isAllowed($userGroups) ) {
    // Display menu item
}

Is this possible with Slim? Any advices on the best way to achieve this ?

You can group routes and also add middleware, but you can’t pass a parameter to it.

$app->group('', function() {
    $this->get('/dashboard', 'DasboardAction');
 })->add('RequireGroupMiddleware');

As an example the RouteGroupMiddleware could look something like this:

class RequireGroupMiddleware
{
    public function __invoke($request, $response, $next) {
        $route = $request->getAttribute('route');
        $routeName = $route->getName();
        // look up required group for $routeName from say
        // $this->get('settings')['route_permissions'] and decide if to let this
        // user through
        $group = $this->get('settings')['route_permissions'][$routeName];
        
        // Assume previous middleware has set the logged in user to an attribute
        $user = $this->getAttribute("user");
        if ($user->belongsToGroup($group)) {
            // allowed to continue
            return $next($request, $response);
        }

        // not allowed
        return $response->withStatus(401)->write("Not allowed here");
    }
}

Thanks for your quick answer.

So, If I understand correctly, there’s no way to set the permissions without adding it to another settings dictionary?

What about a kind of pattern like this:

$this->get('/dashboard', 'DasboardAction')->add(new RequireGroupMiddleware(['administrator']));
class RequireGroupMiddleware
{
    public $allowedGroups = [];
    public function __construct($allowedGroups) {
        $this->allowedGroups = $allowedGroups;
    }

    public function __invoke($request, $response, $next) {
        /* */
    }
}

Is it then possible to find the middleware and get its $allowedGroups property?

You could certainly do that. The only downside is that you are instantiating the RequireGroupMiddleware for every route, even if you don’t use it.

This may or may not be a concern to you of course :slight_smile:

OK, I see. That’s why you suggested me to use groups I suppose.

Thanks for the advice, but now, how do I get this middleware for my menu?

I don’t understand. It’s just a class with an __invoke method.

What is the equivalent code for :

getRouteFor($url)->getRequireGroupMiddleware()->allowedGroups;

In other words :

  • How do I get a Route object, given an URL ?
  • How do I get a Middleware object given a Route object ?

Thanks for your support, I really appreciate it :slight_smile:

You don’t. Slim will call your route callable and any relevant middleware when required.

i.e. you don’t approach it like your sample code. You set up your middleware to know what it needs when it is executed.

Sorry for the misunderstanding… My goal is to put the permissions in only one place.
My menu has an URL that points to “/dashboard”. I don’t want to set the permission on the menu declaration, since I want the value to be consistent with what can actually be visible to the user. I thought it was possible to fetch the information directly from the route.

So If I understand well, there’s no other choice than configuring the permissions in a third place, as suggested in your first reply?