Skip to main content

Configure Roles and Permissions

CRAFT uses OpenFGA for Relationship-Based Access Control (ReBAC). This guide covers setting up roles, assigning permissions, and configuring the permission inheritance model across organizations, projects, and resources.

Prerequisites

Before you begin, ensure you have:
  • A running CRAFT with the Governance service bootstrapped
  • Administrative access (the admin or owner role on the target organization)
  • A valid JWT token for the admin user
  • The OpenFGA schema loaded during Governance bootstrap

How Permissions Work

The platform uses a relationship-based model rather than traditional role-based access control. Permissions are computed at query time from relationships between users, roles, and resources.
User --[role]--> Organization --[parent]--> Project --[parent]--> Resource

Role Hierarchy

RoleScopeCapabilities
ownerOrganizationFull control, can transfer ownership, manage all settings
adminOrganization / ProjectManage users, projects, settings. Cannot transfer organization ownership
memberProjectStandard access to project resources
developerProjectCreate and modify agents, data connections, workflows
operatorProjectDeploy, schedule, and monitor resources
viewerProjectRead-only access to all resources

Computed Permissions

Permissions are derived from roles via the OpenFGA schema:
Permissionowneradminmemberdeveloperoperatorviewer
can_readYesYesYesYesYesYes
can_writeYesYesYesYesNoNo
can_deleteYesYesNoNoNoNo
can_executeYesYesYesYesYesNo
can_manage_secretsYesYesNoNoNoNo

Step 1: Understand the OpenFGA Schema

The permission model is defined in the OpenFGA Schema DSL. Key type definitions used by the platform:
type organization
  relations
    define owner: [user]
    define admin: [user] or owner
    define member: [user] or admin

type project
  relations
    define parent: [organization]
    define admin: [user] or admin from parent
    define developer: [user] or admin
    define operator: [user] or admin
    define viewer: [user] or developer or operator
    define can_read: viewer or member from parent
    define can_write: developer or admin
    define can_delete: admin
    define can_execute: operator or developer or admin

Step 2: Grant a role on a resource

The Governance API exposes a single grant endpoint that is used for every role-on-resource combination — there is no separate “add organization member” or “add project member” endpoint. A “role assignment” is a tuple (user_or_group, relation, resource_type:resource_id) written to the OpenFGA store via POST /governance/permissions/grant.
# Grant the 'admin' relation to a user on an organization
curl -X POST "https://<platform-host>/api/governance/permissions/grant" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Project-ID: <project-id>" \
  -H "Content-Type: application/json" \
  -d '{
    "user_or_group": "<user-id-or-email>",
    "relation": "admin",
    "resource_type": "organization",
    "resource_id": "<org-id>"
  }'
Response (200 OK):
{ "message": "Granted admin permission to user '<user-id>' on organization '<org-id>'" }
The same endpoint also assigns project-scoped roles — change resource_type to project and resource_id to the project UUID:
curl -X POST "https://<platform-host>/api/governance/permissions/grant" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Project-ID: <project-id>" \
  -H "Content-Type: application/json" \
  -d '{
    "user_or_group": "<user-id-or-email>",
    "relation": "developer",
    "resource_type": "project",
    "resource_id": "<project-id>"
  }'
Same shape for any resource type: artifact, data_connection, secret, etc. The relation must be one of the role names defined on that resource type in the OpenFGA schema (Step 1).

Permission Inheritance

Roles inherit downward through the hierarchy because the OpenFGA schema expresses each role on a child as derivable from its parent:
1

Organization owner

Automatically has admin-level permissions on all projects and resources within the organization.
2

Organization admin

Automatically has admin-level permissions on all projects. Can create and delete projects.
3

Project developer

Has can_read, can_write, and can_execute on all resources within the specific project. Cannot delete resources or manage secrets.
4

Project viewer

