trustgraph-cli 2.4.3__tar.gz → 2.4.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/PKG-INFO +1 -1
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/pyproject.toml +14 -1
- trustgraph_cli-2.4.5/trustgraph/cli/_iam.py +75 -0
- trustgraph_cli-2.4.5/trustgraph/cli/bootstrap_iam.py +94 -0
- trustgraph_cli-2.4.5/trustgraph/cli/change_password.py +46 -0
- trustgraph_cli-2.4.5/trustgraph/cli/create_api_key.py +71 -0
- trustgraph_cli-2.4.5/trustgraph/cli/create_user.py +87 -0
- trustgraph_cli-2.4.5/trustgraph/cli/create_workspace.py +46 -0
- trustgraph_cli-2.4.5/trustgraph/cli/delete_user.py +62 -0
- trustgraph_cli-2.4.5/trustgraph/cli/disable_user.py +45 -0
- trustgraph_cli-2.4.5/trustgraph/cli/enable_user.py +45 -0
- trustgraph_cli-2.4.5/trustgraph/cli/list_api_keys.py +69 -0
- trustgraph_cli-2.4.5/trustgraph/cli/list_users.py +65 -0
- trustgraph_cli-2.4.5/trustgraph/cli/list_workspaces.py +53 -0
- trustgraph_cli-2.4.5/trustgraph/cli/login.py +62 -0
- trustgraph_cli-2.4.5/trustgraph/cli/reset_password.py +54 -0
- trustgraph_cli-2.4.5/trustgraph/cli/revoke_api_key.py +44 -0
- trustgraph_cli-2.4.5/trustgraph/cli_version.py +1 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph_cli.egg-info/PKG-INFO +1 -1
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph_cli.egg-info/SOURCES.txt +15 -1
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph_cli.egg-info/entry_points.txt +14 -1
- trustgraph_cli-2.4.3/trustgraph/cli/init_trustgraph.py +0 -271
- trustgraph_cli-2.4.3/trustgraph/cli_version.py +0 -1
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/README.md +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/setup.cfg +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/__init__.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/add_library_document.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/delete_collection.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/delete_config_item.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/delete_flow_blueprint.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/delete_kg_core.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/delete_mcp_tool.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/delete_tool.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/dump_msgpack.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/dump_queues.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/export_workspace_config.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/get_config_item.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/get_document_content.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/get_flow_blueprint.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/get_kg_core.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/graph_to_turtle.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/import_workspace_config.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/init_pulsar_manager.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/invoke_agent.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/invoke_document_embeddings.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/invoke_document_rag.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/invoke_embeddings.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/invoke_graph_embeddings.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/invoke_graph_rag.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/invoke_llm.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/invoke_mcp_tool.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/invoke_nlp_query.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/invoke_prompt.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/invoke_row_embeddings.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/invoke_rows_query.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/invoke_sparql_query.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/invoke_structured_query.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/list_collections.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/list_config_items.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/list_explain_traces.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/load_doc_embeds.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/load_kg_core.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/load_knowledge.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/load_sample_documents.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/load_structured_data.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/load_turtle.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/monitor_prompts.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/put_config_item.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/put_flow_blueprint.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/put_kg_core.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/query_graph.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/remove_library_document.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/save_doc_embeds.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/set_collection.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/set_mcp_tool.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/set_prompt.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/set_token_costs.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/set_tool.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/show_config.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/show_explain_trace.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/show_extraction_provenance.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/show_flow_blueprints.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/show_flow_state.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/show_flows.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/show_graph.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/show_kg_cores.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/show_library_documents.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/show_library_processing.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/show_mcp_tools.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/show_parameter_types.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/show_processor_state.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/show_prompts.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/show_token_costs.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/show_token_rate.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/show_tools.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/start_flow.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/start_library_processing.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/stop_flow.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/stop_library_processing.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/unload_kg_core.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph/cli/verify_system_status.py +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph_cli.egg-info/dependency_links.txt +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph_cli.egg-info/requires.txt +0 -0
- {trustgraph_cli-2.4.3 → trustgraph_cli-2.4.5}/trustgraph_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trustgraph-cli
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.5
|
|
4
4
|
Summary: TrustGraph provides a means to run a pipeline of flexible AI processing components in a flexible means to achieve a processing pipeline.
|
|
5
5
|
Author-email: "trustgraph.ai" <security@trustgraph.ai>
|
|
6
6
|
Project-URL: Homepage, https://github.com/trustgraph-ai/trustgraph
|
|
@@ -40,7 +40,20 @@ tg-get-flow-blueprint = "trustgraph.cli.get_flow_blueprint:main"
|
|
|
40
40
|
tg-get-kg-core = "trustgraph.cli.get_kg_core:main"
|
|
41
41
|
tg-get-document-content = "trustgraph.cli.get_document_content:main"
|
|
42
42
|
tg-graph-to-turtle = "trustgraph.cli.graph_to_turtle:main"
|
|
43
|
-
tg-
|
|
43
|
+
tg-bootstrap-iam = "trustgraph.cli.bootstrap_iam:main"
|
|
44
|
+
tg-login = "trustgraph.cli.login:main"
|
|
45
|
+
tg-create-user = "trustgraph.cli.create_user:main"
|
|
46
|
+
tg-list-users = "trustgraph.cli.list_users:main"
|
|
47
|
+
tg-disable-user = "trustgraph.cli.disable_user:main"
|
|
48
|
+
tg-enable-user = "trustgraph.cli.enable_user:main"
|
|
49
|
+
tg-delete-user = "trustgraph.cli.delete_user:main"
|
|
50
|
+
tg-change-password = "trustgraph.cli.change_password:main"
|
|
51
|
+
tg-reset-password = "trustgraph.cli.reset_password:main"
|
|
52
|
+
tg-create-api-key = "trustgraph.cli.create_api_key:main"
|
|
53
|
+
tg-list-api-keys = "trustgraph.cli.list_api_keys:main"
|
|
54
|
+
tg-revoke-api-key = "trustgraph.cli.revoke_api_key:main"
|
|
55
|
+
tg-list-workspaces = "trustgraph.cli.list_workspaces:main"
|
|
56
|
+
tg-create-workspace = "trustgraph.cli.create_workspace:main"
|
|
44
57
|
tg-invoke-agent = "trustgraph.cli.invoke_agent:main"
|
|
45
58
|
tg-invoke-document-rag = "trustgraph.cli.invoke_document_rag:main"
|
|
46
59
|
tg-invoke-graph-rag = "trustgraph.cli.invoke_graph_rag:main"
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shared helpers for IAM CLI tools.
|
|
3
|
+
|
|
4
|
+
All IAM operations go through the gateway's ``/api/v1/iam`` forwarder,
|
|
5
|
+
with the three public auth operations (``login``, ``bootstrap``,
|
|
6
|
+
``change-password``) served via ``/api/v1/auth/...`` instead. These
|
|
7
|
+
helpers encapsulate the HTTP plumbing so each CLI can stay focused
|
|
8
|
+
on its own argument parsing and output formatting.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
import requests
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
DEFAULT_URL = os.getenv("TRUSTGRAPH_URL", "http://localhost:8088/")
|
|
19
|
+
DEFAULT_TOKEN = os.getenv("TRUSTGRAPH_TOKEN", None)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _fmt_error(resp_json):
|
|
23
|
+
err = resp_json.get("error", {})
|
|
24
|
+
if isinstance(err, dict):
|
|
25
|
+
t = err.get("type", "")
|
|
26
|
+
m = err.get("message", "")
|
|
27
|
+
return f"{t}: {m}" if t else m or "error"
|
|
28
|
+
return str(err)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _post(url, path, token, body):
|
|
32
|
+
endpoint = url.rstrip("/") + path
|
|
33
|
+
headers = {"Content-Type": "application/json"}
|
|
34
|
+
if token:
|
|
35
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
36
|
+
|
|
37
|
+
resp = requests.post(
|
|
38
|
+
endpoint, headers=headers, data=json.dumps(body),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
if resp.status_code != 200:
|
|
42
|
+
try:
|
|
43
|
+
payload = resp.json()
|
|
44
|
+
detail = _fmt_error(payload)
|
|
45
|
+
except Exception:
|
|
46
|
+
detail = resp.text
|
|
47
|
+
raise RuntimeError(f"HTTP {resp.status_code}: {detail}")
|
|
48
|
+
|
|
49
|
+
body = resp.json()
|
|
50
|
+
if "error" in body:
|
|
51
|
+
raise RuntimeError(_fmt_error(body))
|
|
52
|
+
return body
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def call_iam(url, token, request):
|
|
56
|
+
"""Forward an IAM request through ``/api/v1/iam``. ``request`` is
|
|
57
|
+
the ``IamRequest`` dict shape."""
|
|
58
|
+
return _post(url, "/api/v1/iam", token, request)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def call_auth(url, path, token, body):
|
|
62
|
+
"""Hit one of the public auth endpoints
|
|
63
|
+
(``/api/v1/auth/login``, ``/api/v1/auth/change-password``, etc.).
|
|
64
|
+
``token`` is optional — login and bootstrap don't need one."""
|
|
65
|
+
return _post(url, path, token, body)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def run_main(fn, parser):
|
|
69
|
+
"""Standard error-handling wrapper for CLI main() bodies."""
|
|
70
|
+
args = parser.parse_args()
|
|
71
|
+
try:
|
|
72
|
+
fn(args)
|
|
73
|
+
except Exception as e:
|
|
74
|
+
print("Exception:", e, file=sys.stderr, flush=True)
|
|
75
|
+
sys.exit(1)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bootstraps the IAM service. Only works when iam-svc is running in
|
|
3
|
+
bootstrap mode with empty tables. Prints the initial admin API key
|
|
4
|
+
to stdout.
|
|
5
|
+
|
|
6
|
+
This is a one-time, trust-sensitive operation. The resulting token
|
|
7
|
+
is shown once and never again — capture it on use. Rotate and
|
|
8
|
+
revoke it as soon as a real admin API key has been issued.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
|
|
16
|
+
import requests
|
|
17
|
+
|
|
18
|
+
default_url = os.getenv("TRUSTGRAPH_URL", "http://localhost:8088/")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def bootstrap(url):
|
|
22
|
+
|
|
23
|
+
# Unauthenticated public endpoint — IAM refuses the bootstrap
|
|
24
|
+
# operation unless the service is running in bootstrap mode with
|
|
25
|
+
# empty tables, so the safety gate lives on the server side.
|
|
26
|
+
endpoint = url.rstrip("/") + "/api/v1/auth/bootstrap"
|
|
27
|
+
|
|
28
|
+
headers = {"Content-Type": "application/json"}
|
|
29
|
+
|
|
30
|
+
resp = requests.post(
|
|
31
|
+
endpoint,
|
|
32
|
+
headers=headers,
|
|
33
|
+
data=json.dumps({}),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if resp.status_code != 200:
|
|
37
|
+
raise RuntimeError(
|
|
38
|
+
f"HTTP {resp.status_code}: {resp.text}"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
body = resp.json()
|
|
42
|
+
|
|
43
|
+
if "error" in body:
|
|
44
|
+
raise RuntimeError(
|
|
45
|
+
f"IAM {body['error'].get('type', 'error')}: "
|
|
46
|
+
f"{body['error'].get('message', '')}"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
api_key = body.get("bootstrap_admin_api_key")
|
|
50
|
+
user_id = body.get("bootstrap_admin_user_id")
|
|
51
|
+
|
|
52
|
+
if not api_key:
|
|
53
|
+
raise RuntimeError(
|
|
54
|
+
"IAM response did not contain a bootstrap token — the "
|
|
55
|
+
"service may already be bootstrapped, or may be running "
|
|
56
|
+
"in token mode."
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
return user_id, api_key
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def main():
|
|
63
|
+
|
|
64
|
+
parser = argparse.ArgumentParser(
|
|
65
|
+
prog="tg-bootstrap-iam",
|
|
66
|
+
description=__doc__,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
parser.add_argument(
|
|
70
|
+
"-u", "--api-url",
|
|
71
|
+
default=default_url,
|
|
72
|
+
help=f"API URL (default: {default_url})",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
args = parser.parse_args()
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
user_id, api_key = bootstrap(args.api_url)
|
|
79
|
+
except Exception as e:
|
|
80
|
+
print("Exception:", e, file=sys.stderr, flush=True)
|
|
81
|
+
sys.exit(1)
|
|
82
|
+
|
|
83
|
+
# Stdout gets machine-readable output (the key). Any operator
|
|
84
|
+
# context goes to stderr.
|
|
85
|
+
print(f"Admin user id: {user_id}", file=sys.stderr)
|
|
86
|
+
print(
|
|
87
|
+
"Admin API key (shown once, capture now):",
|
|
88
|
+
file=sys.stderr,
|
|
89
|
+
)
|
|
90
|
+
print(api_key)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
if __name__ == "__main__":
|
|
94
|
+
main()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Change your own password. Requires the current password.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import getpass
|
|
7
|
+
|
|
8
|
+
from ._iam import DEFAULT_URL, DEFAULT_TOKEN, call_auth, run_main
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def do_change_password(args):
|
|
12
|
+
current = args.current or getpass.getpass("Current password: ")
|
|
13
|
+
new = args.new or getpass.getpass("New password: ")
|
|
14
|
+
|
|
15
|
+
call_auth(
|
|
16
|
+
args.api_url, "/api/v1/auth/change-password", args.token,
|
|
17
|
+
{"current_password": current, "new_password": new},
|
|
18
|
+
)
|
|
19
|
+
print("Password changed.")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def main():
|
|
23
|
+
parser = argparse.ArgumentParser(
|
|
24
|
+
prog="tg-change-password", description=__doc__,
|
|
25
|
+
)
|
|
26
|
+
parser.add_argument(
|
|
27
|
+
"-u", "--api-url", default=DEFAULT_URL,
|
|
28
|
+
help=f"API URL (default: {DEFAULT_URL})",
|
|
29
|
+
)
|
|
30
|
+
parser.add_argument(
|
|
31
|
+
"-t", "--token", default=DEFAULT_TOKEN,
|
|
32
|
+
help="Auth token (default: $TRUSTGRAPH_TOKEN)",
|
|
33
|
+
)
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
"--current", default=None,
|
|
36
|
+
help="Current password (prompted if omitted)",
|
|
37
|
+
)
|
|
38
|
+
parser.add_argument(
|
|
39
|
+
"--new", default=None,
|
|
40
|
+
help="New password (prompted if omitted)",
|
|
41
|
+
)
|
|
42
|
+
run_main(do_change_password, parser)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
main()
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Create an API key for a user. Prints the plaintext key to stdout —
|
|
3
|
+
shown once only.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from ._iam import DEFAULT_URL, DEFAULT_TOKEN, call_iam, run_main
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def do_create_api_key(args):
|
|
13
|
+
key = {
|
|
14
|
+
"user_id": args.user_id,
|
|
15
|
+
"name": args.name,
|
|
16
|
+
}
|
|
17
|
+
if args.expires:
|
|
18
|
+
key["expires"] = args.expires
|
|
19
|
+
|
|
20
|
+
req = {"operation": "create-api-key", "key": key}
|
|
21
|
+
if args.workspace:
|
|
22
|
+
req["workspace"] = args.workspace
|
|
23
|
+
resp = call_iam(args.api_url, args.token, req)
|
|
24
|
+
|
|
25
|
+
plaintext = resp.get("api_key_plaintext", "")
|
|
26
|
+
rec = resp.get("api_key", {})
|
|
27
|
+
print(f"Key id: {rec.get('id', '')}", file=sys.stderr)
|
|
28
|
+
print(f"Name: {rec.get('name', '')}", file=sys.stderr)
|
|
29
|
+
print(f"Prefix: {rec.get('prefix', '')}", file=sys.stderr)
|
|
30
|
+
print(
|
|
31
|
+
"API key (shown once, capture now):", file=sys.stderr,
|
|
32
|
+
)
|
|
33
|
+
print(plaintext)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def main():
|
|
37
|
+
parser = argparse.ArgumentParser(
|
|
38
|
+
prog="tg-create-api-key", description=__doc__,
|
|
39
|
+
)
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
"-u", "--api-url", default=DEFAULT_URL,
|
|
42
|
+
help=f"API URL (default: {DEFAULT_URL})",
|
|
43
|
+
)
|
|
44
|
+
parser.add_argument(
|
|
45
|
+
"-t", "--token", default=DEFAULT_TOKEN,
|
|
46
|
+
help="Auth token (default: $TRUSTGRAPH_TOKEN)",
|
|
47
|
+
)
|
|
48
|
+
parser.add_argument(
|
|
49
|
+
"--user-id", required=True,
|
|
50
|
+
help="Owner user id",
|
|
51
|
+
)
|
|
52
|
+
parser.add_argument(
|
|
53
|
+
"--name", required=True,
|
|
54
|
+
help="Operator-facing label (e.g. 'laptop', 'ci')",
|
|
55
|
+
)
|
|
56
|
+
parser.add_argument(
|
|
57
|
+
"--expires", default=None,
|
|
58
|
+
help="ISO-8601 expiry (optional; empty = no expiry)",
|
|
59
|
+
)
|
|
60
|
+
parser.add_argument(
|
|
61
|
+
"-w", "--workspace", default=None,
|
|
62
|
+
help=(
|
|
63
|
+
"Target workspace (admin only; defaults to caller's "
|
|
64
|
+
"assigned workspace)"
|
|
65
|
+
),
|
|
66
|
+
)
|
|
67
|
+
run_main(do_create_api_key, parser)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
if __name__ == "__main__":
|
|
71
|
+
main()
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Create a user in the caller's workspace. Prints the new user id.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import getpass
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from ._iam import DEFAULT_URL, DEFAULT_TOKEN, call_iam, run_main
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def do_create_user(args):
|
|
13
|
+
password = args.password
|
|
14
|
+
if not password:
|
|
15
|
+
password = getpass.getpass(
|
|
16
|
+
f"Password for new user {args.username}: "
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
user = {
|
|
20
|
+
"username": args.username,
|
|
21
|
+
"password": password,
|
|
22
|
+
"roles": args.roles,
|
|
23
|
+
}
|
|
24
|
+
if args.name:
|
|
25
|
+
user["name"] = args.name
|
|
26
|
+
if args.email:
|
|
27
|
+
user["email"] = args.email
|
|
28
|
+
if args.must_change_password:
|
|
29
|
+
user["must_change_password"] = True
|
|
30
|
+
|
|
31
|
+
req = {"operation": "create-user", "user": user}
|
|
32
|
+
if args.workspace:
|
|
33
|
+
req["workspace"] = args.workspace
|
|
34
|
+
resp = call_iam(args.api_url, args.token, req)
|
|
35
|
+
|
|
36
|
+
rec = resp.get("user", {})
|
|
37
|
+
print(f"User id: {rec.get('id', '')}", file=sys.stderr)
|
|
38
|
+
print(f"Username: {rec.get('username', '')}", file=sys.stderr)
|
|
39
|
+
print(f"Roles: {', '.join(rec.get('roles', []))}", file=sys.stderr)
|
|
40
|
+
print(rec.get("id", ""))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def main():
|
|
44
|
+
parser = argparse.ArgumentParser(
|
|
45
|
+
prog="tg-create-user", description=__doc__,
|
|
46
|
+
)
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
"-u", "--api-url", default=DEFAULT_URL,
|
|
49
|
+
help=f"API URL (default: {DEFAULT_URL})",
|
|
50
|
+
)
|
|
51
|
+
parser.add_argument(
|
|
52
|
+
"-t", "--token", default=DEFAULT_TOKEN,
|
|
53
|
+
help="Auth token (default: $TRUSTGRAPH_TOKEN)",
|
|
54
|
+
)
|
|
55
|
+
parser.add_argument(
|
|
56
|
+
"--username", required=True, help="Username (unique in workspace)",
|
|
57
|
+
)
|
|
58
|
+
parser.add_argument(
|
|
59
|
+
"--password", default=None,
|
|
60
|
+
help="Password (prompted if omitted)",
|
|
61
|
+
)
|
|
62
|
+
parser.add_argument(
|
|
63
|
+
"--name", default=None, help="Display name",
|
|
64
|
+
)
|
|
65
|
+
parser.add_argument(
|
|
66
|
+
"--email", default=None, help="Email",
|
|
67
|
+
)
|
|
68
|
+
parser.add_argument(
|
|
69
|
+
"--roles", nargs="+", default=["reader"],
|
|
70
|
+
help="One or more role names (default: reader)",
|
|
71
|
+
)
|
|
72
|
+
parser.add_argument(
|
|
73
|
+
"--must-change-password", action="store_true",
|
|
74
|
+
help="Force password change on next login",
|
|
75
|
+
)
|
|
76
|
+
parser.add_argument(
|
|
77
|
+
"-w", "--workspace", default=None,
|
|
78
|
+
help=(
|
|
79
|
+
"Target workspace (admin only; defaults to caller's "
|
|
80
|
+
"assigned workspace)"
|
|
81
|
+
),
|
|
82
|
+
)
|
|
83
|
+
run_main(do_create_user, parser)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
if __name__ == "__main__":
|
|
87
|
+
main()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Create a workspace (system-level; requires admin).
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
from ._iam import DEFAULT_URL, DEFAULT_TOKEN, call_iam, run_main
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def do_create_workspace(args):
|
|
11
|
+
ws = {"id": args.workspace_id, "enabled": True}
|
|
12
|
+
if args.name:
|
|
13
|
+
ws["name"] = args.name
|
|
14
|
+
|
|
15
|
+
resp = call_iam(args.api_url, args.token, {
|
|
16
|
+
"operation": "create-workspace",
|
|
17
|
+
"workspace_record": ws,
|
|
18
|
+
})
|
|
19
|
+
rec = resp.get("workspace", {})
|
|
20
|
+
print(f"Workspace created: {rec.get('id', '')}")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def main():
|
|
24
|
+
parser = argparse.ArgumentParser(
|
|
25
|
+
prog="tg-create-workspace", description=__doc__,
|
|
26
|
+
)
|
|
27
|
+
parser.add_argument(
|
|
28
|
+
"-u", "--api-url", default=DEFAULT_URL,
|
|
29
|
+
help=f"API URL (default: {DEFAULT_URL})",
|
|
30
|
+
)
|
|
31
|
+
parser.add_argument(
|
|
32
|
+
"-t", "--token", default=DEFAULT_TOKEN,
|
|
33
|
+
help="Auth token (default: $TRUSTGRAPH_TOKEN)",
|
|
34
|
+
)
|
|
35
|
+
parser.add_argument(
|
|
36
|
+
"--workspace-id", required=True,
|
|
37
|
+
help="New workspace id (must not start with '_')",
|
|
38
|
+
)
|
|
39
|
+
parser.add_argument(
|
|
40
|
+
"--name", default=None, help="Display name",
|
|
41
|
+
)
|
|
42
|
+
run_main(do_create_workspace, parser)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
main()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Delete a user. Removes the user record, their username lookup,
|
|
3
|
+
and all their API keys. The freed username becomes available for
|
|
4
|
+
re-use.
|
|
5
|
+
|
|
6
|
+
Irreversible. Use tg-disable-user if you want to preserve the
|
|
7
|
+
record (audit trail, username squatting protection).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
|
|
12
|
+
from ._iam import DEFAULT_URL, DEFAULT_TOKEN, call_iam, run_main
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def do_delete_user(args):
|
|
16
|
+
if not args.yes:
|
|
17
|
+
confirm = input(
|
|
18
|
+
f"Delete user {args.user_id}? This is irreversible. "
|
|
19
|
+
f"[type 'yes' to confirm]: "
|
|
20
|
+
)
|
|
21
|
+
if confirm.strip() != "yes":
|
|
22
|
+
print("Aborted.")
|
|
23
|
+
return
|
|
24
|
+
|
|
25
|
+
req = {"operation": "delete-user", "user_id": args.user_id}
|
|
26
|
+
if args.workspace:
|
|
27
|
+
req["workspace"] = args.workspace
|
|
28
|
+
call_iam(args.api_url, args.token, req)
|
|
29
|
+
print(f"Deleted user {args.user_id}")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def main():
|
|
33
|
+
parser = argparse.ArgumentParser(
|
|
34
|
+
prog="tg-delete-user", description=__doc__,
|
|
35
|
+
)
|
|
36
|
+
parser.add_argument(
|
|
37
|
+
"-u", "--api-url", default=DEFAULT_URL,
|
|
38
|
+
help=f"API URL (default: {DEFAULT_URL})",
|
|
39
|
+
)
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
"-t", "--token", default=DEFAULT_TOKEN,
|
|
42
|
+
help="Auth token (default: $TRUSTGRAPH_TOKEN)",
|
|
43
|
+
)
|
|
44
|
+
parser.add_argument(
|
|
45
|
+
"--user-id", required=True, help="User id to delete",
|
|
46
|
+
)
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
"-w", "--workspace", default=None,
|
|
49
|
+
help=(
|
|
50
|
+
"Target workspace (admin only; defaults to caller's "
|
|
51
|
+
"assigned workspace)"
|
|
52
|
+
),
|
|
53
|
+
)
|
|
54
|
+
parser.add_argument(
|
|
55
|
+
"--yes", action="store_true",
|
|
56
|
+
help="Skip the interactive confirmation prompt",
|
|
57
|
+
)
|
|
58
|
+
run_main(do_delete_user, parser)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
62
|
+
main()
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Disable a user. Soft-deletes (enabled=false) and revokes all their
|
|
3
|
+
API keys.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
|
|
8
|
+
from ._iam import DEFAULT_URL, DEFAULT_TOKEN, call_iam, run_main
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def do_disable_user(args):
|
|
12
|
+
req = {"operation": "disable-user", "user_id": args.user_id}
|
|
13
|
+
if args.workspace:
|
|
14
|
+
req["workspace"] = args.workspace
|
|
15
|
+
call_iam(args.api_url, args.token, req)
|
|
16
|
+
print(f"Disabled user {args.user_id}")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def main():
|
|
20
|
+
parser = argparse.ArgumentParser(
|
|
21
|
+
prog="tg-disable-user", description=__doc__,
|
|
22
|
+
)
|
|
23
|
+
parser.add_argument(
|
|
24
|
+
"-u", "--api-url", default=DEFAULT_URL,
|
|
25
|
+
help=f"API URL (default: {DEFAULT_URL})",
|
|
26
|
+
)
|
|
27
|
+
parser.add_argument(
|
|
28
|
+
"-t", "--token", default=DEFAULT_TOKEN,
|
|
29
|
+
help="Auth token (default: $TRUSTGRAPH_TOKEN)",
|
|
30
|
+
)
|
|
31
|
+
parser.add_argument(
|
|
32
|
+
"--user-id", required=True, help="User id to disable",
|
|
33
|
+
)
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
"-w", "--workspace", default=None,
|
|
36
|
+
help=(
|
|
37
|
+
"Target workspace (admin only; defaults to caller's "
|
|
38
|
+
"assigned workspace)"
|
|
39
|
+
),
|
|
40
|
+
)
|
|
41
|
+
run_main(do_disable_user, parser)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == "__main__":
|
|
45
|
+
main()
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Re-enable a previously disabled user. Does not restore their API
|
|
3
|
+
keys — those must be re-issued by an admin.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
|
|
8
|
+
from ._iam import DEFAULT_URL, DEFAULT_TOKEN, call_iam, run_main
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def do_enable_user(args):
|
|
12
|
+
req = {"operation": "enable-user", "user_id": args.user_id}
|
|
13
|
+
if args.workspace:
|
|
14
|
+
req["workspace"] = args.workspace
|
|
15
|
+
call_iam(args.api_url, args.token, req)
|
|
16
|
+
print(f"Enabled user {args.user_id}")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def main():
|
|
20
|
+
parser = argparse.ArgumentParser(
|
|
21
|
+
prog="tg-enable-user", description=__doc__,
|
|
22
|
+
)
|
|
23
|
+
parser.add_argument(
|
|
24
|
+
"-u", "--api-url", default=DEFAULT_URL,
|
|
25
|
+
help=f"API URL (default: {DEFAULT_URL})",
|
|
26
|
+
)
|
|
27
|
+
parser.add_argument(
|
|
28
|
+
"-t", "--token", default=DEFAULT_TOKEN,
|
|
29
|
+
help="Auth token (default: $TRUSTGRAPH_TOKEN)",
|
|
30
|
+
)
|
|
31
|
+
parser.add_argument(
|
|
32
|
+
"--user-id", required=True, help="User id to enable",
|
|
33
|
+
)
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
"-w", "--workspace", default=None,
|
|
36
|
+
help=(
|
|
37
|
+
"Target workspace (admin only; defaults to caller's "
|
|
38
|
+
"assigned workspace)"
|
|
39
|
+
),
|
|
40
|
+
)
|
|
41
|
+
run_main(do_enable_user, parser)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == "__main__":
|
|
45
|
+
main()
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""
|
|
2
|
+
List the API keys for a user.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
import tabulate
|
|
8
|
+
|
|
9
|
+
from ._iam import DEFAULT_URL, DEFAULT_TOKEN, call_iam, run_main
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def do_list_api_keys(args):
|
|
13
|
+
req = {"operation": "list-api-keys", "user_id": args.user_id}
|
|
14
|
+
if args.workspace:
|
|
15
|
+
req["workspace"] = args.workspace
|
|
16
|
+
resp = call_iam(args.api_url, args.token, req)
|
|
17
|
+
|
|
18
|
+
keys = resp.get("api_keys", [])
|
|
19
|
+
if not keys:
|
|
20
|
+
print("No keys.")
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
rows = [
|
|
24
|
+
[
|
|
25
|
+
k.get("id", ""),
|
|
26
|
+
k.get("name", ""),
|
|
27
|
+
k.get("prefix", ""),
|
|
28
|
+
k.get("created", ""),
|
|
29
|
+
k.get("last_used", "") or "—",
|
|
30
|
+
k.get("expires", "") or "never",
|
|
31
|
+
]
|
|
32
|
+
for k in keys
|
|
33
|
+
]
|
|
34
|
+
print(tabulate.tabulate(
|
|
35
|
+
rows,
|
|
36
|
+
headers=["id", "name", "prefix", "created", "last used", "expires"],
|
|
37
|
+
tablefmt="pretty",
|
|
38
|
+
stralign="left",
|
|
39
|
+
))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def main():
|
|
43
|
+
parser = argparse.ArgumentParser(
|
|
44
|
+
prog="tg-list-api-keys", description=__doc__,
|
|
45
|
+
)
|
|
46
|
+
parser.add_argument(
|
|
47
|
+
"-u", "--api-url", default=DEFAULT_URL,
|
|
48
|
+
help=f"API URL (default: {DEFAULT_URL})",
|
|
49
|
+
)
|
|
50
|
+
parser.add_argument(
|
|
51
|
+
"-t", "--token", default=DEFAULT_TOKEN,
|
|
52
|
+
help="Auth token (default: $TRUSTGRAPH_TOKEN)",
|
|
53
|
+
)
|
|
54
|
+
parser.add_argument(
|
|
55
|
+
"--user-id", required=True,
|
|
56
|
+
help="Owner user id",
|
|
57
|
+
)
|
|
58
|
+
parser.add_argument(
|
|
59
|
+
"-w", "--workspace", default=None,
|
|
60
|
+
help=(
|
|
61
|
+
"Target workspace (admin only; defaults to caller's "
|
|
62
|
+
"assigned workspace)"
|
|
63
|
+
),
|
|
64
|
+
)
|
|
65
|
+
run_main(do_list_api_keys, parser)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
if __name__ == "__main__":
|
|
69
|
+
main()
|