forked from sagnik/Velocity-OS
142 lines
5.0 KiB
Python
142 lines
5.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
seed_test_users.py — Velocity-OS Test Credential Seeder
|
|
========================================================
|
|
FOR TESTING ONLY. Never include in production build.
|
|
|
|
Inserts 7 test broker accounts into users_and_roles.
|
|
All passwords follow the pattern: Name@Velocity26
|
|
All emails follow: name@desineuron.in
|
|
|
|
Run against the local or remote Velocity-OS PostgreSQL:
|
|
python seed_test_users.py # uses env vars
|
|
python seed_test_users.py --dsn "postgresql://..." # explicit DSN
|
|
"""
|
|
|
|
import argparse
|
|
import hashlib
|
|
import os
|
|
import sys
|
|
|
|
# ── Generate bcrypt hashes OFFLINE (no DB dep) ────────────────────────────
|
|
# Uses passlib with same config as backend/auth/dependencies.py
|
|
|
|
try:
|
|
from passlib.context import CryptContext
|
|
pwd_ctx = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
|
|
|
def hash_password(plain: str) -> str:
|
|
raw = plain.encode("utf-8")
|
|
if len(raw) > 72:
|
|
plain = raw[:72].decode("utf-8", errors="ignore")
|
|
return pwd_ctx.hash(plain)
|
|
|
|
except ImportError:
|
|
print("[ERROR] passlib not installed. Run: pip install passlib[bcrypt]")
|
|
sys.exit(1)
|
|
|
|
# ── Test user definitions ─────────────────────────────────────────────────
|
|
# Format: (full_name, email, plain_password, role)
|
|
TEST_USERS = [
|
|
("Sagnik", "sagnik@desineuron.in", "Sagnik@Velocity26", "ADMIN"),
|
|
("Sayan", "sayan@desineuron.in", "Sayan@Velocity26", "ADMIN"),
|
|
("Sourik", "sourik@desineuron.in", "Sourik@Velocity26", "ADMIN"),
|
|
("Abantika", "abantika@desineuron.in", "Abantika@Velocity26", "ADMIN"),
|
|
("Sinjini", "sinjini@desineuron.in", "Sinjini@Velocity26", "ADMIN"),
|
|
("Swastika", "swastika@desineuron.in", "Swastika@Velocity26", "ADMIN"),
|
|
("Debargha", "debargha@desineuron.in", "Debargha@Velocity26", "ADMIN"),
|
|
]
|
|
|
|
TENANT_ID = "tenant_velocity"
|
|
|
|
def build_sql() -> str:
|
|
"""Generate idempotent INSERT SQL (ON CONFLICT DO NOTHING)."""
|
|
lines = [
|
|
"-- ================================================================",
|
|
"-- Velocity-OS Test Users Seed (FOR TESTING ONLY — NOT FOR PROD)",
|
|
"-- ================================================================",
|
|
"-- Generated by seed_test_users.py",
|
|
"",
|
|
"BEGIN;",
|
|
"",
|
|
]
|
|
|
|
for full_name, email, plain, role in TEST_USERS:
|
|
pw_hash = hash_password(plain)
|
|
lines.append(f"-- {full_name} ({role})")
|
|
lines.append("INSERT INTO users_and_roles")
|
|
lines.append(" (email, password_hash, role, tenant_id, full_name, is_active)")
|
|
lines.append("VALUES")
|
|
lines.append(f" ('{email}', '{pw_hash}', '{role}', '{TENANT_ID}', '{full_name}', TRUE)")
|
|
lines.append("ON CONFLICT (email) DO UPDATE")
|
|
lines.append(" SET password_hash = EXCLUDED.password_hash,")
|
|
lines.append(" role = EXCLUDED.role,")
|
|
lines.append(" full_name = EXCLUDED.full_name,")
|
|
lines.append(" is_active = TRUE;")
|
|
lines.append("")
|
|
|
|
lines.append("COMMIT;")
|
|
lines.append("")
|
|
lines.append("-- Verify:")
|
|
lines.append("SELECT email, role, full_name FROM users_and_roles ORDER BY role DESC, email;")
|
|
return "\n".join(lines)
|
|
|
|
|
|
def run_against_db(dsn: str, sql: str) -> None:
|
|
try:
|
|
import asyncpg
|
|
import asyncio
|
|
|
|
async def _insert():
|
|
conn = await asyncpg.connect(dsn)
|
|
try:
|
|
await conn.execute(sql)
|
|
print("[OK] Test users inserted successfully.")
|
|
finally:
|
|
await conn.close()
|
|
|
|
asyncio.run(_insert())
|
|
|
|
except ImportError:
|
|
print("[WARN] asyncpg not installed — writing SQL file only.")
|
|
write_sql_file(sql)
|
|
|
|
|
|
def write_sql_file(sql: str) -> None:
|
|
out = os.path.join(os.path.dirname(__file__), "seed_test_users.sql")
|
|
with open(out, "w", encoding="utf-8") as f:
|
|
f.write(sql)
|
|
print(f"[OK] SQL written to: {out}")
|
|
print(" Apply with: psql -U velocity_user -d velocity_db -f seed_test_users.sql")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Seed Velocity-OS test users")
|
|
parser.add_argument("--dsn", help="PostgreSQL DSN (overrides env VELOCITY_DB_DSN)")
|
|
parser.add_argument("--sql-only", action="store_true", help="Only write SQL file, don't connect")
|
|
args = parser.parse_args()
|
|
|
|
sql = build_sql()
|
|
|
|
print("Generating bcrypt hashes for test users...")
|
|
print("Users to seed:")
|
|
for full_name, email, plain, role in TEST_USERS:
|
|
print(f" [{role:16}] {email:30} / {plain}")
|
|
print()
|
|
|
|
if args.sql_only:
|
|
write_sql_file(sql)
|
|
return
|
|
|
|
dsn = args.dsn or os.getenv("VELOCITY_DB_DSN") or os.getenv("DATABASE_URL")
|
|
|
|
if dsn:
|
|
run_against_db(dsn, sql)
|
|
else:
|
|
print("[INFO] No DSN provided — writing SQL file for manual application.")
|
|
write_sql_file(sql)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|