mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-04 08:56:59 +01:00
feat(cli,core): trace the keys that open My Files in pdfs diagnose
When the session is stored and unlocked but the daemon does not answer, pdfs diagnose asks Proton for a key report and prints a Keys section: each account key, each address key and the account key its token is for, and the address key the My Files share is encrypted to. It holds key IDs only, so it can be posted in an issue. Refs #40
This commit is contained in:
1 parent
eb25c16fe6
commit
fd80c34152
7 files changed
+361
-4
No files matched your search
@@ -3519,10 +3519,13 @@ fn cmd_diagnose() -> Result<()> {
|
||||
);
|
||||
|
||||
println!("\nAccount");
|
||||
let mut keys_unlocked = false;
|
||||
match pdfs_core::auth::load() {
|
||||
Ok(session) => {
|
||||
report.finding(DiagnoseLevel::Ok, " keyring session", session.username);
|
||||
match key_state_name() {
|
||||
let key_state = key_state_name();
|
||||
keys_unlocked = key_state != "locked";
|
||||
match key_state {
|
||||
"locked" => report.finding(
|
||||
DiagnoseLevel::Fail,
|
||||
" account keys",
|
||||
@@ -3561,6 +3564,7 @@ fn cmd_diagnose() -> Result<()> {
|
||||
);
|
||||
// A stale socket file with no listener is the common failure, so the reply
|
||||
// is what decides this rather than the file's existence.
|
||||
let mut daemon_responding = false;
|
||||
match control_request(CtlRequest::Status) {
|
||||
Ok(CtlResponse::Status {
|
||||
mountpoint,
|
||||
@@ -3572,6 +3576,7 @@ fn cmd_diagnose() -> Result<()> {
|
||||
version,
|
||||
..
|
||||
}) => {
|
||||
daemon_responding = true;
|
||||
report.finding(DiagnoseLevel::Ok, " daemon responding", "");
|
||||
if daemon_outdated(&version) {
|
||||
report.finding(
|
||||
@@ -3713,6 +3718,14 @@ fn cmd_diagnose() -> Result<()> {
|
||||
mountpoint.display().to_string(),
|
||||
);
|
||||
|
||||
// The daemon opens its control socket only once My Files is mounted, so a
|
||||
// daemon that answers has the keys it needs. One that does not may be stuck
|
||||
// on a key that does not open (#40); only then is Proton asked.
|
||||
if keys_unlocked && !daemon_responding {
|
||||
println!("\nKeys");
|
||||
diagnose_keys(&mut report);
|
||||
}
|
||||
|
||||
if report.failures == 0 {
|
||||
if report.warnings == 0 {
|
||||
println!("\nNo problems found.");
|
||||
@@ -3732,6 +3745,43 @@ fn cmd_diagnose() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Ask Proton which account and address keys open, and which of them the My
|
||||
/// Files share needs. The findings hold key IDs and no secrets.
|
||||
fn diagnose_keys(report: &mut DiagnoseReport) {
|
||||
use pdfs_core::keycheck::{self, Level};
|
||||
|
||||
let checked = tokio::runtime::Runtime::new()
|
||||
.map_err(anyhow::Error::from)
|
||||
.and_then(|rt| {
|
||||
rt.block_on(async {
|
||||
tokio::time::timeout(std::time::Duration::from_secs(30), keycheck::run())
|
||||
.await
|
||||
.map_err(|_| anyhow!("Proton did not answer within 30 seconds"))?
|
||||
.map_err(anyhow::Error::from)
|
||||
})
|
||||
});
|
||||
let (keys, share) = match checked {
|
||||
Ok(checked) => checked,
|
||||
Err(e) => {
|
||||
report.finding(DiagnoseLevel::Warn, " key check", format!("{e:#}"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
let findings = keycheck::findings(&keys, &share);
|
||||
let any_failed = findings.iter().any(|f| f.level != Level::Ok);
|
||||
for finding in findings {
|
||||
let level = match finding.level {
|
||||
Level::Ok => DiagnoseLevel::Ok,
|
||||
Level::Warn => DiagnoseLevel::Warn,
|
||||
Level::Fail => DiagnoseLevel::Fail,
|
||||
};
|
||||
report.finding(level, &format!(" {}", finding.label), finding.detail);
|
||||
}
|
||||
if any_failed {
|
||||
println!(" (key IDs are not secret; include this section when you report it)");
|
||||
}
|
||||
}
|
||||
|
||||
/// Send one request to the running mount daemon's control socket and read its
|
||||
/// reply. Errors if no daemon is listening.
|
||||
fn control_request(req: CtlRequest) -> Result<CtlResponse> {
|
||||
|
||||
Reference in new issue
Block a user