Users and Roles

Accounts, roles, and the dot-notation permissions that guard routes and templates.

Users

A user is a folder at data/users/{username}/ with an index.json and a data.json, the same split as a page. The role and hashed password live in data.json.

phlat user:create adam --role=admin
phlat user:role adam editor
phlat user:password adam

A built-in guest user stands in for anyone not signed in.

Roles

A role is a folder with an index.json listing permissions. Site roles are checked before the framework's:

phlat/roles/admin/index.json    ← ships with the framework
phlat/roles/guest/index.json
site/roles/editor/index.json    ← yours
{
    "locked": false,
    "permissions": {
        "admin": { "access": true },
        "pages": { "edit": true, "publish": true }
    }
}

Permissions are nested and checked with dot notation: pages.publish looks up permissions.pages.publish. A * key at any level is a wildcard for everything not listed there; the admin role is { "*": true }. A missing permission is denied.

Checking permissions

requirePermission() guards a route. It redirects to login when nobody is signed in and throws ForbiddenException (403) when the user lacks the permission:

$page->router->post('publish', function () use ($app, $page) {
    $app->session->requirePermission('pages.publish');
    // ...
});

requireLogin() accepts any signed-in user. can() checks without stopping the request:

if ($app->session->can('pages.edit')) { /* ... */ }

In Latte:

{if permission('pages.edit')}
    <a href="{$page->url()}/edit">Edit</a>
{/if}

Put checks in a before() hook to guard a whole section (see Routing). To restrict a page and everything under it to signed-in users or specific roles, set its access instead. See Status and access.

Profile fields

Extra user fields are declared in roles/index.json under fields, like a view declares page fields. Each name refers to a global field in site/fields/, and values read like page fields.

{
    "fields": {
        "password": { "required": true },
        "avatar": {},
        "jobTitle": {}
    }
}

Rate limiting

$app->session->isThrottled($key, $max, $window) and throttle($key, $window) count attempts per key. Login uses them to slow repeated failures; use them for any form.