Skip to content

Kirby 3.6.6

Going beyond Kirby's permissions features

Kirby has basically three ways to limit what users can do in the Panel:

  1. Create user roles with permissions for this role.
  2. Limit these role permissions on a per-blueprint basis.
  3. In additon to the last two options, you can also use before hooks to say "no no" to users if they try to do something they are not supposed to do.

If all of the above is not enough, there are other ways to put users in their place.

Completely different blueprint sets per user (role)

When is this useful

The method we show here is useful if you really need a completely different form setup for different user roles, because some roles are not supposed to read or edit certain parts of a blueprint on a tab or even down to the field level.

This implementation gives you absolute freedom, and you are not even limited to roles, but could create blueprint sets for individual users.

How does it work

This method leverages Kirby's custom folder setup feature.

index.php
require __DIR__ . '/kirby/bootstrap.php';

$kirby = new Kirby();

$currentUser = $kirby->user();

if ( $currentUser && $currentUser->role()->name() === 'editor' ):
   $kirby = new Kirby([
       'roots' => [
           'blueprints' => __DIR__ . '/site/blueprints-editor',
       ],
   ]);

elseif ( $currentUser && $currentUser->role()->name() === 'client' ):
   $kirby = new Kirby([
       'roots' => [
           'blueprints' => __DIR__ . '/site/blueprints-client',
       ],
   ]);
endif;

echo $kirby->render();

After Kirby is loaded, we check the user role of the current user and then assign a different blueprint folder for each user role or multiple user roles.

Possible downsides

You need to maintain and keep in sync multiple complete blueprint sets. It might make sense to use symlinks to reuse blueprint parts across sets. Also, Kirby is loaded twice to make this work.

Load single different blueprints per user (role)

When is this useful

This method is useful if Kirby's default permission rules work for most blueprints and there are only one or very few where you need more control.

How does it work

This implementation registers blueprints in a plugin, for example, a custom home.yml for clients, and a general home.yml for all other user roles.

/site/plugins/role-blueprints/index.php
<?php

if(($user = kirby()->user()) && $user->role()->name() === 'client') {
    $dir = __DIR__ . '/blueprints/pages/client/home.yml';
} else {
    $dir = __DIR__ . '/blueprints/pages/home.yml';
}

Kirby::plugin('cookbook/role-blueprints', [
    'blueprints' => [
        'pages/home' => $dir
    ]
]);

This approach works for all types of blueprints, so you could do this even for individual sections used in your blueprints if necessary.

Possible downsides

You have to maintain multiple blueprints for a page type.

Make sure to not override blueprints registered in the plugin by putting blueprints with the same name into the /site/blueprints/pages folder.

Override the isReadable() method in a page model

When is this useful

This method is useful if you want to limit read access to individual pages in the Panel that use the same blueprint.

Typical usecase: Editors should only be able to have access to a single parent page and the subpages they have created, not to siblings created by another user. So you are no longer limited to user roles.

How does it work

To make this work, the subpage blueprints needs to have a field that stores the current user when the page is created. In this example, we modify the author field in the /site/blueprints/pages/note.yml template in the Starterkit and disable it so that it cannot be changed.

author:
  type: users
  label: Author
  disabled: true

Now, whenever a user creates a new note page, the user is automatically stored.

The second step is the page model. Create a new file /site/models/note.php with the following code:

/site/models/note.php
<?php

use Kirby\Cms\Page;

class NotePage extends Page
{
  public function isReadable(): bool
  {
    if (($user = $this->author()->toUser()) && $user->is($this->kirby()->user()) || 
        $this->kirby()->user()->isAdmin()) {
      return true;
    }

    return false;
  }
}

In this model we check if the current user is stored in the author field or is an admin user. If the condition is true, they can access the page, otherwise they cannot.

Possible downsides

Since we cannot use a static variable like in the original method, this might have some performance implications.

Use the Bouncer plugin

When is this useful

This plugin is useful if you want to restrict access of a user role to a specific page or pages (and its children) in the Panel.

How does it work

Install the Bouncer plugin and follow the instructions in the documentation.

Possible downsides

See the plugin's disclaimer. Limited to user roles.