Skip to content

Roles & Entitlements System

Overview

The Roles & Entitlements system enables Floh to provision and deprovision external resources (entitlements) when users are granted or revoked business roles. Roles are distinct from RBAC system roles — they represent business-level access such as "Project X Participant" and map to concrete resources in external systems like Authifi groups and Active Directory accounts.

Key Concepts

Entitlement Definition (Shared)

An independent, reusable template for a single external resource tied to a specific connector. Entitlement definitions are first-class entities that exist independently of roles and can be shared across multiple roles via a many-to-many relationship.

Each entitlement definition specifies:

  • Provision config: connector command and parameters to create/grant the resource
  • Deprovision config: connector command and parameters to remove the resource
  • Reconciliation config (optional): policy that controls what happens when the system detects a mismatch between the entitlement and the upstream resource

For example, a single "AD Group X Membership" entitlement can be linked to both "Project X Participant" and "Project X Administrator" roles.

Role Definition

A reusable template that defines what a role means and which entitlements it provisions. Each role definition:

  • Has a unique name, description, and status (active/inactive)
  • Can specify a default expires_after_days TTL for automatic expiry
  • Is linked to one or more shared entitlement definitions via a join table
  • Supports soft-delete for deactivation without data loss

Role Assignment

An instance of a role granted to a specific user, tracking:

  • Who granted it and when
  • Which workflow run triggered it (if any)
  • Expiry date (from the role definition's TTL or an explicit value)
  • Status: provisioning, active, partially_provisioned, expired, revoked

Entitlement Instance

A tracked provisioned resource within a role assignment. Each instance records:

  • The external system's resource ID (external_id)
  • Provisioning/deprovisioning timestamps
  • Reconciliation status (ok, missing, error)

Data Model

entitlement_definition (independent, shared)
    ├── connector_id → connector_definition
    ├── provision_config, deprovision_config, reconciliation_config
    └── linked to roles via role_entitlement join table

role_definition
    └── linked to entitlements via role_entitlement (many-to-many)

role_entitlement (join table)
    ├── role_definition_id → role_definition
    └── entitlement_definition_id → entitlement_definition

role_assignment (instance of a granted role)
    ├── role_definition_id → role_definition
    ├── user_id → user
    └── entitlement_instance[] (provisioned resources)

entitlement_instance
    ├── entitlement_definition_id → entitlement_definition
    ├── role_assignment_id → role_assignment
    └── status, external_id, timestamps

Architecture

Workflow Run
    └─ role_grant step ──→ RoleService.grantRole()
                               ├─ Creates RoleAssignment
                               └─ For each linked EntitlementDefinition:
                                   ├─ Creates EntitlementInstance (pending)
                                   ├─ Calls connector with provision_config
                                   └─ Updates EntitlementInstance (provisioned/failed)

Scheduled Jobs
    ├─ role-expiry-check (hourly) ──→ Revokes expired assignments
    ├─ document-expiry-check (hourly) ──→ Expires documents, revokes linked roles
    └─ entitlement-reconciliation (daily at 02:00 UTC) ──→ Checks upstream systems

API Endpoints

Entitlement Definitions (/api/entitlements)

Method Path Description Permission
GET / List entitlement definitions (paginated, filterable by connector/search) entitlement:read
GET /:id Get single entitlement definition entitlement:read
POST / Create entitlement definition entitlement:manage
PUT /:id Update entitlement definition entitlement:manage
DELETE /:id Delete (rejects if active instances exist) entitlement:manage

Role Definitions (/api/roles)

Method Path Description Permission
GET / List role definitions (paginated, filterable) role_definition:read
POST / Create with optional entitlementIds to link role_definition:manage
GET /:id Get with linked entitlements role_definition:read
PUT /:id Update name, description, status, expiry role_definition:manage
DELETE /:id Soft-delete role_definition:manage
POST /:id/restore Restore soft-deleted role_definition:manage
POST /:id/entitlements Link an existing entitlement definition role_definition:manage
DELETE /:id/entitlements/:eid Unlink an entitlement definition role_definition:manage

Role Assignments (/api/role-assignments)

Method Path Description Permission
GET / List (filter by userId, roleDefinitionId, status; ?include=entitlements) entitlement:read
GET /:id Get with entitlement instances entitlement:read
POST / Manual grant outside workflow entitlement:manage
POST /:id/revoke Revoke and deprovision entitlement:manage
POST /:id/reprovision Re-provision failed/orphaned entitlement:manage

POST / body supports an onDuplicate field (skip, error, renew, update) to control behavior when the user already has an active assignment for the role. Defaults to skip. Returns 201 for new assignments and 200 when an existing assignment was skipped, renewed, or updated.

Reconciliation (/api/reconciliation)

Method Path Description Permission
POST /run Trigger manual reconciliation entitlement:manage
GET /status Last reconciliation results entitlement:read

Webhook (/api/entitlements/webhook)

Method Path Description
POST /:connectorId Receive upstream change events

Documents (/api/documents)

Enhanced with new query parameters:

  • userId — filter by subject user
  • status — filter by document status (uploaded, approved, rejected, expired)
  • expiringBefore — ISO date, show documents expiring before this date
  • expiringSoon — boolean, shorthand for documents expiring within 30 days

Enriched response includes uploader_name, workflow_name, and role_assignments[].

Workflow Step Types

role_grant

Grants a role to a user during workflow execution. Supports configurable duplicate handling for cases where the user already has an active assignment for the same role.

Config:

{
  "roleDefinitionId": "uuid-of-role-definition",
  "userId": "uuid-of-target-user",
  "expiresAt": "2025-12-31T00:00:00Z",
  "failOnPartial": false,
  "onDuplicate": "skip"
}

onDuplicate strategies:

Strategy Behavior
skip (default) If the user already has an active assignment, return success without changes. Ideal for idempotent workflows.
error If the user already has an active assignment, fail the step. Forces explicit handling of duplicates.
renew Update the expires_at on the existing assignment. Use for recertification flows where the goal is to extend access.
update Reconcile entitlements on the existing assignment against the current role definition (provision new, deprovision removed) and optionally update expires_at. Use when the role's entitlements may have changed since the original grant.

Output variables: - roleAssignmentId — ID of the created or existing assignment - roleProvisioned — boolean, true if all entitlements provisioned - provisionedCount — number of successfully provisioned entitlements - failedCount — number of failed entitlements - roleGrantAction — what actually happened: created, skipped, renewed, or updated

role_revoke

Revokes all active assignments for a role/user combination.

Config:

{
  "roleDefinitionId": "uuid-of-role-definition",
  "userId": "uuid-of-target-user",
  "reason": "Access no longer required"
}

Output variables: - revokedCount — number of assignments revoked

document_submission (enhanced)

New config field: - expiresAfterDays — integer, auto-calculates expires_at when a document is uploaded

Document Lifecycle

Documents now track expiry and subject user:

  1. Upload: expires_at calculated from step config's expiresAfterDays or explicit field
  2. Approval: Document becomes approved
  3. Expiry: Scheduled job detects expires_at < now() for approved docs
  4. Revocation: Expired documents trigger revocation of linked role assignments (via run_id)

Connector Extensions

Authifi

New commands: - checkGroupMembership — verifies user is still a member of a specified group - Config: { "command": "checkGroupMembership", "groupId": "...", "userId": "..." } - Returns: { "isMember": boolean }

Test Active Directory

New commands: - checkGroupMembership — checks if user is in an AD group - checkAccountExists — checks if an AD account exists - createAccount — creates a new AD account (simulated)

Reconciliation

Reconciliation Policies

Each entitlement definition can specify a reconciliation policy that controls what happens when the system detects a mismatch between the entitlement and the upstream resource. The policy is stored as { "policy": "<value>" } in the reconciliation_config column.

Policy Label (UI) Behavior
null None No reconciliation checks are performed for this entitlement.
log_only Log only Run the check and log any discrepancy to the audit log, but don't change entitlement status. Useful for monitoring without automated remediation.
flag Flag as out of sync Mark the entitlement instance as orphaned when the upstream resource is missing. This was the only behavior prior to the policy system.
sync Keep synced Automatically re-provision the resource via the connector if it is missing upstream.

Auto-derived Check Commands

The system automatically derives which connector command to run during reconciliation. Each provision command in a connector schema can declare a reconcilesWith field that names the corresponding check command. For example:

{
  "addToGroup": {
    "params": ["groupId", "userId"],
    "reconcilesWith": "checkGroupMembership"
  },
  "checkGroupMembership": {
    "params": ["groupId", "userId"]
  }
}

When reconciling, the system: 1. Reads the policy from the entitlement's reconciliation_config 2. Looks up reconcilesWith on the provision command in the connector schema 3. Copies matching parameter values from the entitlement's provision_config to build the check command 4. Executes the check command via the connector 5. Applies the policy if a mismatch is detected

Backward Compatibility

Legacy reconciliation_config values that contain a command field (e.g., { "command": "checkGroupMembership", "groupId": "..." }) are treated as flag policy and the config is used directly as the check command. No data migration is required.

Scheduled Reconciliation

Runs daily at 02:00 UTC. For each provisioned entitlement instance with a reconciliation_config:

  1. Resolves the reconciliation policy and derives the check command
  2. Executes the check command via the connector
  3. If the response confirms existence → updates last_reconciled_at and status to ok
  4. If the response indicates the resource is missing → applies the policy (log, flag, or sync)
  5. If an error occurs → marks reconciliation_status as error

Webhook Support

External systems can notify Floh of resource changes via POST /api/entitlements/webhook/:connectorId:

{
  "action": "removed",
  "resourceId": "external-resource-id"
}

The system correlates resourceId with entitlement_instance.external_id and marks matching instances as orphaned.

Permissions

Permission Description Roles
role_definition:read View role definitions admin, approver, resource_manager
role_definition:manage Create/update/delete role definitions, link/unlink entitlements admin, resource_manager
entitlement:read View entitlement definitions and assignment status admin, approver, resource_manager
entitlement:manage Create/update/delete entitlement definitions, grant/revoke roles, trigger reconciliation admin, resource_manager

Database Schema

Tables (migrations 021–022)

  • role_definition — business role templates with soft-delete
  • entitlement_definition — shared resource templates tied to connectors (independent of roles)
  • role_entitlement — join table linking roles to entitlements (many-to-many, composite PK)
  • role_assignment — user-role grants with lifecycle tracking
  • entitlement_instance — provisioned resource tracking with reconciliation status
  • document — added expires_at (timestamp) and subject_user_id (FK to user)

Example: Complete Flow

  1. Admin creates entitlement definitions on the Entitlements page:
  2. Genomics Portal Access — Authifi group membership (provision: addToGroup, deprovision: removeFromGroup, reconciliation policy: flag)
  3. Research File Share Access — AD account (provision: createAccount, deprovision: disableAccount, reconciliation policy: sync)

  4. Admin creates role "Project X Participant" on the Role Definitions page and links both entitlements

  5. Workflow: document_submission (with expiresAfterDays: 365) → approvalrole_grant

  6. User submits document, gets approved, role_grant step executes:

  7. Creates role assignment
  8. Provisions Authifi group membership via connector
  9. Provisions AD account via connector
  10. Assignment status → active

  11. Daily reconciliation confirms entitlements exist upstream

  12. One year later: document expires → document-expiry-check job detects it → role revoked → Authifi membership removed → AD account disabled

  13. If admin manually removes user from Authifi group: reconciliation detects orphan → entitlement instance marked as orphaned → admin can reprovision or revoke via UI

UI Pages

  • Entitlements (/entitlements) — shared entitlement definition management (CRUD), with connector selection, config editors for provision/deprovision, and a reconciliation policy dropdown
  • Role Definitions (/roles) — list and detail views; link/unlink shared entitlements to roles
  • Role Assignments (/role-assignments) — global view with status filters, revoke/reprovision actions
  • User Entitlements (/users/:id/entitlements) — per-user view of role assignments and entitlement instances, with active/history toggle
  • Documents (/documents) — enriched document browser with expiry badges and role linkage
  • Workflow Designerrole_grant and role_revoke step types with role lookup dialog
  • Sidebar — "Access" section with Entitlements, Role Definitions, Role Assignments, and Documents links