diff --git a/syncserver-settings/src/lib.rs b/syncserver-settings/src/lib.rs index 6732ffc..1c9ef83 100644 --- a/syncserver-settings/src/lib.rs +++ b/syncserver-settings/src/lib.rs @@ -18,6 +18,7 @@ static PREFIX: &str = "sync"; pub struct Settings { pub port: u16, pub host: String, + pub public_url: Option, /// Keep-alive header value (seconds) pub actix_keep_alive: Option, /// The master secret, from which are derived @@ -182,6 +183,7 @@ impl Default for Settings { Settings { port: 8000, host: "127.0.0.1".to_string(), + public_url: None, actix_keep_alive: None, master_secret: Secrets::default(), statsd_host: Some("localhost".to_owned()), diff --git a/syncserver/src/server/mod.rs b/syncserver/src/server/mod.rs index f1e0a18..d686693 100644 --- a/syncserver/src/server/mod.rs +++ b/syncserver/src/server/mod.rs @@ -80,7 +80,7 @@ pub struct Server; #[macro_export] macro_rules! build_app { - ($syncstorage_state: expr, $tokenserver_state: expr, $secrets: expr, $limits: expr, $cors: expr, $metrics: expr) => { + ($public_url: expr, $syncstorage_state: expr, $tokenserver_state: expr, $secrets: expr, $limits: expr, $cors: expr, $metrics: expr) => { App::new() .configure(|cfg| { cfg.app_data(Data::new($syncstorage_state)); @@ -90,6 +90,7 @@ macro_rules! build_app { cfg.app_data(Data::new(state)); } }) + .app_data(Data::new($public_url)) .app_data(Data::new($secrets)) // Middleware is applied LIFO // These will wrap all outbound responses with matching status codes. @@ -336,6 +337,7 @@ impl Server { }; build_app!( + settings.public_url.clone(), syncstorage_state, tokenserver_state.clone(), Arc::clone(&secrets), diff --git a/syncserver/src/server/test.rs b/syncserver/src/server/test.rs index 8690526..7efbe98 100644 --- a/syncserver/src/server/test.rs +++ b/syncserver/src/server/test.rs @@ -127,6 +127,7 @@ macro_rules! init_app { let state = get_test_state(&$settings).await; let metrics = state.metrics.clone(); test::init_service(build_app!( + $settings.public_url.clone(), state, None::, Arc::clone(&SECRETS), @@ -248,6 +249,7 @@ where let state = get_test_state(&settings).await; let metrics = state.metrics.clone(); let app = test::init_service(build_app!( + settings.public_url.clone(), state, None::, Arc::clone(&SECRETS), @@ -292,6 +294,7 @@ async fn test_endpoint_with_body( let state = get_test_state(&settings).await; let metrics = state.metrics.clone(); let app = test::init_service(build_app!( + settings.public_url.clone(), state, None::, Arc::clone(&SECRETS), diff --git a/syncserver/src/web/auth.rs b/syncserver/src/web/auth.rs index 9153d38..ca1d123 100644 --- a/syncserver/src/web/auth.rs +++ b/syncserver/src/web/auth.rs @@ -170,6 +170,7 @@ impl HawkPayload { pub fn extrude( header: &str, method: &str, + public_url: &Option, secrets: &Secrets, ci: &ConnectionInfo, uri: &Uri, @@ -190,6 +191,15 @@ impl HawkPayload { } else { 80 }; + + let prefix = match &public_url { + None => "".to_owned(), + Some(url) => match url.parse::() { + Err(_) => "".to_owned(), + Ok(uri) => uri.path().to_owned() + } + }; + let path = uri.path_and_query().ok_or(HawkErrorKind::MissingPath)?; let expiry = if path.path().ends_with("/info/collections") { 0 @@ -197,7 +207,7 @@ impl HawkPayload { Utc::now().timestamp() as u64 }; - HawkPayload::new(header, method, path.as_str(), host, port, secrets, expiry) + HawkPayload::new(header, method, (prefix + path.as_str()).as_str(), host, port, secrets, expiry) } } diff --git a/syncserver/src/web/extractors.rs b/syncserver/src/web/extractors.rs index 4bd617b..b9aee1c 100644 --- a/syncserver/src/web/extractors.rs +++ b/syncserver/src/web/extractors.rs @@ -1056,6 +1056,7 @@ impl HawkIdentifier { method: &str, uri: &Uri, ci: &ConnectionInfo, + public_url: &Option, secrets: &Secrets, ) -> Result where @@ -1072,6 +1073,7 @@ impl HawkIdentifier { .to_str() .map_err(|e| -> ApiError { HawkErrorKind::Header(e).into() })?; let identifier = Self::generate( + public_url, secrets, method, auth_header, @@ -1084,6 +1086,7 @@ impl HawkIdentifier { } pub fn generate( + public_url: &Option, secrets: &Secrets, method: &str, header: &str, @@ -1091,7 +1094,7 @@ impl HawkIdentifier { uri: &Uri, exts: &mut Extensions, ) -> Result { - let payload = HawkPayload::extrude(header, method, secrets, connection_info, uri)?; + let payload = HawkPayload::extrude(header, method, &public_url, secrets, connection_info, uri)?; let puid = Self::uid_from_path(uri)?; if payload.user_id != puid { warn!("⚠️ Hawk UID not in URI: {:?} {:?}", payload.user_id, uri); @@ -1145,6 +1148,12 @@ impl FromRequest for HawkIdentifier { // NOTE: `connection_info()` will get a mutable reference lock on `extensions()` let connection_info = req.connection_info().clone(); let method = req.method().clone(); + + let public_url: &Option = match &req.app_data::>>() { + Some(data) => data, + None => &None + }; + // Tried collapsing this to a `.or_else` and hit problems with the return resolving // to an appropriate error state. Can't use `?` since the function does not return a result. let secrets = match req.app_data::>>() { @@ -1155,7 +1164,7 @@ impl FromRequest for HawkIdentifier { } }; - let result = Self::extrude(&req, method.as_str(), uri, &connection_info, secrets); + let result = Self::extrude(&req, method.as_str(), uri, &connection_info, &public_url, secrets); if let Ok(ref hawk_id) = result { // Store the origin of the token as an extra to be included when emitting a Sentry error