File size: 2,876 Bytes
9d14418
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
"""Provision reviewer accounts into the store (#128).

The write endpoints authenticate a per-user login, so a deployment needs accounts before anyone can
confirm a value. Two ways to supply them, both idempotent (a re-run rotates a password or role in place):

  # From the ENDOPATH_SEED_USERS secret (the same JSON the Space seeds from at startup):
  ENDOPATH_SEED_USERS='[{"username":"apath","password":"...","role":"pathologist","holds_licence":true,
                         "display_name":"A. Pathologist"}]' \
      PYTHONPATH=src python scripts/seed_users.py

  # Or one account from flags:
  PYTHONPATH=src python scripts/seed_users.py \
      --username apath --password ... --role pathologist --licence --display-name "A. Pathologist"

Writes to SQLite by default and to Postgres when DATABASE_URL is set (the deployed store), the same
resolution storage.connect uses. The plaintext password is hashed once and never stored.
"""

from __future__ import annotations

import argparse
import os

from endopath import auth, storage


def main() -> None:
    parser = argparse.ArgumentParser(description="Provision reviewer accounts (#128).")
    parser.add_argument("--username")
    parser.add_argument("--password")
    parser.add_argument("--role", default="pathologist")
    parser.add_argument(
        "--licence",
        action="store_true",
        help="the account holds a pathology licence (may confirm licence-required fields)",
    )
    parser.add_argument("--display-name", default="")
    args = parser.parse_args()

    if args.username:
        seeds = [
            auth.SeedUser(
                username=args.username,
                password=args.password or "",
                role=args.role,
                holds_licence=args.licence,
                display_name=args.display_name,
            )
        ]
    else:
        seeds = auth.parse_seed_users(os.environ.get(auth.SEED_USERS_ENV, ""))

    if not seeds:
        parser.error(
            f"no accounts to seed: pass --username/--password, or set {auth.SEED_USERS_ENV} to a JSON list"
        )

    conn = storage.connect()
    try:
        for seed in seeds:
            if not seed.password:
                parser.error(f"account {seed.username!r} has no password")
            storage.upsert_user(
                conn,
                username=seed.username,
                password_hash=auth.hash_password(seed.password),
                role=seed.role,
                holds_licence=seed.holds_licence,
                display_name=seed.display_name,
            )
            print(
                f"provisioned {seed.username!r} "
                f"(role={seed.role}, licence={'yes' if seed.holds_licence else 'no'})"
            )
        print(f"{storage.count_users(conn)} account(s) total")
    finally:
        conn.close()


if __name__ == "__main__":
    main()