Skip to content

defineRoles

Declares roles for a module. Roles are registered in-memory via PermissionRegistry at boot and used for permission enforcement.

See Permissions concept for how permissions are resolved and enforced.

Signature

typescript
import { defineRoles } from 'rangka';

export default defineRoles({
  sales_user: {
    label: 'Sales User',
    models: {
      'sales.customer': { read: true, create: true, write: true },
      'sales.order': { read: true, create: true, write: true },
    },
    pages: ['sales.orders', 'sales.customers', 'sales.dashboard'],
  },

  sales_manager: {
    label: 'Sales Manager',
    extends: 'sales_user',
    models: {
      'sales.order': { delete: true },
      'sales.customer': { delete: true },
    },
    pages: ['sales.reports', 'sales.settings'],
  },
});

File Location

modules/{module}/roles.ts

One file per module. Each module declares its own roles.

RoleDefinition

typescript
interface RoleDefinition {
  label: string;
  extends?: string;
  models?: Record<string, ModelPermissions>;
  pages?: string[];
}
FieldTypeRequiredDescription
labelstringYesHuman-readable display name.
extendsstringNoName of another role to inherit from. Permissions merge permissively.
modelsRecord<string, ModelPermissions>NoModel-level CRUD permissions.
pagesstring[]NoPage keys this role can access.

ModelPermissions

typescript
interface ModelPermissions {
  read?: boolean | 'own';
  create?: boolean;
  write?: boolean | 'own';
  delete?: boolean | 'own';
  fieldPermissions?: Record<string, { read?: boolean; write?: boolean }>;
}
FieldTypeDefaultDescription
readboolean | 'own'falseCan view records. 'own' restricts to records the user created.
createbooleanfalseCan create new records.
writeboolean | 'own'falseCan update records. 'own' restricts to own records.
deleteboolean | 'own'falseCan delete records. 'own' restricts to own records.
fieldPermissionsRecord<string, ...>undefinedPer-field read/write overrides within this model.

Unspecified permissions default to false.

FieldPermissions

Field-level overrides are declared inline in ModelPermissions.fieldPermissions:

typescript
models: {
  'sales.order': {
    read: true,
    write: true,
    fieldPermissions: {
      cost_price: { read: false },
      discount_percent: { read: true, write: false },
    },
  },
}

Inheritance

typescript
sales_manager: {
  label: 'Sales Manager',
  extends: 'sales_user',
  models: { ... },
}

When a role extends another:

  • All model permissions from the parent are inherited
  • All page permissions from the parent are inherited
  • Permissions declared on the child are merged on top (most permissive wins)
  • Multi-level inheritance is supported (controller extends manager extends user)
  • Circular inheritance is detected and raises an error at boot

The extends target must be a role defined in the same module or a previously registered module (respects module dependency order).

Admin operations

Planned — not yet implemented.

Example: Cross-Module Roles

A role can reference models from any installed module:

typescript
// modules/core/roles.ts
export default defineRoles({
  system_admin: {
    label: 'System Admin',
    models: {
      'core.user': { read: true, create: true, write: true, delete: true },
      'core.role': { read: true, create: true, write: true, delete: true },
    },
    pages: ['core.users', 'core.roles', 'core.settings'],
  },
});
typescript
// modules/sales/roles.ts
export default defineRoles({
  sales_user: {
    label: 'Sales User',
    models: {
      'sales.order': { read: true, create: true, write: true },
      'core.user': { read: true }, // can see users (for assignment fields)
    },
    pages: ['sales.orders'],
  },
});

A user assigned both system_admin and sales_user gets the union of all permissions.