File size: 1,355 Bytes
c962775
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
"""Minimal secret-loading boundary that never renders secret values."""

from __future__ import annotations

import os
from collections.abc import Mapping
from dataclasses import dataclass


class SecretConfigurationError(ValueError):
    """Raised when a required secret is unavailable or blank."""


@dataclass(frozen=True, slots=True, repr=False)
class SecretValue:
    """A secret value whose string representations are always redacted."""

    _value: str

    def __post_init__(self) -> None:
        if not self._value.strip():
            raise SecretConfigurationError("Secret values must not be blank.")

    def reveal_for_transport(self) -> str:
        """Return the value only at a trusted integration transport boundary."""

        return self._value

    def __repr__(self) -> str:
        return "SecretValue([REDACTED])"

    def __str__(self) -> str:
        return "[REDACTED]"


def load_required_secret(
    variable_name: str, environ: Mapping[str, str] | None = None
) -> SecretValue:
    """Load a required secret without including its value in validation errors."""

    source = os.environ if environ is None else environ
    value = source.get(variable_name)
    if value is None or not value.strip():
        raise SecretConfigurationError(f"Required secret {variable_name} is not configured.")
    return SecretValue(value)