feat(infra): auto-commit con 56 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
"""Parsea el texto de un secreto de `pass` para credenciales de Metabase.
|
||||
|
||||
Distingue dos formatos sin tocar disco ni red (funcion pura):
|
||||
|
||||
- API-key: una sola linea con la clave (las API keys de Metabase empiezan por
|
||||
``mb_``, p.ej. el secreto ``metabase/aurgi-api-key``).
|
||||
- Sesion: multi-linea estilo ``captacion/metabase`` — la primera linea es la
|
||||
contrasena y una linea posterior lleva el email/usuario con un prefijo
|
||||
reconocible (``email:``, ``user:``, ``login:`` o ``username:``).
|
||||
|
||||
El caller decide el ``mode`` y este parser solo extrae los campos del texto.
|
||||
"""
|
||||
|
||||
# Prefijos (case-insensitive) que identifican la linea del email/usuario en un
|
||||
# secreto multi-linea de pass. Se prueban en este orden.
|
||||
_EMAIL_PREFIXES = ("email:", "login:", "username:", "user:")
|
||||
|
||||
|
||||
def parse_metabase_secret(secret_text: str, mode: str = "auto") -> dict:
|
||||
"""Extrae credenciales de Metabase del texto crudo de un secreto de pass.
|
||||
|
||||
No ejecuta `pass` ni hace I/O: recibe el texto ya leido y lo interpreta.
|
||||
Funcion pura y determinista, apta para tests unitarios.
|
||||
|
||||
Args:
|
||||
secret_text: contenido completo del secreto (varias lineas separadas por
|
||||
``\\n``). Por convencion de pass la primera linea es la
|
||||
contrasena/clave; las siguientes son metadata.
|
||||
mode: ``"api_key"``, ``"session"`` o ``"auto"`` (default). En ``auto`` se
|
||||
detecta el formato: si hay una linea de email/usuario reconocible se
|
||||
asume sesion; si no, se asume api_key (una sola linea de clave).
|
||||
|
||||
Returns:
|
||||
Dict. Nunca lanza:
|
||||
|
||||
- api_key -> ``{"status": "ok", "mode": "api_key", "api_key": str}``
|
||||
- session -> ``{"status": "ok", "mode": "session", "email": str,
|
||||
"password": str}``
|
||||
- error -> ``{"status": "error", "error": str}`` para texto vacio, modo
|
||||
invalido, o session sin email/password localizables.
|
||||
|
||||
Example:
|
||||
>>> parse_metabase_secret("mb_abc123")
|
||||
{'status': 'ok', 'mode': 'api_key', 'api_key': 'mb_abc123'}
|
||||
>>> parse_metabase_secret("hunter2\\nemail: a@b.com\\nurl: http://x")
|
||||
{'status': 'ok', 'mode': 'session', 'email': 'a@b.com', 'password': 'hunter2'}
|
||||
"""
|
||||
if mode not in ("api_key", "session", "auto"):
|
||||
return {"status": "error", "error": f"invalid mode {mode!r}"}
|
||||
|
||||
lines = secret_text.splitlines()
|
||||
if not lines or not lines[0].strip():
|
||||
return {"status": "error", "error": "empty secret"}
|
||||
|
||||
email = _find_email(lines)
|
||||
|
||||
if mode == "auto":
|
||||
mode = "session" if email is not None else "api_key"
|
||||
|
||||
if mode == "api_key":
|
||||
return {
|
||||
"status": "ok",
|
||||
"mode": "api_key",
|
||||
"api_key": lines[0].strip(),
|
||||
}
|
||||
|
||||
# mode == "session"
|
||||
if email is None:
|
||||
return {
|
||||
"status": "error",
|
||||
"error": (
|
||||
"session secret without email/user line "
|
||||
f"(expected one of {', '.join(_EMAIL_PREFIXES)})"
|
||||
),
|
||||
}
|
||||
password = lines[0].strip()
|
||||
if not password:
|
||||
return {"status": "error", "error": "session secret without password"}
|
||||
return {
|
||||
"status": "ok",
|
||||
"mode": "session",
|
||||
"email": email,
|
||||
"password": password,
|
||||
}
|
||||
|
||||
|
||||
def _find_email(lines: list[str]) -> str | None:
|
||||
"""Devuelve el email/usuario de la primera linea con prefijo reconocido."""
|
||||
for raw in lines[1:]:
|
||||
low = raw.strip().lower()
|
||||
for prefix in _EMAIL_PREFIXES:
|
||||
if low.startswith(prefix):
|
||||
# Conserva el valor original (no el lowercased) tras el prefijo.
|
||||
value = raw.strip()[len(prefix):].strip()
|
||||
if value:
|
||||
return value
|
||||
return None
|
||||
Reference in New Issue
Block a user