97 lines
3.4 KiB
Python
97 lines
3.4 KiB
Python
"""One durable credential for the fixed RepoPulse legacy adoption target."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import re
|
|
import secrets
|
|
|
|
from sqlalchemy.dialects.sqlite import insert as sqlite_insert
|
|
from sqlalchemy.exc import SQLAlchemyError
|
|
from sqlmodel import Session
|
|
|
|
from ..core.time import utc_now
|
|
from ..db.models import Secret
|
|
from . import secret_store
|
|
|
|
_INTENT_ID = re.compile(r"^rpa-[0-9a-f]{20}$")
|
|
_REF = "ENROLL_REPOPULSE_TARGET_HMAC"
|
|
|
|
|
|
class RepoPulseTargetCredentialError(RuntimeError):
|
|
"""A sanitized fixed-target credential error."""
|
|
|
|
|
|
def get_or_create_repopulse_target_credential(
|
|
session: Session, *, intent_id: str
|
|
) -> tuple[str, bytes]:
|
|
"""Insert once, then reuse the encrypted credential on every retry.
|
|
|
|
The caller must have durably reserved the same adoption intent before this
|
|
method is called. The plaintext is returned only to the in-process private
|
|
TLS client and must never be added to a receipt, response, or log.
|
|
"""
|
|
if not isinstance(intent_id, str) or _INTENT_ID.fullmatch(intent_id) is None:
|
|
raise RepoPulseTargetCredentialError("adoption_intent_invalid")
|
|
if secret_store.secret_storage_status().get("productionReady") is not True:
|
|
raise RepoPulseTargetCredentialError("secret_store_not_ready")
|
|
|
|
enrollment_id = f"enr-{intent_id.removeprefix('rpa-')}"
|
|
candidate = secrets.token_urlsafe(48)
|
|
metadata = {
|
|
"intentId": intent_id,
|
|
"enrollmentId": enrollment_id,
|
|
"rotationState": "active",
|
|
"credentialFingerprint": hashlib.sha256(candidate.encode("ascii")).hexdigest()[
|
|
:16
|
|
],
|
|
}
|
|
now = utc_now()
|
|
try:
|
|
session.execute(
|
|
sqlite_insert(Secret)
|
|
.values(
|
|
id=_REF,
|
|
name=_REF,
|
|
encrypted_value=secret_store._fernet()
|
|
.encrypt(candidate.encode("ascii"))
|
|
.decode("ascii"),
|
|
created_at=now,
|
|
updated_at=now,
|
|
used_by_json=[f"enrollment:{enrollment_id}", "deployment-controller"],
|
|
metadata_json=metadata,
|
|
)
|
|
.on_conflict_do_nothing(index_elements=[Secret.id])
|
|
)
|
|
session.commit()
|
|
row = session.get(Secret, _REF, populate_existing=True)
|
|
if (
|
|
row is None
|
|
or row.name != _REF
|
|
or not isinstance(row.metadata_json, dict)
|
|
or row.metadata_json.get("intentId") != intent_id
|
|
or row.metadata_json.get("enrollmentId") != enrollment_id
|
|
):
|
|
raise RepoPulseTargetCredentialError("target_credential_collision")
|
|
value = secret_store.get_secret(session, _REF, touch=False)
|
|
if (
|
|
not value
|
|
or not 32 <= len(value) <= 256
|
|
or not value.isascii()
|
|
or not value.isprintable()
|
|
):
|
|
raise RepoPulseTargetCredentialError("target_credential_unavailable")
|
|
if (
|
|
row.metadata_json.get("credentialFingerprint")
|
|
!= hashlib.sha256(value.encode("utf-8")).hexdigest()[:16]
|
|
):
|
|
raise RepoPulseTargetCredentialError(
|
|
"target_credential_fingerprint_mismatch"
|
|
)
|
|
return enrollment_id, value.encode("utf-8")
|
|
except SQLAlchemyError:
|
|
session.rollback()
|
|
raise RepoPulseTargetCredentialError(
|
|
"target_credential_storage_unavailable"
|
|
) from None
|