Skip to content

Commit 22bef9e

Browse files
authored
chore: Improve README (#36)
1 parent 89d6a3f commit 22bef9e

File tree

1 file changed

+183
-24
lines changed

1 file changed

+183
-24
lines changed

README.md

Lines changed: 183 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,204 @@
1-
# STAC Auth Proxy
1+
<div align="center">
2+
<h1 style="font-family: monospace">stac auth proxy</h1>
3+
<p align="center">Reverse proxy to apply auth*n scenarios to STAC APIs.</p>
4+
</div>
5+
6+
---
27

38
> [!WARNING]
49
> This project is currently in active development and may change drastically in the near future while we work towards solidifying a first release.
510
6-
STAC Auth Proxy is a proxy API that mediates between the client and and some internally accessible STAC API in order to provide a flexible authentication mechanism.
11+
STAC Auth Proxy is a proxy API that mediates between the client and an internally accessible STAC API in order to provide a flexible authentication, authorization, and content filtering mechanism.
712

813
## Features
914

10-
- 🔐 Selectively apply OIDC auth to some or all endpoints & methods
11-
- 📖 Augments [OpenAPI](https://swagger.io/specification/) with auth information, keeping auto-generated docs (e.g. [Swagger UI](https://swagger.io/tools/swagger-ui/)) accurate
15+
- 🔐 Authentication: Selectively apply OIDC auth to some or all endpoints & methods
16+
- 🎟️ Content Filtering: Apply CQL2 filters to client requests, filtering API content based on user context
17+
- 📖 OpenAPI Augmentation: Update [OpenAPI](https://swagger.io/specification/) with security requirements, keeping auto-generated docs (e.g. [Swagger UI](https://swagger.io/tools/swagger-ui/)) accurate
1218

13-
### CQL2 Filters
19+
## Usage
1420

15-
| Method | Endpoint | Action | Filter | Strategy |
16-
| -------- | ---------------------------------------------- | ------ | ------ | ---------------------------------------------------------------------------------------------------------- |
17-
| `POST` | `/search` | Read | Item | Append body with generated CQL2 query. |
18-
| `GET` | `/search` | Read | Item | Append query params with generated CQL2 query. |
19-
| `GET` | `/collections/{collection_id}/items` | Read | Item | Append query params with generated CQL2 query. |
20-
| `POST` | `/collections/{collection_id}/items` | Create | Item | Validate body with generated CQL2 query. |
21-
| `PUT` | `/collections/{collection_id}/items/{item_id}` | Update | Item | Fetch STAC Item and validate CQL2 query; merge STAC Item with body and validate with generated CQL2 query. |
22-
| `DELETE` | `/collections/{collection_id}/items/{item_id}` | Delete | Item | Fetch STAC Item and validate with CQL2 query. |
21+
> [!NOTE]
22+
> Currently, the project is only installable by downlaoding the repository. It will eventually be available on Docker ([#5](https://github.com/developmentseed/issues/5)) and PyPi ([#30](https://github.com/developmentseed/issues/30)).
2323
24-
#### Recipes
24+
### Installation
2525

26-
Only return collections that are mentioned in a `collections` array encoded within the auth token.
26+
For local development, his project uses [`uv`](https://docs.astral.sh/uv/) to manage project dependencies and environment.
2727

28+
```sh
29+
uv sync
2830
```
29-
"A_CONTAINEDBY(id, ('{{ token.collections | join(\"', '\") }}' ))"
31+
32+
Otherwise, the application can be installed as a standard Python module:
33+
34+
```sh
35+
python3 install src
3036
```
3137

32-
## Installation
38+
### Running
3339

34-
Set up connection to upstream STAC API and the OpenID Connect provider by setting the following environment variables:
40+
The simplest way to run the project is by calling the module directly:
3541

36-
```bash
37-
export STAC_AUTH_PROXY_UPSTREAM_URL="https://some.url"
38-
export STAC_AUTH_PROXY_OIDC_DISCOVERY_URL="https://your-openid-connect-provider.com/.well-known/openid-configuration"
42+
```sh
43+
python -m stac_auth_proxy
3944
```
4045

41-
Install software:
46+
Alternatively, the application's factory can be passed to Uvicorn:
4247

43-
```bash
44-
uv run python -m stac_auth_proxy
48+
```sh
49+
uvicorn --factory stac_auth_proxy:create_app
4550
```
51+
52+
### Configuration
53+
54+
The application is configurable via environment variables.
55+
56+
- `UPSTREAM_URL`
57+
- The STAC API to proxy requests to
58+
- **Type:** HTTP(S) URL
59+
- **Required:** Yes
60+
- **Example:** `https://your-stac-api.com/stac`
61+
- `OIDC_DISCOVERY_URL`
62+
- OpenID Connect discovery document URL
63+
- **Type:** HTTP(S) URL
64+
- **Required:** Yes
65+
- **Example:** `https://auth.example.com/.well-known/openid-configuration`
66+
- `OIDC_DISCOVERY_INTERNAL_URL`
67+
- The internal network OpenID Connect discovery document URL
68+
- **Type:** HTTP(S) URL
69+
- **Required:** No, defaults to value of `OIDC_DISCOVERY_URL`
70+
- **Example:** `http://auth/.well-known/openid-configuration`
71+
- `DEFAULT_PUBLIC`
72+
- **Description:** Default access policy for endpoints
73+
- **Type:** boolean
74+
- **Default:** `false`
75+
- **Example:** `false`, `1`, `True`
76+
- `PRIVATE_ENDPOINTS`
77+
- **Description:** Endpoints explicitely marked as requiring authentication, for use when `DEFAULT_PUBLIC == True`
78+
- **Type:** JSON object mapping regex patterns to HTTP methods OR to tuples of HTTP methods and an array of strings representing required scopes.
79+
- **Default:**
80+
```json
81+
{
82+
"^/collections$": ["POST"],
83+
"^/collections/([^/]+)$": ["PUT", "PATCH", "DELETE"],
84+
"^/collections/([^/]+)/items$": ["POST"],
85+
"^/collections/([^/]+)/items/([^/]+)$": ["PUT", "PATCH", "DELETE"],
86+
"^/collections/([^/]+)/bulk_items$": ["POST"]
87+
}
88+
```
89+
- `PUBLIC_ENDPOINTS`
90+
- **Description:** Endpoints explicitely marked as not requiring authentication, for use when `DEFAULT_PUBLIC == False`
91+
- **Type:** JSON object mapping regex patterns to HTTP methods
92+
- **Default:**
93+
```json
94+
{
95+
"^/api.html$": ["GET"],
96+
"^/api$": ["GET"]
97+
}
98+
```
99+
- `OPENAPI_SPEC_ENDPOINT`
100+
- Path to serve OpenAPI specification
101+
- **Type:** string or null
102+
- **Default:** `null` (disabled)
103+
- **Example:** `/api`
104+
- `ITEMS_FILTER`
105+
- Configuration for item-level filtering
106+
- **Type:** JSON object with class configuration
107+
- **Default:** `null`
108+
- Components:
109+
- `cls`: Python import path
110+
- `args`: List of positional arguments
111+
- `kwargs`: Dictionary of keyword arguments
112+
- **Example:**
113+
```json
114+
{
115+
"cls": "my_package.filters.OrganizationFilter",
116+
"args": ["org1"],
117+
"kwargs": {
118+
"field_name": "properties.organization"
119+
}
120+
}
121+
```
122+
- `ITEMS_FILTER_ENDPOINTS`
123+
- Where to apply item filtering
124+
- **Type:** JSON object mapping regex patterns to HTTP methods
125+
- **Default:**
126+
```json
127+
{
128+
"^/search$": ["POST"],
129+
"^/collections/([^/]+)/items$": ["GET", "POST"]
130+
}
131+
```
132+
133+
## Architecture
134+
135+
### Middleware Stack
136+
137+
The middleware stack is processed in reverse order (bottom to top):
138+
139+
1. **EnforceAuthMiddleware**
140+
141+
- Handles authentication and authorization
142+
- Configurable public/private endpoints
143+
- OIDC integration
144+
145+
2. **BuildCql2FilterMiddleware**
146+
147+
- Builds CQL2 filters based on request context
148+
- Stores filter in request state
149+
150+
3. **ApplyCql2FilterMiddleware**
151+
152+
- Retrieves filter from request state
153+
- Applies the built CQL2 filter to requests
154+
- Modifies query strings for GET requests
155+
- Modifies JSON bodies for POST/PUT/PATCH requests
156+
157+
4. **OpenApiMiddleware**
158+
159+
- Modifies OpenAPI specification
160+
- Adds security requirements
161+
- Only active if `openapi_spec_endpoint` is configured
162+
163+
5. **AddProcessTimeHeaderMiddleware**
164+
- Adds processing time headers
165+
- Useful for monitoring/debugging
166+
167+
### Data filtering via CQL2
168+
169+
In order to provide row-level content filtering, the system supports generating CQL2 filters based on request context. These CQL2 filters are then set on outgoing requests prior to the upstream API.
170+
171+
> [!IMPORTANT]
172+
> The upstream STAC API must support the [STAC API Filter Extension](https://github.com/stac-api-extensions/filter/blob/main/README.md).
173+
174+
> [!TIP]
175+
> Integration with external authorization systems (e.g. [Open Policy Agent](https://www.openpolicyagent.org/)) can be achieved by replacing the default `BuildCql2FilterMiddleware` with a custom async middleware that is capable of generating [`cql2.Expr` objects](https://developmentseed.org/cql2-rs/latest/python/#cql2.Expr).
176+
177+
#### Example GET Request Flow
178+
179+
```mermaid
180+
sequenceDiagram
181+
Client->>Proxy: GET /collections
182+
Note over Proxy: EnforceAuth checks credentials
183+
Note over Proxy: BuildCql2Filter creates filter immediately
184+
Note over Proxy: ApplyCql2Filter modifies query string
185+
Proxy->>STAC API: GET /collection?filter=(collection=landsat)
186+
STAC API->>Client: Response
187+
```
188+
189+
#### Filters
190+
191+
| Supported | Method | Endpoint | Action | Filter | Strategy |
192+
| -------------------------------------------------------- | -------- | ---------------------------------------------- | ------ | ---------- | ------------------------------------------------------------------------------------------------------ |
193+
|| `POST` | `/search` | Read | Item | Append body with generated CQL2 query. |
194+
|| `GET` | `/search` | Read | Item | Append query params with generated CQL2 query. |
195+
| ❌ ([#22](https://github.com/developmentseed/issues/22)) | `POST` | `/collections/` | Create | Collection | Validate body with generated CQL2 query. |
196+
| ❌ ([#23](https://github.com/developmentseed/issues/23)) | `GET` | `/collections/{collection_id}` | Read | Collection | Append query params with generated CQL2 query. |
197+
| ❌ ([#22](https://github.com/developmentseed/issues/22)) | `PUT` | `/collections/{collection_id}}` | Update | Collection | Fetch Collection and validate CQL2 query; merge Item with body and validate with generated CQL2 query. |
198+
| ❌ ([#22](https://github.com/developmentseed/issues/22)) | `DELETE` | `/collections/{collection_id}` | Delete | Collection | Fetch Collectiion and validate with CQL2 query. |
199+
|| `GET` | `/collections/{collection_id}/items` | Read | Item | Append query params with generated CQL2 query. |
200+
| ❌ ([#25](https://github.com/developmentseed/issues/25)) | `GET` | `/collections/{collection_id}/items/{item_id}` | Read | Item | Validate response against CQL2 query. |
201+
| ❌ ([#21](https://github.com/developmentseed/issues/21)) | `POST` | `/collections/{collection_id}/items` | Create | Item | Validate body with generated CQL2 query. |
202+
| ❌ ([#21](https://github.com/developmentseed/issues/21)) | `PUT` | `/collections/{collection_id}/items/{item_id}` | Update | Item | Fetch Item and validate CQL2 query; merge Item with body and validate with generated CQL2 query. |
203+
| ❌ ([#21](https://github.com/developmentseed/issues/21)) | `DELETE` | `/collections/{collection_id}/items/{item_id}` | Delete | Item | Fetch Item and validate with CQL2 query. |
204+
| ❌ ([#21](https://github.com/developmentseed/issues/21)) | `POST` | `/collections/{collection_id}/bulk_items` | Create | Item | Validate items in body with generated CQL2 query. |

0 commit comments

Comments
 (0)