
# Konfigurationsplan anwenden

`POST /v1/config-plans/{plan_id}:apply` · Berechtigung `config:write`, die Leseberechtigungen des Plans und die Schreibberechtigung jeder Art, die er ändert

Wendet einen gespeicherten [Plan](create.md) auf die Umgebung an. Jede Änderung wird in einer Transaktion geschrieben: Schlägt eine fehl, wird keine übernommen. Ein Plan kann nur einmal angewendet werden.

## Anfrage

| Parameter | Beschreibung |
|---|---|
| `plan_id` | Die Plan-ID, `cfgp_…`. |

Der Body ist optional.

| Feld | Beschreibung |
|---|---|
| `resolved_secrets` | Objekt mit `secret_ref`-Name → Wert für jede Änderung mit `requires_secret: true` (zum Beispiel `KEENAUTH_CONNECTION_GITHUB_CLIENT_SECRET`). Werte sind höchstens 8 KiB groß. Sie werden vom Plan nie gespeichert, protokolliert oder zurückgegeben. |
| `confirm_deletions` | Liste der `<kind>/<key>`-Löschungen, die Sie bestätigen, zum Beispiel `connection/legacy`. Die Warnungen des Plans (`deletion_requires_confirmation`) listen die Löschungen auf, die dies erfordern. |
| `sp_host` | Der Service-Provider-Host der Umgebung, zum Beispiel `login.example.com`. Nur erforderlich, wenn der Plan das OIDC-Material einer Enterprise-Verbindung erstellt (der Callback `https://<sp_host>/sso/<connection id>/callback` wird daraus festgelegt); eine Anfrage, die ihn nicht braucht, darf ihn nicht senden. |

- Die Schreibberechtigungen sind `environment-settings:write`, `identity-settings:write`, `attack-protection:write`, `branding:write` (für `branding` und `application_branding`), `connections:write`, `apis:write`, `roles:write` und `applications:write`, nur für Arten mit einer anderen Änderung als `no_op`.
- Vor dem Schreiben wird das Ziel erneut gelesen. Hat sich die Version einer geplanten Ressource oder der Inhalt der Arten des Plans geändert, nachdem der Plan erstellt wurde, wird nichts geschrieben, der Plan wird `conflicted` und die Anfrage schlägt mit `409 config_conflict` fehl. Erstellen Sie einen neuen Plan. Mit `prune` werden auch die Arten verglichen, die sich auf die geplanten Ressourcen beziehen, und die Verweise zwischen Ressourcen werden erneut geprüft; ein defekter Verweis macht den Plan ebenfalls `conflicted`.
- Das Löschen einer Verbindung oder einer Anwendung, die in den letzten 30 Tagen ein Token erhalten hat, muss in `confirm_deletions` wiederholt werden. Andernfalls schlägt die Anfrage mit `409 deletion_not_confirmed` fehl und der Plan bleibt `planned`.
- Das OIDC-Material einer Enterprise-Verbindung (`spec.oidc`) und ihr Client-Secret werden nach dem Verbindungsdatensatz an den Federation-Service geschrieben. Wird dieser zweite Schreibvorgang abgelehnt, bleibt der Plan `planned`, der Verbindungsdatensatz existiert möglicherweise bereits, und ein neuer Plan schreibt nur das fehlende Material. Ein `spec.oidc` von `null` lässt das Material unverändert. Das Client-Secret einer Social-Verbindung kann noch nicht geschrieben werden: Ein Plan mit einer solchen `requires_secret`-Änderung gibt `501 not_implemented` zurück, nachdem die Secrets geprüft wurden. Es wird nichts geschrieben.
- `Idempotency-Key` wird unterstützt. Senden Sie nach Möglichkeit keine Secrets im Body: Der Datensatz des Schlüssels speichert einen Hash des Body.

## Antwort

`200 OK` mit `Cache-Control: no-store`: der Plan in Form von [Einen Konfigurationsplan abrufen](get.md) mit `status: applied` und `applied_at`.

Der Konfigurationsstand der betroffenen Ressourcen vor der Änderung wird als Revision aufbewahrt (die neuesten 50 pro Umgebung, höchstens 90 Tage). Das Anwenden schreibt einen `config.applied`-Eintrag in das Verwaltungsprotokoll der Umgebung mit den Anzahlen erstellter, geänderter, gelöschter und unveränderter Ressourcen; jede geänderte Ressource schreibt zusätzlich ihren eigenen Eintrag.

## Fehler

