clusterflux-public/crates/clusterflux-cli/src/logout.rs
Clusterflux release c1769967c1 Public release release-01875e88a3e2
Source commit: 01875e88a3e25379c309489f9f057dd97c0d37de

Public tree identity: sha256:8f37a1aa0cc8f408975daf9cabbe193f8f4c169c6d25e8795e67a3ed5083c76b
2026-07-17 06:09:42 +02:00

127 lines
4.6 KiB
Rust

use std::path::PathBuf;
use anyhow::{Context, Result};
use serde_json::{json, Value};
use crate::client::JsonLineSession;
use crate::config::{read_cli_session, session_config_file, StoredCliSession};
use crate::confirm::confirmation_required_report;
use crate::errors::cli_error_summary;
use crate::AuthLogoutArgs;
pub(crate) fn auth_logout_report(args: AuthLogoutArgs, cwd: PathBuf) -> Result<Value> {
logout_report(args, cwd, "auth logout")
}
pub(crate) fn logout_report(args: AuthLogoutArgs, cwd: PathBuf, command: &str) -> Result<Value> {
let session_file = session_config_file(&cwd);
if !args.yes {
return Ok(confirmation_required_report(
command,
"delete_cli_session",
json!({
"session_file": session_file,
"node_credentials_untouched": true,
}),
format!("clusterflux {command} --yes"),
));
}
let stored_session = read_cli_session(&cwd).ok().flatten();
let coordinator_revocation = revoke_stored_cli_session_if_possible(stored_session.as_ref());
let existed = session_file.exists();
if existed {
std::fs::remove_file(&session_file)
.with_context(|| format!("failed to remove {}", session_file.display()))?;
}
Ok(json!({
"command": command,
"requires_confirmation": !args.yes,
"removed_cli_session_file": existed,
"server_session_revocation": coordinator_revocation,
"node_credentials_untouched": true,
"session_file": session_file,
}))
}
fn revoke_stored_cli_session_if_possible(stored_session: Option<&StoredCliSession>) -> Value {
let Some(session) = stored_session else {
return json!({
"attempted": false,
"revoked": false,
"reason": "no_parseable_cli_session",
});
};
let Some(session_secret) = session.session_secret.as_deref() else {
return json!({
"attempted": false,
"revoked": false,
"reason": "no_cli_session_secret",
"coordinator": session.coordinator,
});
};
// A CLI session credential is authority issued by one coordinator. Never
// redirect it to a command-line endpoint override.
let coordinator = session.coordinator.as_str();
let mut coordinator_session = match JsonLineSession::connect(coordinator) {
Ok(session) => session,
Err(error) => {
let message = error.to_string();
return json!({
"attempted": true,
"revoked": false,
"reachable": false,
"coordinator": coordinator,
"error": message,
"machine_error": cli_error_summary(&message),
"next_actions": ["clusterflux login --browser"],
});
}
};
let response = match coordinator_session.request_allow_error(json!({
"type": "authenticated",
"session_secret": session_secret,
"request": {
"type": "revoke_cli_session",
},
})) {
Ok(response) => response,
Err(error) => {
let message = error.to_string();
return json!({
"attempted": true,
"revoked": false,
"reachable": false,
"coordinator": coordinator,
"error": message,
"machine_error": cli_error_summary(&message),
"coordinator_session_requests": coordinator_session.requests(),
"next_actions": ["clusterflux login --browser"],
});
}
};
let revoked = response.get("type").and_then(Value::as_str) == Some("cli_session_revoked");
if !revoked {
let message = response
.get("message")
.and_then(Value::as_str)
.unwrap_or("coordinator did not revoke CLI session");
return json!({
"attempted": true,
"revoked": false,
"reachable": true,
"coordinator": coordinator,
"coordinator_response_type": response.get("type").and_then(Value::as_str).unwrap_or("unknown"),
"machine_error": cli_error_summary(message),
"coordinator_session_requests": coordinator_session.requests(),
"next_actions": ["clusterflux login --browser"],
});
}
json!({
"attempted": true,
"revoked": true,
"reachable": true,
"coordinator": coordinator,
"coordinator_response_type": "cli_session_revoked",
"coordinator_session_requests": coordinator_session.requests(),
})
}