bootstrap-super-admin.sh 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. #!/usr/bin/env bash
  2. # bootstrap-super-admin.sh — Create the first super_admin user.
  3. #
  4. # Per M13 decision 2.4 (magic-link with psql fallback), this is
  5. # the fallback path: when the operator can't use the invite flow
  6. # (e.g. no email service configured yet, or recovery from a
  7. # forgotten password), they can bootstrap a super_admin directly
  8. # via psql.
  9. #
  10. # Usage:
  11. # scripts/bootstrap-super-admin.sh [email] [password]
  12. #
  13. # Defaults: super@broad-announce.test / test-password-123
  14. #
  15. # Requires:
  16. # - psql in PATH
  17. # - $BA_POSTGRES_DSN (or pass as PG_DSN env var)
  18. # - The 009_auth migration applied (the 'auth' schema exists)
  19. #
  20. # Idempotent: if the user already exists with status='active', the
  21. # script just updates the password. If the user doesn't exist, it
  22. # creates the user with the given credentials.
  23. set -euo pipefail
  24. cd "$(dirname "$0")/.."
  25. EMAIL="${1:-super@broad-announce.test}"
  26. PASSWORD="${2:-test-password-123}"
  27. DSN="${BA_POSTGRES_DSN:-${PG_DSN:-postgres://ba:ba@localhost:5432/ba?sslmode=disable}}"
  28. # Generate bcrypt hash at cost 10 (lower than prod's 12 because
  29. # the bootstrap runs in seconds, not milliseconds).
  30. HASH=$(python3 -c "
  31. import bcrypt
  32. print(bcrypt.hashpw(b'${PASSWORD}', bcrypt.gensalt(rounds=10)).decode())
  33. ")
  34. export PGPASSWORD="$(echo "$DSN" | sed -E 's|.*://[^:]+:([^@]+)@.*|\1|')"
  35. psql "$DSN" <<SQL
  36. DO \$\$
  37. DECLARE
  38. v_user_id UUID;
  39. BEGIN
  40. -- Upsert the super_admin. Email is unique globally when
  41. -- tenant_id IS NULL.
  42. INSERT INTO auth.users (tenant_id, email, role, status, password_hash)
  43. VALUES (NULL, '$EMAIL', 'super_admin', 'active', '$HASH')
  44. ON CONFLICT (email) WHERE tenant_id IS NULL DO UPDATE
  45. SET status = 'active',
  46. password_hash = EXCLUDED.password_hash,
  47. updated_at = NOW()
  48. RETURNING id INTO v_user_id;
  49. RAISE NOTICE 'super_admin ready: % (id=%)', '$EMAIL', v_user_id;
  50. END \$\$;
  51. SQL
  52. echo ""
  53. echo "next steps:"
  54. echo " 1. docker compose up -d authd"
  55. echo " 2. curl -X POST http://localhost:8804/v1/auth/login \\"
  56. echo " -H 'Content-Type: application/json' \\"
  57. echo " -d '{\"email\":\"$EMAIL\",\"password\":\"$PASSWORD\"}'"
  58. echo " 3. bash scripts/m13a_smoke.sh"