mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-04-25 00:16:23 +02:00
Added delete user
This commit is contained in:
parent
9ae79ff712
commit
ef412f2a99
5 changed files with 129 additions and 0 deletions
|
|
@ -201,6 +201,16 @@ class IamClient(RequestResponse):
|
||||||
timeout=timeout,
|
timeout=timeout,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def delete_user(self, workspace, user_id, actor="",
|
||||||
|
timeout=IAM_TIMEOUT):
|
||||||
|
await self._request(
|
||||||
|
operation="delete-user",
|
||||||
|
workspace=workspace,
|
||||||
|
actor=actor,
|
||||||
|
user_id=user_id,
|
||||||
|
timeout=timeout,
|
||||||
|
)
|
||||||
|
|
||||||
async def create_workspace(self, workspace_record, actor="",
|
async def create_workspace(self, workspace_record, actor="",
|
||||||
timeout=IAM_TIMEOUT):
|
timeout=IAM_TIMEOUT):
|
||||||
resp = await self._request(
|
resp = await self._request(
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ tg-create-user = "trustgraph.cli.create_user:main"
|
||||||
tg-list-users = "trustgraph.cli.list_users:main"
|
tg-list-users = "trustgraph.cli.list_users:main"
|
||||||
tg-disable-user = "trustgraph.cli.disable_user:main"
|
tg-disable-user = "trustgraph.cli.disable_user:main"
|
||||||
tg-enable-user = "trustgraph.cli.enable_user:main"
|
tg-enable-user = "trustgraph.cli.enable_user:main"
|
||||||
|
tg-delete-user = "trustgraph.cli.delete_user:main"
|
||||||
tg-change-password = "trustgraph.cli.change_password:main"
|
tg-change-password = "trustgraph.cli.change_password:main"
|
||||||
tg-reset-password = "trustgraph.cli.reset_password:main"
|
tg-reset-password = "trustgraph.cli.reset_password:main"
|
||||||
tg-create-api-key = "trustgraph.cli.create_api_key:main"
|
tg-create-api-key = "trustgraph.cli.create_api_key:main"
|
||||||
|
|
|
||||||
62
trustgraph-cli/trustgraph/cli/delete_user.py
Normal file
62
trustgraph-cli/trustgraph/cli/delete_user.py
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
"""
|
||||||
|
Delete a user. Removes the user record, their username lookup,
|
||||||
|
and all their API keys. The freed username becomes available for
|
||||||
|
re-use.
|
||||||
|
|
||||||
|
Irreversible. Use tg-disable-user if you want to preserve the
|
||||||
|
record (audit trail, username squatting protection).
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
from ._iam import DEFAULT_URL, DEFAULT_TOKEN, call_iam, run_main
|
||||||
|
|
||||||
|
|
||||||
|
def do_delete_user(args):
|
||||||
|
if not args.yes:
|
||||||
|
confirm = input(
|
||||||
|
f"Delete user {args.user_id}? This is irreversible. "
|
||||||
|
f"[type 'yes' to confirm]: "
|
||||||
|
)
|
||||||
|
if confirm.strip() != "yes":
|
||||||
|
print("Aborted.")
|
||||||
|
return
|
||||||
|
|
||||||
|
req = {"operation": "delete-user", "user_id": args.user_id}
|
||||||
|
if args.workspace:
|
||||||
|
req["workspace"] = args.workspace
|
||||||
|
call_iam(args.api_url, args.token, req)
|
||||||
|
print(f"Deleted user {args.user_id}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog="tg-delete-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(
|
||||||
|
"--user-id", required=True, help="User id to delete",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-w", "--workspace", default=None,
|
||||||
|
help=(
|
||||||
|
"Target workspace (admin only; defaults to caller's "
|
||||||
|
"assigned workspace)"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--yes", action="store_true",
|
||||||
|
help="Skip the interactive confirmation prompt",
|
||||||
|
)
|
||||||
|
run_main(do_delete_user, parser)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
@ -236,6 +236,8 @@ class IamService:
|
||||||
return await self.handle_disable_user(v)
|
return await self.handle_disable_user(v)
|
||||||
if op == "enable-user":
|
if op == "enable-user":
|
||||||
return await self.handle_enable_user(v)
|
return await self.handle_enable_user(v)
|
||||||
|
if op == "delete-user":
|
||||||
|
return await self.handle_delete_user(v)
|
||||||
if op == "create-workspace":
|
if op == "create-workspace":
|
||||||
return await self.handle_create_workspace(v)
|
return await self.handle_create_workspace(v)
|
||||||
if op == "list-workspaces":
|
if op == "list-workspaces":
|
||||||
|
|
@ -730,6 +732,46 @@ class IamService:
|
||||||
)
|
)
|
||||||
return IamResponse()
|
return IamResponse()
|
||||||
|
|
||||||
|
async def handle_delete_user(self, v):
|
||||||
|
"""Hard-delete a user. Removes the ``iam_users`` row, the
|
||||||
|
``iam_users_by_username`` lookup row, and every API key
|
||||||
|
belonging to the user.
|
||||||
|
|
||||||
|
Unlike disable, this frees the username for re-use and
|
||||||
|
removes the user's personal data from storage (intended to
|
||||||
|
cover GDPR erasure-style requirements). When audit logging
|
||||||
|
lands, the decision to delete vs. anonymise referenced audit
|
||||||
|
rows will need to be revisited."""
|
||||||
|
if not v.workspace:
|
||||||
|
return _err("invalid-argument", "workspace required")
|
||||||
|
if not v.user_id:
|
||||||
|
return _err("invalid-argument", "user_id required")
|
||||||
|
|
||||||
|
user_row, err = await self._user_in_workspace(
|
||||||
|
v.user_id, v.workspace,
|
||||||
|
)
|
||||||
|
if err is not None:
|
||||||
|
return err
|
||||||
|
|
||||||
|
# user_row indices match get_user columns. Username is [2].
|
||||||
|
username = user_row[2]
|
||||||
|
|
||||||
|
# Revoke all API keys.
|
||||||
|
key_rows = await self.table_store.list_api_keys_by_user(v.user_id)
|
||||||
|
for kr in key_rows:
|
||||||
|
await self.table_store.delete_api_key(kr[0])
|
||||||
|
|
||||||
|
# Remove username lookup.
|
||||||
|
if username:
|
||||||
|
await self.table_store.delete_username_lookup(
|
||||||
|
v.workspace, username,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Remove user record.
|
||||||
|
await self.table_store.delete_user(v.user_id)
|
||||||
|
|
||||||
|
return IamResponse()
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Workspace CRUD
|
# Workspace CRUD
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,9 @@ class IamTableStore:
|
||||||
DELETE FROM iam_users_by_username
|
DELETE FROM iam_users_by_username
|
||||||
WHERE workspace = ? AND username = ?
|
WHERE workspace = ? AND username = ?
|
||||||
""")
|
""")
|
||||||
|
self.delete_user_stmt = c.prepare("""
|
||||||
|
DELETE FROM iam_users WHERE id = ?
|
||||||
|
""")
|
||||||
|
|
||||||
self.put_api_key_stmt = c.prepare("""
|
self.put_api_key_stmt = c.prepare("""
|
||||||
INSERT INTO iam_api_keys (
|
INSERT INTO iam_api_keys (
|
||||||
|
|
@ -301,6 +304,17 @@ class IamTableStore:
|
||||||
self.cassandra, self.list_users_by_workspace_stmt, (workspace,),
|
self.cassandra, self.list_users_by_workspace_stmt, (workspace,),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def delete_user(self, id):
|
||||||
|
await async_execute(
|
||||||
|
self.cassandra, self.delete_user_stmt, (id,),
|
||||||
|
)
|
||||||
|
|
||||||
|
async def delete_username_lookup(self, workspace, username):
|
||||||
|
await async_execute(
|
||||||
|
self.cassandra, self.delete_username_lookup_stmt,
|
||||||
|
(workspace, username),
|
||||||
|
)
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# API keys
|
# API keys
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue