From 9b7c5d4ebd441c4f75c53ad0fd3f6812a2087dca Mon Sep 17 00:00:00 2001 From: Reactor Scram Date: Thu, 11 Jan 2024 15:57:55 -0600 Subject: [PATCH] =?UTF-8?q?feat(windows):=20use=20ProgramData=20to=20store?= =?UTF-8?q?=20device=20ID=20instead=20of=20per-user=E2=80=A6=20(#3172)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fulfills #3159. This means the device ID is only tied to the Windows install instead of the user account. I also fixed up the logs and errors for that module real quick since I was already there. --- rust/Cargo.lock | 10 +++++ rust/windows-client/src-tauri/Cargo.toml | 1 + .../src-tauri/src/client/device_id.rs | 40 ++++++++++++++----- .../src-tauri/src/client/gui.rs | 2 +- 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 02fdec8a3..24cec5331 100755 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -2053,6 +2053,7 @@ dependencies = [ "hostname", "ipconfig", "keyring", + "known-folders", "rand 0.8.5", "ring 0.17.7", "secrecy", @@ -3232,6 +3233,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "known-folders" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4397c789f2709d23cfcb703b316e0766a8d4b17db2d47b0ab096ef6047cae1d8" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "kuchikiki" version = "0.8.2" diff --git a/rust/windows-client/src-tauri/Cargo.toml b/rust/windows-client/src-tauri/Cargo.toml index 2e3c4144f..d9a4df456 100755 --- a/rust/windows-client/src-tauri/Cargo.toml +++ b/rust/windows-client/src-tauri/Cargo.toml @@ -41,6 +41,7 @@ tracing-panic = "0.1.1" zip = { version = "0.6.6", features = ["deflate", "time"], default-features = false } rand = "0.8.5" windows-implement = "0.52.0" +known-folders = "1.1.0" # These dependencies are locked behind `cfg(windows)` because they either can't compile at all on Linux, or they need native dependencies like glib that are difficult to get. Try not to add more here. diff --git a/rust/windows-client/src-tauri/src/client/device_id.rs b/rust/windows-client/src-tauri/src/client/device_id.rs index 283116c57..dcfd59e2c 100755 --- a/rust/windows-client/src-tauri/src/client/device_id.rs +++ b/rust/windows-client/src-tauri/src/client/device_id.rs @@ -1,15 +1,31 @@ +use known_folders::{get_known_folder_path, KnownFolder}; use tokio::fs; -/// Get the device ID, generating it if it's not already on disk. +#[derive(thiserror::Error, Debug)] +pub(crate) enum Error { + #[error(transparent)] + Io(#[from] std::io::Error), + #[error("Can't find well-known folder")] + KnownFolder, +} + +/// Returns the device ID, generating it and saving it to disk if needed. +/// /// Per and , /// clients must generate their own random IDs and persist them to disk, to handle situations like VMs where a hardware ID is not unique or not available. /// +/// # Arguments +/// +/// * `identifier` - Our Tauri bundle identifier, e.g. "dev.firezone.client" +/// /// Returns: The UUID as a String, suitable for sending verbatim to `connlib_client_shared::Session::connect`. +/// /// Errors: If the disk is unwritable when initially generating the ID, or unwritable when re-generating an invalid ID. -pub(crate) async fn device_id( - app_local_data_dir: &crate::client::AppLocalDataDir, -) -> anyhow::Result { - let dir = app_local_data_dir.0.join("config"); +pub(crate) async fn device_id(identifier: &str) -> Result { + let dir = get_known_folder_path(KnownFolder::ProgramData) + .ok_or(Error::KnownFolder)? + .join(identifier) + .join("config"); let path = dir.join("device_id.json"); // Try to read it back from disk @@ -18,8 +34,9 @@ pub(crate) async fn device_id( .ok() .and_then(|s| serde_json::from_str::(&s).ok()) { - tracing::debug!("device ID loaded from disk is {}", j.id.to_string()); - return Ok(j.device_id()); + let device_id = j.device_id(); + tracing::debug!(?device_id, "Loaded device ID from disk"); + return Ok(device_id); } // Couldn't read, it's missing or invalid, generate a new one and save it. @@ -28,9 +45,14 @@ pub(crate) async fn device_id( // TODO: This file write has the same possible problems with power loss as described here https://github.com/firezone/firezone/pull/2757#discussion_r1416374516 // Since the device ID is random, typically only written once in the device's lifetime, and the read will error out if it's corrupted, it's low-risk. fs::create_dir_all(&dir).await?; - fs::write(&path, serde_json::to_string(&j)?).await?; + fs::write( + &path, + serde_json::to_string(&j).expect("Device ID should always be serializable"), + ) + .await?; - tracing::debug!("device ID saved to disk is {}", j.id.to_string()); + let device_id = j.device_id(); + tracing::debug!(?device_id, "Saved device ID to disk"); Ok(j.device_id()) } diff --git a/rust/windows-client/src-tauri/src/client/gui.rs b/rust/windows-client/src-tauri/src/client/gui.rs index 4b260ec0c..f74df6c79 100755 --- a/rust/windows-client/src-tauri/src/client/gui.rs +++ b/rust/windows-client/src-tauri/src/client/gui.rs @@ -309,7 +309,7 @@ impl Controller { advanced_settings: AdvancedSettings, notify_controller: Arc, ) -> Result { - let device_id = client::device_id::device_id(&app_local_data_dir(&app)?).await?; + let device_id = client::device_id::device_id(&app.config().tauri.bundle.identifier).await?; let mut this = Self { advanced_settings,