mirror of
https://github.com/yuezk/GlobalProtect-openconnect.git
synced 2025-05-20 07:26:58 -04:00
feat: gpauth support macos
This commit is contained in:
@@ -33,8 +33,9 @@ sha256.workspace = true
|
||||
|
||||
tauri = { workspace = true, optional = true }
|
||||
clap = { workspace = true, optional = true }
|
||||
clap-verbosity-flag = { workspace = true, optional = true }
|
||||
|
||||
[features]
|
||||
tauri = ["dep:tauri"]
|
||||
clap = ["dep:clap"]
|
||||
clap = ["dep:clap", "dep:clap-verbosity-flag"]
|
||||
webview-auth = []
|
||||
|
@@ -72,15 +72,12 @@ impl SamlAuthData {
|
||||
let prelogin_cookie = parse_xml_tag(html, "prelogin-cookie");
|
||||
let portal_userauthcookie = parse_xml_tag(html, "portal-userauthcookie");
|
||||
|
||||
SamlAuthData::new(username, prelogin_cookie, portal_userauthcookie).map_err(|e| {
|
||||
warn!("Failed to parse auth data: {}", e);
|
||||
AuthDataParseError::Invalid
|
||||
})
|
||||
}
|
||||
Some(status) => {
|
||||
warn!("Found invalid auth status: {}", status);
|
||||
Err(AuthDataParseError::Invalid)
|
||||
SamlAuthData::new(username, prelogin_cookie, portal_userauthcookie).map_err(AuthDataParseError::Invalid)
|
||||
}
|
||||
Some(status) => Err(AuthDataParseError::Invalid(anyhow::anyhow!(
|
||||
"SAML auth status: {}",
|
||||
status
|
||||
))),
|
||||
None => Err(AuthDataParseError::NotFound),
|
||||
}
|
||||
}
|
||||
@@ -100,7 +97,7 @@ impl SamlAuthData {
|
||||
let auth_data: SamlAuthData = serde_urlencoded::from_str(auth_data.borrow()).map_err(|e| {
|
||||
warn!("Failed to parse token auth data: {}", e);
|
||||
warn!("Auth data: {}", auth_data);
|
||||
AuthDataParseError::Invalid
|
||||
AuthDataParseError::Invalid(anyhow::anyhow!(e))
|
||||
})?;
|
||||
|
||||
return Ok(auth_data);
|
||||
@@ -108,7 +105,7 @@ impl SamlAuthData {
|
||||
|
||||
let auth_data = decode_to_string(auth_data).map_err(|e| {
|
||||
warn!("Failed to decode SAML auth data: {}", e);
|
||||
AuthDataParseError::Invalid
|
||||
AuthDataParseError::Invalid(anyhow::anyhow!(e))
|
||||
})?;
|
||||
let auth_data = Self::from_html(&auth_data)?;
|
||||
|
||||
@@ -128,7 +125,7 @@ impl SamlAuthData {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_xml_tag(html: &str, tag: &str) -> Option<String> {
|
||||
fn parse_xml_tag(html: &str, tag: &str) -> Option<String> {
|
||||
let re = Regex::new(&format!("<{}>(.*)</{}>", tag, tag)).unwrap();
|
||||
re.captures(html)
|
||||
.and_then(|captures| captures.get(1))
|
||||
|
@@ -1,3 +1,5 @@
|
||||
use clap_verbosity_flag::{LogLevel, Verbosity, VerbosityFilter};
|
||||
|
||||
use crate::error::PortalError;
|
||||
|
||||
pub mod args;
|
||||
@@ -26,3 +28,41 @@ pub fn handle_error(err: anyhow::Error, args: &impl Args) {
|
||||
eprintln!("{} --ignore-tls-errors {}\n", args[0], args[1..].join(" "));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InfoLevel;
|
||||
|
||||
pub type InfoLevelVerbosity = Verbosity<InfoLevel>;
|
||||
|
||||
impl LogLevel for InfoLevel {
|
||||
fn default_filter() -> VerbosityFilter {
|
||||
VerbosityFilter::Info
|
||||
}
|
||||
|
||||
fn verbose_help() -> Option<&'static str> {
|
||||
Some("Enable verbose output, -v for debug, -vv for trace")
|
||||
}
|
||||
|
||||
fn quiet_help() -> Option<&'static str> {
|
||||
Some("Decrease logging verbosity, -q for warnings, -qq for errors")
|
||||
}
|
||||
}
|
||||
|
||||
pub trait VerbosityToCliArg {
|
||||
fn to_cli_arg(&self) -> Option<&'static str>;
|
||||
}
|
||||
|
||||
/// Convert the verbosity to the CLI argument value
|
||||
/// The default verbosity is `Info`, which means no argument is needed
|
||||
impl VerbosityToCliArg for InfoLevelVerbosity {
|
||||
fn to_cli_arg(&self) -> Option<&'static str> {
|
||||
match self.filter() {
|
||||
VerbosityFilter::Off => Some("-qqq"),
|
||||
VerbosityFilter::Error => Some("-qq"),
|
||||
VerbosityFilter::Warn => Some("-q"),
|
||||
VerbosityFilter::Info => None,
|
||||
VerbosityFilter::Debug => Some("-v"),
|
||||
VerbosityFilter::Trace => Some("-vv"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -26,12 +26,12 @@ impl PortalError {
|
||||
pub enum AuthDataParseError {
|
||||
#[error("No auth data found")]
|
||||
NotFound,
|
||||
#[error("Invalid auth data")]
|
||||
Invalid,
|
||||
#[error(transparent)]
|
||||
Invalid(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
impl AuthDataParseError {
|
||||
pub fn is_invalid(&self) -> bool {
|
||||
matches!(self, AuthDataParseError::Invalid)
|
||||
matches!(self, AuthDataParseError::Invalid(_))
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
use anyhow::bail;
|
||||
use dns_lookup::lookup_addr;
|
||||
use log::{debug, info, warn};
|
||||
use log::{info, warn};
|
||||
use reqwest::{Client, StatusCode};
|
||||
use roxmltree::{Document, Node};
|
||||
use serde::Serialize;
|
||||
@@ -135,8 +135,6 @@ pub async fn retrieve_config(portal: &str, cred: &Credential, gp_params: &GpPara
|
||||
bail!(PortalError::ConfigError("Empty portal config response".to_string()))
|
||||
}
|
||||
|
||||
debug!("Portal config response: {}", res_xml);
|
||||
|
||||
let doc = Document::parse(&res_xml).map_err(|e| PortalError::ConfigError(e.to_string()))?;
|
||||
let root = doc.root();
|
||||
|
||||
|
@@ -3,7 +3,12 @@ use std::process::Stdio;
|
||||
use anyhow::bail;
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::{auth::SamlAuthResult, credential::Credential, GP_AUTH_BINARY};
|
||||
use crate::{
|
||||
auth::SamlAuthResult,
|
||||
clap::{InfoLevelVerbosity, VerbosityToCliArg},
|
||||
credential::Credential,
|
||||
GP_AUTH_BINARY,
|
||||
};
|
||||
|
||||
use super::command_traits::CommandExt;
|
||||
|
||||
@@ -23,6 +28,7 @@ pub struct SamlAuthLauncher<'a> {
|
||||
#[cfg(feature = "webview-auth")]
|
||||
default_browser: bool,
|
||||
browser: Option<&'a str>,
|
||||
verbose: Option<&'a InfoLevelVerbosity>,
|
||||
}
|
||||
|
||||
impl<'a> SamlAuthLauncher<'a> {
|
||||
@@ -43,6 +49,7 @@ impl<'a> SamlAuthLauncher<'a> {
|
||||
#[cfg(feature = "webview-auth")]
|
||||
default_browser: false,
|
||||
browser: None,
|
||||
verbose: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +111,11 @@ impl<'a> SamlAuthLauncher<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn verbose(mut self, verbose: &'a InfoLevelVerbosity) -> Self {
|
||||
self.verbose = Some(verbose);
|
||||
self
|
||||
}
|
||||
|
||||
/// Launch the authenticator binary as the current user or SUDO_USER if available.
|
||||
pub async fn launch(self) -> anyhow::Result<Credential> {
|
||||
let mut auth_cmd = Command::new(GP_AUTH_BINARY);
|
||||
@@ -156,6 +168,11 @@ impl<'a> SamlAuthLauncher<'a> {
|
||||
auth_cmd.arg("--browser").arg(browser);
|
||||
}
|
||||
|
||||
if let Some(verbose) = self.verbose {
|
||||
let arg = verbose.to_cli_arg();
|
||||
arg.map(|arg| auth_cmd.arg(arg));
|
||||
}
|
||||
|
||||
let mut non_root_cmd = auth_cmd.into_non_root()?;
|
||||
let output = non_root_cmd
|
||||
.kill_on_drop(true)
|
||||
|
Reference in New Issue
Block a user