# PyAuthz Architecture

This document describes the internal architecture of PyAuthz for contributors and advanced users.

## Core Concepts

PyAuthz evaluates authorization as: **Can this Subject perform this Action on this Resource?**

```
Subject  ──┐
Action   ──┤──> Engine ──> Decision (allow/deny + reasoning)
Resource ──┤
Env      ──┘
```

## Evaluation Pipeline

When `rp.authorize(subject, action, resource)` is called:

1. **Cache check** — If a `DecisionCache` is attached, check for a cached decision.
2. **Action resolution** — If the action has no colon, prepend the resource type: `"write"` → `"documents:write"`.
3. **Environment merge** — Merge default environment with per-request env kwargs.
4. **Role resolution** — Walk the role hierarchy (parents) to compute the subject's effective roles.
5. **Policy matching** — Find all applicable policies:
   - Policy is enabled
   - Subject has at least one of the policy's required roles (or policy has no role restriction)
   - Action matches at least one of the policy's permission patterns
   - All conditions evaluate to true (ABAC)
6. **Combination** — Run the combiner algorithm on matched policies:
   - `deny-override`: any DENY wins over all ALLOWs
   - `allow-override`: any ALLOW wins over all DENYs
   - `priority-first`: highest priority policy wins
   - `first-match`: first matched policy wins
7. **Decision** — Build and return a `Decision` object with the result, reasoning, matched policies, and timing.
8. **Cache store** — If caching is enabled, store the decision for future lookups.

## Module Map

### `pyauthz.core.engine` — The Heart

The `PyAuthz` class is the central coordinator. It owns storage, cache, audit, and the evaluation pipeline. All authorization flows through `_evaluate()`.

### `pyauthz.models.*` — Data Models

Pydantic models for all domain objects. These are the public API surface:

- `Subject` — who is requesting access (id, roles, attributes)
- `Resource` — what is being accessed (id, type, owner_id, attributes)
- `Role` — named permission set with optional parent hierarchy
- `Policy` — explicit allow/deny rule with conditions
- `Permission` — parsed `resource_type:action` with wildcard support
- `Condition` — ABAC condition (field, operator, value)
- `Decision` — evaluation result with full reasoning chain

### `pyauthz.core.conditions` — ABAC Engine

Evaluates conditions against an `AccessRequest` context. Handles:
- Dot-notation field resolution (`subject.attributes.department`)
- Cross-references (`resource.owner_id == subject.id`)
- 10 operators: `==`, `!=`, `>`, `<`, `>=`, `<=`, `in`, `not_in`, `contains`, `matches`
- Fail-safe: missing attributes produce DENY, never crash

### `pyauthz.policies.combiners` — Decision Algorithms

Pure functions that take a list of matched policies and return (effect, matched_names, reasoning). Each combiner implements a different conflict resolution strategy.

### `pyauthz.storage.*` — Persistence

- `StorageBackend` — protocol defining the storage interface
- `MemoryStorage` — thread-safe in-memory implementation (default)
- `yaml_file` — YAML loader with schema validation
- `watcher` — PolicyWatcher for hot-reload on file changes

### `pyauthz.filtering.*` — Query Filters

Converts policies into database-compatible filter expressions:
- `expressions.py` — FilterExpression AST (frozen dataclasses)
- `compiler.py` — policy-to-AST compilation
- `sql.py` — SQLAlchemy WHERE clause generation
- `django.py` — Django Q object generation
- `raw_sql.py` — Parameterized raw SQL generation

### `pyauthz.core.cache` — Decision Caching

TTL-based LRU cache with automatic invalidation when policies or roles change.

### `pyauthz.core.tenancy` — Multi-Tenancy

Per-tenant isolated storage instances via `TenantManager`.

### `pyauthz.core.temporal` — Time-Bounded Roles

Role assignments with `effective_from` and `expires_at` windows.

### `pyauthz.core.delegation` — Permission Delegation

User A grants User B temporary permissions with expiration and audit trail.

### `pyauthz.core.simulation` — Policy Simulation

"What would happen if..." analysis by cloning the engine with modified policies/roles.

## Thread Safety

The engine uses `threading.RLock` in `MemoryStorage` for all reads and writes. The `DecisionCache` has its own lock. The engine is safe for concurrent use in WSGI workers.

## Async Support

Async methods (`aauthorize`, `ais_allowed`, `aauthorize_bulk`) use `asyncio.to_thread()` to run the synchronous engine in a thread pool. The engine itself is not async-native — this keeps the code simple while providing a real async interface.

## Extension Points

- **Custom storage backends**: Implement `StorageBackend` for SQL, Redis, etc.
- **Custom combiners**: Add functions to `COMBINERS` dict
- **Framework integrations**: Follow the `pyauthz_fastapi`/`pyauthz_flask` pattern
- **Audit exporters**: Implement custom exporters for the audit logger

## Design Principles

1. **Deny by default** — If nothing explicitly allows access, deny.
2. **Fail-safe** — Missing attributes, bad conditions, or errors produce DENY, never ALLOW.
3. **Separation of concerns** — Models don't know about the engine. The engine doesn't know about frameworks.
4. **Structured decisions** — Every authorization produces a `Decision` with reasoning, not just a boolean.
5. **Zero external dependencies for core** — Only Pydantic and PyYAML for the core engine.
