feat: add list-my-workspaces operation and document IAM in API specs (#961)

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:
cybermaggedon 2026-05-29 19:17:37 +01:00 committed by GitHub
parent 2a10e16c02
commit 6564adad80
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 689 additions and 2 deletions

View file

@ -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",

View file

@ -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"):

View file

@ -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(