Skip to content

RBAC & permissions

Genie’s authorization is role-based and database-driven at runtime. Permissions, roles and role assignments live in tables managed through the built-in Auth management views — they are not authored in XML in the sample app. The server re-validates the caller’s verbs on every operation.

A user’s access to a resource is a set of verbs. The verbs are a [Flags] enum (PermissionVerb in Core/Persistence/Entities/Identity/Permission.cs):

Verb Grants
List View lists/tables (grid rows).
View View individual records.
Create Create new records.
Update Update existing records.
Delete Delete records.
Export Export data.
Import Import data.
Execute Execute actions / workflows / procedures.
Any All of the above (full access).

Because it’s a flags enum, a grant like View | Update is a single value. Any is the union.

Every protected resource has a ResourceCategory so the permission system can organise and reason about it: Dashboard, Report, Table, Form, Api, Page, External, Workflow, Configuration.

The runtime asks an IPermissionManager two questions:

  • HasAccessAsync(userId, resourceName, verb) — a yes/no gate before an operation (e.g. submit, delete, execute-row-action).
  • GetAccessVerbsAsync(userId, resourceName) — the full verb set for a resource, used to shape what the UI is told it can do (buttons, canSubmit) — always alongside a real gate on the actual operation.

For example, POST /object/submit computes the required verb from the request — Update when editing an existing record (a valid non-zero Id on a form that isn’t a Create/New/Add-style form), otherwise Create — and calls HasAccessAsync before doing anything. See The disclosure gate for the full endpoint→verb mapping.

Roles, permissions and assignments are administered through the engine’s built-in Auth management surface (AuthManagementController, base route /api/v1/genie/auth) and its hardcoded system views. This means access control is data you manage at runtime, not a redeploy.