Query relationships in SpiceDB/Kessel via the RBAC relations API
Use this skill to query relationships stored in SpiceDB/Kessel via the RBAC internal relations API.
Before running queries, you MUST:
Check for config.env file: Verify that .cursor/skills/config.env exists. If it does not exist:
.cursor/skills/config.env with STAGE_DOMAIN, PROD_DOMAIN, PROXYconfig.env is missing or incomplete. Always ensure config.env exists before proceeding.Check if SESSION is set: If the SESSION environment variable is not set, ask the user to get the token from the Turnpike Session by pasting the URL into their browser:
${STAGE_DOMAIN}/api/turnpike/session/ (domain from .cursor/skills/config.env)${PROD_DOMAIN}/api/turnpike/session/ (domain from ).cursor/skills/config.envexport SESSION=<token_value>Confirm the environment: If the environment (stage or prod) is not explicitly specified by the user, ask them to confirm which environment they want to query.
You can obtain the SESSION value using one of these methods:
Get the session token by pasting the URL into your browser:
For stage environment:
${STAGE_DOMAIN}/api/turnpike/session/ (where STAGE_DOMAIN is from .cursor/skills/config.env, default: https://internal.console.stage.redhat.com)export SESSION=<token_from_response>For prod environment:
${PROD_DOMAIN}/api/turnpike/session/ (where PROD_DOMAIN is from .cursor/skills/config.env, default: https://internal.console.redhat.com)export SESSION=<token_from_response>Alternatively, the session cookie can be obtained from browser cookies when logged into console.stage.redhat.com or console.redhat.com:
session cookieexport SESSION=<session_cookie_value>Run the script at .cursor/skills/relationship/scripts/relationship.sh:
sh .cursor/skills/relationship/scripts/relationship.sh <stage|prod> <read_tuples|lookup_resource> '<JSON_PAYLOAD>'
Important: Always verify SESSION is set and environment is confirmed before executing queries.
When a user requests a relationship query:
Check for config.env: Verify .cursor/skills/config.env exists
config.env is required and provide instructions to create it from the example fileconfig.env existsCheck SESSION: Verify if SESSION environment variable is set
${STAGE_DOMAIN}/api/turnpike/session/ (domain from .cursor/skills/config.env)${PROD_DOMAIN}/api/turnpike/session/ (domain from .cursor/skills/config.env)export SESSION=<token_value>Confirm Environment: Determine which environment to query
Execute Query: Only after config.env exists, SESSION is set, and environment is confirmed, run the relationship query script
Find all principals that are members of a group:
sh .cursor/skills/relationship/scripts/relationship.sh stage read_tuples '{
"filter": {
"resource_namespace": "rbac",
"resource_type": "group",
"resource_id": "<group_uuid>",
"relation": "member",
"subject_filter": {
"subject_namespace": "rbac",
"subject_type": "principal",
"subject_id": ""
}
}
}'
Verify if a specific user/principal is a member of a group:
sh .cursor/skills/relationship/scripts/relationship.sh stage read_tuples '{
"filter": {
"resource_namespace": "rbac",
"resource_type": "group",
"resource_id": "<group_uuid>",
"relation": "member",
"subject_filter": {
"subject_namespace": "rbac",
"subject_type": "principal",
"subject_id": "redhat/<user_id>"
}
}
}'
Note: Use redhat/<user_id> format for both stage and prod environments. See Principal Resource ID Format section for details.
Find all role bindings where a specific group is bound as a subject:
sh .cursor/skills/relationship/scripts/relationship.sh stage read_tuples '{
"filter": {
"resource_namespace": "rbac",
"resource_type": "role_binding",
"resource_id": "",
"relation": "subject",
"subject_filter": {
"subject_namespace": "rbac",
"subject_type": "group",
"subject_id": "<group_uuid>"
}
}
}'
Alternative method to find role bindings for a group:
sh .cursor/skills/relationship/scripts/relationship.sh stage lookup_resource '{
"resource_type": {
"name": "role_binding",
"namespace": "rbac"
},
"subject": {
"subject": {
"type": {
"name": "group",
"namespace": "rbac"
},
"id": "<group_uuid>"
}
},
"relation": "subject"
}'
Find all relationships for a specific role binding:
sh .cursor/skills/relationship/scripts/relationship.sh stage read_tuples '{
"filter": {
"resource_namespace": "rbac",
"resource_type": "role_binding",
"resource_id": "<role_binding_uuid>",
"relation": "",
"subject_filter": {
"subject_namespace": "rbac",
"subject_type": "group",
"subject_id": ""
}
}
}'
Find the role associated with a specific role binding:
sh .cursor/skills/relationship/scripts/relationship.sh stage read_tuples '{
"filter": {
"resource_namespace": "rbac",
"resource_type": "role_binding",
"resource_id": "<role_binding_uuid>",
"relation": "role",
"subject_filter": {
"subject_namespace": "rbac",
"subject_type": "role",
"subject_id": ""
}
}
}'
Find all groups where a specific principal is a member:
sh .cursor/skills/relationship/scripts/relationship.sh stage read_tuples '{
"filter": {
"resource_namespace": "rbac",
"resource_type": "group",
"resource_id": "",
"relation": "member",
"subject_filter": {
"subject_namespace": "rbac",
"subject_type": "principal",
"subject_id": "redhat/<user_id>"
}
}
}'
Note: Replace <user_id> with the numeric user ID. Use redhat/<user_id> format for both stage and prod environments. See Principal Resource ID Format section for details.
Check if a specific group is bound as a subject to a role binding:
sh .cursor/skills/relationship/scripts/relationship.sh stage read_tuples '{
"filter": {
"resource_namespace": "rbac",
"resource_type": "role_binding",
"resource_id": "<role_binding_uuid>",
"relation": "subject",
"subject_filter": {
"subject_namespace": "rbac",
"subject_type": "group",
"subject_id": "<group_uuid>"
}
}
}'
Find what role is assigned to a specific role binding:
sh .cursor/skills/relationship/scripts/relationship.sh stage read_tuples '{
"filter": {
"resource_namespace": "rbac",
"resource_type": "role_binding",
"resource_id": "<role_binding_uuid>",
"relation": "role",
"subject_filter": {
"subject_namespace": "rbac",
"subject_type": "role",
"subject_id": ""
}
}
}'
Verify the complete access chain: User → Group → Role Binding → Role → Workspace
Step 1: Verify user is member of group
sh .cursor/skills/relationship/scripts/relationship.sh stage read_tuples '{
"filter": {
"resource_namespace": "rbac",
"resource_type": "group",
"resource_id": "<group_uuid>",
"relation": "member",
"subject_filter": {
"subject_namespace": "rbac",
"subject_type": "principal",
"subject_id": "redhat/<user_id>"
}
}
}'
Step 2: Verify group is subject of role binding
sh .cursor/skills/relationship/scripts/relationship.sh stage read_tuples '{
"filter": {
"resource_namespace": "rbac",
"resource_type": "role_binding",
"resource_id": "<role_binding_uuid>",
"relation": "subject",
"subject_filter": {
"subject_namespace": "rbac",
"subject_type": "group",
"subject_id": "<group_uuid>"
}
}
}'
Step 3: Verify role binding has role
sh .cursor/skills/relationship/scripts/relationship.sh stage read_tuples '{
"filter": {
"resource_namespace": "rbac",
"resource_type": "role_binding",
"resource_id": "<role_binding_uuid>",
"relation": "role",
"subject_filter": {
"subject_namespace": "rbac",
"subject_type": "role",
"subject_id": ""
}
}
}'
Step 4: Check role binding resource (workspace) via database Use gabi skill to verify which workspace the role binding is bound to:
SELECT rb.uuid, rb.resource_type, rb.resource_id, w.name as workspace_name
FROM management_rolebinding rb
LEFT JOIN management_workspace w ON rb.resource_id = w.id::text
WHERE rb.uuid = '<role_binding_uuid>' AND rb.resource_type = 'workspace';
When querying for principals (users), use the following format:
redhat/<user_id> (same for both stage and prod environments)
58872914, use redhat/58872914redhat/<service_account_id> format<domain>/<user_id> where domain comes from PRINCIPAL_USER_DOMAIN setting (typically redhat for both environments)To find a user's user_id:
SELECT user_id FROM management_principal WHERE username = '<username>';To determine the correct format for your environment:
PRINCIPAL_USER_DOMAIN setting in the application configuration<domain>/<user_id> where domain comes from PRINCIPAL_USER_DOMAIN"tuples": [] or "resources": []): No relationships found matching the queryread_tuples with relation: "member" on a group resourceread_tuples or lookup_resource with relation: "subject" on role_binding resourceCommon resource types in the rbac namespace:
role_binding - Binds roles to subjects (groups/principals)group - Groups of principalsrole - Role definitionsworkspace - Workspace resourcesprincipal - Individual users/service accountsCommon relations:
subject - Subject of a role binding (group or principal)member - Member of a grouprole - Role assigned to a bindingparent - Parent workspace relationshipuser_grant - User grant on a resource (workspace, etc.)This workflow verifies if a user has access to a specific workspace by checking the complete access chain in SpiceDB relationships.
Get user_id: Find the user's user_id from the database:
SELECT user_id, username FROM management_principal WHERE username = '<username>';
Or use gabi skill to find principals in a group.
Get workspace_id: The workspace UUID you want to check access for.
sh .cursor/skills/relationship/scripts/relationship.sh stage read_tuples '{
"filter": {
"resource_namespace": "rbac",
"resource_type": "group",
"resource_id": "",
"relation": "member",
"subject_filter": {
"subject_namespace": "rbac",
"subject_type": "principal",
"subject_id": "redhat/<user_id>"
}
}
}'
Expected result: List of groups where the user is a member. Note the group UUIDs.
For each group UUID found in Step 1:
sh .cursor/skills/relationship/scripts/relationship.sh stage read_tuples '{
"filter": {
"resource_namespace": "rbac",
"resource_type": "role_binding",
"resource_id": "",
"relation": "subject",
"subject_filter": {
"subject_namespace": "rbac",
"subject_type": "group",
"subject_id": "<group_uuid>"
}
}
}'
Expected result: List of role bindings where the group is a subject. Note the role binding UUIDs.
For each role binding UUID found in Step 2, query the database to find the workspace:
SELECT rb.uuid as role_binding_uuid, rb.resource_type, rb.resource_id as workspace_id, w.name as workspace_name, rv2.uuid as role_uuid, rv2.name as role_name
FROM management_rolebinding rb
LEFT JOIN management_workspace w ON rb.resource_id = w.id::text
LEFT JOIN management_rolev2 rv2 ON rb.role_id = rv2.id
WHERE rb.uuid = '<role_binding_uuid>' AND rb.resource_type = 'workspace';
Expected result: Workspace information for each role binding. Check if any workspace_id matches your target workspace.
For the role binding that matches your target workspace, verify each link in the chain:
4a. Verify user is member of group:
sh .cursor/skills/relationship/scripts/relationship.sh stage read_tuples '{
"filter": {
"resource_namespace": "rbac",
"resource_type": "group",
"resource_id": "<group_uuid>",
"relation": "member",
"subject_filter": {
"subject_namespace": "rbac",
"subject_type": "principal",
"subject_id": "redhat/<user_id>"
}
}
}'
4b. Verify group is subject of role binding:
sh .cursor/skills/relationship/scripts/relationship.sh stage read_tuples '{
"filter": {
"resource_namespace": "rbac",
"resource_type": "role_binding",
"resource_id": "<role_binding_uuid>",
"relation": "subject",
"subject_filter": {
"subject_namespace": "rbac",
"subject_type": "group",
"subject_id": "<group_uuid>"
}
}
}'
4c. Verify role binding has role:
sh .cursor/skills/relationship/scripts/relationship.sh stage read_tuples '{
"filter": {
"resource_namespace": "rbac",
"resource_type": "role_binding",
"resource_id": "<role_binding_uuid>",
"relation": "role",
"subject_filter": {
"subject_namespace": "rbac",
"subject_type": "role",
"subject_id": ""
}
}
}'
4d. Check role permissions (via database):
SELECT DISTINCT p.permission, p.application, p.resource_type, p.verb
FROM management_permission p
JOIN management_access a ON a.permission_id = p.id
JOIN management_role r ON a.role_id = r.id
JOIN management_rolev2 rv2 ON rv2.v1_source_id = r.id
WHERE rv2.uuid = '<role_uuid>';
If all steps verify successfully, the access chain is:
User (redhat/<user_id>)
→ member of → Group (<group_uuid>)
→ subject of → Role Binding (<role_binding_uuid>)
→ role → Role (<role_uuid>)
→ bound to → Workspace (<workspace_id>)
→ grants → Permissions (<permissions>)
To quickly check if a user has ANY access to a workspace, you can:
SELECT rb.uuid as role_binding_uuid, rb.resource_id as workspace_id, w.name as workspace_name, rv2.name as role_name, g.uuid as group_uuid, g.name as group_name
FROM management_rolebinding rb
JOIN management_rolebindinggroup rbg ON rbg.binding_id = rb.id
JOIN management_group g ON rbg.group_id = g.id
JOIN management_rolev2 rv2 ON rb.role_id = rv2.id
LEFT JOIN management_workspace w ON rb.resource_id = w.id::text
WHERE rb.resource_type = 'workspace'
AND rb.resource_id = '<workspace_id>'
AND g.uuid IN (<list_of_group_uuids>);
Replace <list_of_group_uuids> with comma-separated UUIDs from Step 1.