use anyhow::Result; use clusterflux_core::Digest; use serde_json::{json, Value}; use crate::client::{authenticated_or_local_trusted_request, JsonLineSession}; use crate::config::StoredCliSession; use crate::{confirmation_required_report, KeyAddArgs, KeyListArgs, KeyRevokeArgs}; #[cfg(test)] pub(crate) fn key_add_report(args: KeyAddArgs) -> Result { key_add_report_with_session(args, None) } pub(crate) fn key_add_report_with_session( args: KeyAddArgs, stored_session: Option<&StoredCliSession>, ) -> Result { let coordinator = effective_coordinator(&args.scope.coordinator, stored_session); if let Some(coordinator) = &coordinator { let tenant = effective_session_value(stored_session, &args.scope.tenant, |session| { &session.tenant }); let project = effective_session_value(stored_session, &args.scope.project, |session| { &session.project }); let user = effective_session_value(stored_session, &args.scope.user, |session| &session.user); let agent = args.agent.clone(); let public_key_fingerprint = Digest::sha256(&args.public_key); let mut session = JsonLineSession::connect(coordinator)?; let response = session.request(authenticated_or_local_trusted_request( coordinator, stored_session, json!({ "type": "register_agent_public_key", "agent": agent, "public_key": args.public_key, }), json!({ "type": "register_agent_public_key", "tenant": tenant, "project": project, "user": user, "agent": agent, "public_key": args.public_key, }), )?)?; let record = response.get("record").cloned().unwrap_or_else(|| { json!({ "tenant": tenant, "project": project, "user": user, "agent": agent, "public_key_fingerprint": public_key_fingerprint, "scopes": ["project:read", "project:run"], "human_account_creation_privilege": false, "browser_interaction_required_each_run": false, }) }); return Ok(json!({ "command": "key add", "coordinator": coordinator, "tenant": tenant, "project": project, "user": user, "agent": agent, "public_key_fingerprint": record .get("public_key_fingerprint") .cloned() .unwrap_or_else(|| json!(public_key_fingerprint)), "credential_scope": { "tenant": tenant, "project": project, "actions": record .get("scopes") .cloned() .unwrap_or_else(|| json!(["project:read", "project:run"])), "human_account_creation_privilege": record .get("human_account_creation_privilege") .cloned() .unwrap_or(json!(false)), }, "browser_interaction_required_each_run": record .get("browser_interaction_required_each_run") .cloned() .unwrap_or(json!(false)), "attribution": { "registered_by_user": user, "agent": agent, "credential_kind": "public_key", }, "response": response, "coordinator_session_requests": session.requests(), })); } Ok(json!({ "command": "key add", "status": "planned_without_coordinator", "agent": args.agent, "public_key_fingerprint": Digest::sha256(args.public_key), "browser_interaction_required_each_run": false, })) } #[cfg(test)] pub(crate) fn key_list_report(args: KeyListArgs) -> Result { key_list_report_with_session(args, None) } pub(crate) fn key_list_report_with_session( args: KeyListArgs, stored_session: Option<&StoredCliSession>, ) -> Result { let coordinator = effective_coordinator(&args.scope.coordinator, stored_session); if let Some(coordinator) = &coordinator { let tenant = effective_session_value(stored_session, &args.scope.tenant, |session| { &session.tenant }); let project = effective_session_value(stored_session, &args.scope.project, |session| { &session.project }); let user = effective_session_value(stored_session, &args.scope.user, |session| &session.user); let mut session = JsonLineSession::connect(coordinator)?; let response = session.request(authenticated_or_local_trusted_request( coordinator, stored_session, json!({ "type": "list_agent_public_keys", }), json!({ "type": "list_agent_public_keys", "tenant": tenant, "project": project, "user": user, }), )?)?; return Ok(json!({ "command": "key list", "coordinator": coordinator, "tenant": tenant, "project": project, "user": user, "records": response .get("records") .cloned() .unwrap_or_else(|| json!([])), "credential_scope": { "tenant": tenant, "project": project, "listed_for_user": user, "human_account_creation_privilege": false, }, "response": response, "coordinator_session_requests": session.requests(), })); } Ok(json!({ "command": "key list", "status": "requires_coordinator", "records": [], })) } #[cfg(test)] pub(crate) fn key_revoke_report(args: KeyRevokeArgs) -> Result { key_revoke_report_with_session(args, None) } pub(crate) fn key_revoke_report_with_session( args: KeyRevokeArgs, stored_session: Option<&StoredCliSession>, ) -> Result { if !args.yes { return Ok(confirmation_required_report( "key revoke", "revoke_agent_public_key", json!({ "coordinator": args.scope.coordinator, "tenant": args.scope.tenant, "project": args.scope.project, "user": args.scope.user, "agent": args.agent, }), format!("clusterflux key revoke --agent {} --yes", args.agent), )); } let coordinator = effective_coordinator(&args.scope.coordinator, stored_session); if let Some(coordinator) = &coordinator { let tenant = effective_session_value(stored_session, &args.scope.tenant, |session| { &session.tenant }); let project = effective_session_value(stored_session, &args.scope.project, |session| { &session.project }); let user = effective_session_value(stored_session, &args.scope.user, |session| &session.user); let agent = args.agent.clone(); let mut session = JsonLineSession::connect(coordinator)?; let response = session.request(authenticated_or_local_trusted_request( coordinator, stored_session, json!({ "type": "revoke_agent_public_key", "agent": agent, }), json!({ "type": "revoke_agent_public_key", "tenant": tenant, "project": project, "user": user, "agent": agent, }), )?)?; return Ok(json!({ "command": "key revoke", "coordinator": coordinator, "requires_confirmation": !args.yes, "tenant": tenant, "project": project, "user": user, "agent": agent, "revoked": response .pointer("/record/revoked") .cloned() .unwrap_or(json!(true)), "credential_scope": { "tenant": tenant, "project": project, "revoked_for_user": user, "human_account_creation_privilege": false, }, "attribution": { "revoked_by_user": user, "agent": agent, "credential_kind": "public_key", }, "response": response, "coordinator_session_requests": session.requests(), })); } Ok(json!({ "command": "key revoke", "status": "requires_coordinator", "requires_confirmation": !args.yes, "agent": args.agent, })) } fn effective_coordinator( requested: &Option, stored_session: Option<&StoredCliSession>, ) -> Option { requested.clone().or_else(|| { stored_session .filter(|session| session.session_secret.is_some()) .map(|session| session.coordinator.clone()) }) } fn effective_session_value( stored_session: Option<&StoredCliSession>, requested: &str, value: impl FnOnce(&StoredCliSession) -> &String, ) -> String { stored_session .filter(|session| session.session_secret.is_some()) .map(value) .cloned() .unwrap_or_else(|| requested.to_owned()) }