Portal Community

NodeCapabilityPolicy

NodeCapabilityPolicy is a ProcessSecurity entity that maps a tenant (or specific actor role) to a set of allowed capability types. It enforces capability access at two levels:

  1. Design time: The palette and API filter out capabilities not allowed for the tenant
  2. Execution time: The engine checks policy before running any node with a restricted capability

Policy Schema

public class NodeCapabilityPolicy
{
    public Guid Id { get; set; }
    public string TenantId { get; set; }
    public string? RoleId { get; set; }          // null = applies to all roles in tenant
    public List<CapabilityType> AllowedTypes { get; set; }
    public List<CapabilityType> DeniedTypes { get; set; }
    public bool AllowAllByDefault { get; set; }   // if true, DeniedTypes is the blocklist
}

Execution-Time Check

// ProcessEngine/Security/CapabilityAuthorizationService.cs
public async Task<bool> IsAllowedAsync(
    CapabilityType capabilityType,
    string tenantId,
    string actorId)
{
    var policy = await _policyRepo.GetForTenantAsync(tenantId);
    if (policy == null) return true; // no policy = allow all

    if (policy.AllowAllByDefault)
        return !policy.DeniedTypes.Contains(capabilityType);
    else
        return policy.AllowedTypes.Contains(capabilityType);
}

Security Layers

LayerEnforcement PointEffect
Palette filterGET /api/capabilities (designer)Capability sections hidden from UI
Publish validationPOST /api/processes/{id}/publishCannot publish workflow using denied capabilities
Execution checkBefore each node executionNode fails with CapabilityAccessDenied error
Defense in depth: The execution-time check is the authoritative gate. The palette and publish filters are convenience — they improve UX but must not be relied upon as the sole security control. An attacker with direct API access could publish a workflow with restricted nodes; the execution-time check stops it at runtime.

Example Policy Configuration

{
  "tenantId": "tenant-acme",
  "allowAllByDefault": true,
  "deniedTypes": ["DIDComm", "MCP"],
  "comment": "Acme has not licensed the DIDComm or MCP add-ons"
}