Skip to content

App Permissions Module

Overview

The App Permissions system controls access to specific application features (modules) beyond standard data CRUD permissions. While odp_permissions controls which collections/fields a user can read/write, App Permissions control things like: can this user access the MCP interface? Can they view the workflow module? Can they export data?

Permissions are granted per policy (which maps to roles via odp_access). Each permission record specifies:

  • Which module (e.g., mcp, workflow, data_export)
  • Which action (e.g., access, execute, read)
  • Optional collection scope (restrict to a specific collection)

Key Concepts

Modules

Modules are registered in appModuleRegistry. Each module defines:

  • A module ID and name
  • A set of allowed actions
  • Whether actions support collection-level scoping

Policies

App permissions link to odp_policies, not directly to roles. A role can have multiple policies via odp_access. Effective permissions = union of all permissions across all policies linked to the user's roles.


Endpoints

All endpoints require Admin access unless noted.

GET /app-permissions/modules

List all registered app modules.

Response:

json
{
  "data": [
    {
      "id": "mcp",
      "name": "MCP Server",
      "actions": [
        { "id": "access", "name": "Access", "collectionScopeSupport": false }
      ]
    },
    {
      "id": "workflow",
      "name": "Workflow",
      "actions": [
        { "id": "access", "name": "Access", "collectionScopeSupport": false },
        { "id": "manage", "name": "Manage", "collectionScopeSupport": true }
      ]
    }
  ]
}

GET /app-permissions/modules/:moduleId

Get a specific module definition.

URL Parameters:

  • moduleId — Module identifier

Response: Single module object (same structure as list item).


GET /app-permissions

List all app permission records.

Query Parameters:

ParameterDescription
policyFilter by policy UUID
moduleFilter by module ID
actionFilter by action ID
limitPage size (default: 100)
offsetPage offset (default: 0)

Response:

json
{
  "data": [
    {
      "id": "perm-uuid",
      "policy": "policy-uuid",
      "module": "mcp",
      "action": "access",
      "collectionScope": "__global__",
      "createdAt": "2024-01-01T00:00:00.000Z",
      "createdBy": "user-uuid"
    }
  ]
}

POST /app-permissions

Create a new app permission.

Request Body:

json
{
  "policy": "policy-uuid",
  "module": "workflow",
  "action": "manage",
  "collection_scope": "my_collection"
}
FieldTypeRequiredDescription
policyUUIDYesPolicy to grant permission to
modulestringYesRegistered module ID
actionstringYesAction within the module, or * for all
collection_scopestringNo__global__ (default) or collection name

Validation:

  • Module must be registered in appModuleRegistry
  • Action must exist in the module (unless *)
  • collection_scope only allowed if action has collectionScopeSupport = true
  • Policy must exist in odp_policies
  • Unique constraint: (policy, module, action, collection_scope)

Response: 201 Created

json
{
  "data": {
    "id": "new-perm-uuid",
    "policy": "policy-uuid",
    "module": "workflow",
    "action": "manage",
    "collectionScope": "my_collection"
  }
}

DELETE /app-permissions/:id

Delete an app permission record.

URL Parameters:

  • id — Permission UUID

Response: 204 No Content

Side Effect: Invalidates app permission cache for all users linked to the permission's policy.


GET /app-permissions/user/:userId/effective

Get the effective (resolved) app permissions for a specific user.

Auth required: Admin

URL Parameters:

  • userId — User UUID

Response:

json
{
  "data": {
    "mcp": {
      "access": true
    },
    "workflow": {
      "access": true,
      "manage": ["my_collection", "__global__"]
    }
  }
}

Business Logic:

  1. Fetches all policies linked to the user via roles → odp_access
  2. Unions all odp_app_permissions records across those policies
  3. Returns a map of { module: { action: true | string[] } }

GET /app-permissions/me/effective

Get effective app permissions for the current authenticated user.

Auth required: Yes

Response: Same structure as /user/:userId/effective.


Audit Logs

GET /app-access-logs

Query app permission access log entries.

Auth required: Admin

Query Parameters:

ParameterDescription
user_idFilter by user ID
moduleFilter by module
actionFilter by action
resultpass or fail
fromStart datetime (ISO 8601)
toEnd datetime (ISO 8601)
limitPage size
offsetPage offset

Response:

json
{
  "data": [
    {
      "id": "log-uuid",
      "user_id": "user-uuid",
      "initiated_by": "user-uuid",
      "module": "mcp",
      "action": "access",
      "collection_scope": null,
      "result": "pass",
      "policy_id": "policy-uuid",
      "fail_reason": null,
      "ip": "192.168.1.1",
      "user_agent": "Mozilla/5.0...",
      "created_at": "2024-01-01T12:00:00.000Z"
    }
  ]
}

Data Model

odp_app_permissions

ColumnTypeDescription
idUUID PK
policyUUID FK → odp_policiesPolicy granting permission (CASCADE delete)
moduletextModule identifier
actiontextAction identifier (or *)
collection_scopetext__global__ or collection name
created_attimestamp
created_byUUID FK → odp_usersAdmin who created the permission

Unique constraint: (policy, module, action, collection_scope)

Indexes:

  • idx_app_perm_policy on policy
  • idx_app_perm_module_action on (module, action)

odp_app_access_logs

ColumnTypeDescription
idUUID PK
user_idtextUser making the request
initiated_bytextActual user (differs if impersonating)
moduletextModule being accessed
actiontextAction being performed
collection_scopetextCollection scope (if applicable)
resulttextpass or fail
policy_idtextPolicy that granted access (on pass)
fail_reasontextReason for failure (on fail)
iptextClient IP
user_agenttextBrowser/client
created_attimestamp

Indexes:

  • idx_app_log_user on user_id
  • idx_app_log_created on created_at
  • idx_app_log_module_action on (module, action)
  • idx_app_log_result on result

Permission Validation Flow

ODP Internal API Documentation