Files
oidc-test/CLAUDE.md
T
2026-06-17 17:17:18 +02:00

84 lines
3.1 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Flask 2.x Proof-of-Concept to demonstrate fine-grained, dynamic RBAC and multi-tenancy using **Zitadel** as OIDC IdP. Authentication is fully delegated to Zitadel; authorization and project assignments are handled locally via SQLite.
The full specification is in [claude-context.md](claude-context.md).
## Commands
### Local Development (venv)
```bash
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # then fill in Zitadel credentials
flask db-init # initialize SQLite schema
flask seed-db # seed mock data (Firma Müller, 3 projects, sample notes)
flask run
```
### Docker / Production
```bash
docker compose up --build # builds image from Dockerfile, reads .env
docker compose down
```
### Database CLI commands
```bash
flask seed-db # wipes and re-seeds the SQLite database with test fixtures
```
## Architecture
**Flat backend, rich local authorization:**
- **Zitadel (external)** — handles login, issues OIDC tokens carrying the user's global role claim (`Admin`, `Mitarbeiter`, or `Kunde`).
- **`app/auth.py`** — Authlib OIDC client; extracts `sub`, role claims, and token scopes from the IdP response; logs token payload to stdout in development.
- **`app/models.py`** — SQLAlchemy models for the local relational layer:
- `User` (keyed on Zitadel `sub`)
- `Client` (top-level company, e.g. "Firma Müller")
- `Project` (belongs to one Client)
- `ProjectMitarbeiterMapping` (N:M: employees ↔ projects)
- `ProjectKundenMapping` (N:M: external users ↔ projects)
- `Note` (timestamped text entry tied to a Project)
- **`app/routes.py`** — all routing guards live here; unauthorized access always redirects to `login_error.html` (never raises HTTP errors).
- **`app/config.py`** — reads `.env`; follows 12-Factor style.
- **`app/database.py`** — SQLAlchemy engine/session setup.
## Role-Based Access (RBAC) Logic
| Role | Access |
|---|---|
| `Admin` | Full visibility; all clients, projects, users; global export |
| `Mitarbeiter` | Only projects linked via `ProjectMitarbeiterMapping` |
| `Kunde` | Only projects linked via `ProjectKundenMapping` for their client |
**Routing guard pattern:** Every protected route checks (1) Zitadel role from session, (2) local DB mapping for the requested resource. Any mismatch → redirect to `/error` (renders `login_error.html`). The error page has a JS back-button to support test flows.
## Environment Variables
See `.env.example`:
```
FLASK_APP=app
FLASK_ENV=development
SECRET_KEY=...
ZITADEL_CLIENT_ID=...
ZITADEL_CLIENT_SECRET=...
ZITADEL_DISCOVERY_URL=https://<instance>.zitadel.cloud/.well-known/openid-configuration
DATABASE_URL=sqlite:///app.db
```
## Docker Notes
- `Dockerfile` uses `python:3.11-slim`, runs app via `gunicorn`.
- `docker-compose.yml` builds from source (no pre-built images), mounts a volume for the SQLite file, and reads `.env` directly.
- Setting `pull_policy: always` (or equivalent) in the compose file ensures a base-image update triggers a rebuild.