test(rust/gui-client/auth): manual test for auto-sign-in with invalid token (#6792)

Synthetic replication for #6791.

The diff for the fix will probably be short, so I wanted this diff for
the test to be reviewed separately.

In your normal terminal: `cargo build -p firezone-gui-client -p
gui-smoke-test`

With sudo / admin powers: `./target/debug/gui-smoke-test.exe
--manual-tests`

Some customers _must_ have hit this, it's so easy to trigger.

I can't add it to the CI smoke test because there's no portal in CI
during the smoke test, unless we use Staging.
This commit is contained in:
Reactor Scram
2024-09-23 09:06:26 -05:00
committed by GitHub
parent 4c6a64defe
commit be058fdd96
8 changed files with 74 additions and 8 deletions

1
rust/Cargo.lock generated
View File

@@ -3184,6 +3184,7 @@ name = "gui-smoke-test"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"subprocess",
"tracing",
"tracing-subscriber",

View File

@@ -169,17 +169,22 @@ impl Auth {
);
let token = SecretString::from(token);
self.save_session(&resp.actor_name, &token)?;
self.state = State::SignedIn(Session {
actor_name: resp.actor_name,
});
Ok(SecretString::from(token))
}
fn save_session(&self, actor_name: &str, token: &SecretString) -> Result<(), Error> {
// This MUST be the only place the GUI can call `set_password`, since
// the actor name is also saved here.
self.token_store.set_password(token.expose_secret())?;
let path = actor_name_path()?;
std::fs::create_dir_all(path.parent().ok_or(Error::ActorNamePathWrong)?)
.map_err(Error::CreateDirAll)?;
std::fs::write(path, resp.actor_name.as_bytes()).map_err(Error::WriteActorName)?;
self.state = State::SignedIn(Session {
actor_name: resp.actor_name,
});
Ok(SecretString::from(token))
std::fs::write(path, actor_name.as_bytes()).map_err(Error::WriteActorName)?;
Ok(())
}
/// Returns the token if we are signed in
@@ -259,6 +264,16 @@ fn secure_equality(a: &SecretString, b: &SecretString) -> bool {
a.ct_eq(b).into()
}
pub fn replicate_6791() -> Result<()> {
tracing::warn!("Debugging issue #6791, pretending to be signed in with a bad token");
let this = Auth::new()?;
this.save_session(
"Jane Doe",
&SecretString::from("obviously invalid token for testing #6791".to_string()),
)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -158,6 +158,10 @@ struct Cli {
#[arg(long, hide = true)]
panic: bool,
/// Quit gracefully after a given number of seconds
#[arg(long, hide = true)]
quit_after: Option<u64>,
/// If true, slow down I/O operations to test how the GUI handles slow I/O
#[arg(long, hide = true)]
inject_faults: bool,

View File

@@ -5,6 +5,7 @@ use anyhow::Result;
#[derive(clap::Subcommand)]
pub(crate) enum Cmd {
Replicate6791,
SetAutostart(SetAutostartArgs),
}
@@ -26,6 +27,7 @@ pub(crate) struct StoreTokenArgs {
pub fn run(cmd: Cmd) -> Result<()> {
match cmd {
Cmd::Replicate6791 => firezone_gui_client_common::auth::replicate_6791(),
Cmd::SetAutostart(SetAutostartArgs { enabled }) => set_autostart(enabled),
}
}

View File

@@ -223,16 +223,27 @@ pub(crate) fn run(
let ctlr_tx = ctlr_tx.clone();
tokio::spawn(async move {
let delay = 5;
tracing::info!(
tracing::warn!(
"Will crash / error / panic on purpose in {delay} seconds to test error handling."
);
tokio::time::sleep(Duration::from_secs(delay)).await;
tracing::info!("Crashing / erroring / panicking on purpose");
tracing::warn!("Crashing / erroring / panicking on purpose");
ctlr_tx.send(ControllerRequest::Fail(failure)).await?;
Ok::<_, anyhow::Error>(())
});
}
if let Some(delay) = cli.quit_after {
let ctlr_tx = ctlr_tx.clone();
tokio::spawn(async move {
tracing::warn!("Will quit gracefully in {delay} seconds.");
tokio::time::sleep(Duration::from_secs(delay)).await;
tracing::warn!("Quitting gracefully due to `--quit-after`");
ctlr_tx.send(ControllerRequest::SystemTrayMenu(firezone_gui_client_common::system_tray::Event::Quit)).await?;
Ok::<_, anyhow::Error>(())
});
}
assert_eq!(
firezone_bin_shared::BUNDLE_ID,
app.handle().config().tauri.bundle.identifier,

View File

@@ -35,7 +35,7 @@ mod platform;
use platform::default_token_path;
/// Command-line args for the headless Client
#[derive(clap::Parser)]
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
// Needed to preserve CLI arg compatibility

View File

@@ -5,6 +5,7 @@ edition = "2021"
[dependencies]
anyhow = { version = "1.0" }
clap = { version = "4.5", features = ["derive"] }
subprocess = "0.2.9"
tracing = { workspace = true }
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }

View File

@@ -3,6 +3,7 @@
// Starts up the IPC service and GUI app and lets them run for a bit
use anyhow::{bail, Context as _, Result};
use clap::Parser;
use std::{
ffi::OsStr,
path::{Path, PathBuf},
@@ -21,8 +22,19 @@ const EXE_EXTENSION: &str = "";
#[cfg(target_os = "windows")]
const EXE_EXTENSION: &str = "exe";
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
/// Run tests that can't run in CI, like tests that need access to the staging network.
#[arg(long)]
manual_tests: bool,
}
fn main() -> Result<()> {
tracing_subscriber::fmt::init();
tracing::info!("Started logging");
let cli = Cli::try_parse()?;
let app = App::new()?;
dump_syms()?;
@@ -46,6 +58,26 @@ fn main() -> Result<()> {
app.check_crash_dump()?;
if cli.manual_tests {
manual_tests(&app)?;
}
Ok(())
}
fn manual_tests(app: &App) -> Result<()> {
// Replicate #6791
app.gui_command(&["debug", "replicate6791"])?
.popen()?
.wait()?;
let mut ipc_service = ipc_service_command().arg("run-smoke-test").popen()?;
let mut gui = app.gui_command(&["--quit-after", "10"])?.popen()?;
// Expect exit codes of 0
gui.wait()?.fz_exit_ok().context("GUI process")?;
ipc_service.wait()?.fz_exit_ok().context("IPC service")?;
Ok(())
}