Files
patrickandClaude Sonnet 5 140751ea48
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (push) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (push) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (push) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (push) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (push) Canceled after 0s
CI / Web (dms-browser) (push) Canceled after 0s
CI / Web (pdf-viewer) (push) Canceled after 0s
CI / Web (shl) (push) Canceled after 0s
DOC-22: externe freigabelinks mit passwort und ablauf
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 09:04:55 +02:00

144 lines
5.9 KiB
Markdown

# DMS-API v1
Schnittstellenbeschreibung: `GET /api/dms/v1/openapi.json` (OpenAPI 3.0, Quelle `internal/openapi/openapi.json`). Die Oberfläche nutzt ausschließlich diese Schnittstelle.
## Versionierung und Routen
Alle Endpunkte liegen unter `/api/dms/v1` (Registerpräfix aus `deploy/routes.tsv`, wie `/api/core/v1`). Es gibt keine DMS-eigene Versionierung: eine neue Version ist ein neues Präfix des zentralen Schemas, bestehende Pfade bleiben unberührt.
## Authentifizierung
`Authorization: Bearer <Core-Modul-JWT>` (EdDSA, `kid`-Header, Claims `sub` und `tenant`; Schlüssel per JWKS von Core, `NEXARCH_DMS_APP_CORE_JWKS_URL`). Der Mandant im Token muss dem Mandanten der Instanz (`NEXARCH_DMS_APP_TENANT_SLUG`) entsprechen. Die Identität kommt ausschließlich aus dem Token; ein mitgeschickter `caller`-Parameter oder `X-User-Id`-Header wird überschrieben.
Ohne Token: `401`. Ohne Erreichbarkeit von Core und ohne bekannten Schlüsselsatz: `503`. Bereits bekannte Schlüssel gelten bei einem Core-Ausfall weiter.
Ausnahmen ohne Nutzeridentität: `GET /download` (Signatur der zuvor angeforderten URL), `POST /retention/destroy-callback` (Archive, Dienst-zu-Dienst) und `GET /openapi.json`.
Übergangsmodus: mit `NEXARCH_DMS_APP_ALLOW_CALLER_IDENTITY=true` gilt ohne Token der Parameter `caller` als Identität (nicht authentifiziert, nur für Entwicklung). Ohne JWKS-URL und ohne diesen Schalter startet der Dienst nicht.
## Rechte
Objektzugriff (Eigentümer, Admin, Prüfer) erzwingt der Guard vor jedem Handler. Dokumentlisten eines Ordners (`GET /folders/documents`, `GET /folders/{id}/documents`) enthalten nur Dokumente des Aufrufers und verlangen eine Identität (`401` ohne). Mit `NEXARCH_DMS_APP_POLICY_URL` fragt das DMS zusätzlich je Route Core (`POST /authorize`, Header `X-Service-Token`) nach dem in der OpenAPI-Beschreibung als `x-permission` deklarierten Recht. Die Rolle stammt aus dem Token-Claim `role`, sonst gilt `NEXARCH_DMS_APP_POLICY_DEFAULT_ROLE` (Standard `user`), Admin-IDs erhalten `tenant_admin`. Fehler oder Ausfall von Core lehnen die Anfrage ab (`503`, nie stillschweigend erlaubt).
## Fehlerformat
Jede Fehlerantwort (`>= 400`, auch `404`/`405` des Routers) hat das Core-Schema:
```json
{"error": {"code": "not_found", "message": "dokument nicht gefunden"}}
```
`code` ist stabil und maschinenlesbar (`invalid_request`, `unauthenticated`, `forbidden`, `not_found`, `method_not_allowed`, `conflict`, `unprocessable`, `payload_too_large`, `locked`, `unavailable`, `internal_error`). Zusatzfelder (z. B. `documentId` beim Duplikat) stehen daneben.
## Beispiele
Die folgenden Blöcke werden von `TestDocumentationExamples` gegen die verdrahtete Anwendung ausgeführt. `capture name=schluessel` merkt sich einen Wert der JSON-Antwort für spätere `{name}`-Platzhalter.
Upload-Sitzung starten:
```example
POST /uploads -> 201
capture sessionId=sessionId
{"filename":"beleg.txt","mimeType":"text/plain","totalSize":16}
```
Datenblock senden (fortsetzbar über `offset`):
```example
PUT /uploads/{sessionId}/chunk?offset=0 -> 200
Rechnung 4711 ok
```
Stand abfragen:
```example
GET /uploads/{sessionId} -> 200
```
Abschließen, Dokument und erste Revision entstehen:
```example
POST /uploads/{sessionId}/complete -> 200
capture documentId=documentId
```
Dokument lesen:
```example
GET /documents/{documentId} -> 200
```
```example
GET /documents/{documentId}/content -> 200
```
```example
GET /documents/{documentId}/revisions -> 200
```
Tags und Metadaten:
```example
GET /documents/{documentId}/tags -> 200
```
```example
GET /documents/{documentId}/metadata -> 200
```
```example
GET /metadata-fields -> 200
```
Ordner und Papierkorb:
```example
GET /folders/children -> 200
```
```example
GET /trash -> 200
```
Dokument in den Papierkorb verschieben (Auswahl von `document_ids` und/oder `folder_ids`; Antwort mit `trashed_*_ids` und `failed` je ID, ein von anderen ausgeliehenes Dokument und ein Ordner mit Dokumenten anderer Nutzer werden abgelehnt) und wiederherstellen:
```example
POST /trash -> 200
{"document_ids":["{documentId}"]}
```
```example
POST /trash/restore -> 200
{"document_ids":["{documentId}"]}
```
Signierte Download-URL anfordern:
```example
GET /documents/{documentId}/download-url -> 200
```
Fehlerformat bei unbekanntem Dokument:
```example
GET /documents/00000000-0000-0000-0000-000000000000/tags -> 403
```
Schnittstellenbeschreibung (ohne Anmeldung):
```example
GET /openapi.json -> 200 anonymous
```
## Freigabelinks (DOC-22)
Ein Dokument lässt sich ohne DMS-Konto teilen: `POST /documents/{id}/share-links` (nur der Eigentümer, Rechte `dms.sharelink.write`) legt einen Link mit Pflichtablauf, optionalem Passwort und optionalem Download-Limit an; die Antwort enthält den Token nur dieses eine Mal in der URL. `GET /documents/{id}/share-links` (Rechte `dms.sharelink.read`) listet bestehende Links ohne Token/Passwort, `DELETE /documents/{id}/share-links/{linkID}` widerruft sofort.
Der Abruf läuft ohne Anmeldung über `GET /share/{token}` bzw. `POST /share/{token}` (Passwort im JSON-Body, nie in der URL oder im Query-String) und ist in der OpenAPI-Beschreibung als `"security": []` markiert. Jeder nicht nutzbare Ausgang (unbekannt, abgelaufen, widerrufen, Limit erreicht) liefert einheitlich `404`; ein gesetztes Passwort liefert ohne Angabe `401 password_required`, nach zu vielen Fehlversuchen `423 locked`.
Konfiguration per Umgebungsvariable (siehe `BETRIEB-UMGEBUNGSVARIABLEN.md`): `NEXARCH_DMS_APP_SHARE_LINKS_ENABLED` (Mandanten-Schalter), `NEXARCH_DMS_APP_SHARE_LINK_DEFAULT_TTL_SECONDS`, `NEXARCH_DMS_APP_SHARE_LINK_MAX_TTL_SECONDS`, `NEXARCH_DMS_APP_SHARE_LINK_MAX_ATTEMPTS`, `NEXARCH_DMS_APP_SHARE_LINK_LOCKOUT_SECONDS`.
## Optionale CMIS-Schnittstelle
Neben der REST-API gibt es eine abschaltbare CMIS-1.1-Schnittstelle (Browser Binding) unter `/api/dms/v1/cmis/browser`. Sie nutzt intern diese REST-API und damit dieselben Rechte- und Mandantenregeln; Einzelheiten in [CMIS.md](CMIS.md).