Files
gralias/scripts/migrate_passwords.py
2026-02-20 09:15:14 +03:00

114 lines
2.9 KiB
Python
Executable File

#!/usr/bin/env python3
"""
One-time script to migrate player passwords from plaintext to MD5 hashes.
This script:
1. Connects to the SQLite database
2. Retrieves all players with their passwords
3. Skips passwords that already look like MD5 hashes (32 hex chars)
4. Hashes plaintext passwords with MD5
5. Updates the database with hashed passwords
Usage:
python scripts/migrate_passwords.py [path_to_db]
If no path is provided, defaults to 'gralias.db' in the current directory.
"""
import hashlib
import re
import sqlite3
import sys
from pathlib import Path
def is_md5_hash(password: str) -> bool:
"""Check if password looks like an MD5 hash (32 hex characters)."""
return bool(re.match(r"^[a-fA-F0-9]{32}$", password))
def hash_password(password: str) -> str:
"""Hash password with MD5."""
return hashlib.md5(password.encode("utf-8")).hexdigest()
def migrate_passwords(db_path: str) -> None:
"""Migrate all player passwords to MD5 hashes."""
print(f"Connecting to database: {db_path}")
if not Path(db_path).exists():
print(f"Error: Database file not found: {db_path}")
sys.exit(1)
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
try:
# Get all players
cursor.execute("SELECT username, password FROM players;")
players = cursor.fetchall()
if not players:
print("No players found in database.")
return
print(f"Found {len(players)} player(s)")
print("-" * 60)
migrated = 0
skipped = 0
errors = 0
for username, password in players:
if not password:
print(f"[SKIP] {username}: empty password")
skipped += 1
continue
if is_md5_hash(password):
print(f"[SKIP] {username}: already MD5 hashed")
skipped += 1
continue
# Hash the password
hashed = hash_password(password)
try:
cursor.execute(
"UPDATE players SET password = ? WHERE username = ?;",
(hashed, username),
)
print(f"[MIGRATED] {username}: '{password}' -> '{hashed}'")
migrated += 1
except sqlite3.Error as e:
print(f"[ERROR] {username}: {e}")
errors += 1
print("-" * 60)
print(f"Summary: {migrated} migrated, {skipped} skipped, {errors} errors")
# Commit changes
conn.commit()
print("Changes committed to database.")
except sqlite3.Error as e:
print(f"Database error: {e}")
conn.rollback()
sys.exit(1)
finally:
conn.close()
def main():
# Get database path from command line or use default
if len(sys.argv) > 1:
db_path = sys.argv[1]
else:
db_path = "gralias.db"
migrate_passwords(db_path)
if __name__ == "__main__":
main()