Skip to content

Bug Report: Critical Password Verification Flaw in Seeders #38

@alejandro-alarcon-t

Description

@alejandro-alarcon-t

Summary

The admin seeder has a design flaw where it assumes existing admin users are correctly configured, leading to authentication failures when environment variables don't match the database state.

Bug Description

In scripts/seeders/admin_seeder.py (also applicable for scripts/seeders/client_seeder.py), the seeder uses overly simplistic logic for existing users:

# PROBLEMATIC CODE (lines ~74-77)
existing_admin = session.query(User).filter(User.email == admin_email).first()
if existing_admin:
    logger.info(f"Admin with email {admin_email} already exists")
    return True  # Assumes everything is fine!

The Issue: The seeder assumes that if an admin user exists, it's correctly configured. This creates problems when:

  1. Environment Variables Change: ADMIN_INITIAL_PASSWORD is updated but existing admin still has old password
  2. Manual Database Changes: Admin user exists but has wrong settings (inactive, unverified, non-admin)
  3. Configuration Drift: Initial setup used different credentials than current environment

Expected Behavior

  • Seeders should ensure existing users match current environment configuration
  • Admin users should be authenticatable with environment-defined credentials
  • Seeder should update existing users to match environment settings

Actual Behavior

  • Seeder reports "Admin already exists" but doesn't verify configuration
  • Admin users may be unconfigured, inactive, or have mismatched passwords
  • Authentication fails with environment-defined credentials
  • Users must manually fix database inconsistencies

Real-World Scenario

  1. Initial setup creates admin with password "oldpassword"
  2. Later, ADMIN_INITIAL_PASSWORD environment variable is changed to "newpassword"
  3. Seeder runs and says "Admin exists, all good!"
  4. Authentication fails because admin still has "oldpassword" hash
  5. Logs show no errors, but admin cannot log in with environment credentials

Environment

  • Version: Latest (as of current commit)
  • Component: scripts/seeders/admin_seeder.py
  • Related Files: Potentially other seeders with similar patterns

Root Cause

Seeder design assumes database state always matches environment configuration, which is not guaranteed in real deployments.

Proposed Fix

Replace the simplistic check with configuration validation:

# Check if the admin already exists
existing_admin = session.query(User).filter(User.email == admin_email).first()
if existing_admin:
    logger.info(f"Admin with email {admin_email} already exists")
    
    # Ensure the admin configuration matches environment settings
    needs_update = False
    
    # Verify password matches environment
    if not verify_password(admin_password, existing_admin.password_hash):
        logger.info("Admin password doesn't match environment, updating...")
        existing_admin.password_hash = get_password_hash(admin_password)
        needs_update = True
        
    # Ensure admin privileges
    if not existing_admin.is_admin:
        existing_admin.is_admin = True
        needs_update = True
        
    # Ensure user is active
    if not existing_admin.is_active:
        existing_admin.is_active = True
        needs_update = True
        
    # Ensure email is verified
    if not existing_admin.email_verified:
        existing_admin.email_verified = True
        needs_update = True
        
    if needs_update:
        session.commit()
        logger.info("Admin configuration updated to match environment")
    else:
        logger.info("Admin configuration already matches environment")
    
    return True

Required Import Addition:

from src.utils.security import get_password_hash, verify_password  # Add verify_password

Alternative Approach

Add a --force-update flag to seeders that always synchronizes existing users with environment configuration.

Testing Steps to Reproduce

  1. Create admin user with password "test123"
  2. Change ADMIN_INITIAL_PASSWORD environment variable to "newpassword"
  3. Run seeder - it reports success
  4. Try to authenticate with "newpassword" - fails
  5. Original password "test123" still works, proving configuration drift

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions