RBAC Flow
How permissions are checked end-to-end.
Overview
mermaid
graph LR
subgraph "Backend (Source of Truth)"
ROLES[ROLE_PERMISSIONS<br/>permissions.py]
EXPAND[expand_permissions()]
end
subgraph "API Response"
ME[/me endpoint]
end
subgraph "Frontend"
PERMS[usePermissions()]
CAN[can() / canAny()]
UI[v-if gated UI]
NAV[Navigation]
end
ROLES --> EXPAND
EXPAND --> ME
ME --> PERMS
PERMS --> CAN
CAN --> UI
CAN --> NAVPermission Check Flow
mermaid
sequenceDiagram
participant EP as Endpoint
participant DEP as require_permission()
participant CTX as ClinicContext
participant PERM as permissions.py
EP->>DEP: require_permission("clinical.patients.read")
DEP->>CTX: Get user role
CTX-->>DEP: role = "dentist"
DEP->>PERM: has_permission("dentist", "clinical.patients.read")
PERM->>PERM: Get role permissions
Note over PERM: dentist has "clinical.*"
PERM->>PERM: permission_matches("clinical.patients.read", "clinical.*")
Note over PERM: "clinical.*" matches "clinical.patients.read" ✓
PERM-->>DEP: True
DEP-->>EP: AllowedWildcard Resolution
mermaid
graph TD
subgraph "Wildcards"
STAR["*"]
MOD["module.*"]
RES["module.resource.*"]
EXACT["module.resource.action"]
end
subgraph "Matches"
STAR --> |matches| ALL[Everything]
MOD --> |matches| M1["module.x"]
MOD --> |matches| M2["module.x.y"]
MOD --> |matches| M3["module.x.y.z"]
RES --> |matches| R1["module.resource.read"]
RES --> |matches| R2["module.resource.write"]
EXACT --> |matches| E1["module.resource.action only"]
endRole Hierarchy
mermaid
graph TB
ADMIN[admin<br/>"*"]
DENTIST[dentist<br/>"clinical.*", "odontogram.*", ...]
HYGIENIST[hygienist<br/>"clinical.patients.read", ...]
ASSISTANT[assistant<br/>"clinical.patients.*", ...]
RECEPTION[receptionist<br/>"clinical.patients.*", ...]
ADMIN --> |inherits all| DENTIST
DENTIST --> |more than| HYGIENIST
DENTIST --> |more than| ASSISTANT
ASSISTANT --> |similar to| RECEPTIONFrontend Permission Check
mermaid
sequenceDiagram
participant COMP as Component
participant USE as usePermissions()
participant STATE as Auth State
COMP->>USE: can("clinical.patients.write")
USE->>STATE: Get permissions[]
STATE-->>USE: ["clinical.patients.read", "clinical.patients.write", ...]
USE->>USE: permissions.includes("clinical.patients.write")
USE-->>COMP: true
COMP->>COMP: Render edit buttonPermission-gated UI
vue
<script setup lang="ts">
import { PERMISSIONS } from '~/config/permissions'
const { can } = usePermissions()
</script>
<template>
<UButton v-if="can(PERMISSIONS.patients.write)" @click="edit">Edit</UButton>
</template>mermaid
graph LR
REF[PERMISSIONS.patients.write] --> CHECK
CHECK["can('patients.write')"] --> RENDER
RENDER{has permission?}
RENDER --> |yes| SHOW[Render button]
RENDER --> |no| HIDE[Skip]