84 lines
3.1 KiB
Markdown
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.
|