Skip to content

Commit cd37b37

Browse files
committed
Add proxy authentication support with username/password
Add proxy authentication support with username/password # Description This PR adds the ability to use authenticated proxies by including username and password credentials in proxy URLs. # Changes - Added username and password fields to ProxySettings struct - Extended database storage and retrieval for proxy credentials - Added authentication to proxy URLs using the standard format (http://username:password@hostname:port) - Added secure handling of credentials (password masking in UI, credentials hiding in logs) - Added comprehensive test coverage for the authentication functionality # Features - Users can now set proxy username and password in the proxy settings UI - Credentials are automatically applied to HTTP, HTTPS, and ALL proxy settings - Credentials are securely stored in the application database - Works with any proxy server that supports basic authentication # Testing - Added unit tests for URL construction with authentication - Added tests for environment variable handling with credentials - Added tests for database storage and retrieval of credentials - Tested complex URLs with paths, query parameters, and special characters This enhancement allows users to connect through proxies that require authentication, which is commonly needed in corporate environments.
1 parent 2350ed4 commit cd37b37

File tree

4 files changed

+437
-6
lines changed

4 files changed

+437
-6
lines changed

src-tauri/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ zstd = "0.13"
5353
uuid = { version = "1.6", features = ["v4", "serde"] }
5454
walkdir = "2"
5555
serde_yaml = "0.9"
56+
url = "2.5"
5657

5758

5859
[target.'cfg(target_os = "macos")'.dependencies]

src-tauri/src/commands/proxy.rs

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ pub struct ProxySettings {
1010
pub https_proxy: Option<String>,
1111
pub no_proxy: Option<String>,
1212
pub all_proxy: Option<String>,
13+
pub proxy_username: Option<String>,
14+
pub proxy_password: Option<String>,
1315
pub enabled: bool,
1416
}
1517

@@ -20,6 +22,8 @@ impl Default for ProxySettings {
2022
https_proxy: None,
2123
no_proxy: None,
2224
all_proxy: None,
25+
proxy_username: None,
26+
proxy_password: None,
2327
enabled: false,
2428
}
2529
}
@@ -39,6 +43,8 @@ pub async fn get_proxy_settings(db: State<'_, AgentDb>) -> Result<ProxySettings,
3943
("proxy_https", "https_proxy"),
4044
("proxy_no", "no_proxy"),
4145
("proxy_all", "all_proxy"),
46+
("proxy_username", "proxy_username"),
47+
("proxy_password", "proxy_password"),
4248
];
4349

4450
for (db_key, field) in keys {
@@ -53,6 +59,8 @@ pub async fn get_proxy_settings(db: State<'_, AgentDb>) -> Result<ProxySettings,
5359
"https_proxy" => settings.https_proxy = Some(value).filter(|s| !s.is_empty()),
5460
"no_proxy" => settings.no_proxy = Some(value).filter(|s| !s.is_empty()),
5561
"all_proxy" => settings.all_proxy = Some(value).filter(|s| !s.is_empty()),
62+
"proxy_username" => settings.proxy_username = Some(value).filter(|s| !s.is_empty()),
63+
"proxy_password" => settings.proxy_password = Some(value).filter(|s| !s.is_empty()),
5664
_ => {}
5765
}
5866
}
@@ -76,6 +84,8 @@ pub async fn save_proxy_settings(
7684
("proxy_https", settings.https_proxy.clone().unwrap_or_default()),
7785
("proxy_no", settings.no_proxy.clone().unwrap_or_default()),
7886
("proxy_all", settings.all_proxy.clone().unwrap_or_default()),
87+
("proxy_username", settings.proxy_username.clone().unwrap_or_default()),
88+
("proxy_password", settings.proxy_password.clone().unwrap_or_default()),
7989
];
8090

8191
for (key, value) in values {
@@ -93,6 +103,29 @@ pub async fn save_proxy_settings(
93103

94104
/// Apply proxy settings as environment variables
95105
pub fn apply_proxy_settings(settings: &ProxySettings) {
106+
// Helper function to add authentication to proxy URL if username and password are available
107+
fn add_auth_to_url(url: &str, username: Option<&str>, password: Option<&str>) -> String {
108+
if username.is_none() || username.unwrap().is_empty() {
109+
return url.to_string();
110+
}
111+
112+
if let Ok(mut parsed_url) = url::Url::parse(url) {
113+
// Set username
114+
let _ = parsed_url.set_username(username.unwrap());
115+
116+
// Set password if available
117+
if let Some(pwd) = password {
118+
if !pwd.is_empty() {
119+
let _ = parsed_url.set_password(Some(pwd));
120+
}
121+
}
122+
123+
return parsed_url.to_string();
124+
}
125+
126+
// Return original URL if parsing fails
127+
url.to_string()
128+
}
96129
log::info!("Applying proxy settings: enabled={}", settings.enabled);
97130

98131
if !settings.enabled {
@@ -122,15 +155,35 @@ pub fn apply_proxy_settings(settings: &ProxySettings) {
122155
// Set proxy environment variables (uppercase is standard)
123156
if let Some(http_proxy) = &settings.http_proxy {
124157
if !http_proxy.is_empty() {
125-
log::info!("Setting HTTP_PROXY={}", http_proxy);
126-
std::env::set_var("HTTP_PROXY", http_proxy);
158+
// Add authentication to URL if username/password are available
159+
let auth_url = add_auth_to_url(
160+
http_proxy,
161+
settings.proxy_username.as_deref(),
162+
settings.proxy_password.as_deref()
163+
);
164+
165+
// Log URL without credentials for security
166+
log::info!("Setting HTTP_PROXY={}",
167+
if auth_url.contains('@') { "[authenticated proxy URL]" } else { &auth_url });
168+
169+
std::env::set_var("HTTP_PROXY", auth_url);
127170
}
128171
}
129172

130173
if let Some(https_proxy) = &settings.https_proxy {
131174
if !https_proxy.is_empty() {
132-
log::info!("Setting HTTPS_PROXY={}", https_proxy);
133-
std::env::set_var("HTTPS_PROXY", https_proxy);
175+
// Add authentication to URL if username/password are available
176+
let auth_url = add_auth_to_url(
177+
https_proxy,
178+
settings.proxy_username.as_deref(),
179+
settings.proxy_password.as_deref()
180+
);
181+
182+
// Log URL without credentials for security
183+
log::info!("Setting HTTPS_PROXY={}",
184+
if auth_url.contains('@') { "[authenticated proxy URL]" } else { &auth_url });
185+
186+
std::env::set_var("HTTPS_PROXY", auth_url);
134187
}
135188
}
136189

@@ -140,8 +193,18 @@ pub fn apply_proxy_settings(settings: &ProxySettings) {
140193

141194
if let Some(all_proxy) = &settings.all_proxy {
142195
if !all_proxy.is_empty() {
143-
log::info!("Setting ALL_PROXY={}", all_proxy);
144-
std::env::set_var("ALL_PROXY", all_proxy);
196+
// Add authentication to URL if username/password are available
197+
let auth_url = add_auth_to_url(
198+
all_proxy,
199+
settings.proxy_username.as_deref(),
200+
settings.proxy_password.as_deref()
201+
);
202+
203+
// Log URL without credentials for security
204+
log::info!("Setting ALL_PROXY={}",
205+
if auth_url.contains('@') { "[authenticated proxy URL]" } else { &auth_url });
206+
207+
std::env::set_var("ALL_PROXY", auth_url);
145208
}
146209
}
147210

0 commit comments

Comments
 (0)