OutlabsAuth
Auth

Roles & Permissions

Permission catalog and role definitions.
Catalog and role definitions (not who has them).

Mental model

LayerWhat it isWhere
CatalogPermission names (resource:action)/v1/permissions
DefinitionsRoles + which permissions they carry/v1/roles
Direct assignUser ↔ role/v1/users/{id}/roles23
Entity assignUser ↔ entity + roles/v1/memberships54

This page is catalog + definitions only.

from outlabs_auth.routers import get_roles_router, get_permissions_router

app.include_router(get_roles_router(auth, prefix="/v1/roles"))
app.include_router(get_permissions_router(auth, prefix="/v1/permissions"))

Permissions catalog (/v1/permissions)

MethodPathPermissionNotes
GET/permission:readPaginated. Query: page, limit, resource
POST/permission:createPermissionCreateRequestname must be resource:action (409 on duplicate)
GET/{permission_id}permission:readOne permission
PATCH/{permission_id}permission:updateDisplay/status/tags — not rename
DELETE/{permission_id}permission:deleteSystem permissions blocked in service
GET/meAuthenticatedCurrent user’s permission names (list[str])
GET/user/{user_id}permission:readAnother user’s permission names
POST/checkpermission:checkBatch check; optional entity_id for entity/tree context

Schemas: PermissionResponse, PermissionCreateRequest, PermissionUpdateRequest, PermissionCheckRequest / PermissionCheckResponse (has_all_permissions, results map).

/me and /user/{id} accept entity_id in query today but the service path may not apply it. For “can they do X here?”, prefer POST /check with entity_id.

For effective permissions with sources (role vs membership), use GET /v1/users/{id}/permissions on the users router.

ABAC condition groups/conditions also hang off /{permission_id}/… when you use ABAC — same pattern as roles below.


Roles (/v1/roles)

CRUD and permission sets

MethodPathPermissionNotes
GET/role:readPaginated. Query: page, limit, search, is_global, root_entity_id
POST/role:createRoleCreateRequest. Actor must already hold every permission they attach
GET/{role_id}role:readOne role
PATCH/{role_id}role:updateRoleUpdateRequest; permissions replaces the set when sent
DELETE/{role_id}role:delete204
POST/{role_id}/permissionsrole:updateBody: list[str] permission names to add
DELETE/{role_id}/permissionsrole:updateBody: list[str] to remove

Enterprise helper

MethodPathPermissionNotes
GET/entity/{entity_id}Tree role:readRoles available for that entity (pickers / memberships UI)

Role response fields worth knowing

RoleResponse includes: name, display_name, permissions[], is_system_role, is_global, status, optional root_entity_id / scope_entity_id, scope (entity_only | hierarchy), assignable_at_types[], is_auto_assigned.

Enterprise tips:

  • Non-global actors only manage roles in their trees; system-wide roles they cannot touch → 403; out-of-tree → 404
  • SimpleRBAC treats list/create visibility as effectively global
  • assignable_at_types limits which entity types may receive the role
  • is_auto_assigned can auto-apply on membership create/update

ABAC on roles (when enabled)

Under /{role_id}/condition-groups and /{role_id}/conditions — CRUD with role:read / role:update. Walkthrough: ABAC.


Simple vs Enterprise

ConcernSimpleRBACEnterpriseRBAC
Role visibilitySystem-wideScoped to actor’s entity access
GET /roles/entity/{id}Rarely usefulPrimary picker for memberships
Tree permissionsUnusedresource:action_tree + /permissions/check?entity_id=
Assign pathUsers router direct rolesPrefer memberships; direct assign for org-wide roles

Suggested host flow

  1. Seed or create permissions (resource:action).
  2. Create roles and attach permission lists (delegation check applies).
  3. Simple: POST /v1/users/{id}/roles.
    Enterprise: POST /v1/memberships/ with role_ids; use GET /v1/roles/entity/{entity_id} in the UI.
  4. Gate product UI with POST /v1/permissions/check (and entity_id when needed).
  5. Optional attribute conditions: ABAC.