Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,86 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.5.0] - 2026-01-22

### Added

- **Manifest-based authorization**: Package grant and translation grant support for Quilt packages
- New `Package` entity type in Cedar schema with registry, packageName, and hash attributes
- `quilt:ReadPackage` action for package-level authorization
- `PackageToken` model for immutable package grants (`quilt_uri` + `mode`)
- `PackageMapToken` model for logical-to-physical path translation grants
- `PackageAccessRequest` model for S3 access requests in package context
- `PackageMap` class for resolving package manifests to physical S3 locations
- **Package grant enforcement**: Content-based authorization anchored to immutable package manifests
- `enforce_package_grant()` - validates package membership via manifest resolution
- `enforce_translation_grant()` - validates logical path translation to physical S3 locations
- `enforce_with_routing()` - routes enforcement based on token claim structure (scopes vs packages)
- Package name wildcard matching (e.g., `my/pkg/*` matches `my/pkg/subdir`)
- Package scope parsing and validation (`Package:pkg@hash:read`)
- **Token creation functions**: Factory functions for package-based tokens
- `create_token_with_package_grant()` - issue package grant tokens with Quilt URIs
- `create_token_with_package_map()` - issue translation grant tokens with logical paths
- `validate_package_token()` - validate and decode package grant tokens
- `validate_package_map_token()` - validate and decode translation grant tokens
- **Quilt URI utilities**: Parse and validate Quilt package URIs (`src/raja/quilt_uri.py`)
- URI parsing with registry, package name, and hash extraction
- Package name wildcard matching for hierarchical authorization
- URI validation with comprehensive error messages
- **Package map utilities**: S3 path parsing and package manifest resolution (`src/raja/package_map.py`)
- Parse S3 paths into bucket/key components
- Resolve package manifests from registry to physical locations
- **Lambda handler**: Package resolver Lambda for manifest resolution (`lambda_handlers/package_resolver/`)
- **Integration tests**: Comprehensive demonstrations of manifest-based authorization
- `test_rajee_package_grant.py` - 4 tests for package grant enforcement (allow/deny member files, write operations)
- `test_rajee_translation_grant.py` - 6 tests for translation grant enforcement (mapped/unmapped paths, multi-region, write operations)
- `test_package_map.py` - integration test for package map resolution
- **Documentation**: Extensive design and implementation documentation
- `docs/rajee-manifest.md` - admin-facing guide for manifest-based authorization
- `specs/4-manifest/01-package-grant.md` - package grant design (903 lines)
- `specs/4-manifest/02-package-map.md` - package map design (52 lines)
- `specs/4-manifest/03-package-gaps.md` - analysis of gaps and edge cases (336 lines)
- `specs/4-manifest/04-package-hardening.md` - security hardening considerations (441 lines)
- `specs/4-manifest/05-package-more.md` - advanced features and extensions (746 lines)
- `specs/4-manifest/06-demo-coverage.md` - demonstration coverage analysis (371 lines)
- **Unit tests**: Comprehensive unit test coverage for new modules
- `test_manifest.py` - 64 lines of manifest parsing and validation tests
- `test_package_map.py` - 22 lines of package map utility tests
- `test_quilt_uri.py` - 55 lines of Quilt URI parsing and validation tests
- Expanded `test_enforcer.py` with 306+ new lines for package grant enforcement
- Expanded `test_token.py` with 168+ new lines for package token validation
- Expanded `test_compiler.py` with 23+ new lines for package scope compilation
- Expanded `test_control_plane_router.py` with 91+ new lines for package grant API endpoints

### Changed

