From 2f3a735f30e370bb24a3160937443af16e392cd9 Mon Sep 17 00:00:00 2001
From: David Schwarzmann <david.schwarzmann@codecentric.de>
Date: Sun, 8 Aug 2021 23:24:08 +0200
Subject: [PATCH] docs: Add API spec link helper

---
 concepts/event-log.md                         | 227 ++++++++++++++++++
 docs/details/schema-reference.md              |   2 +-
 .../{event-log.md => event-log.mdx}           |   8 +-
 .../receiving/process-and-acknowledge.mdx     |   6 +-
 docs/getting-started/sending/query-status.mdx |   4 +-
 src/components/ApiLink.js                     |  15 ++
 6 files changed, 255 insertions(+), 7 deletions(-)
 create mode 100644 concepts/event-log.md
 rename docs/getting-started/{event-log.md => event-log.mdx} (97%)
 create mode 100644 src/components/ApiLink.js

diff --git a/concepts/event-log.md b/concepts/event-log.md
new file mode 100644
index 000000000..c83c03b26
--- /dev/null
+++ b/concepts/event-log.md
@@ -0,0 +1,227 @@
+---
+title: Event Log
+---
+
+Im Ereignisprotokoll (Event Log) werden relevante Ereignisse (events) aufgezeichnet. Beim Abruf des Ereignisprotokolls
+liefert die API ein Array von JSON Web Token (JWT) gemäß [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519). Der
+JWT ist einen Security-Event-Token (SET) gemäß [RFC 8417](https://datatracker.ietf.org/doc/html/rfc8417). Wie mit den
+Security-Event-Token (SET) umgegangen wird, wird in diesem Abschnitt beschrieben.
+
+## Events
+
+Das folgende Statusdiagramm zeigt die Status und Ereignisse einer Einreichung. Die Status sind als orange Ovale
+dargestellt. Rechtecke stehen für Ereignisse. Blau dargestellte Ereignisse werden vom Zustelldienst, grüne vom
+empfangenden System erstellt und signiert.
+
+![Statusdiagramm](/images/status/status.svg)
+
+Das Akzeptieren oder Zurückweisen von Einreichungen oder Antworten passiert auf einer rein technischen Ebene und trifft
+keine Aussage über die fachliche Korrektheit der Einreichungen. Gründe für technische Rückweisungen wären beispielsweise
+Probleme bei der Entschlüsselung oder Validierungsfehler der Datenstrukturen. In der folgenden Tabelle sind die Ereignisse,
+ihre Beschreibungen und die Autoren aufgeführt und beschrieben.
+
+| Event                                                           | Autor               | Bedeutung                     |
+|-----------------------------------------------------------------|---------------------|-------------------------------|
+| `https://schema.fitko.de/fit-connect/events/create-submission`  | Zustelldienst       | Die Einreichung wurde durch den Sender angelegt. |
+| `https://schema.fitko.de/fit-connect/events/submit-submission`  | Zustelldienst       | Die Einreichung wurde durch den Sender abgesendet. |
+| `https://schema.fitko.de/fit-connect/events/notify-submission`  | Zustelldienst       | Der Empfänger wurde per Webhook über die Einreichung informiert. |
+| `https://schema.fitko.de/fit-connect/events/forward-submission` |                     | Ein nachgelagertes System hat die Einreichung zur Weiterleitung übernommen. |
+| `https://schema.fitko.de/fit-connect/events/reject-submission`  | Empfangendes System | Die Einreichung wurde durch den Empfänger zurückgewiesen. |
+| `https://schema.fitko.de/fit-connect/events/accept-submission`  | Empfangendes System | Die Einreichung wurde durch den Empfänger akzeptiert. |
+| `https://schema.fitko.de/fit-connect/events/delete-submission`  | Zustelldienst       | Die Einreichung wurde durch den Zustelldienst gelöscht. |
+
+<details>
+
+<summary>🚧 Weitere Ereignisse, relevant für die Zukunft ...</summary>
+
+
+### Ereignisse einer Antwort (Reply Events)
+
+| Event                                                           | Autor                   | Bedeutung                     |
+|-----------------------------------------------------------------|-------------------------|-------------------------------|
+| `https://schema.fitko.de/fit-connect/events/create-reply`       | Zustelldienst           | Die Antwort wurde durch den Sender angelegt. |
+| `https://schema.fitko.de/fit-connect/events/submit-reply`       | Zustelldienst           | Die Antwort wurde durch den Sender abgesendet. |
+| `https://schema.fitko.de/fit-connect/events/notify-reply`       | Zustelldienst           | Der Empfänger wurde per Webhook über die Antwort informiert. |
+| `https://schema.fitko.de/fit-connect/events/forward-reply`      | Adressat der Reply[^1]  | Ein nachgelagertes System hat die Antwort zur Weiterleitung übernommen. |
+| `https://schema.fitko.de/fit-connect/events/reject-reply`       | Adressat der Reply[^1]  | Die Antwort wurde durch den Empfänger zurückgewiesen. |
+| `https://schema.fitko.de/fit-connect/events/accept-reply`       | Adressat der Reply[^1]  | Die Antwort wurde durch den Empfänger akzeptiert. |
+| `https://schema.fitko.de/fit-connect/events/delete-reply`       | Zustelldienst           | Die Antwort wurde durch den Zustelldienst gelöscht. |
+
+[^1]: Wird das Reply vom Sender zum Subscriber gesendet, ist der Subscriber der Adressat; wird es vom Subscriber zum Sender gesendet ist der Sender der Adressat.
+
+Das Akzeptieren oder Zurückweisen von Einreichungen oder Antworten passiert auf einer rein technischen Ebene und trifft keine Aussage über die fachliche Korrektheit der Einreichungen.
+Gründe für technische Rückweisungen wären beispielsweise Probleme bei der Entschlüsselung oder Validierungsfehler der Datenstrukturen.
+
+### Ereignisse eines Vorgangs (Case Events)
+
+| Event                                                   | Autor                                           | Bedeutung |
+|---------------------------------------------------------|-------------------------------------------------|-----------------------------|
+| `https://schema.fitko.de/fit-connect/events/close-case` | Das System, das die Vorgangsreferenz schließt.  | Die Vorgangsreferenz wurde geschlossen, es dürfen keine weiteren Replies mehr erfolgen. |
+
+</details>
+
+## Prüfung eines Security-Event-Token (SET) {#set-validation}
+
+Um eine vollständige Prüfung eines Security-Event-Tokens durchzuführen, MUSS zwingend sowohl die Einhaltung der
+(kryptografischen) Vorgaben als auch die Signatur geprüft werden. Die Prüfung der Signatur des SET ist abhängig vom
+ausstellenden System (Zustelldienst, Subscriber oder Sender). Sowohl die Prüfung der kryptografischen Vorgaben, als auch
+die Prüfung der Signatur darf KEINESFALLS ausgelassen werden.
+
+### Prüfung der Einhaltung von kryptografischen Vorgaben und der Struktur
+
+Alle generierten Security-Event-Tokens MÃœSSEN den Vorgaben aus [RFC 7519](https://tools.ietf.org/html/rfc7519)
+entsprechen und über folgende Header-Attribute verfügen:
+
+| Feld  | Inhalt                              | Erläuterung                                                                           |
+|-------|-------------------------------------|---------------------------------------------------------------------------------------|
+| `typ` | `secevent+jwt`                      | Wird gemäß [RFC 8417, Abschnitt 2.3](https://datatracker.ietf.org/doc/html/rfc8417#section-2.3) auf den festen Wert "`secevent+jwt`" gesetzt. |
+| `alg` | `PS512`                             | Zur Signaturerstellung wird der Signaturalgorithmus RSASSA-PSS mit SHA-512 und MGF1 mit SHA-512 verwendet. Vorgabe gemäß [BSI TR-02102](https://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/Publikationen/TechnischeRichtlinien/TR02102/BSI-TR-02102.html) in der Version 2021-01 (Stand 24. März 2021). |
+| `kid` | Key-ID des zugehörigen Public Keys  | Die Key-ID des Public Key, mit dem die Signatur des JWT geprüft werden kann.          |
+
+Im Payload des signierten SET MÃœSSEN die folgenden [standardisierten Felder](https://www.iana.org/assignments/jwt/jwt.xhtml)
+gesetzt sein:
+
+| Feld   | Inhalt                                         | Erläuterung                                                   |
+|--------|------------------------------------------------|---------------------------------------------------------------|
+| iss    | Id des Token Issuers                           | Diese Angabe dient dazu, um herauszufinden, wer den Token ausgestellt hat. Für SETs, die vom Zustelldienst ausgestellt sind, wird die Host-Adresse (API-URL) verwendet. Bei SETs von empfangenden Systemen ist die `destinationId`, an der dieser die Submission schickt. |
+| iat    | Timestamp (UNIX-Format)                        | Zeitpunkt der Ausstellung des SET.                            |
+| jti    | UUID des Token                                 | Die JWT ID ist eine eindeutige ID des SET bzw. JWT. Es wird eine zufällige UUID verwendet. |
+| sub    | URI, die den Gegenstand des SET identifiziert  | Das Subject eines SWT ist entweder eine Ãœbertragung (submission), eine Antwort (reply) oder eine Vorgangsreferenz (case id). Die Angabe besteht jeweils aus Typ und ID (UUID) der Resource. |
+| events | JSON-Objekt der Events in diesem Event-Token   | Das Objekt "events" beschreibt eine oder mehrere Ereignisse zu einem logischen Sachverhalt bzw. Gesamtereignis, wie bspw. der Versendung einer Einreichung wurde durch den Sender. Dieses Objekt beinhaltet immer zwingend eine URI, die das jeweilige Gesamtereignis eindeutig identifiziert. Das Objekt der URI des Gesamtereignisses ist aktuell leer, kann aber zukünftig weitere Details zu einem Gesamtereignis enthalten. |
+| txn    | URI, die den Vorgang identifiziert             | Als "Transaction Identifier" wird die Vorgangsreferenz angegeben, auch wenn das Subject selbst die Vorgangsreferenz ist. In diesem Fall sind Subject und Transaction Identifier gleich. |
+
+:::note SET Beispiel
+
+```json title="SET Header"
+{
+  "typ": "secevent+jwt"
+  "alg": "PS512",
+  "kid": "dd0409e5-410e-4d98-85b6-f81a40b8d980",
+}
+```
+
+```json title="SET Payload"
+{
+  "iss": "https://api.fitko.de/fit-connect/",
+  "iat": 1622796532,
+  "jti": "0BF6DBF6-CE7E-44A3-889F-82FE74C3E715",
+  "sub": "submission:F65FEAB2-4883-4DFF-85FB-169448545D9F",
+  "events": {
+    "https://schema.fitko.de/fit-connect/events/accept-submission": {}
+  },
+  "txn": "case:F73D30C6-8894-4444-8687-00AE756FEA90"
+}
+```
+
+:::
+
+Im folgenden Beispiel kann die allgemeine Struktur eines SET über folgenden Code validiert werden.
+
+```java
+SignedJWT securityEventToken = SignedJWT.parse(eventToken);
+JWTClaimsSet payload = securityEventToken.getJWTClaimsSet();
+UUID keyId = UUID.fromString(securityEventToken.getHeader().getKeyID());
+
+validateTokenStructure(securityEventToken);
+
+verifySignature(securityEventToken, keyId); // Abhängig von der Quelle
+```
+
+```java
+boolean validateTokenStructure(SignedJWT securityEventToken) {
+  try {
+    validateHeader(signedJWT.getHeader());
+    validatePayload(signedJWT.getJWTClaimsSet());
+  } catch (ParseException e) {
+    throw new SecurityEventTokenValidationException("The payload of the SET could not get parsed properly.");
+  }
+}
+
+private void validateHeader(JWSHeader header) {
+  validateTrueOrElseThrow(header.getAlgorithm() == JWSAlgorithm.PS512, "The provided alg in the SET header is not allowed.");
+  validateTrueOrElseThrow(header.getType().toString().equals("secevent+jwt"), "The provided typ in the SET header is not secevent+jwt");
+  validateTrueOrElseThrow(header.getKeyID() != null, "The kid the SET was signed with is not set.");
+}
+
+private void validatePayload(JWTClaimsSet payload) throws ParseException {
+  validateTrueOrElseThrow(payload.getClaim("iss") != null, "The claim iss is missing in the payload of th SET.");
+  validateTrueOrElseThrow(payload.getClaim("iat") != null, "The claim iat is missing in the payload of th SET.");
+  validateTrueOrElseThrow(payload.getClaim("jti") != null, "The claim jti is missing in the payload of th SET.");
+  validateTrueOrElseThrow(payload.getClaim("sub") != null, "The claim sub is missing in the payload of th SET.");
+  validateTrueOrElseThrow(payload.getClaim("txn") != null, "The claim txn is missing in the payload of the SET.");
+  validateTrueOrElseThrow(payload.getClaim("events") != null, "The claim events is missing in the payload of the SET.");
+  validateTrueOrElseThrow(payload.getJSONObjectClaim("events").keySet().size() == 1, "Only exactly one event is allowed.");
+
+  String uuidPattern = "\\b[0-9a-fA-F]{8}\\b-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-\\b[0-9a-fA-F]{12}\\b";
+
+  String subject = payload.getStringClaim("sub");
+  validateTrueOrElseThrow(subject.matches("(submission|case|reply):" + uuidPattern), "The provided subject does not match the allowed pattern.");
+
+  String transactionId = payload.getStringClaim("txn");
+  validateTrueOrElseThrow(transactionId.matches("case:" + uuidPattern), "The provided txn does not match the allowed pattern.");
+}
+
+private void validateTrueOrElseThrow(boolean expression, String msg) {
+  if (!expression) {
+    throw new SecurityEventTokenValidationException(msg);
+  }
+}
+```
+
+### Signaturprüfung eines vom Zustelldienst ausgestellten SET
+
+Um die Signatur eines SET zu überprüfen, welches vom Zustelldienst ausgestellt wurde, ist es notwendig auf die
+verwendeten Schlüssel zugreifen zu können. Der Zustelldienst stellt ein JSON Web Key (JWK) Set öffentlich zugänglich
+unter der URL `/.well-known/jwks.json` bereit. Ein Beispiel für ein JWK Set ist in folgendem Ausschnitt dargestellt:
+
+```json
+{
+  "keys": [
+    {
+      "alg": "PS512",
+      "e": "AQAB",
+      "key_ops": [
+        "verify"
+      ],
+      "kid": "6508dbcd-ab3b-4edb-a42b-37bc69f38fed",
+      "kty": "RSA",
+      "n": "65rmDz943SDKYWt8KhmaU…ga16_y9bAdoQJZRpcRr3_v9Q"
+    },
+    {
+      "alg": "PS512",
+      "e": "AQAB",
+      "key_ops": [
+        "verify"
+      ],
+      "kid": "14a70431-01e6-4d67-867d-d678a3686f4b",
+      "kty": "RSA",
+      "n": "wnqKgmQHSqJhvCfdUWWyi8q…yVv3TrQVvGtsjrJVjvJR-s_D7rWoBcJVM"
+    }
+  ]
+}
+```
+
+Mit diesem JWK Set kann die Signatur eines Security-Event-Tokens überprüft werden. Hierfür muss der Schlüssel mit der
+passenden `kid` aus dem Header des SET’s im JWK Set gesucht werden. Dann kann man mit diesem und einer entsprechenden
+Bibliothek eine Signaturprüfung durchführen. Im folgenden Beispiel wird die
+Bibliothek [nimbus-jose-jwt](https://connect2id.com/products/nimbus-jose-jwt) für die Prüfung genutzt.
+
+```java
+static final ZUSTELLDIENST_BASE_URL = "https://zustelldienst.example.com";
+
+boolean verifySignature(SignedJWT securityEventToken, String keyId) {
+  JWKSet jwks = JWKSet.load(ZUSTELLDIENST_BASE_URL + "/.well-known/jwks.json");
+  JWK publicKey = jwks.getKeyByKeyId(keyId)
+
+  if (publicKey.getAlgorithm() != JWSAlgorithm.PS512) {
+    throw new RuntimeException("The key specified for signature verification doesn't use/specify PS512 as algorithm.")
+  }
+
+  JWSVerifier jwsVerifier = new RSASSAVerifier(publicKey.toRSAKey());
+  return signedJWT.verify(jwsVerifier);
+}
+```
+
+### :construction: Signaturprüfung eines vom Senders/Subscriber ausgestellten SET
+
+TBD
diff --git a/docs/details/schema-reference.md b/docs/details/schema-reference.md
index b20d8ce1c..84e2d2780 100644
--- a/docs/details/schema-reference.md
+++ b/docs/details/schema-reference.md
@@ -306,7 +306,7 @@ Diese enthält unter anderem den verschlüsselten Metadaten- sowie Fachdatensatz
 
 Der Empfänger [entschlüsselt mit seinem privaten Schlüssel](../getting-started/receiving/decrypt.mdx) den Metadatensatz.
 Nach der Entschlüsselung des Metadatensatzes prüft der Empfänger, ob eine Fachdatenreferenz vorhanden ist und diese einer der im Zustellpunkt definierten Fachschemareferenz entspricht.
-Sofern dies nicht der Fall ist, [wird die Einreichung zurückgewiesen](../getting-started/event-log.md#events) (siehe Event `https://schema.fitko.de/fit-connect/events/reject-submission`).
+Sofern dies nicht der Fall ist, [wird die Einreichung zurückgewiesen](../getting-started/event-log.mdx#events) (siehe Event `https://schema.fitko.de/fit-connect/events/reject-submission`).
 
 ### Fachdatensatz prüfen
 
diff --git a/docs/getting-started/event-log.md b/docs/getting-started/event-log.mdx
similarity index 97%
rename from docs/getting-started/event-log.md
rename to docs/getting-started/event-log.mdx
index 290445544..f170efc45 100644
--- a/docs/getting-started/event-log.md
+++ b/docs/getting-started/event-log.mdx
@@ -2,6 +2,8 @@
 title: Event Log
 ---
 
+import ApiLink from '@site/src/components/ApiLink'
+
 Im Ereignisprotokoll (Event Log) werden relevante Ereignisse (events) aufgezeichnet. Beim Abruf des Ereignisprotokolls
 liefert die API ein Array von JSON Web Token (JWT) gemäß [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519). Der
 JWT ist einen Security-Event-Token (SET) gemäß [RFC 8417](https://datatracker.ietf.org/doc/html/rfc8417). Wie mit den
@@ -233,7 +235,7 @@ benötigt, sowie die `submissionId` aus dem Payload. Konkret sind das hier
 }
 ```
 
-Mit der `submissionId` kann über den Endpunkt [`GET /submissions/{submissionID}`](../apis/delivery-service#get-/submissions/-submissionId-) die zugehörige `destinationId` ermittelt
+Mit der `submissionId` kann über den Endpunkt <ApiLink to="/submissions/{submissionId}" /> die zugehörige `destinationId` ermittelt
 werden. Hier ist das konkret der Wert `92f2f581-c89d-44a5-b834-1fe3f6fa48d5`.
 
 ```http title="Abfrage der Submission"
@@ -244,12 +246,12 @@ GET /submissions/F65FEAB2-4883-4DFF-85FB-169448545D9F
 }
 ```
 
-Über diese kann dann das JWK Set der zugehörigen Destination über einen HTTP Request abgefragt werden. In dem Attribut
+Über diese kann dann das JWK Set der zugehörigen Destination über ein <ApiLink to="/destinations/{destinationId}" /> abgefragt werden. In dem Attribut
 `publicKeys` ist das JWK Set abgelegt. In den darin enthaltenen Schlüsseln kann dann der Schlüssel mit `kid` von oben
 gesucht und für die Verifikation genutzt werden.
 
 ```http title="Abfrage der Destination"
-GET /submissions/92f2f581-c89d-44a5-b834-1fe3f6fa48d5
+GET /destinations/92f2f581-c89d-44a5-b834-1fe3f6fa48d5
 {
   "publicKeys": {
     "keys": [
diff --git a/docs/getting-started/receiving/process-and-acknowledge.mdx b/docs/getting-started/receiving/process-and-acknowledge.mdx
index 2635ab3f7..d2d14d4fa 100644
--- a/docs/getting-started/receiving/process-and-acknowledge.mdx
+++ b/docs/getting-started/receiving/process-and-acknowledge.mdx
@@ -6,6 +6,8 @@ title: Empfangsbestätigung
 import Tabs from '@theme/Tabs'
 import TabItem from '@theme/TabItem'
 
+import ApiLink from '@site/src/components/ApiLink'
+
 
 Der letzte Schritt zum Empfang einer Einreichung ist die Bestätigung des Empfangs und damit auch der Gültigkeit der
 Einreichung. Mit Gültigkeit ist hier gemeint, dass alle Informationen erfolgreich heruntergeladen, entschlüsselt und im
@@ -80,13 +82,13 @@ entspricht. Weiterhin ist es notwendig, dass die Id der Einreichung und des zuge
   </TabItem>
 </Tabs>
 
-Dies kann dann über ein [`POST /submissions/<uuid>/events`](../../apis/delivery-service#post-/submissions/-submissionId-/events) erreicht werden. Hier wird das Event über den Request-Body
+Dies kann dann über ein <ApiLink to="/submissions/{submissionId}/events" withMethod="post" /> erreicht werden. Hier wird das Event über den Request-Body
 übertragen und vom Zustelldienst syntaktisch und auf eine korrekte Signatur geprüft. Sind alle Prüfungen erfolgreich
 durchlaufen, wird das SET im Event Log abgespeichert und der Zustand der Einreichung abhängig vom Event geändert.
 
 ## Event Log abfragen
 
-Der Event Log einer Einreichung kann über [`GET /submissions/<uuid>/events`](../../apis/delivery-service#get-/submissions/-submissionId-/events) abgefragt werden. Hierbei wird der Event Log der Einreichung
+Der Event Log einer Einreichung kann über <ApiLink to="/submissions/{submissionId}/events" /> abgefragt werden. Hierbei wird der Event Log der Einreichung
 zurückgeliefert, der die verschiedenen Statusübergänge bzw. abgelegten Ereignisse beinhaltet. Das Ergebnis könnte wie folgt aussehen:
 
 ```json
diff --git a/docs/getting-started/sending/query-status.mdx b/docs/getting-started/sending/query-status.mdx
index 4908f2158..51711ad12 100644
--- a/docs/getting-started/sending/query-status.mdx
+++ b/docs/getting-started/sending/query-status.mdx
@@ -3,6 +3,8 @@ title: Status abfragen
 sidebar_position: 9
 ---
 
+import ApiLink from '@site/src/components/ApiLink'
+
 Nachdem die Einreichung versendet worden ist, kann der Status dieser abgefragt werden. Der Status ist für das sendende
 System relevant, da hierüber das empfangende System mitteilt, ob es die Einreichung technisch korrekt verarbeiten konnte.
 Bis zu einer Bestätigung über den Status *Accepted* sollte das System sämtliche Inhalte einer Einreichung vorhalten.
@@ -26,7 +28,7 @@ der darunterliegenden Tabelle beschrieben sind.
 | `rejected`    | Die Einreichung wurde durch den Empfänger zurückgewiesen. |
 | `accepted`    | Die Einreichung wurde durch den Empfänger akzeptiert. |
 
-Der Status wird über `GET /submissions/<uuid>/events` abgefragt. Hierbei wird der Event Log der Einreichung
+Der Status wird über <ApiLink to="/submissions/{submissionId}/events" /> abgefragt. Hierbei wird der Event Log der Einreichung
 zurückgeliefert, der die verschiedenen Statusübergänge bzw. abgelegten Ereignisse beinhaltet. Das Ergebnis könnte wie folgt aussehen:
 
 ```json
diff --git a/src/components/ApiLink.js b/src/components/ApiLink.js
new file mode 100644
index 000000000..3690b7175
--- /dev/null
+++ b/src/components/ApiLink.js
@@ -0,0 +1,15 @@
+import React from "react"
+import Link from '@docusaurus/Link'
+
+export default ({ to, withMethod='get', children }) => {
+  const url = `/docs/apis/delivery-service#${withMethod.toLowerCase()}-${to.replaceAll(/\{|\}/g, '-')}`
+  if (children) {
+    return <Link to={url}>{children}</Link>
+  } else {
+    return <Link to={url}><code>{withMethod.toUpperCase()} {to}</code></Link>
+  }
+
+}
+
+// http://localhost:3000/docs/apis/delivery-service#post-/submissions/%3CsubmissionId%3E/events
+// http://localhost:3000/docs/apis/delivery-service#post-/submissions/-submissionId-/events
-- 
GitLab