Skip to main content
Version: main 🚧

Data Model: Enterprise RBAC for Slack and CAIPE UI

Phase 1 Output | Date: 2026-03-25 | Plan: plan.md

Entity Overview​

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    groupsβ†’roles    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Enterprise β”‚ ─────────────────▢ β”‚ Keycloak β”‚
β”‚ IdP (Okta, β”‚ identity β”‚ Realm β”‚
β”‚ Entra, SAML)β”‚ brokering β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β–Ό β–Ό β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Realm Roles β”‚ β”‚Resourcesβ”‚ β”‚ Users β”‚
β”‚ (platform + β”‚ β”‚(agents, β”‚ β”‚(sub, attrsβ”‚
β”‚ per-KB/ β”‚ β”‚ KBs) β”‚ β”‚ slack_id) β”‚
β”‚ per-agent) β”‚ β”‚ β”‚ β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ β”‚
β–Ό β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ MongoDB β”‚ β”‚ Slack Link β”‚
β”‚ (teams, KB β”‚ β”‚ (user attr β”‚
β”‚ ownership, β”‚ β”‚ in Keycloak)β”‚
β”‚ channels) β”‚ β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Keycloak Entities​

Realm Roles (Keycloak)​

Platform-level roles assigned to users and mapped from IdP groups.

RoleTypeDescription
adminBuilt-inFull platform administration
chat_userBuilt-inCan use agent chat
team_memberBuilt-inBase role for team-scoped access
kb_adminBuilt-inKB administration (all KBs)
kb_reader:<kb-id>Per-resourceRead access to specific KB
kb_ingestor:<kb-id>Per-resourceRead + ingest for specific KB
kb_admin:<kb-id>Per-resourceFull admin for specific KB
kb_reader:*WildcardRead access to all KBs
kb_ingestor:*WildcardRead + ingest for all KBs
agent_user:<agent-id>Per-resourceView + invoke specific agent
agent_admin:<agent-id>Per-resourceFull admin for specific agent
agent_user:*WildcardView + invoke all agents
agent_admin:*WildcardFull admin for all agents
task_user:<task-config-id>Per-resourceView + invoke a specific Task Builder config (task:<id> resource)
task_admin:<task-config-id>Per-resourceView + invoke + configure + delete for that task config
skill_user:<skill-id>Per-resourceView + invoke a specific Skills Gateway skill (skill:<id> resource)
skill_admin:<skill-id>Per-resourceView + invoke + configure + delete for that skill
offline_accessBuilt-inRefresh token support

Resources (Keycloak Authorization Services)​

Resources registered for policy-based access control.

Resource TypeNamingScopesCreated By
dynamic_agentagent:<agent-id>view, invoke, configure, deletekeycloak_sync.py on agent create
knowledge_basekb:<kb-id>query, ingest, adminAdmin UI on KB create
caipe:tasktask:<task-config-id>view, invoke, configure, deleteTask Builder BFF on user task create
caipe:skillskill:<skill-id>view, invoke, configure, deleteSkills Gateway BFF on user skill create

User Attributes (Keycloak)​

Custom attributes stored on user profiles.

AttributeTypeDescription
slack_user_idstringSlack user ID for identity linking (FR-025)

IdP Mappers (Keycloak)​

Mappers that translate enterprise IdP groups to Keycloak roles.

Mapper TypeSourceTargetCreated By
Identity Provider MapperIdP group nameKeycloak realm roleAdmin UI group mapping (FR-024)

Client Mappers (Keycloak)​

Protocol mappers on the caipe-ui client that add claims to tokens.

MapperClaimContent
Group MembershipgroupsUser's groups (flat names)
Realm Rolesrealm_access.rolesUser's realm roles
Org ClaimorgTenant identifier (FR-020)

MongoDB Entities​

Team​

Collection: teams

interface Team {
_id: ObjectId;
name: string;
description?: string;
keycloak_roles: string[]; // Realm roles assigned to team
members: string[]; // Keycloak user subs
owner_id: string; // Keycloak sub of team owner
created_at: Date;
updated_at: Date;
}

TeamKbOwnership​

Collection: team_kb_ownership

interface TeamKbOwnership {
_id: ObjectId;
team_id: string; // Reference to Team._id
kb_ids: string[]; // Knowledge base IDs owned by team
allowed_datasource_ids: string[]; // Specific datasource restrictions
created_at: Date;
updated_at: Date;
}

Collection: slack_channel_team_links

interface SlackChannelTeamLink {
_id: ObjectId;
slack_channel_id: string; // Slack channel UUID
slack_workspace_id: string; // Slack workspace/enterprise ID
team_id: string; // CAIPE team ID
created_by: string; // Admin who created the mapping
created_at: Date;
active: boolean; // False if channel archived or team deleted
}

SlackLinkNonce​

Collection: slack_link_nonces

interface SlackLinkNonce {
_id: ObjectId;
nonce: string; // Cryptographically random, single-use
slack_user_id: string; // Slack user requesting the link
consumed: boolean; // True after successful use
created_at: Date; // TTL index: expires after 10 minutes
}

Index: { created_at: 1 }, { expireAfterSeconds: 600 } (10-minute TTL)

SlackUserMetrics​

Collection: slack_user_metrics

interface SlackUserMetrics {
_id: ObjectId;
slack_user_id: string;
keycloak_sub?: string;
last_bot_interaction: Date;
obo_exchange_success_count: number;
obo_exchange_fail_count: number;
active_channels: string[]; // Channel IDs where user is active
updated_at: Date;
}

DynamicAgentConfig (existing β€” RBAC fields highlighted)​

Collection: dynamic_agents

interface DynamicAgentConfig {
_id: ObjectId;
name: string;
owner_id: string; // Keycloak sub β€” RBAC field
visibility: 'private' | 'team' | 'global'; // RBAC field
shared_with_teams: string[]; // Team IDs β€” RBAC field
allowed_tools: string[];
subagents: any[];
is_system: boolean;
enabled: boolean;
// ... other fields
}

JWT Claims (Token Structure)​

Access Token (Keycloak-issued)​

{
"sub": "user-uuid",
"act": { "sub": "bot-service-account-uuid" },
"scope": "openid email profile groups",
"realm_access": {
"roles": ["chat_user", "team_member", "kb_reader:kb-team-a", "agent_user:agent-123"]
},
"groups": ["caipe-admins", "caipe-users"],
"org": "cisco",
"email": "user@example.com",
"iss": "https://keycloak.example.com/realms/caipe",
"aud": "caipe-ui"
}

OBO Token (Token Exchange result)​

Same structure as access token but with act claim populated:

  • sub = originating user
  • act.sub = bot or agent service account
  • scope = intersection of user's scope and bot's allowed scope

State Transitions​

UNLINKED ──[bot sends link URL]──▢ PENDING ──[user completes OIDC]──▢ LINKED
β–² β”‚ β”‚
β”‚ β”‚ [nonce expires/10min] β”‚ [KC account disabled]
β”‚ β–Ό β–Ό
β”‚ EXPIRED INVALID
β”‚ β”‚
└─────────────────────[admin re-links]β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Dynamic Agent Keycloak Resource Lifecycle​

[Agent created in MongoDB]
β”‚
β–Ό
[keycloak_sync.py creates KC resource + auto-generated policies]
β”‚
β–Ό
[ACTIVE β€” CEL evaluates access using KC roles + MongoDB visibility]
β”‚
β”‚ [Agent deleted from MongoDB]
β–Ό
[keycloak_sync.py deletes KC resource + cleans up dangling per-agent roles]