mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-06-10 23:35:14 +02:00
feat: add list-my-workspaces operation and document IAM in API specs
Add a new `list-my-workspaces` operation so non-admin users can discover which workspaces they have access to. For OSS IAM, regular users see their home workspace; admins see all workspaces. Also add the full IAM service to both OpenAPI and AsyncAPI specs — it was previously undocumented despite being a first-class service on both HTTP and WebSocket interfaces.
This commit is contained in:
parent
2a10e16c02
commit
f3aaf96588
20 changed files with 689 additions and 2 deletions
|
|
@ -100,6 +100,7 @@ multi-word subsystems.
|
|||
| `users:admin` | Assign / remove roles on users within the workspace |
|
||||
| `keys:self` | Create / revoke / list **own** API keys |
|
||||
| `keys:admin` | Create / revoke / list **any user's** API keys within the workspace |
|
||||
| `workspaces:list-own` | List workspaces the caller has access to |
|
||||
| `workspaces:admin` | Create / delete / disable workspaces (system-level) |
|
||||
| `iam:admin` | JWT signing-key rotation, IAM-level operations |
|
||||
| `metrics:read` | Prometheus metrics proxy |
|
||||
|
|
@ -110,7 +111,7 @@ The open-source edition ships three roles:
|
|||
|
||||
| Role | Capabilities |
|
||||
|---|---|
|
||||
| `reader` | `agent`, `graph:read`, `documents:read`, `rows:read`, `llm`, `embeddings`, `mcp`, `collections:read`, `knowledge:read`, `flows:read`, `config:read`, `keys:self` |
|
||||
| `reader` | `agent`, `graph:read`, `documents:read`, `rows:read`, `llm`, `embeddings`, `mcp`, `collections:read`, `knowledge:read`, `flows:read`, `config:read`, `keys:self`, `workspaces:list-own` |
|
||||
| `writer` | everything in `reader` **+** `graph:write`, `documents:write`, `rows:write`, `collections:write`, `knowledge:write` |
|
||||
| `admin` | everything in `writer` **+** `config:write`, `flows:write`, `users:read`, `users:write`, `users:admin`, `keys:admin`, `workspaces:admin`, `iam:admin`, `metrics:read` |
|
||||
|
||||
|
|
|
|||
|
|
@ -224,6 +224,7 @@ class ApiKeyRecord:
|
|||
| `enable-user` | `user_id`, `workspace` (optional integrity check) | — | Re-enables a previously disabled user; does not restore API keys. |
|
||||
| `delete-user` | `user_id`, `workspace` (optional integrity check) | — | Hard-delete; removes user record, username lookup, and all the user's API keys. |
|
||||
| `create-workspace` | `workspace_record` | `workspace` | System-level. |
|
||||
| `list-my-workspaces` | `actor` (gateway-injected) | `workspaces` | Returns the workspaces the calling user has access to. OSS: the user's home workspace; if the caller holds the `admin` role, returns all workspaces instead. Enterprise regimes return whatever workspaces the user has been granted access to. |
|
||||
| `list-workspaces` | — | `workspaces` | System-level. |
|
||||
| `get-workspace` | `workspace_record` (id only) | `workspace` | System-level. |
|
||||
| `update-workspace` | `workspace_record` | `workspace` | System-level. |
|
||||
|
|
|
|||
21
specs/api/components/schemas/iam/ApiKeyInput.yaml
Normal file
21
specs/api/components/schemas/iam/ApiKeyInput.yaml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
type: object
|
||||
description: |
|
||||
API key creation fields. Used with `create-api-key`.
|
||||
properties:
|
||||
user_id:
|
||||
type: string
|
||||
description: User to create the key for.
|
||||
examples:
|
||||
- usr_abc123
|
||||
name:
|
||||
type: string
|
||||
description: Operator-facing label for the key (e.g. "laptop", "CI").
|
||||
examples:
|
||||
- laptop
|
||||
expires:
|
||||
type: string
|
||||
description: |
|
||||
Optional expiry timestamp in ISO-8601 UTC. Empty string or
|
||||
omitted means the key does not expire.
|
||||
examples:
|
||||
- "2027-01-01T00:00:00Z"
|
||||
38
specs/api/components/schemas/iam/ApiKeyRecord.yaml
Normal file
38
specs/api/components/schemas/iam/ApiKeyRecord.yaml
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
type: object
|
||||
description: API key record returned by IAM operations.
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
description: Key identifier.
|
||||
examples:
|
||||
- key_xyz789
|
||||
user_id:
|
||||
type: string
|
||||
description: Owning user identifier.
|
||||
examples:
|
||||
- usr_abc123
|
||||
name:
|
||||
type: string
|
||||
description: Operator-facing label.
|
||||
examples:
|
||||
- laptop
|
||||
prefix:
|
||||
type: string
|
||||
description: |
|
||||
First 4 characters of the plaintext key, for identification
|
||||
in listings. Never enough to reconstruct the key.
|
||||
examples:
|
||||
- tg_a
|
||||
expires:
|
||||
type: string
|
||||
description: Expiry timestamp (ISO-8601 UTC). Empty if no expiry.
|
||||
examples:
|
||||
- "2027-01-01T00:00:00Z"
|
||||
created:
|
||||
type: string
|
||||
description: Creation timestamp (ISO-8601 UTC).
|
||||
examples:
|
||||
- "2026-01-15T10:30:00Z"
|
||||
last_used:
|
||||
type: string
|
||||
description: Last-used timestamp (ISO-8601 UTC). Empty if never used.
|
||||
106
specs/api/components/schemas/iam/IamRequest.yaml
Normal file
106
specs/api/components/schemas/iam/IamRequest.yaml
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
type: object
|
||||
description: |
|
||||
IAM service request.
|
||||
|
||||
The IAM service is a **global service** — it operates at system level,
|
||||
not scoped to a specific workspace. All operations are dispatched via
|
||||
the `operation` field.
|
||||
|
||||
Some operations require admin capabilities; others (like `whoami` and
|
||||
`list-my-workspaces`) are available to any authenticated user. See
|
||||
the capability vocabulary for details.
|
||||
|
||||
The `actor` field is injected by the gateway and cannot be set by
|
||||
the client. It identifies the authenticated caller.
|
||||
required:
|
||||
- operation
|
||||
properties:
|
||||
operation:
|
||||
type: string
|
||||
enum:
|
||||
- whoami
|
||||
- list-my-workspaces
|
||||
- create-user
|
||||
- list-users
|
||||
- get-user
|
||||
- update-user
|
||||
- disable-user
|
||||
- enable-user
|
||||
- delete-user
|
||||
- create-workspace
|
||||
- list-workspaces
|
||||
- get-workspace
|
||||
- update-workspace
|
||||
- disable-workspace
|
||||
- create-api-key
|
||||
- list-api-keys
|
||||
- revoke-api-key
|
||||
- reset-password
|
||||
- rotate-signing-key
|
||||
description: |
|
||||
Operation to perform.
|
||||
|
||||
**Any authenticated user:**
|
||||
- `whoami`: Return the caller's own user record
|
||||
- `list-my-workspaces`: List workspaces the caller has access to
|
||||
|
||||
**User management (requires `users:read`/`users:write`/`users:admin`):**
|
||||
- `create-user`: Create a new user in a workspace
|
||||
- `list-users`: List users (optionally filtered by workspace)
|
||||
- `get-user`: Get a specific user record
|
||||
- `update-user`: Update user fields (name, email, roles, enabled)
|
||||
- `disable-user`: Soft-disable a user and revoke their API keys
|
||||
- `enable-user`: Re-enable a previously disabled user
|
||||
- `delete-user`: Hard-delete a user and their API keys
|
||||
|
||||
**Workspace management (requires `workspaces:admin`):**
|
||||
- `create-workspace`: Create a new workspace
|
||||
- `list-workspaces`: List all workspaces (admin view)
|
||||
- `get-workspace`: Get a specific workspace record
|
||||
- `update-workspace`: Update workspace name or enabled state
|
||||
- `disable-workspace`: Disable workspace and all its users
|
||||
|
||||
**API key management (requires `keys:self` or `keys:admin`):**
|
||||
- `create-api-key`: Create an API key for a user
|
||||
- `list-api-keys`: List API keys for a user
|
||||
- `revoke-api-key`: Revoke (delete) an API key
|
||||
|
||||
**Password management:**
|
||||
- `reset-password`: Admin-initiated password reset (requires `users:admin`)
|
||||
|
||||
**System (requires `iam:admin`):**
|
||||
- `rotate-signing-key`: Rotate the JWT signing key
|
||||
workspace:
|
||||
type: string
|
||||
description: |
|
||||
Workspace scope. Required on workspace-scoped operations
|
||||
(e.g. `create-user`). Acts as an optional integrity check on
|
||||
operations that target a user or key — when supplied, the target's
|
||||
home workspace must match.
|
||||
|
||||
Omitted for system-level operations (`list-workspaces`,
|
||||
`rotate-signing-key`) and for identity-resolution operations
|
||||
(`whoami`, `list-my-workspaces`).
|
||||
examples:
|
||||
- default
|
||||
- production
|
||||
user_id:
|
||||
type: string
|
||||
description: |
|
||||
Target user identifier. Required for operations that act on a
|
||||
specific user: `get-user`, `update-user`, `disable-user`,
|
||||
`enable-user`, `delete-user`, `reset-password`, `list-api-keys`.
|
||||
examples:
|
||||
- usr_abc123
|
||||
user:
|
||||
$ref: './UserInput.yaml'
|
||||
workspace_record:
|
||||
$ref: './WorkspaceInput.yaml'
|
||||
key:
|
||||
$ref: './ApiKeyInput.yaml'
|
||||
key_id:
|
||||
type: string
|
||||
description: |
|
||||
API key identifier. Required for `revoke-api-key`.
|
||||
examples:
|
||||
- key_xyz789
|
||||
51
specs/api/components/schemas/iam/IamResponse.yaml
Normal file
51
specs/api/components/schemas/iam/IamResponse.yaml
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
type: object
|
||||
description: |
|
||||
IAM service response. Fields are populated depending on the
|
||||
operation that was invoked.
|
||||
properties:
|
||||
user:
|
||||
$ref: './UserRecord.yaml'
|
||||
users:
|
||||
type: array
|
||||
description: List of user records (populated by `list-users`).
|
||||
items:
|
||||
$ref: './UserRecord.yaml'
|
||||
workspace:
|
||||
$ref: './WorkspaceRecord.yaml'
|
||||
workspaces:
|
||||
type: array
|
||||
description: |
|
||||
List of workspace records (populated by `list-workspaces` and
|
||||
`list-my-workspaces`).
|
||||
items:
|
||||
$ref: './WorkspaceRecord.yaml'
|
||||
api_key_plaintext:
|
||||
type: string
|
||||
description: |
|
||||
Plaintext API key. Returned **once** by `create-api-key`.
|
||||
Never populated on any other operation. The caller must
|
||||
capture this value — it cannot be retrieved again.
|
||||
api_key:
|
||||
$ref: './ApiKeyRecord.yaml'
|
||||
api_keys:
|
||||
type: array
|
||||
description: List of API key records (populated by `list-api-keys`).
|
||||
items:
|
||||
$ref: './ApiKeyRecord.yaml'
|
||||
temporary_password:
|
||||
type: string
|
||||
description: |
|
||||
Temporary password returned once by `reset-password`.
|
||||
error:
|
||||
type: object
|
||||
description: Error details (present on failure).
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
description: |
|
||||
Error type. One of: `invalid-argument`, `not-found`,
|
||||
`duplicate`, `auth-failed`, `weak-password`, `disabled`,
|
||||
`operation-not-permitted`, `internal-error`.
|
||||
message:
|
||||
type: string
|
||||
description: Human-readable error description (not surfaced to end users).
|
||||
42
specs/api/components/schemas/iam/UserInput.yaml
Normal file
42
specs/api/components/schemas/iam/UserInput.yaml
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
type: object
|
||||
description: |
|
||||
User creation/update fields. Used with `create-user` and `update-user`.
|
||||
The `password` field is only accepted on `create-user`.
|
||||
properties:
|
||||
username:
|
||||
type: string
|
||||
description: Login username. Unique within a workspace.
|
||||
examples:
|
||||
- alice
|
||||
name:
|
||||
type: string
|
||||
description: Display name.
|
||||
examples:
|
||||
- Alice Smith
|
||||
email:
|
||||
type: string
|
||||
description: Email address.
|
||||
examples:
|
||||
- alice@example.com
|
||||
password:
|
||||
type: string
|
||||
description: |
|
||||
Initial password. Only accepted on `create-user`; rejected on
|
||||
`update-user`. Use `reset-password` or `change-password` to
|
||||
modify passwords.
|
||||
roles:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: |
|
||||
Roles to assign. Open-source roles: `reader`, `writer`, `admin`.
|
||||
examples:
|
||||
- - reader
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Whether the user is enabled.
|
||||
default: true
|
||||
must_change_password:
|
||||
type: boolean
|
||||
description: Force password change on next login.
|
||||
default: false
|
||||
46
specs/api/components/schemas/iam/UserRecord.yaml
Normal file
46
specs/api/components/schemas/iam/UserRecord.yaml
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
type: object
|
||||
description: User record returned by IAM operations.
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
description: Unique user identifier.
|
||||
examples:
|
||||
- usr_abc123
|
||||
workspace:
|
||||
type: string
|
||||
description: User's home workspace.
|
||||
examples:
|
||||
- default
|
||||
username:
|
||||
type: string
|
||||
description: Login username (unique within workspace).
|
||||
examples:
|
||||
- alice
|
||||
name:
|
||||
type: string
|
||||
description: Display name.
|
||||
examples:
|
||||
- Alice Smith
|
||||
email:
|
||||
type: string
|
||||
description: Email address.
|
||||
examples:
|
||||
- alice@example.com
|
||||
roles:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Assigned roles.
|
||||
examples:
|
||||
- - reader
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Whether the user is enabled.
|
||||
must_change_password:
|
||||
type: boolean
|
||||
description: Whether the user must change password on next login.
|
||||
created:
|
||||
type: string
|
||||
description: Creation timestamp (ISO-8601 UTC).
|
||||
examples:
|
||||
- "2026-01-15T10:30:00Z"
|
||||
23
specs/api/components/schemas/iam/WorkspaceInput.yaml
Normal file
23
specs/api/components/schemas/iam/WorkspaceInput.yaml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
type: object
|
||||
description: |
|
||||
Workspace creation/update fields. Used with `create-workspace` and
|
||||
`update-workspace`.
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
description: |
|
||||
Workspace identifier. Required for all workspace operations.
|
||||
Immutable after creation.
|
||||
examples:
|
||||
- default
|
||||
- production
|
||||
name:
|
||||
type: string
|
||||
description: Human-readable workspace name.
|
||||
examples:
|
||||
- Default Workspace
|
||||
- Production
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Whether the workspace is enabled.
|
||||
default: true
|
||||
21
specs/api/components/schemas/iam/WorkspaceRecord.yaml
Normal file
21
specs/api/components/schemas/iam/WorkspaceRecord.yaml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
type: object
|
||||
description: Workspace record returned by IAM operations.
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
description: Workspace identifier.
|
||||
examples:
|
||||
- default
|
||||
name:
|
||||
type: string
|
||||
description: Human-readable workspace name.
|
||||
examples:
|
||||
- Default Workspace
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Whether the workspace is enabled.
|
||||
created:
|
||||
type: string
|
||||
description: Creation timestamp (ISO-8601 UTC).
|
||||
examples:
|
||||
- "2026-01-01T00:00:00Z"
|
||||
|
|
@ -89,6 +89,8 @@ security:
|
|||
- bearerAuth: []
|
||||
|
||||
tags:
|
||||
- name: IAM
|
||||
description: Identity and access management (global)
|
||||
- name: Config
|
||||
description: Configuration management (workspace-scoped)
|
||||
- name: Flow
|
||||
|
|
@ -109,6 +111,11 @@ tags:
|
|||
description: System metrics and monitoring
|
||||
|
||||
paths:
|
||||
# Global services
|
||||
/api/v1/iam:
|
||||
$ref: './paths/iam.yaml'
|
||||
|
||||
# Workspace-scoped services
|
||||
/api/v1/config:
|
||||
$ref: './paths/config.yaml'
|
||||
/api/v1/flow:
|
||||
|
|
|
|||
206
specs/api/paths/iam.yaml
Normal file
206
specs/api/paths/iam.yaml
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
post:
|
||||
tags:
|
||||
- IAM
|
||||
summary: IAM service (global)
|
||||
description: |
|
||||
Identity and access management service.
|
||||
|
||||
This is a **global service** — it operates at system level, not
|
||||
scoped to a specific workspace. The `workspace` field in the
|
||||
request body is used as a scope filter or integrity check on
|
||||
certain operations, not as an addressing component.
|
||||
|
||||
## Authentication
|
||||
|
||||
Most operations require a bearer token. The gateway resolves the
|
||||
token to an authenticated identity and injects the `actor` field
|
||||
(the caller's user ID) into the request. Clients cannot set
|
||||
`actor` — the gateway overwrites it.
|
||||
|
||||
## Operations by Capability
|
||||
|
||||
### Any authenticated user
|
||||
- `whoami`: Return the caller's own user record
|
||||
- `list-my-workspaces`: List workspaces the caller has access to.
|
||||
For open-source IAM: returns the caller's home workspace, or all
|
||||
workspaces if the caller has the `admin` role.
|
||||
|
||||
### User management (`users:read` / `users:write` / `users:admin`)
|
||||
- `create-user`: Create a new user in a workspace
|
||||
- `list-users`: List users, optionally filtered by workspace
|
||||
- `get-user`: Get a user record by ID
|
||||
- `update-user`: Update user fields (name, email, roles, enabled)
|
||||
- `disable-user`: Soft-disable a user and revoke their API keys
|
||||
- `enable-user`: Re-enable a disabled user
|
||||
- `delete-user`: Hard-delete a user and their API keys
|
||||
|
||||
### Workspace management (`workspaces:admin`)
|
||||
- `create-workspace`: Create a new workspace
|
||||
- `list-workspaces`: List all workspaces (admin view)
|
||||
- `get-workspace`: Get a workspace record
|
||||
- `update-workspace`: Update workspace name or enabled state
|
||||
- `disable-workspace`: Disable a workspace and all its users
|
||||
|
||||
### API key management (`keys:self` / `keys:admin`)
|
||||
- `create-api-key`: Create an API key (plaintext returned once)
|
||||
- `list-api-keys`: List API keys for a user
|
||||
- `revoke-api-key`: Revoke (delete) an API key
|
||||
|
||||
### Password management (`users:admin`)
|
||||
- `reset-password`: Admin-initiated password reset (returns temporary password)
|
||||
|
||||
### System (`iam:admin`)
|
||||
- `rotate-signing-key`: Rotate the JWT signing key
|
||||
|
||||
operationId: iamService
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../components/schemas/iam/IamRequest.yaml'
|
||||
examples:
|
||||
whoami:
|
||||
summary: Get the caller's own user record
|
||||
value:
|
||||
operation: whoami
|
||||
listMyWorkspaces:
|
||||
summary: List workspaces the caller has access to
|
||||
value:
|
||||
operation: list-my-workspaces
|
||||
createUser:
|
||||
summary: Create a new user
|
||||
value:
|
||||
operation: create-user
|
||||
workspace: default
|
||||
user:
|
||||
username: alice
|
||||
name: Alice Smith
|
||||
email: alice@example.com
|
||||
password: changeme123
|
||||
roles:
|
||||
- writer
|
||||
listUsers:
|
||||
summary: List users in a workspace
|
||||
value:
|
||||
operation: list-users
|
||||
workspace: default
|
||||
getUser:
|
||||
summary: Get a specific user
|
||||
value:
|
||||
operation: get-user
|
||||
user_id: usr_abc123
|
||||
updateUser:
|
||||
summary: Update a user's roles
|
||||
value:
|
||||
operation: update-user
|
||||
user_id: usr_abc123
|
||||
user:
|
||||
roles:
|
||||
- admin
|
||||
disableUser:
|
||||
summary: Disable a user
|
||||
value:
|
||||
operation: disable-user
|
||||
user_id: usr_abc123
|
||||
createWorkspace:
|
||||
summary: Create a workspace
|
||||
value:
|
||||
operation: create-workspace
|
||||
workspace_record:
|
||||
id: production
|
||||
name: Production Workspace
|
||||
listWorkspaces:
|
||||
summary: List all workspaces (admin)
|
||||
value:
|
||||
operation: list-workspaces
|
||||
createApiKey:
|
||||
summary: Create an API key
|
||||
value:
|
||||
operation: create-api-key
|
||||
key:
|
||||
user_id: usr_abc123
|
||||
name: laptop
|
||||
expires: "2027-01-01T00:00:00Z"
|
||||
listApiKeys:
|
||||
summary: List a user's API keys
|
||||
value:
|
||||
operation: list-api-keys
|
||||
user_id: usr_abc123
|
||||
revokeApiKey:
|
||||
summary: Revoke an API key
|
||||
value:
|
||||
operation: revoke-api-key
|
||||
key_id: key_xyz789
|
||||
resetPassword:
|
||||
summary: Admin-initiated password reset
|
||||
value:
|
||||
operation: reset-password
|
||||
user_id: usr_abc123
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../components/schemas/iam/IamResponse.yaml'
|
||||
examples:
|
||||
whoami:
|
||||
summary: Caller's user record
|
||||
value:
|
||||
user:
|
||||
id: usr_abc123
|
||||
workspace: default
|
||||
username: alice
|
||||
name: Alice Smith
|
||||
email: alice@example.com
|
||||
roles:
|
||||
- writer
|
||||
enabled: true
|
||||
must_change_password: false
|
||||
created: "2026-01-15T10:30:00Z"
|
||||
listMyWorkspaces:
|
||||
summary: Workspaces the caller can access
|
||||
value:
|
||||
workspaces:
|
||||
- id: default
|
||||
name: Default Workspace
|
||||
enabled: true
|
||||
created: "2026-01-01T00:00:00Z"
|
||||
listUsers:
|
||||
summary: Users in a workspace
|
||||
value:
|
||||
users:
|
||||
- id: usr_abc123
|
||||
workspace: default
|
||||
username: alice
|
||||
name: Alice Smith
|
||||
roles:
|
||||
- writer
|
||||
enabled: true
|
||||
created: "2026-01-15T10:30:00Z"
|
||||
createApiKey:
|
||||
summary: New API key (plaintext returned once)
|
||||
value:
|
||||
api_key_plaintext: tg_aBcDeFgHiJkLmNoPqRsTuVwXyZ
|
||||
api_key:
|
||||
id: key_xyz789
|
||||
user_id: usr_abc123
|
||||
name: laptop
|
||||
prefix: tg_a
|
||||
expires: "2027-01-01T00:00:00Z"
|
||||
created: "2026-05-29T14:00:00Z"
|
||||
resetPassword:
|
||||
summary: Temporary password (returned once)
|
||||
value:
|
||||
temporary_password: tmp_xK9mQ2pL
|
||||
'400':
|
||||
description: Bad request (unknown operation, missing required fields)
|
||||
'401':
|
||||
$ref: '../components/responses/Unauthorized.yaml'
|
||||
'403':
|
||||
description: Access denied (insufficient capabilities)
|
||||
'500':
|
||||
$ref: '../components/responses/Error.yaml'
|
||||
|
|
@ -9,6 +9,9 @@ description: |
|
|||
payload:
|
||||
description: Service request envelope with id, service, optional flow, and service-specific request payload
|
||||
oneOf:
|
||||
# Global services
|
||||
- $ref: './requests/IamRequest.yaml'
|
||||
|
||||
# Workspace-scoped services (no flow parameter)
|
||||
- $ref: './requests/ConfigRequest.yaml'
|
||||
- $ref: './requests/FlowRequest.yaml'
|
||||
|
|
|
|||
25
specs/websocket/components/messages/requests/IamRequest.yaml
Normal file
25
specs/websocket/components/messages/requests/IamRequest.yaml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
type: object
|
||||
description: WebSocket request for IAM service (global service)
|
||||
required:
|
||||
- id
|
||||
- service
|
||||
- request
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
description: Unique request identifier
|
||||
service:
|
||||
type: string
|
||||
const: iam
|
||||
description: Service identifier for IAM service
|
||||
request:
|
||||
$ref: '../../../../api/components/schemas/iam/IamRequest.yaml'
|
||||
examples:
|
||||
- id: req-1
|
||||
service: iam
|
||||
request:
|
||||
operation: whoami
|
||||
- id: req-2
|
||||
service: iam
|
||||
request:
|
||||
operation: list-my-workspaces
|
||||
|
|
@ -300,6 +300,14 @@ class IamClient(RequestResponse):
|
|||
)
|
||||
return resp.workspace
|
||||
|
||||
async def list_my_workspaces(self, actor="", timeout=IAM_TIMEOUT):
|
||||
resp = await self._request(
|
||||
operation="list-my-workspaces",
|
||||
actor=actor,
|
||||
timeout=timeout,
|
||||
)
|
||||
return list(resp.workspaces)
|
||||
|
||||
async def list_workspaces(self, actor="", timeout=IAM_TIMEOUT):
|
||||
resp = await self._request(
|
||||
operation="list-workspaces",
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ tg-create-api-key = "trustgraph.cli.create_api_key:main"
|
|||
tg-list-api-keys = "trustgraph.cli.list_api_keys:main"
|
||||
tg-revoke-api-key = "trustgraph.cli.revoke_api_key:main"
|
||||
tg-list-workspaces = "trustgraph.cli.list_workspaces:main"
|
||||
tg-list-my-workspaces = "trustgraph.cli.list_my_workspaces:main"
|
||||
tg-create-workspace = "trustgraph.cli.create_workspace:main"
|
||||
tg-invoke-agent = "trustgraph.cli.invoke_agent:main"
|
||||
tg-invoke-document-rag = "trustgraph.cli.invoke_document_rag:main"
|
||||
|
|
|
|||
53
trustgraph-cli/trustgraph/cli/list_my_workspaces.py
Normal file
53
trustgraph-cli/trustgraph/cli/list_my_workspaces.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
"""
|
||||
List workspaces the current user has access to.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
|
||||
import tabulate
|
||||
|
||||
from ._iam import DEFAULT_URL, DEFAULT_TOKEN, call_iam, run_main
|
||||
|
||||
|
||||
def do_list_my_workspaces(args):
|
||||
resp = call_iam(
|
||||
args.api_url, args.token, {"operation": "list-my-workspaces"},
|
||||
)
|
||||
workspaces = resp.get("workspaces", [])
|
||||
if not workspaces:
|
||||
print("No workspaces.")
|
||||
return
|
||||
rows = [
|
||||
[
|
||||
w.get("id", ""),
|
||||
w.get("name", ""),
|
||||
"yes" if w.get("enabled") else "no",
|
||||
w.get("created", ""),
|
||||
]
|
||||
for w in workspaces
|
||||
]
|
||||
print(tabulate.tabulate(
|
||||
rows,
|
||||
headers=["id", "name", "enabled", "created"],
|
||||
tablefmt="pretty",
|
||||
stralign="left",
|
||||
))
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="tg-list-my-workspaces", 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)",
|
||||
)
|
||||
run_main(do_list_my_workspaces, parser)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -309,6 +309,13 @@ register(Operation(
|
|||
extract_resource=_empty_resource,
|
||||
extract_parameters=_no_parameters,
|
||||
))
|
||||
register(Operation(
|
||||
name="list-my-workspaces",
|
||||
capability="workspaces:list-own",
|
||||
resource_level=ResourceLevel.SYSTEM,
|
||||
extract_resource=_empty_resource,
|
||||
extract_parameters=_no_parameters,
|
||||
))
|
||||
register(Operation(
|
||||
name="list-workspaces",
|
||||
capability="workspaces:admin",
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ class NoAuthHandler:
|
|||
):
|
||||
return IamResponse()
|
||||
|
||||
if op == "list-workspaces":
|
||||
if op in ("list-workspaces", "list-my-workspaces"):
|
||||
return IamResponse()
|
||||
|
||||
if op in ("create-api-key", "list-api-keys", "revoke-api-key"):
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ _READER_CAPS = {
|
|||
"collections:read",
|
||||
"knowledge:read",
|
||||
"keys:self",
|
||||
"workspaces:list-own",
|
||||
}
|
||||
|
||||
_WRITER_CAPS = _READER_CAPS | {
|
||||
|
|
@ -328,6 +329,8 @@ class IamService:
|
|||
return await self.handle_delete_user(v)
|
||||
if op == "create-workspace":
|
||||
return await self.handle_create_workspace(v)
|
||||
if op == "list-my-workspaces":
|
||||
return await self.handle_list_my_workspaces(v)
|
||||
if op == "list-workspaces":
|
||||
return await self.handle_list_workspaces(v)
|
||||
if op == "get-workspace":
|
||||
|
|
@ -915,6 +918,30 @@ class IamService:
|
|||
row = await self.table_store.get_workspace(v.workspace_record.id)
|
||||
return IamResponse(workspace=self._row_to_workspace_record(row))
|
||||
|
||||
async def handle_list_my_workspaces(self, v):
|
||||
if not v.actor:
|
||||
return _err("invalid-argument", "actor required")
|
||||
|
||||
user_row = await self.table_store.get_user(v.actor)
|
||||
if user_row is None:
|
||||
return _err("not-found", "user not found")
|
||||
|
||||
user_roles = user_row[6] or []
|
||||
is_admin = "admin" in user_roles
|
||||
|
||||
if is_admin:
|
||||
rows = await self.table_store.list_workspaces()
|
||||
else:
|
||||
user_workspace = user_row[1]
|
||||
row = await self.table_store.get_workspace(user_workspace)
|
||||
rows = [row] if row else []
|
||||
|
||||
return IamResponse(
|
||||
workspaces=[
|
||||
self._row_to_workspace_record(r) for r in rows
|
||||
],
|
||||
)
|
||||
|
||||
async def handle_list_workspaces(self, v):
|
||||
rows = await self.table_store.list_workspaces()
|
||||
return IamResponse(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue