SolveConPython

Python Reto #19 — Generador de contraseñas (con reglas)

Nivel: Intermedio
Tema: Aleatoriedad, reglas de validación, secrets vs random, composición de strings, tests con pytest
Objetivo: Generar contraseñas que cumplan reglas mínimas (longitud y diversidad de caracteres) usando prácticas seguras.

Enunciado

Crea una función llamada generar_contrasena(longitud=12, usar_mayusculas=True, usar_numeros=True, usar_simbolos=True) que:

  1. Reciba:
    • longitud (int): longitud total de la contraseña
    • flags booleanos para incluir tipos de caracteres
  2. Reglas de validación:
    • Si longitud es None o no es int, lance TypeError
    • Si longitud < 4, lance ValueError
    • Si alguno de los flags no es bool, lance TypeError
    • Si todos los flags (usar_mayusculas, usar_numeros, usar_simbolos) son False, igualmente debe generar una contraseña válida usando solo minúsculas (esto sigue siendo válido)
  3. La contraseña debe:
    • Tener exactamente longitud caracteres
    • Contener al menos 1 minúscula siempre
    • Si usar_mayusculas es True, contener al menos 1 mayúscula
    • Si usar_numeros es True, contener al menos 1 número
    • Si usar_simbolos es True, contener al menos 1 símbolo
  4. Usar el módulo secrets (no random) para elegir caracteres.

Debe devolver un str con la contraseña.

Ejemplos

  • generar_contrasena(12)"aB3!k..."
  • generar_contrasena(8, usar_simbolos=False) → contiene minúsculas, mayúsculas y números, pero sin símbolos
  • generar_contrasena(10, False, False, False) → solo minúsculas, longitud 10
  • generar_contrasena(3)ValueError
  • generar_contrasena("12")TypeError

Pistas

  1. Define conjuntos de caracteres:
    • minúsculas: string.ascii_lowercase
    • mayúsculas: string.ascii_uppercase
    • números: string.digits
    • símbolos: por ejemplo "!@#$%^&*()-_=+[]{}"
  2. Para garantizar reglas, construye primero una lista con los caracteres “obligatorios” y luego completa el resto.
  3. Mezcla el resultado con secrets.SystemRandom().shuffle() o seleccionando posiciones aleatorias (más simple: shuffle con SystemRandom).

Solución explicada (paso a paso)

  1. Validamos tipos y rango de longitud.
  2. Validamos que los flags sean booleanos.
  3. Construimos:
    • una lista obligatorios con 1 carácter de cada tipo requerido
    • un pool permitidos con todos los caracteres habilitados
  4. Completamos la contraseña eligiendo caracteres al azar desde permitidos hasta alcanzar longitud.
  5. Mezclamos el orden para que los “obligatorios” no queden siempre al inicio.
  6. Convertimos la lista en string y devolvemos.

Ejecuta:

  • pytest -q

Variantes para subir de nivel (opcional)

  1. Excluir caracteres ambiguos (O/0, l/1) para contraseñas “human-friendly”
  2. Permitir configuración del set de símbolos
  3. Generar múltiples contraseñas en una llamada
  4. Añadir parámetro semilla (solo para modo demo, no para producción)

Lo que aprendiste

  • Por qué secrets es preferible a random para contraseñas
  • Cómo garantizar reglas mínimas de composición
  • Cómo testear propiedades (no valores exactos) cuando hay aleatoriedad
  • Diseño robusto con validación de entradas

Accede al código completo y a los tests en GitHub para ejecutar y modificar la solución localmente.

Continúa con Reto #20 — Manejo robusto de errores (try/except bien hecho) para escribir funciones resistentes y con mensajes de error útiles.