Compare commits

...

8 Commits

Author SHA1 Message Date
Kevin Yue
5c6a1c77f7
Fix the save credentials not working 2024-10-28 14:42:58 +00:00
Eric Dallo
511cc5ebd7
Fix flake.nix vpnc-script hardcoded path (#413) 2024-08-18 21:13:26 +08:00
Kevin Yue
30bec40338
Release 2.3.7 2024-08-16 01:34:19 +00:00
Kevin Yue
0bb9353a11
fix: build error with rust 1.80 2024-08-16 01:23:26 +00:00
Kevin Yue
80134f5a2b
Release 2.3.6 2024-08-15 13:35:19 +00:00
Kevin Yue
57e20fe478
fix: enhance gpauth to support browser authentication 2024-08-15 13:14:08 +00:00
Kevin Yue
9317430968
Release 2.3.5 2024-08-14 22:47:08 +08:00
Kevin Yue
f92b04e99a
use --browser option 2024-08-14 22:42:35 +08:00
18 changed files with 248 additions and 106 deletions

30
Cargo.lock generated
View File

@ -570,7 +570,7 @@ dependencies = [
[[package]]
name = "common"
version = "2.3.4"
version = "2.3.7"
dependencies = [
"is_executable",
]
@ -1430,7 +1430,7 @@ dependencies = [
[[package]]
name = "gpapi"
version = "2.3.4"
version = "2.3.7"
dependencies = [
"anyhow",
"base64 0.21.5",
@ -1465,7 +1465,7 @@ dependencies = [
[[package]]
name = "gpauth"
version = "2.3.4"
version = "2.3.7"
dependencies = [
"anyhow",
"clap",
@ -1486,7 +1486,7 @@ dependencies = [
[[package]]
name = "gpclient"
version = "2.3.4"
version = "2.3.7"
dependencies = [
"anyhow",
"clap",
@ -1508,7 +1508,7 @@ dependencies = [
[[package]]
name = "gpgui-helper"
version = "2.3.4"
version = "2.3.7"
dependencies = [
"anyhow",
"clap",
@ -1526,7 +1526,7 @@ dependencies = [
[[package]]
name = "gpservice"
version = "2.3.4"
version = "2.3.7"
dependencies = [
"anyhow",
"axum",
@ -2426,6 +2426,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-integer"
version = "0.1.45"
@ -2549,7 +2555,7 @@ dependencies = [
[[package]]
name = "openconnect"
version = "2.3.4"
version = "2.3.7"
dependencies = [
"cc",
"common",
@ -4143,12 +4149,13 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.31"
version = "0.3.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e"
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
"deranged",
"itoa 1.0.10",
"num-conv",
"powerfmt",
"serde",
"time-core",
@ -4163,10 +4170,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
version = "0.2.16"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f"
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
dependencies = [
"num-conv",
"time-core",
]

View File

@ -5,7 +5,7 @@ members = ["crates/*", "apps/gpclient", "apps/gpservice", "apps/gpauth", "apps/g
[workspace.package]
rust-version = "1.70"
version = "2.3.4"
version = "2.3.7"
authors = ["Kevin Yue <k3vinyue@gmail.com>"]
homepage = "https://github.com/yuezk/GlobalProtect-openconnect"
edition = "2021"

View File

@ -44,12 +44,20 @@ Options:
See 'gpclient help <command>' for more information on a specific command.
```
To use the default browser for authentication with the CLI version, you need to use the following command:
To use the external browser for authentication with the CLI version, you need to use the following command:
```bash
sudo -E gpclient connect --default-browser <portal>
sudo -E gpclient connect --browser default <portal>
```
Or you can try the following command if the above command does not work:
```bash
gpauth <portal> --browser default 2>/dev/null | sudo gpclient connect <portal> --cookie-on-stdin
```
You can specify the browser with the `--browser <browser>` option, e.g., `--browser firefox`, `--browser chrome`, etc.
### GUI
The GUI version is also available after you installed it. You can launch it from the application menu or run `gpclient launch-gui` in the terminal.

View File

@ -1,5 +1,6 @@
[package]
name = "gpauth"
authors.workspace = true
version.workspace = true
edition.workspace = true
license.workspace = true

View File

@ -1,3 +1,5 @@
use std::{env::temp_dir, fs, os::unix::fs::PermissionsExt};
use clap::Parser;
use gpapi::{
auth::{SamlAuthData, SamlAuthResult},
@ -11,37 +13,69 @@ use log::{info, LevelFilter};
use serde_json::json;
use tauri::{App, AppHandle, RunEvent};
use tempfile::NamedTempFile;
use tokio::{io::AsyncReadExt, net::TcpListener};
use crate::auth_window::{portal_prelogin, AuthWindow};
const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), " (", compile_time::date_str!(), ")");
#[derive(Parser, Clone)]
#[command(version = VERSION)]
#[command(
version = VERSION,
author,
about = "The authentication component for the GlobalProtect VPN client, supports the SSO authentication method.",
help_template = "\
{before-help}{name} {version}
{author}
{about}
{usage-heading} {usage}
{all-args}{after-help}
See 'gpauth -h' for more information.
"
)]
struct Cli {
#[arg(help = "The portal server to authenticate")]
server: String,
#[arg(long)]
#[arg(long, help = "Treating the server as a gateway")]
gateway: bool,
#[arg(long)]
#[arg(long, help = "The SAML authentication request")]
saml_request: Option<String>,
#[arg(long, default_value = GP_USER_AGENT)]
#[arg(long, default_value = GP_USER_AGENT, help = "The user agent to use")]
user_agent: String,
#[arg(long, default_value = "Linux")]
os: Os,
#[arg(long)]
os_version: Option<String>,
#[arg(long)]
#[arg(long, help = "The HiDPI mode, useful for high-resolution screens")]
hidpi: bool,
#[arg(long)]
#[arg(long, help = "Get around the OpenSSL `unsafe legacy renegotiation` error")]
fix_openssl: bool,
#[arg(long)]
#[arg(long, help = "Ignore TLS errors")]
ignore_tls_errors: bool,
#[arg(long)]
#[arg(long, help = "Clean the cache of the embedded browser")]
clean: bool,
#[arg(long)]
#[arg(long, help = "Use the default browser for authentication")]
default_browser: bool,
#[arg(long)]
external_browser: Option<String>,
#[arg(
long,
help = "The browser to use for authentication, e.g., `default`, `firefox`, `chrome`, `chromium`, or the path to the browser executable"
)]
browser: Option<String>,
}
impl Cli {
@ -61,8 +95,8 @@ impl Cli {
None => portal_prelogin(&self.server, &gp_params).await?,
};
let browser_auth = if let Some(external_browser) = &self.external_browser {
Some(BrowserAuthenticator::new_with_browser(&saml_request, external_browser))
let browser_auth = if let Some(browser) = &self.browser {
Some(BrowserAuthenticator::new_with_browser(&saml_request, browser))
} else if self.default_browser {
Some(BrowserAuthenticator::new(&saml_request))
} else {
@ -74,6 +108,15 @@ impl Cli {
info!("Please continue the authentication process in the default browser");
let auth_result = match wait_auth_data().await {
Ok(auth_data) => SamlAuthResult::Success(auth_data),
Err(err) => SamlAuthResult::Failure(format!("{}", err)),
};
info!("Authentication completed");
println!("{}", json!(auth_result));
return Ok(());
}
@ -181,3 +224,35 @@ pub async fn run() {
std::process::exit(1);
}
}
async fn wait_auth_data() -> anyhow::Result<SamlAuthData> {
// 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();
let port_file = temp_dir().join("gpcallback.port");
// Write the port to a file
fs::write(&port_file, port.to_string())?;
fs::set_permissions(&port_file, fs::Permissions::from_mode(0o600))?;
// Remove the previous log file
let callback_log = temp_dir().join("gpcallback.log");
let _ = fs::remove_file(&callback_log);
info!("Listening authentication data on port {}", port);
info!(
"If it hangs, please check the logs at `{}` for more information",
callback_log.display()
);
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(&port_file)?;
let auth_data = SamlAuthData::from_gpcallback(&data)?;
Ok(auth_data)
}

View File

@ -1,3 +1,5 @@
use std::{env::temp_dir, fs::File};
use clap::{Parser, Subcommand};
use gpapi::utils::openssl;
use log::{info, LevelFilter};
@ -85,14 +87,29 @@ impl Cli {
}
}
fn init_logger() {
env_logger::builder().filter_level(LevelFilter::Info).init();
fn init_logger(command: &CliCommand) {
let mut builder = env_logger::builder();
builder.filter_level(LevelFilter::Info);
// Output the log messages to a file if the command is the auth callback
if let CliCommand::LaunchGui(args) = 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")) {
let target = Box::new(log_file);
builder.target(env_logger::Target::Pipe(target));
}
}
}
builder.init();
}
pub(crate) async fn run() {
let cli = Cli::parse();
init_logger();
init_logger(&cli.command);
info!("gpclient started: {}", VERSION);
if let Err(err) = cli.run().await {

View File

@ -1,8 +1,10 @@
use std::{cell::RefCell, fs, sync::Arc};
use anyhow::bail;
use clap::Args;
use common::vpn_utils::find_csd_wrapper;
use gpapi::{
auth::SamlAuthResult,
clap::args::Os,
credential::{Credential, PasswordCredential},
error::PortalError,
@ -19,9 +21,8 @@ use gpapi::{
use inquire::{Password, PasswordDisplayMode, Select, Text};
use log::info;
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)]
pub(crate) struct ConnectArgs {
@ -37,6 +38,9 @@ pub(crate) struct ConnectArgs {
#[arg(long, help = "Read the password from standard input")]
passwd_on_stdin: bool,
#[arg(long, help = "Read the cookie from standard input")]
cookie_on_stdin: bool,
#[arg(long, short, help = "The VPNC script to use")]
script: Option<String>,
@ -89,7 +93,7 @@ pub(crate) struct ConnectArgs {
#[arg(long, help = "Disable DTLS and ESP")]
no_dtls: bool,
#[arg(long, help = "The HiDPI mode, useful for high resolution screens")]
#[arg(long, help = "The HiDPI mode, useful for high-resolution screens")]
hidpi: bool,
#[arg(long, help = "Do not reuse the remembered authentication cookie")]
@ -100,9 +104,9 @@ pub(crate) struct ConnectArgs {
#[arg(
long,
help = "Use the specified browser to authenticate, e.g., firefox, chromium, chrome, or the path to the browser"
help = "Use the specified browser to authenticate, e.g., `default`, `firefox`, `chrome`, `chromium`, or the path to the browser executable"
)]
external_browser: Option<String>,
browser: Option<String>,
}
impl ConnectArgs {
@ -147,6 +151,10 @@ impl<'a> ConnectHandler<'a> {
}
pub(crate) async fn handle(&self) -> anyhow::Result<()> {
if self.args.default_browser && self.args.browser.is_some() {
bail!("Cannot use `--default-browser` and `--browser` options at the same time");
}
self.latest_key_password.replace(self.args.key_password.clone());
loop {
@ -327,13 +335,17 @@ impl<'a> ConnectHandler<'a> {
}
async fn obtain_credential(&self, prelogin: &Prelogin, server: &str) -> anyhow::Result<Credential> {
if self.args.cookie_on_stdin {
return read_cookie_from_stdin();
}
let is_gateway = prelogin.is_gateway();
match prelogin {
Prelogin::Saml(prelogin) => {
let use_default_browser = prelogin.support_default_browser() && self.args.default_browser;
let external_browser = if prelogin.support_default_browser() {
self.args.external_browser.as_deref()
let browser = if prelogin.support_default_browser() {
self.args.browser.as_deref()
} else {
None
};
@ -349,22 +361,13 @@ impl<'a> ConnectHandler<'a> {
.ignore_tls_errors(self.shared_args.ignore_tls_errors)
.clean(self.args.clean)
.default_browser(use_default_browser)
.external_browser(external_browser)
.browser(browser)
.launch()
.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
Ok(cred)
}
Prelogin::Standard(prelogin) => {
let prefix = if is_gateway { "Gateway" } else { "Portal" };
println!("{} ({}: {})", prelogin.auth_message(), prefix, server);
@ -394,25 +397,17 @@ 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();
fn read_cookie_from_stdin() -> anyhow::Result<Credential> {
info!("Reading cookie from standard input");
// Write the port to a file
fs::write(GP_CLIENT_PORT_FILE, port.to_string())?;
let mut cookie = String::new();
std::io::stdin().read_line(&mut cookie)?;
info!("Listening authentication data on port {}", port);
let (mut socket, _) = listener.accept().await?;
let Ok(auth_result) = serde_json::from_str::<SamlAuthResult>(cookie.trim_end()) else {
bail!("Failed to parse auth data")
};
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)
Credential::try_from(auth_result)
}
fn write_pid_file() {

View File

@ -9,15 +9,13 @@ use gpapi::{
use log::info;
use tokio::io::AsyncWriteExt;
use crate::GP_CLIENT_PORT_FILE;
#[derive(Args)]
pub(crate) struct LaunchGuiArgs {
#[arg(
required = false,
help = "The authentication data, used for the default browser authentication"
)]
auth_data: Option<String>,
pub auth_data: Option<String>,
#[arg(long, help = "Launch the GUI minimized")]
minimized: bool,
}
@ -40,6 +38,7 @@ impl<'a> LaunchGuiHandler<'a> {
let auth_data = self.args.auth_data.as_deref().unwrap_or_default();
if !auth_data.is_empty() {
info!("Received auth callback data");
// Process the authentication data, its format is `globalprotectcallback:<data>`
return feed_auth_data(auth_data).await;
}
@ -81,16 +80,26 @@ impl<'a> LaunchGuiHandler<'a> {
}
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));
let (res_gui, res_cli) = tokio::join!(feed_auth_data_gui(auth_data), feed_auth_data_cli(auth_data));
if let Err(err) = res_gui {
info!("Failed to feed auth data to the GUI: {}", err);
}
if let Err(err) = res_cli {
info!("Failed to feed auth data to the CLI: {}", err);
}
// Cleanup the temporary file
let html_file = temp_dir().join("gpauth.html");
let _ = std::fs::remove_file(html_file);
if let Err(err) = std::fs::remove_file(&html_file) {
info!("Failed to remove {}: {}", html_file.display(), err);
}
Ok(())
}
async fn feed_auth_data_gui(auth_data: &str) -> anyhow::Result<()> {
info!("Feeding auth data to the GUI");
let service_endpoint = http_endpoint().await?;
reqwest::Client::default()
@ -104,7 +113,10 @@ async fn feed_auth_data_gui(auth_data: &str) -> anyhow::Result<()> {
}
async fn feed_auth_data_cli(auth_data: &str) -> anyhow::Result<()> {
let port = tokio::fs::read_to_string(GP_CLIENT_PORT_FILE).await?;
info!("Feeding auth data to the CLI");
let port_file = temp_dir().join("gpcallback.port");
let port = tokio::fs::read_to_string(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?;
@ -124,7 +136,7 @@ async fn try_active_gui() -> anyhow::Result<()> {
Ok(())
}
pub fn get_log_file() -> anyhow::Result<PathBuf> {
fn get_log_file() -> anyhow::Result<PathBuf> {
let dirs = ProjectDirs::from("com.yuezk", "GlobalProtect-openconnect", "gpclient")
.ok_or_else(|| anyhow::anyhow!("Failed to get project dirs"))?;

View File

@ -4,7 +4,6 @@ mod disconnect;
mod launch_gui;
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]
async fn main() {

View File

@ -1,5 +1,23 @@
# Changelog
## 2.3.7 - 2024-08-16
- Fix the Rust type inference regression [issue in 1.80](https://github.com/rust-lang/rust/issues/125319).
## 2.3.6 - 2024-08-15
- CLI: enhance the `gpauth` command to support external browser authentication
- CLI: add the `--cookie-on-stdin` option to support read the cookie from stdin
- CLI: support usage: `gpauth <portal> --browser <browser> 2>/dev/null | sudo gpclient connect <portal> --cookie-on-stdin`
- CLI: fix the `--browser <browser>` option not working
## 2.3.5 - 2024-08-14
- Support configure `no-dtls` option
- GUI: fix the tray icon disk usage issue (#398)
- CLI: support specify the browser with `--browser <browser>` option (#405, #407, #397)
- CLI: fix the `--os` option not working
## 2.3.4 - 2024-07-08
- Support the Internal Host Detection (fix [#377](https://github.com/yuezk/GlobalProtect-openconnect/issues/377))

View File

@ -85,7 +85,6 @@ impl SamlAuthData {
return Ok(auth_data);
}
info!("Parsing SAML auth data...");
let auth_data = decode_to_string(auth_data).map_err(|e| {
warn!("Failed to decode SAML auth data: {}", e);
AuthDataParseError::Invalid

View File

@ -1,9 +1,10 @@
use std::collections::HashMap;
use anyhow::bail;
use serde::{Deserialize, Serialize};
use specta::Type;
use crate::auth::SamlAuthData;
use crate::auth::{SamlAuthData, SamlAuthResult};
#[derive(Debug, Serialize, Deserialize, Type, Clone)]
#[serde(rename_all = "camelCase")]
@ -110,11 +111,11 @@ impl AuthCookieCredential {
pub struct CachedCredential {
username: String,
password: Option<String>,
auth_cookie: AuthCookieCredential,
auth_cookie: Option<AuthCookieCredential>,
}
impl CachedCredential {
pub fn new(username: String, password: Option<String>, auth_cookie: AuthCookieCredential) -> Self {
pub fn new(username: String, password: Option<String>, auth_cookie: Option<AuthCookieCredential>) -> Self {
Self {
username,
password,
@ -130,12 +131,12 @@ impl CachedCredential {
self.password.as_deref()
}
pub fn auth_cookie(&self) -> &AuthCookieCredential {
&self.auth_cookie
pub fn auth_cookie(&self) -> Option<&AuthCookieCredential> {
self.auth_cookie.as_ref()
}
pub fn set_auth_cookie(&mut self, auth_cookie: AuthCookieCredential) {
self.auth_cookie = auth_cookie;
self.auth_cookie = Some(auth_cookie);
}
pub fn set_username(&mut self, username: String) {
@ -149,11 +150,7 @@ impl CachedCredential {
impl From<PasswordCredential> for CachedCredential {
fn from(value: PasswordCredential) -> Self {
Self::new(
value.username().to_owned(),
Some(value.password().to_owned()),
AuthCookieCredential::new("", "", ""),
)
Self::new(value.username().to_owned(), Some(value.password().to_owned()), None)
}
}
#[derive(Debug, Serialize, Deserialize, Type, Clone)]
@ -197,11 +194,16 @@ impl Credential {
Some(cred.prelogon_user_auth_cookie()),
None,
),
// Use the empty string as the password if auth_cookie is present
Credential::Cached(cred) => (
cred.password(),
if cred.auth_cookie.is_some() {
None
} else {
cred.password()
},
None,
Some(cred.auth_cookie.user_auth_cookie()),
Some(cred.auth_cookie.prelogon_user_auth_cookie()),
cred.auth_cookie.as_ref().map(|c| c.user_auth_cookie()),
cred.auth_cookie.as_ref().map(|c| c.prelogon_user_auth_cookie()),
None,
),
};
@ -230,6 +232,17 @@ impl From<SamlAuthData> for Credential {
}
}
impl TryFrom<SamlAuthResult> for Credential {
type Error = anyhow::Error;
fn try_from(value: SamlAuthResult) -> anyhow::Result<Self> {
match value {
SamlAuthResult::Success(auth_data) => Ok(Self::from(auth_data)),
SamlAuthResult::Failure(err) => bail!(err),
}
}
}
impl From<PasswordCredential> for Credential {
fn from(value: PasswordCredential) -> Self {
Self::Password(value)

View File

@ -29,7 +29,7 @@ pub async fn gateway_login(gateway: &str, cred: &Credential, gp_params: &GpParam
params.extend(extra_params);
params.insert("server", &gateway);
info!("Gateway login, user_agent: {}", gp_params.user_agent());
info!("Perform gateway login, user_agent: {}", gp_params.user_agent());
let res = client
.post(&login_url)

View File

@ -109,7 +109,7 @@ pub async fn retrieve_config(portal: &str, cred: &Credential, gp_params: &GpPara
params.insert("server", &server);
params.insert("host", &server);
info!("Portal config, user_agent: {}", gp_params.user_agent());
info!("Retrieve the portal config, user_agent: {}", gp_params.user_agent());
let res = client
.post(&url)

View File

@ -116,6 +116,8 @@ pub async fn prelogin(portal: &str, gp_params: &GpParams) -> anyhow::Result<Prel
let client = Client::try_from(gp_params)?;
info!("Perform prelogin, user_agent: {}", gp_params.user_agent());
let res = client
.post(&prelogin_url)
.form(&params)

View File

@ -19,7 +19,7 @@ pub struct SamlAuthLauncher<'a> {
ignore_tls_errors: bool,
clean: bool,
default_browser: bool,
external_browser: Option<&'a str>,
browser: Option<&'a str>,
}
impl<'a> SamlAuthLauncher<'a> {
@ -36,7 +36,7 @@ impl<'a> SamlAuthLauncher<'a> {
ignore_tls_errors: false,
clean: false,
default_browser: false,
external_browser: None,
browser: None,
}
}
@ -90,13 +90,13 @@ impl<'a> SamlAuthLauncher<'a> {
self
}
pub fn external_browser(mut self, external_browser: Option<&'a str>) -> Self {
self.external_browser = external_browser;
pub fn browser(mut self, browser: Option<&'a str>) -> Self {
self.browser = browser;
self
}
/// 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);
auth_cmd.arg(self.server);
@ -140,8 +140,8 @@ impl<'a> SamlAuthLauncher<'a> {
auth_cmd.arg("--default-browser");
}
if let Some(external_browser) = self.external_browser {
auth_cmd.arg("--external-browser").arg(external_browser);
if let Some(browser) = self.browser {
auth_cmd.arg("--browser").arg(browser);
}
let mut non_root_cmd = auth_cmd.into_non_root()?;
@ -152,17 +152,10 @@ impl<'a> SamlAuthLauncher<'a> {
.wait_with_output()
.await?;
if self.default_browser {
return Ok(None);
}
let Ok(auth_result) = serde_json::from_slice::<SamlAuthResult>(&output.stdout) else {
bail!("Failed to parse auth data")
};
match auth_result {
SamlAuthResult::Success(auth_data) => Ok(Some(Credential::from(auth_data))),
SamlAuthResult::Failure(msg) => bail!(msg),
}
Credential::try_from(auth_result)
}
}

View File

@ -19,7 +19,7 @@ impl BrowserAuthenticator<'_> {
pub fn new_with_browser<'a>(auth_request: &'a str, browser: &'a str) -> BrowserAuthenticator<'a> {
BrowserAuthenticator {
auth_request,
browser: Some(browser),
browser: if browser == "default" { None } else { Some(browser) },
}
}

View File

@ -42,6 +42,8 @@
overrideMain = {...}: {
postPatch = ''
substituteInPlace crates/common/src/vpn_utils.rs \
--replace-fail /etc/vpnc/vpnc-script ${pkgs.vpnc-scripts}/bin/vpnc-script
substituteInPlace crates/gpapi/src/lib.rs \
--replace-fail /usr/bin/gpclient $out/bin/gpclient \
--replace-fail /usr/bin/gpservice $out/bin/gpservice \