Adds display name validation, terms_version, locale sync trigger and pgTAP tests (KTO-01 criteria 1, 2, 10, 11). Includes GoTrue spike results. Refs #1 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
129 lines
18 KiB
Markdown
129 lines
18 KiB
Markdown
# Spike B0 – Ergebnisse zu GoTrue (Spec #1, KTO-01)
|
||
|
||
| | |
|
||
|---|---|
|
||
| Issue | #1 (KTO-01), Aufgabe B0 |
|
||
| Spec | `docs/specs/1-auth-registrierung-anmeldung.md` (§ 4.1, 4.3, 5.4, 7.1, 8.2, 9.1) |
|
||
| Datum | 2026-11-02 |
|
||
| Autor | backend-dev |
|
||
| Versionen | Supabase CLI 3.0.0, GoTrue `v2.197.0` (Image `public.ecr.aws/supabase/gotrue:v2.197.0`), Postgres 17.11, Mailpit v1.31.3 |
|
||
|
||
## Vorgehen und Grenzen der Aussagekraft
|
||
|
||
- Die lokale CLI wurde mit einer **temporären** `config.toml` gestartet: `enable_confirmations = true`, `minimum_password_length = 10`, `email_sent = 100`, `max_frequency = "1s"`, Vorlagen für Bestätigung und Reset mit `.Data.locale`-Verzweigung (Vorlagen und Änderungen sind wieder entfernt; `config.toml` im Repo ist unverändert, das ist Aufgabe B2).
|
||
- Alle Requests liefen gegen `http://127.0.0.1:54321/auth/v1` (Kong, wie die App). Die Drosselungstests (c, d) liefen zusätzlich gegen einen **zweiten GoTrue-Container** desselben Images im Netz der CLI, mit den Umgebungsvariablen des CLI-Containers plus `GOTRUE_RATE_LIMIT_HEADER=X-Forwarded-For` und `GOTRUE_RATE_LIMIT_EMAIL_SENT=5`. Das entspricht dem Self-Hosting-Pfad (Env statt `config.toml`).
|
||
- **Korrektur während des Spikes:** Die erste Messung zu Krit. 4 zeigte 422 `user_already_exists`; die Ursache war `GOTRUE_SMS_AUTOCONFIRM=true` (CLI-Standard), nicht GoTrue selbst. Ein Wiederholungstest mit `[auth.sms] enable_confirmations = true` bestätigt das Verhalten der Spec (siehe A1).
|
||
- **Nicht aussagekräftig lokal:** SMTP-Anbieter, Verhalten hinter dem echten Produktions-Gateway, Header-Behandlung durch den Proxy, Mailzustellung. Die Mail-Rate-Limits der CLI gelten nicht (siehe d).
|
||
- Die Versionsprüfung (h) basiert auf der `docker-compose.yml` im Supabase-Repository (Stand heute, Branch `master`); eine eigene Self-Hosting-Instanz gibt es noch nicht.
|
||
|
||
## Kurzfassung: Abweichungen von der Spec
|
||
|
||
| # | Abweichung | Wirkung |
|
||
|---|---|---|
|
||
| **A1** | **Der Enumerationsschutz (Krit. 4) hängt an einer weiteren Einstellung: `GOTRUE_SMS_AUTOCONFIRM` muss `false` sein.** Die CLI-Vorlage setzt `[auth.sms] enable_confirmations = false` und damit `GOTRUE_SMS_AUTOCONFIRM=true`. Dann liefert Signup mit einer *bestätigten* Adresse **422 `user_already_exists`**. Mit `[auth.sms] enable_confirmations = true` (`GOTRUE_SMS_AUTOCONFIRM=false`) kommt wie in der Spec **200 mit erfundenem Nutzer** (zufällige ID, `identities` leer, keine Mail, Log `user_repeated_signup`). | Neue Zeile AUTH-CFG-24 für B2/B3 (`auth.sms.enable_confirmations = true`, Env `GOTRUE_SMS_AUTOCONFIRM=false`). Die Selbsthosting-Vorlage `docker/.env.example` setzt `ENABLE_PHONE_AUTOCONFIRM=true`, die Falle gilt dort also auch. Die Spec-Annahme 4.3 stimmt dann. |
|
||
| **A2** | **Ohne `GOTRUE_RATE_LIMIT_HEADER` gibt es gar keine IP-Drosselung** (weder Login noch Signup noch Refresh). Auch mit gesetzter Konfiguration wird ein Request **ohne** diesen Header nicht gedrosselt (60 Fehllogins ohne `X-Forwarded-For` gegen den Container mit Header-Konfiguration: kein 429). Die lokale CLI kann den Header nicht setzen. | AUTH-CFG-13 ist Pflicht, nicht optional. GoTrue darf nur über den Proxy erreichbar sein, der den Header setzt (sonst umgeht ein direkter Zugriff die Drosselung). Drosselungstests (Krit. 7) sind gegen die CLI nicht möglich (Frage Q1). Betrifft B2, B3, D1, D2, T3. |
|
||
| **A3** | `email_sent` wirkt in der lokalen CLI nicht (`GOTRUE_RATE_LIMIT_EMAIL_SENT=360000`, obwohl `email_sent = 100` gesetzt war). Wirkt im Self-Hosting-Env und ist dort **instanzweit**. | AUTH-CFG-09 lokal nicht testbar; Seiteneffekt auf Enumeration (siehe d). |
|
||
| **A4** | Trigger-Fehler kommen als **HTTP 500 mit der Meldung des Triggers** im Body (`{"code":"P0001","message":"display_name_invalid"}`), nicht als generisches „Database error saving new user“. | Der Client kann den Grund unterscheiden. Verhalten ist versionsabhängig, siehe (f). |
|
||
| **A5** | Das JWT einer Recovery-Sitzung trägt `amr = [{"method":"otp"}]`, **identisch** zur Sitzung nach Bestätigungslink. Es ist also nicht als Recovery erkennbar. | Der `amr`-Weg aus 5.4 entfällt; es gilt R12 (Pfad „Wenn nein“). |
|
||
| **A6** | Für den Linkablauf zählt `auth.users.confirmation_sent_at` bzw. `recovery_sent_at`, **nicht** `auth.one_time_tokens.created_at`. | T3 setzt die Spalten an `auth.users` zurück. |
|
||
| **A7** | `/otp` und `/magiclink` lassen sich nicht abschalten, und ein neues Konto per `/otp` ist möglich, **wenn** der Aufrufer gültige Metadaten (`data`) mitschickt. Ohne Metadaten blockiert der Trigger (500). | AUTH-CFG-20 (Gateway-Sperre) bleibt nötig; der Trigger ersetzt sie nicht. |
|
||
|
||
Alle übrigen Annahmen der Spec wurden bestätigt (Details unten).
|
||
|
||
## Ergebnis je Punkt
|
||
|
||
### (a) Betreffzeile und Vorlage werten `.Data.locale` aus
|
||
|
||
- **Annahme:** Vorlage und Betreff lassen sich mit `{{ if and .Data (eq (index .Data "locale") "de") }}` verzweigen; sonst Englisch.
|
||
- **Test:** Signup mit `locale` de, en und ohne `locale`; Mails in Mailpit gelesen. Danach `profiles.locale` auf `en` gesetzt (Sync-Trigger) und Passwort-Reset angefordert.
|
||
- **Ergebnis: bestätigt.** `de` führte zu deutschem Betreff und Text, `en` und fehlende Angabe zu Englisch. Betreff und Text verzweigen gleich. Nach der Profiländerung auf `en` kam die Reset-Mail englisch (Sync-Trigger wirkt Ende-zu-Ende). `index .Data "locale"` ist sicher, wenn der Schlüssel fehlt (leer, kein Fehler). Die CLI liefert die Vorlagen per interner URL (`GOTRUE_MAILER_TEMPLATES_*=http://…kong:8088/email/…`), wie beim Self-Hosting.
|
||
- **Konsequenz B2:** Der Rückfall „zweisprachiger Betreff“ ist nicht nötig. `{{ .Data.locale }}` und `index` funktionieren beide; für Else-Zweig `index` verwenden.
|
||
|
||
### (b) `type`-Werte für `token_hash`
|
||
|
||
- **Test:** `POST /verify` mit `token_hash` aus der Mail.
|
||
- **Ergebnis: bestätigt.** Bestätigung: `type=email` **und** `type=signup` werden akzeptiert (beide getestet, Sitzung wird ausgestellt, `email_confirmed_at` gesetzt). Reset: `type=recovery`. Ein Token ist nach der Einlösung verbraucht: zweiter Versuch 403 `otp_expired` („Email link is invalid or has expired“).
|
||
- **Konsequenz B2/A2/A4:** Bestätigungsvorlage mit `type=email`, Reset mit `type=recovery`. Der Client mappt `otp_expired` (403) auf `linkInvalidOrExpired`, auch für „schon benutzt“.
|
||
|
||
### (c) Limiter beim Passwort-Login (AUTH-CFG-10)
|
||
|
||
- **Annahme:** Passwort-Login nutzt den Token-Limiter (150 je 5 min je IP, Burst 30).
|
||
- **Test:** Fehlgeschlagene Logins in Schleife. (1) Gegen die CLI über Kong: 600 Logins, 300 Refresh-Aufrufe, 80 Signups, 80 Recovers in wenigen Sekunden, **kein einziges 429**. (2) Gegen den Spike-Container mit `GOTRUE_RATE_LIMIT_HEADER=X-Forwarded-For` und fester Quell-IP: Login #31 wird mit **429 `over_request_rate_limit`** abgelehnt (Burst 30). Nach 20 s waren wieder 10 Logins möglich (etwa 0,5 pro Sekunde, also 150 je 300 s). Signup: ebenfalls Burst 30, danach 429 (Limiter `RATE_LIMIT_OTP` = 30 je 5 min). Eine zweite Quell-IP war währenddessen nicht betroffen. Die 429-Antwort hat **keinen** `Retry-After`- oder `X-RateLimit-*`-Header, nur `X-Sb-Error-Code: over_request_rate_limit`.
|
||
- **Ergebnis: Limiter bestätigt, aber mit schwerwiegender Einschränkung (A2).** Passwort-Login nutzt `GOTRUE_RATE_LIMIT_TOKEN_REFRESH` (150 je 5 min, Burst 30); Signup/Recover/Resend nutzen `GOTRUE_RATE_LIMIT_OTP` (30 je 5 min). **Ohne `GOTRUE_RATE_LIMIT_HEADER` greift keiner der Limiter.** Die CLI setzt den Header nicht und bietet keinen Schalter dafür; lokal und im CI (CLI) gibt es deshalb keine Drosselung. Zusätzlich zeigen die Audit-Logs `ip_address: ""`.
|
||
- **Konsequenz:**
|
||
- AUTH-CFG-13 ist für Staging/Prod **zwingend** (sonst keine Drosselung von Passwort-Raten, Krit. 7). Der Proxy muss den Header setzen und Client-Werte überschreiben.
|
||
- T3/D2: Der Drosselungstest (Krit. 7) kann nicht gegen die CLI laufen. Optionen siehe Frage Q1.
|
||
- Die Tabelle AUTH-CFG-10/11: Env-Namen stimmen (`…_TOKEN_REFRESH`, `…_OTP`). Die Aussage „Signups, Recover, Resend teilen sich diesen Limiter“ ist nur für Signup geprüft; `/recover` wurde bei unbekannten Adressen nicht gedrosselt, weil keine Mail ausgelöst wird (nur das IP-Limit gilt, und das greift ohne Header nicht).
|
||
|
||
### (d) `email_sent` instanzweit?
|
||
|
||
- **Test:** Spike-Container mit `GOTRUE_RATE_LIMIT_EMAIL_SENT=5`. Sechs Signups mit verschiedenen Adressen und verschiedenen Quell-IPs.
|
||
- **Ergebnis: bestätigt, instanzweit.** Signup 1–5 ok, ab Signup 6 429 `over_email_send_rate_limit` („email rate limit exceeded“) für alle IPs. Das gilt auch für `/recover` an bestehende Adressen. In der **lokalen CLI** wirkt `email_sent` nicht: der CLI-Container läuft mit `GOTRUE_RATE_LIMIT_EMAIL_SENT=360000`, obwohl `config.toml` 100 enthielt (die CLI-Doku nennt „Requires auth.email.smtp to be enabled“). Der Standard der CLI-Vorlage (`email_sent = 2`) greift also lokal ebenfalls nicht.
|
||
- **Neue Nebenwirkung auf Krit. 4:** Ist das Mail-Limit erschöpft, antwortet `/recover` für eine **bestehende** Adresse mit 429, für eine unbekannte Adresse mit 200 (es wird keine Mail ausgelöst). Das ist ein Enumerations-Seitenkanal während der Sperre. Mögliche Gegenmaßnahmen: Limit höher setzen oder ein Proxy vor GoTrue. Siehe Frage Q2.
|
||
- **Konsequenz B2/B3:** AUTH-CFG-09 nur im Env-Block wirksam; in `config.toml` den Wert dokumentieren, aber als „lokal ohne Wirkung“ kennzeichnen. R3 bleibt bestehen.
|
||
|
||
### (e) Mailfänger
|
||
|
||
- **Ergebnis:** **Mailpit** v1.31.3 (Container `supabase_inbucket_shuffle`, Name historisch). Web-Oberfläche und API: `http://127.0.0.1:54324`. Relevante Endpunkte: `GET /api/v1/messages` (Liste mit `To[].Address`, `Subject`, `ID`), `GET /api/v1/message/{id}` (`HTML`, `Text`), `DELETE /api/v1/messages`. SMTP-Port nach außen nur mit `smtp_port` in `[local_smtp]`.
|
||
- **Konsequenz T3:** Der Mail-Helfer spricht die Mailpit-API. Das Token steht im HTML (und im Text) doppelt; das erste Vorkommen genügt. Mails je Test per `To` filtern (zufällige Adressen), nicht per globalem Löschen, wenn Tests parallel laufen.
|
||
|
||
### (f) Trigger-Fehler bei `signUp`
|
||
|
||
- **Test:** Signup ohne `display_name`, ohne Altersbestätigung (`age_confirmed:"false"`), ohne `terms_version`.
|
||
- **Ergebnis: abweichend (A4).** HTTP **500**, Header `X-Sb-Error-Code: unexpected_failure`, Body `{"code":"P0001","message":"display_name_invalid"}` (bzw. `age_confirmation_required`, …). Der Text des Triggers wird also durchgereicht. Im GoTrue-Log steht zusätzlich „current transaction is aborted“. Es entsteht kein Nutzer.
|
||
- **Konsequenz A1/A2 (Flutter):** `registrationRejected` darf an `AuthException.message` unterscheiden, ist aber nur zum Logging und für Zusatzhinweise gedacht; die Hauptvalidierung bleibt im Client. Das Format ist nicht stabil vertraglich zugesichert (bei GoTrue-Updates erneut prüfen; T3 sichert es mit einem API-Test ab).
|
||
|
||
### (g) `weak_password`
|
||
|
||
- **Test:** Signup mit 9 Zeichen.
|
||
- **Ergebnis: bestätigt.** HTTP 422, `error_code: weak_password`, `msg: "Password should be at least 10 characters."`, `weak_password.reasons = ["length"]`. Mit `password_requirements = ""` kommt kein Grund `characters`.
|
||
- **Konsequenz A1:** Mapping 4.4 stimmt.
|
||
|
||
### (h) Versionen CLI und Self-Hosting-Image
|
||
|
||
- **Ergebnis: bestätigt.** CLI 3.0.0 startet GoTrue `v2.197.0`. Die `docker-compose.yml` des Supabase-Repos (Branch `master`, heute) verwendet `supabase/gotrue:v2.197.0`. Das Image `public.ecr.aws/supabase/gotrue:v2.197.0` ließ sich mit den Env-Variablen der CLI direkt starten (zusätzlich erforderlich: `API_EXTERNAL_URL`).
|
||
- **Konsequenz B3/D1:** Das Self-Hosting-Image beim Aufsetzen auf `v2.197.0` pinnen (nicht `latest`) und bei jedem Update die Punkte (c), (d), (f) wiederholen. Die Postgres-Version im Compose (17.6.1.136) weicht von der CLI (17.11.0.011) im Patchstand ab; Hauptversion 17 stimmt mit `config.toml` überein.
|
||
|
||
### (i) Welche Spalte begrenzt die Linkgültigkeit?
|
||
|
||
- **Test:** Neuen Reset angefordert, nur `auth.users.recovery_sent_at` um 2 h zurückgesetzt, Token eingelöst. Dann neuen Reset, nur `auth.one_time_tokens.created_at` um 2 h zurückgesetzt, Token eingelöst. Für die Bestätigung `confirmation_sent_at` ebenso.
|
||
- **Ergebnis: bestätigt für `auth.users`, abweichend von der Tabelle.** Zurückgesetztes `recovery_sent_at` (bzw. `confirmation_sent_at`) ergibt 403 `otp_expired`. Zurückgesetztes `one_time_tokens.created_at` allein hat **keine** Wirkung (Einlösung gelang).
|
||
- **Konsequenz T3:** `e2e/support/db.ts` setzt `auth.users.recovery_sent_at` (Reset) bzw. `confirmation_sent_at` (Bestätigung) auf `now() - (otp_expiry + 1 s)`. Direktzugriff als `postgres` (Port 54322) genügt.
|
||
|
||
### (j) `amr` im Recovery-JWT
|
||
|
||
- **Test:** Access-Token nach `verify` mit `type=recovery`, nach `verify` mit `type=email` und nach Passwort-Login dekodiert.
|
||
- **Ergebnis: abweichend (A5).** Recovery: `amr = [{"method":"otp"}]`, `aal1`. Bestätigung: ebenfalls `otp`. Passwort-Login: `password`. Recovery ist von einer Bestätigungssitzung nicht unterscheidbar; nach dem Passwortwechsel in der Sitzung bleibt der Eintrag gleich.
|
||
- **Konsequenz A4/R12:** Pfad „Wenn nein“ aus 5.4: Der Guard `passwordRecovery` lebt nur im laufenden Prozess (Ereignis `passwordRecovery`). Eine Ableitung aus `amr = otp` würde auch frisch bestätigte Konten in den Reset-Screen zwingen und ist nicht zulässig. R12 bleibt als akzeptiertes Restrisiko.
|
||
|
||
## Weitere geprüfte Annahmen (4.3 und Tabelle 4.1)
|
||
|
||
| Annahme | Test | Ergebnis |
|
||
|---|---|---|
|
||
| **Krit. 4, bestätigte Adresse:** 200 mit erfundenem Nutzer, keine Mail | Signup mit bestätigter Adresse, zuerst mit CLI-Standard, dann mit `[auth.sms] enable_confirmations = true` | **Nur mit `GOTRUE_SMS_AUTOCONFIRM=false` bestätigt (A1):** Dann 200, zufällige ID (nicht die des echten Nutzers), `identities` leer, keine Mail, kein neues Konto, das ursprüngliche Passwort bleibt gültig. Mit dem CLI-Standard (`SMS_AUTOCONFIRM=true`) kommt stattdessen 422 `user_already_exists`. |
|
||
| **Krit. 4, unbestätigte Adresse:** Mail erneut, Passwort und Metadaten unverändert | Signup mit neuem Passwort und neuer `locale` für unbestätigte Adresse | **Bestätigt:** 200 mit echtem Nutzer, Bestätigungsmail erneut, Passwort-Hash und `user_metadata` unverändert, kein zweites Konto. |
|
||
| Resend / Reset immer 200 | `/resend` und `/recover` mit bestätigter, unbestätigter und unbekannter Adresse | **Bestätigt** (200 `{}`); Mail geht nur an eine existierende, passende Adresse. Einschränkung bei Mail-Limit: siehe (d). |
|
||
| **Krit. 6/7:** `email_not_confirmed` erst nach richtigem Passwort | Login unbestätigt mit richtigem/falschem Passwort, unbekannte Adresse | **Bestätigt:** richtig → 400 `email_not_confirmed`; falsch und unbekannt → 400 `invalid_credentials`, identische Meldung. |
|
||
| **Krit. 8:** Reset-Link einmalig | Zweites Einlösen | **Bestätigt** (403 `otp_expired`). |
|
||
| **Krit. 9:** Reset beendet andere Sitzungen | Sitzungen A, B; Reset über Recovery-Sitzung; Passwort geändert | **Bestätigt:** Refresh-Token von A und B → `refresh_token_not_found`. Der alte Access-Token von A wird von GoTrue (`/user`) mit 403 abgelehnt, **von PostgREST aber weiter akzeptiert (200)**, bis er abläuft. Das ist R8 (bis 60 min), bewusst akzeptiert. |
|
||
| `same_password` | Reset mit altem Passwort | **Bestätigt:** 422 `same_password`. |
|
||
| Passwortmindestlänge 10 | Signup mit 9 Zeichen | **Bestätigt** (siehe g). |
|
||
| Benachrichtigungen aus (Krit. 11) | Passwortänderung beobachtet | **Bestätigt:** Es kommt keine Mail. Der CLI-Standard ist „aus“; in `config.toml` trotzdem explizit setzen. |
|
||
| Magic Link abschaltbar (AUTH-CFG-20) | `/otp` und `/magiclink` für bestätigten Nutzer; `/otp` für neue Adresse | **Nicht abschaltbar (A7):** Bestätigter Nutzer bekommt Mail „Your sign-in link“. Neue Adresse **ohne** Metadaten: 500 durch den Trigger (`age_confirmation_required`), kein Nutzer. Neue Adresse **mit** gültigen Metadaten: Konto wird angelegt (unbestätigt, mit Passwort-Hash eines Zufallswerts) und eine Bestätigungsmail geht raus. Ein zweiter Aufruf innerhalb von `max_frequency` ergibt 429. |
|
||
| Zusätzliche Auffälligkeit | – | `GOTRUE_MAILER_SECURE_EMAIL_CHANGE_ENABLED=true` (CLI-Standard) bleibt für KTO-11 offen (AUTH-CFG-23). |
|
||
|
||
## Konsequenzen für B2, B3 und die übrigen Aufgaben
|
||
|
||
- **B2:** Alle Werte aus 4.1 in `config.toml`. `email_sent` und die Rate-Limit-Einstellungen sind lokal wirkungslos bzw. nur mit Header wirksam; das im Kommentar kenntlich machen. Vorlagen: `type=email` / `type=recovery`, `index .Data "locale"`.
|
||
- **B3:** Der Env-Block für Self-Hosting muss `GOTRUE_RATE_LIMIT_HEADER` als Pflicht aufführen, plus `GOTRUE_RATE_LIMIT_EMAIL_SENT`, `…_TOKEN_REFRESH`, `…_OTP`, `…_VERIFY`. Release-Checkliste: Drosselung am echten Gateway mit 31 Fehllogins einer IP prüfen; Test, dass ein gefälschter `X-Forwarded-For` überschrieben wird.
|
||
- **Spec-Anpassungen** (nach Entscheidung von Chris): 4.3 (Krit. 4, Enumeration), 5.4 (`amr`), 7.1 (Krit. 7, 8, 4), 8.2 (B0-Text), R2, R3, R6, R12.
|
||
- **B2/B3 (neu, A1):** Zeile AUTH-CFG-24 ergänzen: `auth.sms.enable_confirmations = true` bzw. `GOTRUE_SMS_AUTOCONFIRM=false` (SMS ist ausgeschaltet, die Einstellung steuert trotzdem den Enumerationsschutz bei E-Mail-Signup). T3 sichert das mit einem API-Test für Krit. 4 ab (bestätigte Adresse → 200, `identities` leer).
|
||
- **T3:** Drosselungstests nur gegen einen GoTrue mit gesetztem Header und Requests mit Header (Q1).
|
||
- **KTO-11-Hinweis:** `user_metadata.locale` kann der Nutzer selbst über `auth.updateUser(data: …)` ändern, die Mailsprache kann so von `profiles.locale` abweichen (Wirkung nur auf das eigene Konto). Der Client ändert die Sprache ausschließlich über `profiles`; der Sync-Trigger zieht `user_metadata` bei der nächsten Profiländerung nach.
|
||
|
||
## Offene Fragen an Chris
|
||
|
||
| # | Frage | Hintergrund |
|
||
|---|---|---|
|
||
| Q1 | **Krit. 7:** Die lokale CLI kann keine IP-Drosselung (kein `RATE_LIMIT_HEADER`). Soll der Drosselungstest im CI gegen einen zusätzlich gestarteten GoTrue-Container mit gesetztem Header laufen (wie in diesem Spike), oder nur als manuelle Prüfung auf Staging? | A2, Aufwand für D2/T3. |
|
||
| Q2 | Seitenkanal bei erschöpftem Mail-Limit (`/recover` bestehende Adresse 429, unbekannte 200). Akzeptiert (Limit 30/h, Angriff erzeugt vor allem Ausfälle), oder soll das Limit höher gesetzt bzw. durch einen Proxy ergänzt werden? | (d), R3, R6. |
|