# Story 4.49: Access Control Enforcement

**Status:** Cancelled
**Priority:** Medium
**Depends on:** None

---

## Context / Background

The `AccessControlSettings` model exists as a configuration primitive (`src/fapilog/core/access_control.py`) but fapilog does not enforce any access control at runtime. Users must manually integrate these settings with their application middleware.

The GPT-5.2 audit flagged this:

> "Encryption/access control are validators/models with unclear runtime use... Users assume encryption/access control protections exist end-to-end"

The enterprise documentation now correctly states that `AccessControlSettings` provides configuration primitives only, but users would benefit from optional enforcement utilities they can integrate with their applications.

**Current state:**

- `AccessControlSettings` model exists with `enabled`, `auth_mode`, `allowed_roles`, etc.
- `validate_access_control()` validates configuration against security baselines
- No actual enforcement - users must build their own middleware

**Desired state:**

- Provide optional enforcement utilities that users can integrate
- FastAPI integration helpers (dependency injection)
- Middleware pattern examples for other frameworks
- Keep enforcement opt-in (not automatic in core pipeline)

---

## Scope (In / Out)

### In Scope

- Create `AccessControlEnforcer` protocol for pluggable enforcement
- Provide FastAPI integration helpers (dependency, middleware)
- Provide ASGI middleware for framework-agnostic use
- Document integration patterns for common frameworks
- Keep all enforcement opt-in (not automatic)

### Out of Scope

- Automatic enforcement in core logging pipeline
- Authentication implementation (users provide identity)
- Session management
- Token validation (users integrate with their IdP)

---

## User Story

**As a** developer using fapilog in an enterprise environment
**I want** optional access control enforcement utilities
**So that** I can easily integrate fapilog's access control settings with my application without building custom middleware from scratch

---

## Acceptance Criteria

### AC1: AccessControlEnforcer Protocol

**Description:** Define a protocol for access control enforcement that can be implemented by different frameworks.

**Validation:**
```python
from fapilog.core.access_control import AccessControlEnforcer, AccessControlSettings

class MyEnforcer(AccessControlEnforcer):
    async def check_access(
        self,
        user_roles: list[str],
        operation: str,
        settings: AccessControlSettings,
    ) -> bool:
        ...

# Type checks pass
enforcer: AccessControlEnforcer = MyEnforcer()
```

### AC2: FastAPI Dependency Helper

**Description:** Provide a FastAPI dependency that enforces access control.

**Validation:**
```python
from fapilog.integrations.fastapi import require_roles
from fastapi import FastAPI, Depends

app = FastAPI()

@app.get("/admin")
async def admin_endpoint(user = Depends(require_roles(["admin"]))):
    return {"message": "admin only"}
```

### AC3: ASGI Middleware

**Description:** Provide framework-agnostic ASGI middleware for access control.

**Validation:**
```python
from fapilog.integrations.asgi import AccessControlMiddleware

app = AccessControlMiddleware(
    app=my_app,
    settings=access_settings,
    get_user_roles=lambda request: request.state.user.roles,
)
```

### AC4: Integration Documentation

**Description:** Document integration patterns for FastAPI, Starlette, and generic ASGI.

**Validation:**
- FastAPI integration guide exists
- Starlette integration example exists
- Generic ASGI pattern documented
- Common IdP integration patterns (OAuth2, JWT) documented

### AC5: Backward Compatible

**Description:** All enforcement is opt-in; existing code continues to work unchanged.

**Validation:**
- Existing tests pass without modification
- `AccessControlSettings` works without enforcer
- No automatic enforcement added to core pipeline

---

## Technical Design / Implementation Notes

### File Structure

```
src/fapilog/core/access_control.py (MODIFIED - add protocol)
src/fapilog/integrations/__init__.py (NEW)
src/fapilog/integrations/fastapi.py (NEW - optional, requires fastapi)
src/fapilog/integrations/asgi.py (NEW - ASGI middleware)
docs/integrations/access-control.md (NEW)
```

### Protocol Definition

```python
# src/fapilog/core/access_control.py

from typing import Protocol

class AccessControlEnforcer(Protocol):
    """Protocol for access control enforcement."""

    async def check_access(
        self,
        user_roles: list[str],
        operation: str,
        settings: AccessControlSettings,
    ) -> bool:
        """Check if user roles permit the operation.

        Args:
            user_roles: Roles assigned to the current user
            operation: The operation being attempted (read, write, admin, etc.)
            settings: Access control configuration

        Returns:
            True if access is permitted, False otherwise
        """
        ...


class DefaultEnforcer:
    """Default access control enforcer using AccessControlSettings."""

    async def check_access(
        self,
        user_roles: list[str],
        operation: str,
        settings: AccessControlSettings,
    ) -> bool:
        if not settings.enabled:
            return True

        # Check if any user role is in allowed roles
        if not any(role in settings.allowed_roles for role in user_roles):
            return False

        # Check admin requirement for sensitive ops
        if operation in {"delete", "modify_config", "export"}:
            if settings.require_admin_for_sensitive_ops:
                return "admin" in user_roles

        return True
```

