Compare commits

..

No commits in common. "ae211a923a0750a99dcfd5761cae9997f2bc0c8a" and "18ae1c5fa5c23d5f6a3badf69d5e7be24bd38b0d" have entirely different histories.

14 changed files with 16 additions and 104 deletions

2
Cargo.lock generated
View File

@ -1439,6 +1439,7 @@ dependencies = [
"dotenvy_macro", "dotenvy_macro",
"log", "log",
"md5", "md5",
"open",
"redact-engine", "redact-engine",
"regex", "regex",
"reqwest", "reqwest",
@ -1470,7 +1471,6 @@ dependencies = [
"gpapi", "gpapi",
"html-escape", "html-escape",
"log", "log",
"open",
"regex", "regex",
"serde_json", "serde_json",
"tauri", "tauri",

View File

@ -44,7 +44,6 @@ compile-time = "0.2"
serde_urlencoded = "0.7" serde_urlencoded = "0.7"
md5="0.7" md5="0.7"
sha256="1" sha256="1"
open = "5"
# Tauri dependencies # Tauri dependencies
tauri = { version = "1.5" } tauri = { version = "1.5" }

View File

@ -22,4 +22,3 @@ html-escape = "0.2.13"
webkit2gtk = "0.18.2" webkit2gtk = "0.18.2"
tauri = { workspace = true, features = ["http-all"] } tauri = { workspace = true, features = ["http-all"] }
compile-time.workspace = true compile-time.workspace = true
open.workspace = true

View File

@ -11,10 +11,7 @@ use serde_json::json;
use tauri::{App, AppHandle, RunEvent}; use tauri::{App, AppHandle, RunEvent};
use tempfile::NamedTempFile; use tempfile::NamedTempFile;
use crate::{ use crate::auth_window::{portal_prelogin, AuthWindow};
auth_window::{portal_prelogin, AuthWindow},
browser_authenticator::BrowserAuthenticator,
};
const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), " (", compile_time::date_str!(), ")"); const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), " (", compile_time::date_str!(), ")");
@ -40,8 +37,6 @@ struct Cli {
ignore_tls_errors: bool, ignore_tls_errors: bool,
#[arg(long)] #[arg(long)]
clean: bool, clean: bool,
#[arg(long)]
default_browser: bool,
} }
impl Cli { impl Cli {
@ -61,15 +56,6 @@ impl Cli {
None => portal_prelogin(&self.server, &gp_params).await?, None => portal_prelogin(&self.server, &gp_params).await?,
}; };
if self.default_browser {
let browser_auth = BrowserAuthenticator::new(&saml_request);
browser_auth.authenticate()?;
info!("Please continue the authentication process in the default browser");
return Ok(());
}
self.saml_request.replace(saml_request); self.saml_request.replace(saml_request);
let app = create_app(self.clone())?; let app = create_app(self.clone())?;

View File

@ -1,7 +1,6 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
mod auth_window; mod auth_window;
mod browser_authenticator;
mod cli; mod cli;
#[tokio::main] #[tokio::main]

View File

@ -19,9 +19,8 @@ use gpapi::{
use inquire::{Password, PasswordDisplayMode, Select, Text}; use inquire::{Password, PasswordDisplayMode, Select, Text};
use log::info; use log::info;
use openconnect::Vpn; use openconnect::Vpn;
use tokio::{io::AsyncReadExt, net::TcpListener};
use crate::{cli::SharedArgs, GP_CLIENT_LOCK_FILE, GP_CLIENT_PORT_FILE}; use crate::{cli::SharedArgs, GP_CLIENT_LOCK_FILE};
#[derive(Args)] #[derive(Args)]
pub(crate) struct ConnectArgs { pub(crate) struct ConnectArgs {
@ -61,8 +60,6 @@ pub(crate) struct ConnectArgs {
hidpi: bool, hidpi: bool,
#[arg(long, help = "Do not reuse the remembered authentication cookie")] #[arg(long, help = "Do not reuse the remembered authentication cookie")]
clean: bool, clean: bool,
#[arg(long, help = "Use the default browser to authenticate")]
default_browser: bool,
} }
impl ConnectArgs { impl ConnectArgs {
@ -243,9 +240,7 @@ impl<'a> ConnectHandler<'a> {
match prelogin { match prelogin {
Prelogin::Saml(prelogin) => { Prelogin::Saml(prelogin) => {
let use_default_browser = prelogin.support_default_browser() && self.args.default_browser; SamlAuthLauncher::new(&self.args.server)
let cred = SamlAuthLauncher::new(&self.args.server)
.gateway(is_gateway) .gateway(is_gateway)
.saml_request(prelogin.saml_request()) .saml_request(prelogin.saml_request())
.user_agent(&self.args.user_agent) .user_agent(&self.args.user_agent)
@ -255,21 +250,8 @@ impl<'a> ConnectHandler<'a> {
.fix_openssl(self.shared_args.fix_openssl) .fix_openssl(self.shared_args.fix_openssl)
.ignore_tls_errors(self.shared_args.ignore_tls_errors) .ignore_tls_errors(self.shared_args.ignore_tls_errors)
.clean(self.args.clean) .clean(self.args.clean)
.default_browser(use_default_browser)
.launch() .launch()
.await?; .await
if let Some(cred) = cred {
return Ok(cred);
}
if !use_default_browser {
// This should never happen
unreachable!("SAML authentication failed without using the default browser");
}
info!("Waiting for the browser authentication to complete...");
wait_credentials().await
} }
Prelogin::Standard(prelogin) => { Prelogin::Standard(prelogin) => {
let prefix = if is_gateway { "Gateway" } else { "Portal" }; let prefix = if is_gateway { "Gateway" } else { "Portal" };
@ -292,27 +274,6 @@ impl<'a> ConnectHandler<'a> {
} }
} }
async fn wait_credentials() -> anyhow::Result<Credential> {
// Start a local server to receive the browser authentication data
let listener = TcpListener::bind("127.0.0.1:0").await?;
let port = listener.local_addr()?.port();
// Write the port to a file
fs::write(GP_CLIENT_PORT_FILE, port.to_string())?;
info!("Listening authentication data on port {}", port);
let (mut socket, _) = listener.accept().await?;
info!("Received the browser authentication data from the socket");
let mut data = String::new();
socket.read_to_string(&mut data).await?;
// Remove the port file
fs::remove_file(GP_CLIENT_PORT_FILE)?;
Credential::from_gpcallback(&data)
}
fn write_pid_file() { fn write_pid_file() {
let pid = std::process::id(); let pid = std::process::id();

View File

@ -7,9 +7,6 @@ use gpapi::{
utils::{endpoint::http_endpoint, env_file, shutdown_signal}, utils::{endpoint::http_endpoint, env_file, shutdown_signal},
}; };
use log::info; use log::info;
use tokio::io::AsyncWriteExt;
use crate::GP_CLIENT_PORT_FILE;
#[derive(Args)] #[derive(Args)]
pub(crate) struct LaunchGuiArgs { pub(crate) struct LaunchGuiArgs {
@ -81,11 +78,6 @@ impl<'a> LaunchGuiHandler<'a> {
} }
async fn feed_auth_data(auth_data: &str) -> anyhow::Result<()> { async fn feed_auth_data(auth_data: &str) -> anyhow::Result<()> {
let _ = tokio::join!(feed_auth_data_gui(auth_data), feed_auth_data_cli(auth_data));
Ok(())
}
async fn feed_auth_data_gui(auth_data: &str) -> anyhow::Result<()> {
let service_endpoint = http_endpoint().await?; let service_endpoint = http_endpoint().await?;
reqwest::Client::default() reqwest::Client::default()
@ -98,15 +90,6 @@ async fn feed_auth_data_gui(auth_data: &str) -> anyhow::Result<()> {
Ok(()) Ok(())
} }
async fn feed_auth_data_cli(auth_data: &str) -> anyhow::Result<()> {
let port = tokio::fs::read_to_string(GP_CLIENT_PORT_FILE).await?;
let mut stream = tokio::net::TcpStream::connect(format!("127.0.0.1:{}", port.trim())).await?;
stream.write_all(auth_data.as_bytes()).await?;
Ok(())
}
async fn try_active_gui() -> anyhow::Result<()> { async fn try_active_gui() -> anyhow::Result<()> {
let service_endpoint = http_endpoint().await?; let service_endpoint = http_endpoint().await?;

View File

@ -4,7 +4,6 @@ mod disconnect;
mod launch_gui; mod launch_gui;
pub(crate) const GP_CLIENT_LOCK_FILE: &str = "/var/run/gpclient.lock"; pub(crate) const GP_CLIENT_LOCK_FILE: &str = "/var/run/gpclient.lock";
pub(crate) const GP_CLIENT_PORT_FILE: &str = "/var/run/gpclient.port";
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {

View File

@ -31,7 +31,9 @@ sha256.workspace = true
tauri = { workspace = true, optional = true } tauri = { workspace = true, optional = true }
clap = { workspace = true, optional = true } clap = { workspace = true, optional = true }
open = { version = "5", optional = true }
[features] [features]
tauri = ["dep:tauri"] tauri = ["dep:tauri"]
clap = ["dep:clap"] clap = ["dep:clap"]
browser-auth = ["dep:open"]

View File

@ -2,7 +2,7 @@ use thiserror::Error;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum PortalError { pub enum PortalError {
#[error("Prelogin error: {0}")] #[error("Portal prelogin error: {0}")]
PreloginError(String), PreloginError(String),
#[error("Portal config error: {0}")] #[error("Portal config error: {0}")]
ConfigError(String), ConfigError(String),

View File

@ -139,23 +139,20 @@ pub async fn prelogin(portal: &str, gp_params: &GpParams) -> anyhow::Result<Prel
Err(anyhow!(PortalError::PreloginError(err.reason))) Err(anyhow!(PortalError::PreloginError(err.reason)))
})?; })?;
let prelogin = parse_res_xml(&res_xml, is_gateway).map_err(|err| { let prelogin = parse_res_xml(res_xml, is_gateway).map_err(|e| PortalError::PreloginError(e.to_string()))?;
warn!("Parse response error, response: {}", res_xml);
PortalError::PreloginError(err.to_string())
})?;
Ok(prelogin) Ok(prelogin)
} }
fn parse_res_xml(res_xml: &str, is_gateway: bool) -> anyhow::Result<Prelogin> { fn parse_res_xml(res_xml: String, is_gateway: bool) -> anyhow::Result<Prelogin> {
let doc = Document::parse(res_xml)?; let doc = Document::parse(&res_xml)?;
let status = xml::get_child_text(&doc, "status") let status = xml::get_child_text(&doc, "status")
.ok_or_else(|| anyhow::anyhow!("Prelogin response does not contain status element"))?; .ok_or_else(|| anyhow::anyhow!("Prelogin response does not contain status element"))?;
// Check the status of the prelogin response // Check the status of the prelogin response
if status.to_uppercase() != "SUCCESS" { if status.to_uppercase() != "SUCCESS" {
let msg = xml::get_child_text(&doc, "msg").unwrap_or(String::from("Unknown error")); let msg = xml::get_child_text(&doc, "msg").unwrap_or(String::from("Unknown error"));
bail!("{}", msg) bail!("Prelogin failed: {}", msg)
} }
let region = xml::get_child_text(&doc, "region").unwrap_or_else(|| { let region = xml::get_child_text(&doc, "region").unwrap_or_else(|| {

View File

@ -18,7 +18,6 @@ pub struct SamlAuthLauncher<'a> {
fix_openssl: bool, fix_openssl: bool,
ignore_tls_errors: bool, ignore_tls_errors: bool,
clean: bool, clean: bool,
default_browser: bool,
} }
impl<'a> SamlAuthLauncher<'a> { impl<'a> SamlAuthLauncher<'a> {
@ -34,7 +33,6 @@ impl<'a> SamlAuthLauncher<'a> {
fix_openssl: false, fix_openssl: false,
ignore_tls_errors: false, ignore_tls_errors: false,
clean: false, clean: false,
default_browser: false,
} }
} }
@ -83,13 +81,8 @@ impl<'a> SamlAuthLauncher<'a> {
self self
} }
pub fn default_browser(mut self, default_browser: bool) -> Self {
self.default_browser = default_browser;
self
}
/// Launch the authenticator binary as the current user or SUDO_USER if available. /// Launch the authenticator binary as the current user or SUDO_USER if available.
pub async fn launch(self) -> anyhow::Result<Option<Credential>> { pub async fn launch(self) -> anyhow::Result<Credential> {
let mut auth_cmd = Command::new(GP_AUTH_BINARY); let mut auth_cmd = Command::new(GP_AUTH_BINARY);
auth_cmd.arg(self.server); auth_cmd.arg(self.server);
@ -129,10 +122,6 @@ impl<'a> SamlAuthLauncher<'a> {
auth_cmd.arg("--clean"); auth_cmd.arg("--clean");
} }
if self.default_browser {
auth_cmd.arg("--default-browser");
}
let mut non_root_cmd = auth_cmd.into_non_root()?; let mut non_root_cmd = auth_cmd.into_non_root()?;
let output = non_root_cmd let output = non_root_cmd
.kill_on_drop(true) .kill_on_drop(true)
@ -141,16 +130,12 @@ impl<'a> SamlAuthLauncher<'a> {
.wait_with_output() .wait_with_output()
.await?; .await?;
if self.default_browser {
return Ok(None);
}
let Ok(auth_result) = serde_json::from_slice::<SamlAuthResult>(&output.stdout) else { let Ok(auth_result) = serde_json::from_slice::<SamlAuthResult>(&output.stdout) else {
bail!("Failed to parse auth data") bail!("Failed to parse auth data")
}; };
match auth_result { match auth_result {
SamlAuthResult::Success(auth_data) => Ok(Some(Credential::from(auth_data))), SamlAuthResult::Success(auth_data) => Ok(Credential::from(auth_data)),
SamlAuthResult::Failure(msg) => bail!(msg), SamlAuthResult::Failure(msg) => bail!(msg),
} }
} }

View File

@ -2,6 +2,8 @@ pub(crate) mod command_traits;
pub(crate) mod gui_helper_launcher; pub(crate) mod gui_helper_launcher;
pub mod auth_launcher; pub mod auth_launcher;
#[cfg(feature = "browser-auth")]
pub mod browser_authenticator;
pub mod gui_launcher; pub mod gui_launcher;
pub mod hip_launcher; pub mod hip_launcher;
pub mod service_launcher; pub mod service_launcher;