Permission System Architecture
Overview
The permission system enables gateway modules to describe their authorization requirements and exposes REST endpoints for reading and updating user permissions. This document explains the format, registry, backend endpoints, and frontend integration.
Permission Format
- Pattern:
{module}:[{resource-path}]:{action} - Resource Path:
{resource-type}:{resource-id|*}(/{resource-type}:{resource-id|*})* - Module Name: Lowercase alphanumeric with hyphens (
[a-z0-9-]+) - Action: Lowercase alphanumeric with hyphens
- Examples:
user-containers:[user-container:*]:createuser-containers:[user-container:{uuid}]:deleteuser-containers:[user-container:{uuid}/service:*]:readgateway:[permissions:*]:write
Characteristics:
- Modules define permissions using * for resource IDs to describe capabilities.
- When granting to users, permissions can remain wildcarded or be specialized with specific IDs.
- Format supports hierarchical trees of resources separated by /.
Permission Registry
Purpose
- Allows gateway modules to register their permission definitions during module loading.
- Produces a compiled catalog that can be exposed via REST APIs.
- Ensures consistent metadata (resource path, human-readable description).
Implementation
- Located at
packages/modules/gateway/src/lib/permission-registry.ts. - Maintains an in-memory Map keyed by the full permission string.
- Validates format using the pattern above.
- Provides accessors:
getAll()— All registered permissions (used by/permissionsendpoint).getByModule(module)— Filtered view for module-specific tooling.get(permission)— Lookup by full string.
Usage by Modules
- Each gateway module receives
depsExports.gateway.permissionRegistry. - During
load(), modules register permissions describing their features. - Example metadata per permission:
resourcePath— e.g.,user-container:*action— e.g.,createdescription— Human-friendly label shown in UIs
Gateway API Endpoints
1. GET /permissions
- Purpose: List all compiled permission definitions.
- Auth:
gateway:[permissions:*]:read - Response:
{ permissions: PermissionDefinition[] } - Use Case: Populate dropdowns or documentation of available permissions.
2. GET /permissions/projects/{project_id}
- Purpose: Retrieve user permissions for a specific project.
- Auth:
gateway:[permissions:*]:read+ project access check. - Response:
{ permissions: { [user_id: string]: string[] } } - Use Case: Display collaborator permissions in the frontend.
3. PATCH /permissions/projects/{project_id}/users/{user_id}
- Purpose: Update permissions assigned to a user within a project.
- Auth:
gateway:[permissions:*]:write+ project access check. - Body:
{ permissions: string[] } - Response:
{ success: true } - Use Case: Grant/revoke permissions from the frontend.
All routes are defined in packages/app-gateway/src/routes/permissions.ts and documented in packages/app-gateway/src/oas30.json.
Frontend Integration
Hooks (from frontend-data)
useQueryScope(organization_id)— Loads all permissions via gateway.useQueryProjectUsersScopes(organization_id, project_id)— Retrieves assignments per user.useCollaborators(project_id)— Combines Ganymede project members with gateway permissions.useMutationUserScope(project_id)— Updates user permissions and invalidates caches.
Workflow
- OrganizationContext ensures gateway hostname is set for
fetchGateway(). - Hooks call Gateway endpoints via
ganymedeApi.fetchGateway. - UI components render lists of permissions and allow modifications.
- Mutations invalidate relevant React Query keys to keep UI consistent.
Availability Requirements
- Gateway must be running (OrganizationContext handles availability states).
- User must have
gateway:[permissions:*]:readto view,...:writeto modify.
Best Practices
-
Module Authors:
- Register permissions during module load to ensure they appear in/permissions.
- Provide descriptive metadata to help frontend users understand capabilities.
- Use hierarchical resource paths to enable fine-grained future grants. -
Frontend Developers:
- Treat permissions as opaque strings; rely on metadata for labels.
- Use provided hooks instead of manual fetches to benefit from caching.
- Handle loading and unavailable states when gateway is down. -
Backend Developers:
- UsePermissionRegistryfor any new module.
- Protect routes withrequirePermissionmiddleware referencing the same strings.
- Keep OpenAPI specs aligned with endpoint behavior for autogenerated docs.
Related Documents
doc/architecture/FRONTEND_ARCHITECTURE.mdpackages/modules/gateway/src/lib/permission-registry.tspackages/app-gateway/src/routes/permissions.tspackages/frontend-data/src/lib/queries.ts