trustgraph/trustgraph-cli/trustgraph/cli/create_user.py
cybermaggedon 0a828379be
feat: global usernames and rename workspace to default_workspace (#1001)
Users are global entities, not scoped to workspaces. This change:

Track A — Global usernames:
- Change iam_users_by_username to PRIMARY KEY (username), removing
  workspace from the lookup key
- Login looks up username globally, no workspace required
- Username uniqueness is enforced globally, not per-workspace
- Login -w now overrides the JWT workspace (session workspace)
  rather than selecting which user registry to search

Track B — Rename workspace to default_workspace:
- UserRecord.workspace → UserRecord.default_workspace
- Identity.workspace → Identity.default_workspace
- JWT claim "workspace" → "default_workspace"
- IamResponse.resolved_workspace → resolved_default_workspace
- WebSocket auth-ok frame field → default_workspace
- Socket clients read default_workspace from auth-ok
- _user_record_to_dict wire key → default_workspace
- CLI help text and output updated throughout
- Test files updated for renamed fields
2026-06-25 16:34:31 +01:00

84 lines
2.3 KiB
Python

"""
Create a user in the caller's workspace. Prints the new user id.
"""
import argparse
import getpass
import sys
from ._iam import DEFAULT_URL, DEFAULT_TOKEN, call_iam, run_main
def do_create_user(args):
password = args.password
if not password:
password = getpass.getpass(
f"Password for new user {args.username}: "
)
user = {
"username": args.username,
"password": password,
"roles": args.roles,
}
if args.name:
user["name"] = args.name
if args.email:
user["email"] = args.email
if args.must_change_password:
user["must_change_password"] = True
req = {"operation": "create-user", "user": user}
if args.workspace:
req["workspace"] = args.workspace
resp = call_iam(args.api_url, args.token, req)
rec = resp.get("user", {})
print(f"User id: {rec.get('id', '')}", file=sys.stderr)
print(f"Username: {rec.get('username', '')}", file=sys.stderr)
print(f"Roles: {', '.join(rec.get('roles', []))}", file=sys.stderr)
print(rec.get("id", ""))
def main():
parser = argparse.ArgumentParser(
prog="tg-create-user", description=__doc__,
)
parser.add_argument(
"-u", "--api-url", default=DEFAULT_URL,
help=f"API URL (default: {DEFAULT_URL})",
)
parser.add_argument(
"-t", "--token", default=DEFAULT_TOKEN,
help="Auth token (default: $TRUSTGRAPH_TOKEN)",
)
parser.add_argument(
"--username", required=True, help="Username (globally unique)",
)
parser.add_argument(
"--password", default=None,
help="Password (prompted if omitted)",
)
parser.add_argument(
"--name", default=None, help="Display name",
)
parser.add_argument(
"--email", default=None, help="Email",
)
parser.add_argument(
"--roles", nargs="+", default=["reader"],
help="One or more role names (default: reader)",
)
parser.add_argument(
"--must-change-password", action="store_true",
help="Force password change on next login",
)
parser.add_argument(
"-w", "--workspace", default=None,
help="Default workspace for the new user",
)
run_main(do_create_user, parser)
if __name__ == "__main__":
main()