Enha: input check; hash passwords
This commit is contained in:
113
scripts/migrate_passwords.py
Executable file
113
scripts/migrate_passwords.py
Executable file
@@ -0,0 +1,113 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user