Раскройте credential
Покажите сохранённый секрет названному оператору — и встретьте каждую защиту, которую ставит путь reveal.
Самое чувствительное чтение в сервисе, прогнанное защита за защитой: Reveal возвращает plaintext ровно один раз на осознанный, именованный, аудируемый запрос.
Цель
Раскрыть plaintext одного credential токеном со scope reveal и наблюдать каждый отказ: неверный scope, анонимный вызов, удалённая запись.
Предварительные условия
- Сервисный токен со scope
provider-credentials.reveal:export REVEAL_TOKEN=...; admin-scoped токен для контраста (Service Plane Tokens). idсохранённого credential (сценарий 07).
Шаги
1. Админского scope недостаточно
Вызовите POST /internal/v1/admin/credentials/{id}/reveal с admin-токеном и меткой актора. Ожидаемый 403 FORBIDDEN — права на редактирование не подразумевают видеть секреты; эти scope выдаются раздельно.
2. Анонимности отказано
Тот же вызов с reveal-токеном, но без X-Admin-Actor. Ожидаемый 400 INVALID_REQUEST — reveal без названного оператора оставил бы бессмысленный audit-след, поэтому он отвергнут конструкцией.
3. Сам reveal
curl -s -X POST "$CREDS_BASE/internal/v1/admin/credentials/$CRED_ID/reveal" \
-H "Authorization: Bearer $REVEAL_TOKEN" \
-H "X-Admin-Actor: panel:olha"
Ожидаемый 200: plaintext credentials, открытая configuration, координаты владельца, revealed_at. Это работает даже на disabled-записи — предположительно сломанный ключ инспектируют именно тогда, когда он выключен (Defaults and Lifecycle).
revealed = r.json()
assert revealed["credentials"]["api_key"].startswith("sk-") # the actual secret
4. След переживает ответ
Запросите audit-ленту по event_type=credential.revealed. Ожидаемо: событие с actor_id = "service:<client_id>!panel:olha", метаданные называют раскрытые имена полей (никогда значения) — закоммичено до отправки ответа и освобождено от очистки по ретенции.
5. Удалено — значит исчезло
Мягко удалите credential, раскройте снова. Ожидаемый 404 CREDENTIAL_NOT_FOUND — окно восстановления хранит шифротекст для операторов задачи очистки, а не для reveal.
Проверено тестом test_s08_reveal_a_credential.