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:
@@ -24,6 +24,9 @@ tokio.workspace = true
|
||||
tempfile.workspace = true
|
||||
compile-time.workspace = true
|
||||
|
||||
# Pin the version of home because the latest version requires Rust 1.81
|
||||
home = "=0.5.9"
|
||||
|
||||
# webview auth dependencies
|
||||
tauri = { workspace = true, optional = true }
|
||||
|
||||
|
@@ -1,21 +1,19 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use auth::{auth_prelogin, Authenticator, BrowserAuthenticator};
|
||||
use auth::{auth_prelogin, BrowserAuthenticator};
|
||||
use clap::Parser;
|
||||
use gpapi::{
|
||||
auth::{SamlAuthData, SamlAuthResult},
|
||||
clap::{args::Os, handle_error, Args},
|
||||
clap::{args::Os, handle_error, Args, InfoLevelVerbosity},
|
||||
gp_params::{ClientOs, GpParams},
|
||||
utils::{normalize_server, openssl},
|
||||
GP_USER_AGENT,
|
||||
};
|
||||
use log::{info, LevelFilter};
|
||||
use log::info;
|
||||
use serde_json::json;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), " (", compile_time::date_str!(), ")");
|
||||
|
||||
#[derive(Parser, Clone)]
|
||||
#[derive(Parser)]
|
||||
#[command(
|
||||
version = VERSION,
|
||||
author,
|
||||
@@ -33,7 +31,7 @@ const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), " (", compile_time::dat
|
||||
See 'gpauth -h' for more information.
|
||||
"
|
||||
)]
|
||||
pub(crate) struct Cli {
|
||||
struct Cli {
|
||||
#[arg(help = "The portal server to authenticate")]
|
||||
server: String,
|
||||
|
||||
@@ -75,6 +73,9 @@ pub(crate) struct Cli {
|
||||
#[cfg(feature = "webview-auth")]
|
||||
#[arg(long, help = "Clean the cache of the embedded browser")]
|
||||
pub clean: bool,
|
||||
|
||||
#[command(flatten)]
|
||||
verbose: InfoLevelVerbosity,
|
||||
}
|
||||
|
||||
impl Args for Cli {
|
||||
@@ -110,28 +111,26 @@ impl Cli {
|
||||
let openssl_conf = self.prepare_env()?;
|
||||
|
||||
let server = normalize_server(&self.server)?;
|
||||
let server: &'static str = Box::leak(server.into_boxed_str());
|
||||
let gp_params: &'static GpParams = Box::leak(Box::new(self.build_gp_params()));
|
||||
let gp_params = self.build_gp_params();
|
||||
|
||||
let auth_request = match self.saml_request.as_deref() {
|
||||
Some(auth_request) => Cow::Borrowed(auth_request),
|
||||
None => Cow::Owned(auth_prelogin(server, gp_params).await?),
|
||||
Some(auth_request) => auth_request.to_string(),
|
||||
None => auth_prelogin(&server, &gp_params).await?,
|
||||
};
|
||||
|
||||
let auth_request: &'static str = Box::leak(auth_request.into_owned().into_boxed_str());
|
||||
let authenticator = Authenticator::new(&server, gp_params).with_auth_request(&auth_request);
|
||||
|
||||
#[cfg(feature = "webview-auth")]
|
||||
let browser = self
|
||||
.browser
|
||||
.as_deref()
|
||||
.or_else(|| self.default_browser.then_some("default"));
|
||||
.or_else(|| self.default_browser.then(|| "default"));
|
||||
|
||||
#[cfg(not(feature = "webview-auth"))]
|
||||
let browser = self.browser.as_deref().or(Some("default"));
|
||||
|
||||
if browser.is_some() {
|
||||
let auth_result = authenticator.browser_authenticate(browser).await;
|
||||
if let Some(browser) = browser {
|
||||
let authenticator = BrowserAuthenticator::new(&auth_request, browser);
|
||||
let auth_result = authenticator.authenticate().await;
|
||||
|
||||
print_auth_result(auth_result);
|
||||
|
||||
// explicitly drop openssl_conf to avoid the unused variable warning
|
||||
@@ -140,7 +139,7 @@ impl Cli {
|
||||
}
|
||||
|
||||
#[cfg(feature = "webview-auth")]
|
||||
crate::webview_auth::authenticate(&self, authenticator, openssl_conf)?;
|
||||
crate::webview_auth::authenticate(server, gp_params, auth_request, self.clean, openssl_conf).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -158,14 +157,16 @@ impl Cli {
|
||||
}
|
||||
}
|
||||
|
||||
fn init_logger() {
|
||||
env_logger::builder().filter_level(LevelFilter::Info).init();
|
||||
fn init_logger(cli: &Cli) {
|
||||
env_logger::builder()
|
||||
.filter_level(cli.verbose.log_level_filter())
|
||||
.init();
|
||||
}
|
||||
|
||||
pub async fn run() {
|
||||
let cli = Cli::parse();
|
||||
|
||||
init_logger();
|
||||
init_logger(&cli);
|
||||
info!("gpauth started: {}", VERSION);
|
||||
|
||||
if let Err(err) = cli.run().await {
|
||||
|
@@ -1,6 +1,7 @@
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
mod cli;
|
||||
|
||||
#[cfg(feature = "webview-auth")]
|
||||
mod webview_auth;
|
||||
|
||||
|
@@ -1,23 +1,28 @@
|
||||
use auth::{Authenticator, WebviewAuthenticator};
|
||||
use auth::WebviewAuthenticator;
|
||||
use gpapi::gp_params::GpParams;
|
||||
use log::info;
|
||||
use tauri::RunEvent;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
use crate::cli::{print_auth_result, Cli};
|
||||
use crate::cli::print_auth_result;
|
||||
|
||||
pub fn authenticate(
|
||||
cli: &Cli,
|
||||
authenticator: Authenticator<'static>,
|
||||
pub async fn authenticate(
|
||||
server: String,
|
||||
gp_params: GpParams,
|
||||
auth_request: String,
|
||||
clean: bool,
|
||||
mut openssl_conf: Option<NamedTempFile>,
|
||||
) -> anyhow::Result<()> {
|
||||
let authenticator = authenticator.with_clean(cli.clean);
|
||||
|
||||
tauri::Builder::default()
|
||||
.setup(move |app| {
|
||||
let app_handle = app.handle().clone();
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let auth_result = authenticator.webview_authenticate(&app_handle).await;
|
||||
let authenticator = WebviewAuthenticator::new(&server, &gp_params)
|
||||
.with_auth_request(&auth_request)
|
||||
.with_clean(clean);
|
||||
|
||||
let auth_result = authenticator.authenticate(&app_handle).await;
|
||||
print_auth_result(auth_result);
|
||||
|
||||
// Ensure the app exits after the authentication process
|
||||
|
@@ -2,10 +2,10 @@ use std::{env::temp_dir, fs::File};
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use gpapi::{
|
||||
clap::{handle_error, Args},
|
||||
clap::{handle_error, Args, InfoLevelVerbosity},
|
||||
utils::openssl,
|
||||
};
|
||||
use log::{info, LevelFilter};
|
||||
use log::info;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
use crate::{
|
||||
@@ -16,9 +16,10 @@ use crate::{
|
||||
|
||||
const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), " (", compile_time::date_str!(), ")");
|
||||
|
||||
pub(crate) struct SharedArgs {
|
||||
pub(crate) struct SharedArgs<'a> {
|
||||
pub(crate) fix_openssl: bool,
|
||||
pub(crate) ignore_tls_errors: bool,
|
||||
pub(crate) verbose: &'a InfoLevelVerbosity,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
@@ -60,6 +61,9 @@ struct Cli {
|
||||
fix_openssl: bool,
|
||||
#[arg(long, help = "Ignore the TLS errors")]
|
||||
ignore_tls_errors: bool,
|
||||
|
||||
#[command(flatten)]
|
||||
verbose: InfoLevelVerbosity,
|
||||
}
|
||||
|
||||
impl Args for Cli {
|
||||
@@ -89,6 +93,7 @@ impl Cli {
|
||||
let shared_args = SharedArgs {
|
||||
fix_openssl: self.fix_openssl,
|
||||
ignore_tls_errors: self.ignore_tls_errors,
|
||||
verbose: &self.verbose,
|
||||
};
|
||||
|
||||
if self.ignore_tls_errors {
|
||||
@@ -103,12 +108,12 @@ impl Cli {
|
||||
}
|
||||
}
|
||||
|
||||
fn init_logger(command: &CliCommand) {
|
||||
fn init_logger(cli: &Cli) {
|
||||
let mut builder = env_logger::builder();
|
||||
builder.filter_level(LevelFilter::Info);
|
||||
builder.filter_level(cli.verbose.log_level_filter());
|
||||
|
||||
// Output the log messages to a file if the command is the auth callback
|
||||
if let CliCommand::LaunchGui(args) = command {
|
||||
if let CliCommand::LaunchGui(args) = &cli.command {
|
||||
let auth_data = args.auth_data.as_deref().unwrap_or_default();
|
||||
if !auth_data.is_empty() {
|
||||
if let Ok(log_file) = File::create(temp_dir().join("gpcallback.log")) {
|
||||
@@ -124,7 +129,7 @@ fn init_logger(command: &CliCommand) {
|
||||
pub(crate) async fn run() {
|
||||
let cli = Cli::parse();
|
||||
|
||||
init_logger(&cli.command);
|
||||
init_logger(&cli);
|
||||
|
||||
info!("gpclient started: {}", VERSION);
|
||||
|
||||
|
@@ -5,7 +5,7 @@ use clap::Args;
|
||||
use common::vpn_utils::find_csd_wrapper;
|
||||
use gpapi::{
|
||||
auth::SamlAuthResult,
|
||||
clap::args::Os,
|
||||
clap::{args::Os, ToVerboseArg},
|
||||
credential::{Credential, PasswordCredential},
|
||||
error::PortalError,
|
||||
gateway::{gateway_login, GatewayLogin},
|
||||
@@ -19,7 +19,7 @@ use gpapi::{
|
||||
GP_USER_AGENT,
|
||||
};
|
||||
use inquire::{Password, PasswordDisplayMode, Select, Text};
|
||||
use log::info;
|
||||
use log::{info, warn};
|
||||
use openconnect::Vpn;
|
||||
|
||||
use crate::{cli::SharedArgs, GP_CLIENT_LOCK_FILE};
|
||||
@@ -128,7 +128,7 @@ impl ConnectArgs {
|
||||
|
||||
pub(crate) struct ConnectHandler<'a> {
|
||||
args: &'a ConnectArgs,
|
||||
shared_args: &'a SharedArgs,
|
||||
shared_args: &'a SharedArgs<'a>,
|
||||
latest_key_password: RefCell<Option<String>>,
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ impl<'a> ConnectHandler<'a> {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
info!("Failed to connect portal with prelogin: {}", err);
|
||||
warn!("Failed to connect portal with prelogin: {}", err);
|
||||
if err.root_cause().downcast_ref::<PortalError>().is_some() {
|
||||
info!("Trying the gateway authentication workflow...");
|
||||
self.connect_gateway_with_prelogin(server).await?;
|
||||
@@ -356,6 +356,7 @@ impl<'a> ConnectHandler<'a> {
|
||||
};
|
||||
|
||||
let os_version = self.args.os_version();
|
||||
let verbose = self.shared_args.verbose.to_verbose_arg();
|
||||
let auth_launcher = SamlAuthLauncher::new(&self.args.server)
|
||||
.gateway(is_gateway)
|
||||
.saml_request(prelogin.saml_request())
|
||||
@@ -364,7 +365,8 @@ impl<'a> ConnectHandler<'a> {
|
||||
.os_version(Some(&os_version))
|
||||
.fix_openssl(self.shared_args.fix_openssl)
|
||||
.ignore_tls_errors(self.shared_args.ignore_tls_errors)
|
||||
.browser(browser);
|
||||
.browser(browser)
|
||||
.verbose(verbose);
|
||||
|
||||
#[cfg(feature = "webview-auth")]
|
||||
let use_default_browser = prelogin.support_default_browser() && self.args.default_browser;
|
||||
|
@@ -10,7 +10,7 @@ license.workspace = true
|
||||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
gpapi = { path = "../../../crates/gpapi", features = ["tauri"] }
|
||||
gpapi = { path = "../../../crates/gpapi", features = ["tauri", "clap"] }
|
||||
tauri.workspace = true
|
||||
|
||||
tokio.workspace = true
|
||||
|
@@ -1,6 +1,9 @@
|
||||
use clap::Parser;
|
||||
use gpapi::utils::{base64, env_utils};
|
||||
use log::{info, LevelFilter};
|
||||
use gpapi::{
|
||||
clap::InfoLevelVerbosity,
|
||||
utils::{base64, env_utils},
|
||||
};
|
||||
use log::info;
|
||||
|
||||
use crate::app::App;
|
||||
|
||||
@@ -15,6 +18,9 @@ struct Cli {
|
||||
|
||||
#[arg(long, default_value = env!("CARGO_PKG_VERSION"), help = "The version of the GUI")]
|
||||
gui_version: String,
|
||||
|
||||
#[command(flatten)]
|
||||
verbose: InfoLevelVerbosity,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
@@ -41,14 +47,16 @@ impl Cli {
|
||||
}
|
||||
}
|
||||
|
||||
fn init_logger() {
|
||||
env_logger::builder().filter_level(LevelFilter::Info).init();
|
||||
fn init_logger(cli: &Cli) {
|
||||
env_logger::builder()
|
||||
.filter_level(cli.verbose.log_level_filter())
|
||||
.init();
|
||||
}
|
||||
|
||||
pub fn run() {
|
||||
let cli = Cli::parse();
|
||||
|
||||
init_logger();
|
||||
init_logger(&cli);
|
||||
info!("gpgui-helper started: {}", VERSION);
|
||||
|
||||
if let Err(e) = cli.run() {
|
||||
|
@@ -5,7 +5,7 @@ edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
gpapi = { path = "../../crates/gpapi" }
|
||||
gpapi = { path = "../../crates/gpapi", features = ["clap", "logger"] }
|
||||
openconnect = { path = "../../crates/openconnect" }
|
||||
clap.workspace = true
|
||||
anyhow.workspace = true
|
||||
|
@@ -3,13 +3,15 @@ use std::{collections::HashMap, io::Write};
|
||||
|
||||
use anyhow::bail;
|
||||
use clap::Parser;
|
||||
use gpapi::clap::InfoLevelVerbosity;
|
||||
use gpapi::logger;
|
||||
use gpapi::{
|
||||
process::gui_launcher::GuiLauncher,
|
||||
service::{request::WsRequest, vpn_state::VpnState},
|
||||
utils::{crypto::generate_key, env_utils, lock_file::LockFile, redact::Redaction, shutdown_signal},
|
||||
GP_SERVICE_LOCK_FILE,
|
||||
};
|
||||
use log::{info, warn, LevelFilter};
|
||||
use log::{info, warn};
|
||||
use tokio::sync::{mpsc, watch};
|
||||
|
||||
use crate::{vpn_task::VpnTask, ws_server::WsServer};
|
||||
@@ -26,10 +28,16 @@ struct Cli {
|
||||
#[cfg(debug_assertions)]
|
||||
#[clap(long)]
|
||||
no_gui: bool,
|
||||
|
||||
#[command(flatten)]
|
||||
verbose: InfoLevelVerbosity,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
async fn run(&mut self, redaction: Arc<Redaction>) -> anyhow::Result<()> {
|
||||
async fn run(&mut self) -> anyhow::Result<()> {
|
||||
let redaction = self.init_logger()?;
|
||||
info!("gpservice started: {}", VERSION);
|
||||
|
||||
let lock_file = Arc::new(LockFile::new(GP_SERVICE_LOCK_FILE));
|
||||
|
||||
if lock_file.check_health().await {
|
||||
@@ -92,6 +100,33 @@ impl Cli {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_logger(&self) -> anyhow::Result<Arc<Redaction>> {
|
||||
let redaction = Arc::new(Redaction::new());
|
||||
let redaction_clone = Arc::clone(&redaction);
|
||||
|
||||
let inner_logger = env_logger::builder()
|
||||
// Set the log level to the Trace level, the logs will be filtered
|
||||
.filter_level(log::LevelFilter::Trace)
|
||||
.format(move |buf, record| {
|
||||
let timestamp = buf.timestamp();
|
||||
writeln!(
|
||||
buf,
|
||||
"[{} {} {}] {}",
|
||||
timestamp,
|
||||
record.level(),
|
||||
record.module_path().unwrap_or_default(),
|
||||
redaction_clone.redact_str(&record.args().to_string())
|
||||
)
|
||||
})
|
||||
.build();
|
||||
|
||||
let level = self.verbose.log_level_filter().to_level().unwrap_or(log::Level::Info);
|
||||
|
||||
logger::init_with_logger(level, inner_logger)?;
|
||||
|
||||
Ok(redaction)
|
||||
}
|
||||
|
||||
fn prepare_api_key(&self) -> Vec<u8> {
|
||||
#[cfg(debug_assertions)]
|
||||
if self.no_gui {
|
||||
@@ -102,29 +137,6 @@ impl Cli {
|
||||
}
|
||||
}
|
||||
|
||||
fn init_logger() -> Arc<Redaction> {
|
||||
let redaction = Arc::new(Redaction::new());
|
||||
let redaction_clone = Arc::clone(&redaction);
|
||||
// let target = Box::new(File::create("log.txt").expect("Can't create file"));
|
||||
env_logger::builder()
|
||||
.filter_level(LevelFilter::Info)
|
||||
.format(move |buf, record| {
|
||||
let timestamp = buf.timestamp();
|
||||
writeln!(
|
||||
buf,
|
||||
"[{} {} {}] {}",
|
||||
timestamp,
|
||||
record.level(),
|
||||
record.module_path().unwrap_or_default(),
|
||||
redaction_clone.redact_str(&record.args().to_string())
|
||||
)
|
||||
})
|
||||
// .target(env_logger::Target::Pipe(target))
|
||||
.init();
|
||||
|
||||
redaction
|
||||
}
|
||||
|
||||
async fn launch_gui(envs: Option<HashMap<String, String>>, api_key: Vec<u8>, mut minimized: bool) {
|
||||
loop {
|
||||
let gui_launcher = GuiLauncher::new(env!("CARGO_PKG_VERSION"), &api_key)
|
||||
@@ -153,10 +165,7 @@ async fn launch_gui(envs: Option<HashMap<String, String>>, api_key: Vec<u8>, mut
|
||||
pub async fn run() {
|
||||
let mut cli = Cli::parse();
|
||||
|
||||
let redaction = init_logger();
|
||||
info!("gpservice started: {}", VERSION);
|
||||
|
||||
if let Err(e) = cli.run(redaction).await {
|
||||
if let Err(e) = cli.run().await {
|
||||
eprintln!("Error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
@@ -1,8 +1,11 @@
|
||||
use std::{sync::Arc, thread};
|
||||
|
||||
use gpapi::service::{
|
||||
request::{ConnectRequest, WsRequest},
|
||||
vpn_state::VpnState,
|
||||
use gpapi::{
|
||||
logger,
|
||||
service::{
|
||||
request::{ConnectRequest, UpdateLogLevelRequest, WsRequest},
|
||||
vpn_state::VpnState,
|
||||
},
|
||||
};
|
||||
use log::{info, warn};
|
||||
use openconnect::Vpn;
|
||||
@@ -158,5 +161,12 @@ async fn process_ws_req(req: WsRequest, ctx: Arc<VpnTaskContext>) {
|
||||
WsRequest::Disconnect(_) => {
|
||||
ctx.disconnect().await;
|
||||
}
|
||||
WsRequest::UpdateLogLevel(UpdateLogLevelRequest(level)) => {
|
||||
let level = level.parse().unwrap_or_else(|_| log::Level::Info);
|
||||
info!("Updating log level to: {}", level);
|
||||
if let Err(err) = logger::set_max_level(level) {
|
||||
warn!("Failed to update log level: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user