- **Cedar parser**: Removed legacy Cedar statement parsing (`parse_cedar_to_statements()`)
- Parser now focuses on policy extraction and validation
- Simplified parser interface with fewer internal parsing steps
- **Compiler**: Enhanced to support package scopes in policy compilation
- Added package scope extraction from Cedar policies
- Support for `Package` entity types in policy analysis
- **Enforcer**: Extended with package-aware authorization logic
- Package scope matching with wildcard support
- Package action validation (read-only enforcement)
- Routing logic to dispatch between scope-based and package-based enforcement
- **Token operations**: Extended with package grant validation and creation
- Token validation now handles multiple claim structures (scopes, quilt_uri, logical paths)
- Comprehensive error handling for malformed package tokens
- **Control plane API**: Enhanced with package grant token issuance endpoints
- Extended `/token` endpoint to support `grant_type=package` and `grant_type=translation`
- API now accepts `quilt_uri`, `logical_bucket`, `logical_key`, and `logical_s3_path` parameters
- Expanded API response models to include package grant tokens
- **Public API**: Expanded exports to include package grant functionality
- 15+ new exports in `src/raja/__init__.py` for package grants
- All package-related models, functions, and utilities now publicly accessible
- **Dependencies**: Added `pyproject.toml` dev dependencies for manifest testing

### Fixed

- **Type checking**: Fixed type errors in package grant enforcement logic
- **Code formatting**: Applied ruff formatting across all new modules

## [0.4.4] - 2026-01-21

### Added
Expand Down
File renamed without changes.
257 changes: 257 additions & 0 deletions docs/rajee-manifest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
# RAJEE, Immutable Manifests, and Authorization

## 1. Purpose of this document

This document explains **how RAJEE uses immutable Quilt manifests to enforce authorization**, and what that design implies for:

- the shape of **Cedar policies** stored in Amazon Verified Permissions (AVP)
- the shape of **RAJA authorization requests**
- the mental model admins should use when granting access

This is an **admin- and architecture-facing** document, not an API reference.

---

## 2. The core idea (in one paragraph)

In Quilt, **authority is not defined by paths or file lists**.
Authority is defined by **immutable manifests**.

Cedar determines *whether* a role may access a given manifest.
RAJA turns that decision into a **RAJ capability token**.
RAJEE enforces that capability by **unfolding the manifest** and allowing access *only* to the objects named by it.

The manifest does **not grant authority**.
It **defines the boundary of authority**.

---

## 3. Why immutable manifests matter

Immutable manifests give Quilt a property most authorization systems lack:

> A stable, content-defined identifier whose meaning never changes.

This allows authorization to be expressed as:

> “Role R may read Manifest M”

without ever enumerating the files inside M in policy.

Because manifests are immutable:

- the authorized set cannot silently expand
- caching is safe
- tokens remain truthful for their lifetime
- enforcement does not rely on mutable storage structure (prefixes, folders)

---

## 4. Two kinds of grants in Quilt

Quilt supports **two distinct grant types**, because they solve different problems.

### 4.1 Path grants (location-based)

Path grants authorize access to:

- a bucket
- an optional path (prefix or exact key)

They are useful for:

- infrastructure data
- shared prefixes
- operational workflows

Path grants compile into Cedar policies over **S3Path resources** and explicit S3 actions.

### 4.2 Manifest grants (content-based)

Manifest grants authorize access to:

- an **immutable manifest identifier** (as Quilt package with hash)

They are useful for:

- packages
- datasets
- any collection with thousands of files
- cross-bucket layouts

Manifest grants compile into **one Cedar policy per grant**, regardless of file count.

This document focuses on **manifest grants**.

---

## 5. Cedar model for manifest grants

### 5.1 Resource model

Cedar treats each immutable manifest as a first-class resource:

- Resource type: `Manifest`
- Resource ID: the immutable manifest identifier (hash or versioned ID)

Cedar does **not** know or care about the files inside the manifest.

### 5.2 Action model

Cedar actions are **package-level**, not S3-level:

- `quilt:ReadPackage`
- `quilt:WritePackage`

This avoids leaking storage details into policy.

### 5.3 Example Cedar policy

```cedar
permit(
principal == Role::"analyst",
action == Action::"quilt:ReadPackage",
resource == Manifest::"pkg-abc@sha256:deadbeef"
);
```

This policy says exactly one thing:

> The role `analyst` may read the package identified by this immutable manifest.

It does **not** enumerate buckets, paths, or files.

---

## 6. RAJA authorization requests for manifest grants

### 6.1 What RAJA asks Cedar