Has can_read only. Cannot modify, execute, or delete any resource.
When you create a child resource (e.g. an artifact within a project), register the parent relationship so inheritance applies:
curl -X POST "https://<platform-host>/api/governance/permissions/set-parent" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Project-ID: <project-id>" \
  -H "Content-Type: application/json" \
  -d '{
    "resource_type": "artifact",
    "resource_id": "<artifact-uri>",
    "parent_type": "project",
    "parent_id": "<project-id>"
  }'

Step 3: Check or list permissions

Two read endpoints answer the common questions:
# 1) Can <current user> perform <action> on <resource>?
#    Note: this is a GET with query params — NOT a POST with a body.
curl -G "https://<platform-host>/api/governance/permissions/check" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Project-ID: <project-id>" \
  --data-urlencode "action=can_write" \
  --data-urlencode "resource_type=agent" \
  --data-urlencode "resource_id=<agent-id>"
Response (200 OK): the response body for an allowed call is null (absence of a 403 body means the action is permitted). A denied check returns 403 Forbidden.
# 2) Which objects can <current user> read across every resource type?
curl "https://<platform-host>/api/governance/permissions/accessible-objects" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Project-ID: <project-id>"
Response (200 OK):
{
  "object_ids": [
    "project:<project-id>",
    "data_connection:data:<org-id>:<project-id>:<connection-name>",
    "agent:agent:<org-id>:<project-id>:<agent-name>"
  ]
}
Each entry is <resource_type>:<resource_id>, with resource_id echoing the canonical resource_uri for agent / data_connection / artifact resources.

Step 4: Revoke a role

POST /governance/permissions/revoke mirrors grant:
curl -X POST "https://<platform-host>/api/governance/permissions/revoke" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Project-ID: <project-id>" \
  -H "Content-Type: application/json" \
  -d '{
    "user_or_group": "<user-id-or-email>",
    "relation": "developer",
    "resource_type": "project",
    "resource_id": "<project-id>"
  }'
Response (200 OK):
{ "message": "Revoked developer permission from user '<user-id>' on project '<project-id>'" }
To delete every permission tuple for a resource (e.g. before deleting the resource itself), use POST /governance/permissions/delete-all:
curl -X POST "https://<platform-host>/api/governance/permissions/delete-all" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Project-ID: <project-id>" \
  -H "Content-Type: application/json" \
  -d '{ "resource_type": "project", "resource_id": "<project-id>" }'
Response includes deleted_count so you can confirm the sweep.

Common Permission Patterns

Assign viewer on the project containing data connections and dashboards. Users can run queries via Data Insights but cannot modify data connections or agent configurations.
Assign operator on the target project. Operators can deploy agents, trigger schedules, and monitor health, but cannot modify agent code or data connection credentials.
Create Keycloak client credentials and assign developer or operator on the target project. Service accounts authenticate via client credentials grant and have the same permission model as human users.
A user can have different roles on different projects within the same organization. Assign roles per project to implement least-privilege access.

Integration with SSO Groups

When SSO is configured, IdP groups map through Keycloak to OpenFGA:
IdP Group -> Keycloak Group -> OpenFGA Relation (role assignment)
This pipeline automates role assignment when users are provisioned via SSO. See the SSO Integration guide for configuring the IdP side.

Troubleshooting

Verify the OpenFGA schema is loaded by checking Governance bootstrap logs. Ensure the user’s JWT contains the correct groups claim. Confirm the user has been assigned a role on the target project.
Check that the project has a parent relationship to the organization in OpenFGA. This is set automatically when projects are created via the Governance API.
OpenFGA evaluates permissions at query time — there is no caching delay. If changes are not reflected, verify the role assignment was successful by listing members on the project.

Next Steps

SSO Integration

Configure SSO to automate group-to-role mapping from your IdP.

Authorization Deep Dive

Learn about the full OpenFGA schema and computed permissions model.

Multi-Tenancy

Understand how permissions integrate with the multi-tenant architecture.

Security Overview

Review the complete security model including authentication and data protection.