118 lines
4.0 KiB
Python
118 lines
4.0 KiB
Python
#!/opt/homebrew/bin/python3.11
|
|
"""
|
|
Sync users from SFTPGo to Momentry users table.
|
|
|
|
Usage:
|
|
python3 scripts/sync_users_from_sftpgo.py
|
|
python3 scripts/sync_users_from_sftpgo.py --sftpgo-url http://localhost:8080
|
|
python3 scripts/sync_users_from_sftpgo.py --db "dbname=momentry user=accusys"
|
|
|
|
Environment:
|
|
SFTPGO_BASE_URL Default: http://localhost:8080
|
|
DATABASE_URL Default: dbname=momentry user=accusys host=localhost
|
|
|
|
This script does NOT copy passwords. It creates user records with placeholder
|
|
password hashes. The real password will be captured on the user's first
|
|
login through Momentry (which verifies against SFTPGo and caches the hash).
|
|
"""
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sys
|
|
from typing import Any
|
|
|
|
import psycopg2
|
|
import psycopg2.extras
|
|
import requests
|
|
|
|
|
|
def get_sftpgo_users(sftpgo_url: str, admin_user: str, admin_pass: str) -> list[dict[str, Any]]:
|
|
"""Get all users from SFTPGo."""
|
|
# Get admin token (SFTPGo uses GET, not POST)
|
|
token_url = f"{sftpgo_url}/api/v2/token"
|
|
resp = requests.get(token_url, auth=(admin_user, admin_pass), timeout=10)
|
|
resp.raise_for_status()
|
|
token = resp.json().get("access_token")
|
|
if not token:
|
|
print("ERROR: Failed to get SFTPGo admin token", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
# List users
|
|
users_url = f"{sftpgo_url}/api/v2/users"
|
|
headers = {"Authorization": f"Bearer {token}"}
|
|
resp = requests.get(users_url, headers=headers, timeout=10)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Sync SFTPGo users to Momentry")
|
|
parser.add_argument("--sftpgo-url", default=os.getenv("SFTPGO_BASE_URL", "http://localhost:8080"))
|
|
parser.add_argument("--db", default=os.getenv("DATABASE_URL", "dbname=momentry user=accusys host=localhost"))
|
|
parser.add_argument("--admin-user", default="admin")
|
|
parser.add_argument("--admin-pass", default=os.getenv("SFTPGO_ADMIN_PASSWORD", "Test3200Test3200"))
|
|
parser.add_argument("--dry-run", action="store_true", help="Print what would be done without executing")
|
|
args = parser.parse_args()
|
|
|
|
# Fetch users from SFTPGo
|
|
print(f"[SFTPGo] Connecting to {args.sftpgo_url}...")
|
|
try:
|
|
sftpgo_users = get_sftpgo_users(args.sftpgo_url, args.admin_user, args.admin_pass)
|
|
except Exception as e:
|
|
print(f"ERROR: Failed to fetch SFTPGo users: {e}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
print(f"[SFTPGo] Found {len(sftpgo_users)} users")
|
|
|
|
# Connect to Momentry DB and set schema
|
|
conn = psycopg2.connect(args.db)
|
|
cur = conn.cursor()
|
|
cur.execute("SET search_path TO dev")
|
|
|
|
synced = 0
|
|
skipped = 0
|
|
|
|
for user in sftpgo_users:
|
|
username = user.get("username")
|
|
status = user.get("status", 0)
|
|
|
|
if not username or status != 1:
|
|
skipped += 1
|
|
continue
|
|
|
|
role = "admin" if username == "admin" else "user"
|
|
# Placeholder hash — will be updated on first login via SFTPGo fallback
|
|
placeholder_hash = "$placeholder$synced_from_sftpgo"
|
|
|
|
if args.dry_run:
|
|
print(f" Would insert: {username} (role={role})")
|
|
synced += 1
|
|
continue
|
|
|
|
try:
|
|
cur.execute(
|
|
"INSERT INTO users (username, password_hash, role) VALUES (%s, %s, %s) "
|
|
"ON CONFLICT (username) DO NOTHING",
|
|
(username, placeholder_hash, role),
|
|
)
|
|
if cur.rowcount > 0:
|
|
print(f" ✅ {username} (role={role})")
|
|
synced += 1
|
|
else:
|
|
print(f" ⏭️ {username} already exists, skipped")
|
|
skipped += 1
|
|
except Exception as e:
|
|
print(f" ❌ {username}: {e}", file=sys.stderr)
|
|
skipped += 1
|
|
|
|
conn.commit()
|
|
cur.close()
|
|
conn.close()
|
|
|
|
print(f"\nDone: {synced} synced, {skipped} skipped/errors")
|
|
print("Note: Password hashes are placeholders. First login via Momentry will cache the real hash.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|