"""Helpers for translating account names to Kubernetes namespace names.""" from __future__ import annotations import hashlib import re _DNS_LABEL = re.compile(r"^[a-z0-9](?:[-a-z0-9]*[a-z0-9])?$") _INVALID_DNS_LABEL_CHARS = re.compile(r"[^a-z0-9-]+") def is_valid_namespace_name(namespace_name: str) -> bool: """Return whether a name meets Kubernetes DNS-label constraints.""" return ( isinstance(namespace_name, str) and len(namespace_name) <= 63 and bool(_DNS_LABEL.fullmatch(namespace_name)) ) def user_namespace(username: str) -> str: """Return a stable, DNS-label-safe namespace for a username. Existing valid usernames retain their current namespace. Names that need normalization receive a short digest so normalization cannot silently merge accounts such as ``miki.dz08`` and ``miki-dz08``. """ normalized_username = str(username).strip().lower() if not normalized_username: raise ValueError("Username must not be empty") candidate = f"user-{normalized_username}" if is_valid_namespace_name(candidate): return candidate safe_username = _INVALID_DNS_LABEL_CHARS.sub("-", normalized_username) safe_username = re.sub(r"-+", "-", safe_username).strip("-") or "user" digest = hashlib.sha256(normalized_username.encode("utf-8")).hexdigest()[:8] suffix = f"-{digest}" max_base_length = 63 - len("user-") - len(suffix) safe_username = safe_username[:max_base_length].rstrip("-") or "user" return f"user-{safe_username}{suffix}"