ABAC Policy Creation Guide
A comprehensive reference for creating, testing, and managing ABAC policies.
In ABAC, policies are the rules that determine whether access should be granted or denied. Unlike RBAC where you assign roles and permissions, ABAC policies evaluate attributes dynamically.
Key Concepts
- Policy: A rule that evaluates to Permit or Deny
- Condition: The logic that checks attribute values
- Target: Optional pre-filter for when a policy applies
- Obligation: Required action when policy matches
- Advice: Optional recommendation when policy matches
Policy Anatomy
Understanding the structure and components of an ABAC policy.
Basic Structure
interface ABACPolicy {
id: string; // Unique identifier
version: string; // Policy version (e.g., "1.0.0")
description?: string; // Human-readable description
effect: 'Permit' | 'Deny'; // What happens when condition matches
target?: PolicyTarget; // Optional pre-filter
condition?: Condition; // Main evaluation logic
priority?: number; // Higher = evaluated first
obligations?: Obligation[]; // Required actions
advice?: Advice[]; // Optional recommendations
metadata?: PolicyMetadata; // Creation info, tags, etc.
}Example Policy (JSON)
{
"id": "department-document-access",
"version": "1.0.0",
"description": "Allow users to access documents from their department",
"effect": "Permit",
"condition": {
"operator": "and",
"conditions": [
{
"operator": "equals",
"left": {
"category": "subject",
"attributeId": "department"
},
"right": {
"category": "resource",
"attributeId": "department"
}
},
{
"operator": "in",
"left": {
"category": "action",
"attributeId": "id"
},
"right": ["read", "view"]
}
]
},
"obligations": [
{
"id": "log-access",
"type": "log",
"parameters": {
"reason": "department_access",
"level": "info"
}
}
]
}Creating Policies
Three different approaches to creating policies in abac-engine.
Method 1: Policy Builder (Recommended)
The fluent PolicyBuilder API provides type safety and better IDE support:
import { PolicyBuilder, ConditionBuilder, AttributeRef } from 'abac-engine';
const policy = PolicyBuilder.create('my-policy')
.version('1.0.0')
.description('My access control policy')
.permit() // or .deny()
.condition(
ConditionBuilder.equals(
AttributeRef.subject('department'),
AttributeRef.resource('department')
)
)
.build();Method 2: Direct Object Creation
For programmatic generation or JSON-based policies:
import { ABACPolicy } from 'abac-engine';
const policy: ABACPolicy = {
id: 'my-policy',
version: '1.0.0',
effect: 'Permit',
condition: {
operator: 'equals',
left: { category: 'subject', attributeId: 'department' },
right: { category: 'resource', attributeId: 'department' }
}
};Method 3: Pre-built Patterns
For common scenarios:
import { PolicyPatterns } from 'abac-engine';
const ownershipPolicy = PolicyPatterns.ownership(['read', 'update', 'delete']);
const deptPolicy = PolicyPatterns.departmentAccess(
['read'],
['public', 'internal']
);
const timePolicy = PolicyPatterns.businessHoursOnly(['create', 'update']);Conditions & Operators
Building complex conditions using comparison and logical operators.
Comparison Operators
Equality
// Exact match
ConditionBuilder.equals(
AttributeRef.subject('status'),
'active'
);
// Not equal
ConditionBuilder.notEquals(
AttributeRef.resource('type'),
'archived'
);Numeric Comparison
// Greater than
ConditionBuilder.greaterThan(
AttributeRef.subject('clearanceLevel'),
3
);
// Greater than or equal
ConditionBuilder.greaterThanOrEqual(
AttributeRef.subject('age'),
18
);Set Operations
// In array
ConditionBuilder.in(
AttributeRef.action('id'),
['read', 'view', 'list']
);
// Not in array
ConditionBuilder.notIn(
AttributeRef.resource('status'),
['deleted', 'archived']
);String Operations
// Contains
ConditionBuilder.contains(
AttributeRef.resource('title'),
'confidential'
);
// Starts with
ConditionBuilder.startsWith(
AttributeRef.resource('path'),
'/secure/'
);Logical Operators
// AND - All conditions must be true
ConditionBuilder.and(
ConditionBuilder.equals(AttributeRef.subject('department'), 'engineering'),
ConditionBuilder.in(AttributeRef.action('id'), ['read', 'write'])
);
// OR - Any condition can be true
ConditionBuilder.or(
ConditionBuilder.equals(AttributeRef.subject('role'), 'admin'),
ConditionBuilder.equals(AttributeRef.subject('role'), 'manager')
);
// NOT - Negation
ConditionBuilder.not(
ConditionBuilder.equals(AttributeRef.resource('status'), 'archived')
);
// Complex Combinations
const condition = ConditionBuilder.and(
ConditionBuilder.equals(AttributeRef.subject('department'), 'engineering'),
ConditionBuilder.or(
ConditionBuilder.in(AttributeRef.subject('role'), ['admin', 'manager']),
ConditionBuilder.greaterThanOrEqual(AttributeRef.subject('level'), 5)
)
);Common Policy Patterns
Ready-to-use patterns for common access control scenarios.
1. Ownership-Based Access
Grant access to resources owned by the user
const policy = PolicyBuilder
.create('owner-access')
.permit()
.condition(
ConditionBuilder.equals(
AttributeRef.subject('id'),
AttributeRef.resource('ownerId')
)
)
.build();2. Department-Based Access
Access resources within the same department
const policy = PolicyBuilder
.create('dept-access')
.permit()
.condition(
ConditionBuilder.equals(
AttributeRef.subject('department'),
AttributeRef.resource('department')
)
)
.build();3. Time-Based Access
Restrict access to business hours
const policy = PolicyBuilder
.create('business-hours')
.permit()
.condition(
ConditionBuilder.and(
ConditionBuilder.gte(
AttributeRef.environment('hour'),
9
),
ConditionBuilder.lte(
AttributeRef.environment('hour'),
17
)
)
)
.build();4. Hierarchical Access
Managers can access subordinate resources
const policy = PolicyBuilder
.create('manager-access')
.permit()
.condition(
ConditionBuilder.and(
ConditionBuilder.equals(
AttributeRef.subject('role'),
'manager'
),
ConditionBuilder.equals(
AttributeRef.subject('department'),
AttributeRef.resource('department')
)
)
)
.build();Testing Policies
Ensure your policies work correctly with comprehensive testing.
Unit Testing Policies
import { ABACEngine, PolicyBuilder, ConditionBuilder, AttributeRef } from 'abac-engine';
import { describe, test, expect } from 'vitest';
describe('Policy Tests', () => {
test('ownership policy allows owner', async () => {
const policy = PolicyBuilder
.create('owner-policy')
.permit()
.condition(
ConditionBuilder.equals(
AttributeRef.subject('id'),
AttributeRef.resource('ownerId')
)
)
.build();
const engine = new ABACEngine({ policies: [policy] });
const decision = await engine.evaluate({
subject: { id: 'user-123' },
resource: { ownerId: 'user-123' },
action: { id: 'read' },
environment: {}
});
expect(decision.decision).toBe('Permit');
});
test('ownership policy denies non-owner', async () => {
const policy = PolicyBuilder
.create('owner-policy')
.permit()
.condition(
ConditionBuilder.equals(
AttributeRef.subject('id'),
AttributeRef.resource('ownerId')
)
)
.build();
const engine = new ABACEngine({ policies: [policy] });
const decision = await engine.evaluate({
subject: { id: 'user-123' },
resource: { ownerId: 'user-456' },
action: { id: 'read' },
environment: {}
});
expect(decision.decision).toBe('Deny');
});
});Integration Testing
describe('ABAC Integration Tests', () => {
test('complex scenario: department manager accessing team document', async () => {
const policies = [
PolicyBuilder.create('manager-dept-access')
.permit()
.condition(
ConditionBuilder.and(
ConditionBuilder.equals(
AttributeRef.subject('role'),
'manager'
),
ConditionBuilder.equals(
AttributeRef.subject('department'),
AttributeRef.resource('department')
),
ConditionBuilder.in(
AttributeRef.action('id'),
['read', 'update']
)
)
)
.build()
];
const engine = new ABACEngine({ policies });
const decision = await engine.evaluate({
subject: {
id: 'manager-1',
role: 'manager',
department: 'engineering'
},
resource: {
id: 'doc-1',
department: 'engineering'
},
action: { id: 'read' },
environment: {}
});
expect(decision.decision).toBe('Permit');
});
});Best Practices
Guidelines for creating maintainable and effective policies.
DO: Use Meaningful IDs
// Good
PolicyBuilder.create('dept-read-access')
// Bad
PolicyBuilder.create('policy-1')DO: Add Descriptions
PolicyBuilder.create('access')
.description(
'Allow dept users to read documents'
)
.permit()DO: Use Priority for Exceptions
// Exception policy (high priority)
PolicyBuilder.create('admin-override')
.priority(100)
.permit()
// Base policy (normal priority)
PolicyBuilder.create('base-access')
.priority(1)
.permit()DO: Add Obligations for Audit
.obligation({
id: 'audit-access',
type: 'log',
parameters: {
action: 'document_access',
level: 'info'
}
})DON'T: Create Overly Complex Conditions
Split complex policies into multiple simpler policies instead of nesting many conditions.
DON'T: Forget to Version Policies
Always include version information for policy tracking and rollback capabilities.