Skip to content

Permissions

Office App has a built-in role-based access control (RBAC) system. You can control access at the model level, action level, and field level.

Model-Level: Permissive vs. Restrictive

In meta(), set the default access policy:

1
2
3
4
5
6
public static function meta(): ?ModelMeta
{
    return (new ModelMeta('Tasks', 'check-square'))
        ->enable()
        ->permissive();    // everyone can do everything (default)
}
1
2
3
4
5
6
public static function meta(): ?ModelMeta
{
    return (new ModelMeta('Contracts', 'file-text'))
        ->enable()
        ->restrictive();   // deny everything, then grant explicitly
}
Policy Behavior
permissive() All authenticated users can view, edit, create, delete. Grant/deny rules narrow access.
restrictive() No access by default. You must explicitly grant permissions.

Granting Permissions in Hooks

Set up permissions in App/Hooks.php:

<?php

namespace App;

use System\HookContext;
use System\Permission;
use App\Model\Contracts\Contract;
use App\Model\Contracts\ContractType;
use App\Model\Contracts\ContractReminder;

class Hooks extends \System\Hooks
{
    public function bootstrap(): void
    {
        $this->setupPermissions();
        Permission::flush();
    }

    private function setupPermissions(): void
    {
        // Create groups and assign users
        Permission::createGroup('Editors');
        Permission::createGroup('Managers');

        Permission::addUserToGroup('editor', 'Editors');
        Permission::addUserToGroup('manager', 'Managers');

        // Revoke all existing rules (start fresh)
        Permission::revokeAll();

        // Editors: can view and edit contracts, view-only for types
        Permission::on(Contract::class)
            ->grant([Permission::VIEW, Permission::EDIT])
            ->to('Editors');

        Permission::on(ContractType::class)
            ->grant([Permission::VIEW])
            ->to('Editors');

        Permission::on(ContractReminder::class)
            ->grant(Permission::ALL_ACTIONS)
            ->to('Editors');

        // Managers: full access to everything
        Permission::on(Contract::class)
            ->grant(Permission::ALL_ACTIONS)
            ->to('Managers');

        Permission::on(ContractType::class)
            ->grant(Permission::ALL_ACTIONS)
            ->to('Managers');
    }
}

Available Permission Constants

Constant Allows
Permission::VIEW View records in list and detail
Permission::CREATE Create new records
Permission::EDIT Edit existing records
Permission::DELETE Soft-delete and hard-delete
Permission::ALL_ACTIONS All of the above

Field-Level Visibility

Control which fields specific users can see using beforeRender():

use System\Auth;

protected function beforeRender(): void
{
    // Only superadmins see the admin notes field
    $this->getProperty('admin_notes')?->setVisible(Auth::isSuperAdmin());

    // Only admins can edit the status field
    $this->getProperty('status')?->setReadonly(!Auth::isAdmin());
}

Action-Level Permissions

Control who can trigger specific actions:

public function getActions(): array
{
    return [
        ...parent::getActions(),

        (new Action('approve', 'Approve'))
            ->withIcon('check')
            ->showWhen(fn($m) => Auth::isAdmin())           // only admins see it
            ->withEnabled(fn($m) => $m->status === 'review'), // only in review state
    ];
}

Record-Level Permissions

Override can*() methods to control access per record:

// Only the creator can edit their own records
public function canSave(): bool
{
    if ($this->isNew()) return true;
    return $this->created_by === Auth::userId() || Auth::isAdmin();
}

// Only admins can permanently delete
public function canDelete(): bool
{
    return Auth::isAdmin();
}

// Disable checkout for archived records
public function canCheckout(): bool
{
    return $this->getState()?->name !== 'archived';
}

Custom Permission Checks

Use checkPermission() for model-wide access control:

1
2
3
4
public static function checkPermission(): bool
{
    return Auth::isSuperAdmin();   // entire model restricted to superadmins
}

User & Admin Checks

1
2
3
4
5
6
7
8
9
use System\Auth;
use System\Model\User;

Auth::userId()              // current user ID
Auth::user()                // current User object
Auth::isSuperAdmin()        // checks sys_user.is_superadmin flag
Auth::isAdmin()             // superadmin OR member of admin group
User::isSuperAdmin()        // instance method on User object
User::isAdmin()             // instance method on User object