From a83a70b4c4b35eadcf31ae0734a665ffabfa02b8 Mon Sep 17 00:00:00 2001 From: Nathael Bonnal Date: Thu, 3 Sep 2026 00:46:09 +0200 Subject: [PATCH] fix(client): filter client lookup by client_id client-side MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GET /realms/{realm}/clients ignores the clientId query filter and always returns the full list, so get_client kept whatever the server happened to return first — usually not the requested client. client delete resolved its target the same way, making the mismatch destructive: 'client delete zukquote-realm' could delete an unrelated client instead. Filter the list client-side by client_id, error when nothing matches, and move delete's confirmation prompt after resolution so it names the client actually about to be deleted. --- libs/ferriskey-cli-client/src/lib.rs | 13 ++++++------- libs/ferriskey-cli-core/src/client.rs | 16 ++++++++++------ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/libs/ferriskey-cli-client/src/lib.rs b/libs/ferriskey-cli-client/src/lib.rs index c93c970..d8fb831 100644 --- a/libs/ferriskey-cli-client/src/lib.rs +++ b/libs/ferriskey-cli-client/src/lib.rs @@ -332,18 +332,17 @@ impl FerriskeyClient { Ok(()) } + /// The server ignores the `clientId` query filter on this route and + /// always returns the full list, so the match is done client-side. pub fn get_client( &self, realm: &str, client_id: &str, ) -> Result, FerriskeyClientError> { - let url = format!( - "{}?clientId={}", - self.endpoint(&format!("realms/{realm}/clients")), - client_id - ); - let mut results: Vec = self.get_list(&url)?; - Ok(results.drain(..).next()) + let results = self.list_clients(realm)?; + Ok(results + .into_iter() + .find(|client| client.client_id.as_deref() == Some(client_id))) } pub fn list_users(&self, realm: &str) -> Result, FerriskeyClientError> { diff --git a/libs/ferriskey-cli-core/src/client.rs b/libs/ferriskey-cli-core/src/client.rs index edd93e4..b27104a 100644 --- a/libs/ferriskey-cli-core/src/client.rs +++ b/libs/ferriskey-cli-core/src/client.rs @@ -114,24 +114,28 @@ fn delete_client( inline_context: Option, args: ClientDeleteArgs, ) -> Result<()> { - confirm( - &format!("Delete client '{}'?", args.client_id), - args.force, - )?; let context = resolve_context(context_override, inline_context)?; let realm = resolve_realm(&context, args.realm.clone())?; let client = auth_client(&context)?; let found = client .get_client(&realm, &args.client_id)? .ok_or_else(|| ClientCommandError::ClientNotFound(args.client_id.clone()))?; - let uuid = found .id + .clone() .ok_or_else(|| ClientCommandError::ClientNotFound(args.client_id.clone()))?; + let resolved_client_id = found.client_id.unwrap_or_else(|| args.client_id.clone()); + + // Show what is actually about to be deleted, not just what was asked + // for — the two can differ if the lookup resolved something unexpected. + confirm( + &format!("Delete client '{resolved_client_id}' (id: {uuid})?"), + args.force, + )?; client.delete_client(&realm, &uuid)?; render_message( output_format, - &format!("client '{}' deleted", args.client_id), + &format!("client '{resolved_client_id}' deleted"), ) }