0057: Decision Strategy Selection
Date: 2024-12-10
Status: Accepted
Context
The policy-service combines two authorization mechanisms:
- ReBAC (Relationship-Based Access Control) via
permissions-service- checks graph relationships - ABAC (Attribute-Based Access Control) via Cedar policies - evaluates attributes and conditions
Different use cases require different approaches to combining these decisions:
- Some resources need BOTH relationship AND policy approval (high security)
- Some resources only need one or the other (flexibility)
- Some scenarios should check relationships first, others should check policies first
We need to define the available strategies and how they are selected.
Decision
We will support four decision strategies for combining ReBAC and ABAC decisions, with a default and per-request override capability.
Available Strategies
1. rebac-first (Default)
Behavior:
- Check ReBAC (permissions-service) first
- If ReBAC allows → ALLOW (skip ABAC)
- If ReBAC denies → Check ABAC (Cedar)
- Return ABAC result
Use Case: Most common pattern. Relationship-based access is the primary model, with policies as fallback for exceptions.
Example: "User is an editor of this document" (ReBAC allows) OR "User has emergency access policy" (ABAC allows).
ReBAC: ALLOW → Result: ALLOW
ReBAC: DENY → ABAC: ALLOW → Result: ALLOW
ReBAC: DENY → ABAC: DENY → Result: DENY
2. policy-first
Behavior:
- Check ABAC (Cedar) first
- If ABAC explicitly forbids → DENY (skip ReBAC)
- If ABAC explicitly permits → ALLOW (skip ReBAC)
- If ABAC has no matching policy → Check ReBAC
- Return ReBAC result
Use Case: AWS IAM-style behavior where explicit policies take precedence over relationships.
Example: "Policy explicitly denies access to production" (ABAC denies, even if user is an admin via ReBAC).
ABAC: FORBID → Result: DENY
ABAC: PERMIT → Result: ALLOW
ABAC: NO_MATCH → ReBAC: ALLOW → Result: ALLOW
ABAC: NO_MATCH → ReBAC: DENY → Result: DENY
3. require-both (AND)
Behavior:
- Check BOTH ReBAC AND ABAC
- If BOTH allow → ALLOW
- If EITHER denies → DENY
Use Case: High-security resources requiring multiple layers of authorization.
Example: "User must be a document editor (ReBAC) AND have an active security clearance (ABAC)."
ReBAC: ALLOW + ABAC: ALLOW → Result: ALLOW
ReBAC: ALLOW + ABAC: DENY → Result: DENY
ReBAC: DENY + ABAC: ALLOW → Result: DENY
ReBAC: DENY + ABAC: DENY → Result: DENY
4. require-any (OR)
Behavior:
- Check BOTH ReBAC AND ABAC
- If EITHER allows → ALLOW
- If BOTH deny → DENY
Use Case: Flexible access where multiple paths to authorization exist.
Example: "User is a document viewer (ReBAC) OR user has public access policy (ABAC)."
ReBAC: ALLOW + ABAC: ALLOW → Result: ALLOW
ReBAC: ALLOW + ABAC: DENY → Result: ALLOW
ReBAC: DENY + ABAC: ALLOW → Result: ALLOW
ReBAC: DENY + ABAC: DENY → Result: DENY
Strategy Selection
The strategy is selected via hierarchy with per-request override:
1. Request Parameter (Highest Priority)
POST /authorize
{
"principal": "user:alice",
"action": "document:edit",
"resource": "document:123",
"strategy": "require-both"
}
2. Resource-Type Configuration
Configure default strategy per resource type in service configuration:
strategy_defaults:
default: "rebac-first"
resource_types:
"document:*": "rebac-first"
"invoice:*": "policy-first"
"secret:*": "require-both"
3. Service Default (Lowest Priority)
If no request parameter or resource-type config, use service-wide default: rebac-first.
Decision Hierarchy Summary
┌─────────────────────────────────────────────┐
│ 1. Request `strategy` parameter │ ← Highest priority
├─────────────────────────────────────────────┤
│ 2. Resource-type configuration │
├─────────────────────────────────────────────┤
│ 3. Service default: "rebac-first" │ ← Lowest priority
└─────────────────────────────────────────────┘
API Response
The response includes which strategy was used and the source of decisions:
{
"authorized": true,
"strategy": "rebac-first",
"decision_source": "rebac",
"rebac_result": "allow",
"abac_result": "not_evaluated",
"duration_ms": 3
}
Consequences
Positive
- Flexibility: Different resources can use different authorization models
- Transparency: Response includes which strategy was used
- Override capability: Per-request override for special cases
- Sensible default:
rebac-firstworks for most use cases - Performance: Short-circuit evaluation avoids unnecessary checks
Negative
- Complexity: Four strategies require documentation and understanding
- Configuration management: Resource-type configs must be maintained
- Debugging: Strategy selection adds another variable to debug (mitigated by including strategy in response)
Performance Considerations
| Strategy | Best Case | Worst Case |
|---|---|---|
rebac-first | 1 call (ReBAC allows) | 2 calls |
policy-first | 1 call (ABAC matches) | 2 calls |
require-both | 2 calls (parallel) | 2 calls |
require-any | 2 calls (parallel) | 2 calls |
For require-both and require-any, both checks are executed in parallel for performance.