iam: pass the scope through, and let the server's refusal speak

Which list routes insist on an owner is the server's rule, and it moves. The
deployed binary requires one on users, sessions and applications and not on
roles, permissions, providers, invitations, tokens or audit-logs; the change
that resolves a listing's tenant centrally is written and unreleased.

So nothing here keeps a copy of that rule. `apps` no longer refuses a missing
owner before asking, and no description promises that leaving it off makes the
credential decide — today that answers 400, carrying the reason, which the
transport already surfaces.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-08-24 03:44:00 -07:00
co-authored by Hanzo Dev
parent 08159273e4
commit 63198ee88b
2 changed files with 7 additions and 9 deletions
+6 -8
View File
@@ -365,8 +365,11 @@ impl HanzoTool {
async fn iam(&self, action: &str, p: &Map<String, Value>) -> Result<Value> {
let ok = |d| Ok(envelope_ok("hanzo", action, d));
let id = pstr(p, "id");
// Scope follows the caller: name an owner only when the caller names one,
// and the server resolves the rest from the credential.
// `owner` is passed through when the caller names one and left off when
// they do not. Which routes insist on one is the server's to decide and
// it varies by release, so nothing here keeps a copy of that rule — a
// refusal arrives carrying its reason, and the reason is what the caller
// sees.
let owner = pstr(p, "owner");
let scope: Vec<(String, String)> =
owner.iter().map(|o| ("owner".to_string(), o.clone())).collect();
@@ -445,12 +448,7 @@ impl HanzoTool {
"permissions" => ok(self.call("iam", "GET", "/v1/iam/permissions", &scope, None).await?),
"providers" => ok(self.call("iam", "GET", "/v1/iam/providers", &scope, None).await?),
// Applications are listed one owner at a time; ask rather than guess.
"apps" => {
if owner.is_none() {
return Ok(need(action, "owner"));
}
ok(self.call("iam", "GET", "/v1/iam/applications", &scope, None).await?)
}
"apps" => ok(self.call("iam", "GET", "/v1/iam/applications", &scope, None).await?),
"tokens" => ok(self
.call("iam", "GET", "/v1/iam/tokens", &with(&["organization"]), None)
.await?),
+1 -1
View File
@@ -77,7 +77,7 @@ export const iamTool: Tool = {
'list_permissions', 'assign_role', 'remove_role',
'list_sessions', 'delete_session'
] },
owner: { type: 'string', description: 'Organization owning the row; omit to let the credential decide' },
owner: { type: 'string', description: 'Organization to scope the call to; the server decides which routes require it' },
id: { type: 'string', description: 'owner/name — owner/name/application for delete_session' },
name: { type: 'string' }, email: { type: 'string' }, password: { type: 'string' },
displayName: { type: 'string' }, phone: { type: 'string' },