From 7a12c6f8cf1126a29036c932c01851f1ac471fb6 Mon Sep 17 00:00:00 2001 From: Matthias Sauer Date: Wed, 8 Jul 2026 14:54:06 +0200 Subject: [PATCH] Allow configuring the User-Agent sent to Nextcloud Some deployments front Nextcloud with proxies/WAFs that filter requests based on User-Agent, so the push daemon needs a way to send a custom one for the requests it makes to the Nextcloud instance (including credential verification). Adds a USER_AGENT env var / --user-agent flag, following the same config plumbing as ALLOW_SELF_SIGNED. Signed-off-by: Matthias Sauer --- README.md | 1 + src/config.rs | 10 ++++++++++ src/lib.rs | 12 ++++++++++-- src/nc.rs | 15 +++++++++++---- tests/integration.rs | 1 + 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index cad17913..c9b8ab2b 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,7 @@ variables: - `DATABASE_PREFIX` database prefix configured in Nextcloud, e.g. `oc_` - `REDIS_URL` connection url for redis, e.g. `redis://redis_host` - `NEXTCLOUD_URL` url for the nextcloud instance, e.g. `https://cloud.example.com` +- `USER_AGENT` user agent to send when connecting to the nextcloud instance, no `User-Agent` header is sent if unset Or you can specify the options as command line arguments, see `notify_push --help` for information about the command line arguments. diff --git a/src/config.rs b/src/config.rs index 93b39cb0..5b07b168 100644 --- a/src/config.rs +++ b/src/config.rs @@ -83,6 +83,9 @@ pub struct Opt { /// Disable validating of certificates when connecting to the nextcloud instance #[clap(long)] pub allow_self_signed: bool, + /// The user agent to use when connecting to the nextcloud instance + #[clap(long)] + pub user_agent: Option, /// The path to the nextcloud config file #[clap(name = "CONFIG_FILE")] pub config_file: Option, @@ -125,6 +128,7 @@ pub struct Config { pub log_level: String, pub bind: Bind, pub allow_self_signed: bool, + pub user_agent: Option, pub no_ansi: bool, pub tls: Option, pub max_debounce_time: usize, @@ -225,6 +229,7 @@ impl TryFrom for Config { log_level: config.log_level.unwrap_or_else(|| String::from("warn")), bind, allow_self_signed: config.allow_self_signed.unwrap_or(false), + user_agent: config.user_agent, no_ansi: config.no_ansi.unwrap_or(false), tls: config.tls, max_debounce_time: config.max_debounce_time.unwrap_or(15), @@ -262,6 +267,7 @@ struct PartialConfig { pub socket: Option, pub socket_permissions: Option, pub allow_self_signed: Option, + pub user_agent: Option, pub no_ansi: Option, pub tls: Option, pub max_debounce_time: Option, @@ -288,6 +294,7 @@ impl PartialConfig { let socket = var("SOCKET_PATH").map(PathBuf::from).ok(); let socket_permissions = var("SOCKET_PERMISSIONS").ok(); let allow_self_signed = var("ALLOW_SELF_SIGNED").map(|val| val == "true").ok(); + let user_agent = var("USER_AGENT").ok(); let no_ansi = var("NO_ANSI").map(|val| val == "true").ok(); let tls_cert = parse_var("TLS_CERT")?; @@ -340,6 +347,7 @@ impl PartialConfig { socket, socket_permissions, allow_self_signed, + user_agent, no_ansi, tls, max_debounce_time, @@ -431,6 +439,7 @@ impl PartialConfig { } else { None }, + user_agent: opt.user_agent, no_ansi: if opt.no_ansi { Some(true) } else { None }, tls, max_debounce_time: opt.max_debounce_time, @@ -456,6 +465,7 @@ impl PartialConfig { socket: self.socket.or(fallback.socket), socket_permissions: self.socket_permissions.or(fallback.socket_permissions), allow_self_signed: self.allow_self_signed.or(fallback.allow_self_signed), + user_agent: self.user_agent.or(fallback.user_agent), no_ansi: self.no_ansi.or(fallback.no_ansi), tls: self.tls.or(fallback.tls), max_debounce_time: self.max_debounce_time.or(fallback.max_debounce_time), diff --git a/src/lib.rs b/src/lib.rs index e0a8e9bc..ac95698b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,7 +68,11 @@ pub struct App { impl App { pub async fn new(config: Config, log_handle: LoggerHandle) -> Result { let connections = ActiveConnections::default(); - let nc_client = nc::Client::new(&config.nextcloud_url, config.allow_self_signed)?; + let nc_client = nc::Client::new( + &config.nextcloud_url, + config.allow_self_signed, + config.user_agent.as_deref(), + )?; let test_cookie = AtomicU32::new(0); let storage_mapping = StorageMapping::new(config.database, config.database_prefix).await?; @@ -98,7 +102,11 @@ impl App { allow_self_signed: bool, ) -> Result { let connections = ActiveConnections::default(); - let nc_client = nc::Client::new(&config.nextcloud_url, allow_self_signed)?; + let nc_client = nc::Client::new( + &config.nextcloud_url, + allow_self_signed, + config.user_agent.as_deref(), + )?; let test_cookie = AtomicU32::new(0); let storage_mapping = StorageMapping::from_connection(connection, config.database_prefix); diff --git a/src/nc.rs b/src/nc.rs index 72a16ffe..bf48dd64 100644 --- a/src/nc.rs +++ b/src/nc.rs @@ -18,16 +18,23 @@ pub struct Client { } impl Client { - pub fn new(base_url: &str, allow_self_signed: bool) -> Result { + pub fn new( + base_url: &str, + allow_self_signed: bool, + user_agent: Option<&str>, + ) -> Result { let base_url = Url::parse(base_url)?; - let http = reqwest::Client::builder() + let mut builder = reqwest::Client::builder() .tls_certs_merge( webpki_root_certs::TLS_SERVER_ROOT_CERTS .iter() .map(|root| Certificate::from_der(root).unwrap()), ) - .tls_danger_accept_invalid_certs(allow_self_signed) - .build()?; + .tls_danger_accept_invalid_certs(allow_self_signed); + if let Some(user_agent) = user_agent { + builder = builder.user_agent(user_agent); + } + let http = builder.build()?; Ok(Client { http, base_url }) } diff --git a/tests/integration.rs b/tests/integration.rs index 6e8d78f2..bfd580a6 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -171,6 +171,7 @@ impl Services { log_level: "".to_string(), bind: Bind::Tcp(self.nextcloud), allow_self_signed: false, + user_agent: None, no_ansi: false, tls: None, max_debounce_time: 15,