### FastAPI Integration

```python
# src/fapilog/integrations/fastapi.py

from typing import Callable
from fastapi import Depends, HTTPException, Request

def require_roles(
    roles: list[str],
    settings: AccessControlSettings | None = None,
) -> Callable:
    """FastAPI dependency for role-based access control."""

    async def dependency(request: Request):
        # Get settings from app state or parameter
        ac_settings = settings or request.app.state.access_control_settings

        if not ac_settings.enabled:
            return

        # Get user roles from request (user must set this)
        user_roles = getattr(request.state, "user_roles", [])

        if not any(role in ac_settings.allowed_roles for role in user_roles):
            raise HTTPException(403, "Insufficient permissions")

        if not any(role in roles for role in user_roles):
            raise HTTPException(403, f"Requires one of: {roles}")

    return dependency
```

---

## Tasks

### Phase 1: Core Protocol

- [ ] Define `AccessControlEnforcer` Protocol
- [ ] Implement `DefaultEnforcer`
- [ ] Add `check_access` helper function
- [ ] Add tests for protocol and default enforcer

### Phase 2: FastAPI Integration

- [ ] Create `src/fapilog/integrations/fastapi.py`
- [ ] Implement `require_roles` dependency
- [ ] Implement `AccessControlMiddleware` for FastAPI
- [ ] Add optional dependency on fastapi in pyproject.toml
- [ ] Add tests (require fastapi in test env)

### Phase 3: ASGI Middleware

- [ ] Create `src/fapilog/integrations/asgi.py`
- [ ] Implement generic ASGI middleware
- [ ] Add tests with mock ASGI app

### Phase 4: Documentation

- [ ] Create `docs/integrations/access-control.md`
- [ ] Document FastAPI integration
- [ ] Document Starlette integration
- [ ] Document generic ASGI pattern
- [ ] Add OAuth2/JWT integration examples
- [ ] Update enterprise.md to reference new utilities

---

## Tests

### Unit Tests

- `tests/unit/test_access_control_enforcer.py`
  - `test_default_enforcer_permits_allowed_roles`
  - `test_default_enforcer_denies_unknown_roles`
  - `test_default_enforcer_checks_admin_for_sensitive_ops`
  - `test_default_enforcer_disabled_permits_all`

### Integration Tests

- `tests/integration/test_fastapi_access_control.py` (requires fastapi)
  - `test_require_roles_permits_valid_role`
  - `test_require_roles_denies_invalid_role`
  - `test_middleware_enforces_settings`

---

## Definition of Done

### Code Complete

- [ ] Protocol defined and documented
- [ ] Default enforcer implemented
- [ ] FastAPI integration working
- [ ] ASGI middleware working
- [ ] All acceptance criteria met

### Quality Assurance

- [ ] Unit tests passing (>90% coverage)
- [ ] Integration tests passing
- [ ] `ruff check` passes
- [ ] `mypy` passes
- [ ] No regression in existing tests

### Documentation

- [ ] Integration guide created
- [ ] API reference updated
- [ ] Examples for common patterns
- [ ] CHANGELOG updated

---

## Risks / Rollback

### Risks

1. **Risk:** FastAPI dependency version conflicts
   - **Mitigation:** Make fastapi integration optional extra

2. **Risk:** Users expect automatic enforcement
   - **Mitigation:** Document clearly that enforcement is opt-in

3. **Risk:** Breaking existing AccessControlSettings usage
   - **Mitigation:** All changes are additive; existing code unchanged

### Rollback Plan

If issues occur:
1. Remove integration modules
2. Keep protocol for future use
3. Existing settings/validation unchanged

---

## Related Stories

- **Related:** Story 4.12 - Alerting Interface (similar pattern: protocol + implementations)
- **Related:** ADR-0004 - Encryption Out of Scope (documents what fapilog doesn't do)

---

## Change Log

| Date | Change | Author |
|------|--------|--------|
| 2026-01-19 | Initial story creation from doc review | Claude |
| 2026-01-21 | Cancelled: scope creep - generic RBAC middleware not logging-specific | Claude |
