Commit c782fadb authored by Alex Kakiris's avatar Alex Kakiris
Browse files

read invoker credentials from Vault

parent 66b3187d
Loading
Loading
Loading
Loading
+40 −0
Changes for capif_backend/backoffice_service/utils/vault.py: 40 added lines, 0 removed lines.
Original line number Diff line number Diff line
import os
import re
import requests

class VaultError(Exception):
    """Vault could not be reached or returned an unexpected response"""
# invoker_id comes from the URL and becomes part of the Vault path. Restricting it to
# [A-Za-z0-9_-] prevents path traversal (e.g. "..%2f..%2fca") out of the user's subtree.
# Do not relax this without another guard.
_INVOKER_ID_RE = re.compile(r"[A-Za-z0-9_-]+")

def vault_addr():
    """Base URL of Vault. VAULT_ADDR, otherwise falls back to http://VAULT_HOSTNAME:VAULT_PORT as before."""
    addr = os.getenv("VAULT_ADDR")
@@ -42,3 +50,35 @@ def store_invoker(user_uuid, invoker_id, private_key, certificate):
        return False

    return True

def read_invoker(user_uuid, invoker_id):
    """Read an invoker's stored credentials from Vault KV v2.

    Returns the stored dict (private_key, certificate, api_invoker_id), or None if
    no such invoker exists for this user. Raises VaultError on any other failure,
    so callers can tell "not found" (404) apart from "Vault is down" (5xx).
    """

    if not _INVOKER_ID_RE.fullmatch(invoker_id or ""):
        return None

    path = f"secret/data/backoffice/users/{user_uuid}/invokers/{invoker_id}"
    try:
        response = requests.get(
            f"{vault_addr()}/v1/{path}",
            headers=vault_headers(),
            verify=vault_verify(),
            timeout=10,
        )
    except requests.exceptions.RequestException as e:
        raise VaultError(f"Vault read failed for invoker {invoker_id}: {e}") from e

    if response.status_code == 404:
        return None
    if not response.ok:
        raise VaultError(f"Vault read failed for invoker {invoker_id}: {response.status_code}")

    try:
        return response.json()["data"]["data"]
    except (ValueError, KeyError, TypeError) as e:
        raise VaultError(f"Unexpected Vault response shape for invoker {invoker_id}") from e
 No newline at end of file