When a client requests access to a package, RAJA asks AVP:

- principal: the role
- action: `quilt:ReadPackage` or `quilt:WritePackage`
- resource: the manifest ID
- optional context: time, client posture, etc.

Cedar returns **ALLOW or DENY**.

### 6.2 What goes into the RAJ (JWT)

If allowed, RAJA mints a RAJ containing only **mechanically enforceable claims**:

- `package_uri` (immutable)
- `mode` (read or readwrite)
- `exp` / `nbf`
- `aud`
- optional audit metadata

The RAJ does **not** contain:

- file lists
- prefixes
- buckets
- mutable references

The RAJ is a **capability**: possession implies authority.

---

## 7. How RAJEE enforces manifest grants

RAJEE is the **enforcement point**.

For each client request:

1. RAJEE validates the RAJ:
- signature
- audience
- expiry
- action compatibility

2. RAJEE resolves the manifest:
- using the immutable `package_uri`
- from a trusted, canonical location
- optionally from cache

3. RAJEE enforces membership:
- requested `(bucket, key)` must be a member of the manifest
- no other objects are permitted

4. RAJEE executes the S3 operation on behalf of the client.

At no point does RAJEE:

- consult Cedar again
- trust caller-supplied paths
- allow access outside the manifest boundary

---

## 8. Why this does not introduce ambient authority

This design remains **capability-based**, not ambient.

- Authority originates in Cedar.
- Authority is made explicit in the RAJ.
- The manifest is used only as **evidence** to check membership.

The client cannot:

- choose the manifest
- modify the manifest
- expand the authorized set

The manifest **constrains** authority; it does not create it.

---

## 9. Implications for admins

### 9.1 What admins grant

Admins grant access to:

- **packages (manifests)**, or
- **paths**

They do not grant access to individual files.

### 9.2 Why grants scale

A single manifest grant can safely authorize:

- thousands of files
- across multiple buckets
- without policy explosion

Revocation is simple:

- disable the grant
- let outstanding RAJs expire

---

## 10. Design invariants (non-negotiable)

- Manifests used for authorization are immutable.
- Cedar policies never enumerate files.
- RAJ scopes are explicit and minimal.
- RAJEE enforces exact membership.
- AVP is a decision engine, not a data catalog.

---

## 11. Summary

Manifest grants invert traditional authorization:

- IAM authorizes **locations**
- Quilt authorizes **meaning**

By anchoring authority to immutable manifests and enforcing it via RAJEE, Quilt achieves:

- precision
- scale
- auditability
- and security properties that prefix-based IAM cannot provide.
1 change: 1 addition & 0 deletions lambda_handlers/package_resolver/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Package manifest resolver Lambda handler."""
18 changes: 18 additions & 0 deletions lambda_handlers/package_resolver/handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from __future__ import annotations

from raja.manifest import package_membership_checker, resolve_package_manifest, resolve_package_map


def resolve_manifest(quilt_uri: str):
"""Resolve a Quilt+ URI to a list of physical locations."""
return resolve_package_manifest(quilt_uri)


def resolve_translation_map(quilt_uri: str):
"""Resolve a Quilt+ URI to a logical-to-physical map."""
return resolve_package_map(quilt_uri)


def check_membership(quilt_uri: str, bucket: str, key: str) -> bool:
"""Return True if the bucket/key is a member of the Quilt package."""
return package_membership_checker(quilt_uri, bucket, key)
13 changes: 13 additions & 0 deletions policies/schema.cedar
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ entity Role {}
entity S3Bucket {}
entity S3Object in [S3Bucket] {}

// Quilt Package Resources
entity Package {
registry: String,
packageName: String,
hash: String,
}

// S3 Actions
action "s3:GetObject" appliesTo {
principal: [User, Role],
Expand Down Expand Up @@ -89,3 +96,9 @@ action "s3:DeleteBucket" appliesTo {
principal: [User, Role],
resource: [S3Bucket]
}

// Quilt Package Actions
action "quilt:ReadPackage" appliesTo {
principal: [User, Role],
resource: [Package]
}
Loading