| HTTP | Code | Wann |
|---|---|---|
| 400 | `malformed_request`, `unknown_field` | Der Body ist kein JSON oder hat ein anderes Feld als `resolved_secrets`, `confirm_deletions` und `sp_host`. |
| 401 | `unauthenticated` | Die Zugangsdaten fehlen, sind ungültig oder widerrufen. |
| 403 | `insufficient_permissions` | Den Zugangsdaten fehlt eine erforderliche Berechtigung; `required_permissions` listet alle auf. |
| 404 | `not_found` | Der Plan existiert nicht, gehört zu einer anderen Umgebung oder wurde gelöscht. |
| 409 | `config_conflict` | Das Ziel hat sich geändert, nachdem der Plan erstellt wurde; der Plan ist jetzt `conflicted`. |
| 409 | `config_plan_not_applicable` | Der Plan wurde bereits angewendet oder ist `conflicted`, oder eine andere Anfrage wendet ihn gerade an (`plan_status: applying`); `plan_status` gibt seinen Status an. |
| 409 | `deletion_not_confirmed` | Eine geschützte Löschung fehlt in `confirm_deletions`; `unconfirmed_deletions` listet sie auf. |
| 409 | `conflict` | Eine andere Ressource verwendet bereits einen eindeutigen Wert einer Änderung (`errors[].pointer` ist `/changes/<i>/spec/<field>`, Code `duplicate`); der Plan bleibt `planned`. |
| 409 | `connection_in_use` | Die zu löschende Verbindung wird noch verwendet; der Plan bleibt `planned`. |
| 409 | `connection_test_required` | Eine Änderung erstellt eine `enterprise`-Verbindung `active` oder aktiviert eine ohne erfolgreiche Test-Anmeldung (`errors[].pointer` ist `/changes/<i>/spec`, Code `test_required`); der Plan bleibt `planned`. |
| 410 | `config_plan_expired` | Die Gültigkeitsdauer des Plans ist abgelaufen; erstellen Sie einen neuen Plan. |
| 413 | `config_document_too_large` | Die Änderungen oder die dafür aufbewahrte Revision (höchstens 10 MiB) sind zu groß, um sie in einer Anfrage anzuwenden; planen Sie weniger Arten. |
| 422 | `validation_failed` | Siehe unten. |
| 422 | `policy_conflict` | Die Änderung steht im Widerspruch zu einer Richtlinie der Umgebung; der Plan bleibt `planned`. |
| 429 | `rate_limited` | Das `config_plan`-Ratenlimit (mit der Planerstellung geteilt) wurde überschritten, oder es werden für diese Umgebung gleichzeitig zu viele Pläne berechnet oder angewendet; der Plan bleibt `planned`; wiederholen Sie die Anfrage nach Ablauf des Zeitraums `Retry-After`. |
| 501 | `not_implemented` | Der Plan benötigt das Client-Secret einer Social-Verbindung, das noch nicht geschrieben werden kann. |
| 503 | `service_unavailable` | Eine Abhängigkeit (das Verzeichnis oder bei OIDC-Material einer Enterprise-Verbindung der Federation-Service) ist vorübergehend nicht verfügbar, oder die Plankapazität ist vorübergehend ausgeschöpft; es wurde nichts geschrieben bzw. beim Federation-Service nur der Verbindungsdatensatz. Wiederholen Sie die Anfrage nach Ablauf des Zeitraums `Retry-After`. |

| `errors[].pointer` | `errors[].code` | Bedeutung |
|---|---|---|
| `/resolved_secrets/<NAME>` | `secret_missing` | Der Plan benötigt dieses Secret. |
| `/resolved_secrets/<NAME>` | `secret_unused` | Der Plan benötigt dieses Secret nicht. |
| `/resolved_secrets/<NAME>` | `secret_invalid` | Der Wert ist leer, länger als 8 KiB oder kein Text. |
| `/resolved_secrets/<NAME>` | `invalid` | Der Name hat nicht die Form `KEENAUTH_…` (`A-Z`, `0-9`, `_`). |
| `/sp_host` | `sp_host_required`, `sp_host_unused`, `invalid` | Der Plan erstellt OIDC-Material, und es wurde kein Host angegeben, keiner wird benötigt, oder der Host ist kein kleingeschriebener DNS-Name. |
| `/changes/<i>/spec/oidc/...` | wie in der OIDC-API der Verbindung | Der Federation-Service hat das Material an dieser Stelle des Plans `changes` abgelehnt. |
| `/confirm_deletions/<i>` | `duplicate`, `not_a_deletion` | Der Eintrag ist doppelt oder im Plan keine Löschung. |
| `/confirm_deletions/<i>` | `invalid` | Der Eintrag hat nicht die Form `<kind>/<key>`. |
| `/resolved_secrets`, `/confirm_deletions` | `too_many` | Mehr als 5.000 Einträge. |
| `/changes/<i>/spec/...` | wie in der eigenen API der Ressource | Die Umgebung hat die Änderung an dieser Stelle des Plans `changes` abgelehnt. |

## Beispiel

```bash
curl -X POST "https://api.eu.keenauth.com/v1/config-plans/cfgp_01m4qpqtywfs7930tb6kqn5rc5:apply" \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm_deletions": ["connection/legacy"]}'

keenauth config apply cfgp_01m4qpqtywfs7930tb6kqn5rc5 --confirm-deletion connection/legacy
```

`keenauth config apply --secrets-from-env` liest jedes Secret, das der Plan benötigt, aus der Umgebungsvariablen gleichen Namens und gibt es nie aus.
