Policy Guide

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.

Next Steps