diff --git a/crates/common/src/config/network.rs b/crates/common/src/config/network.rs index cea7ca3d..d80880ac 100644 --- a/crates/common/src/config/network.rs +++ b/crates/common/src/config/network.rs @@ -59,7 +59,6 @@ pub struct Http { pub rate_authenticated: Option, pub rate_anonymous: Option, pub url_https: String, - pub url_http: String, pub allowed_endpoint: IfBlock, pub response_headers: Vec<(hyper::header::HeaderName, hyper::header::HeaderValue)>, pub use_forwarded: bool, @@ -435,12 +434,11 @@ impl Http { Http { url_https: if !bp.registry.is_recovery_mode() { - format!("https://{server_name}") - } else { - String::new() - }, - url_http: if !bp.registry.is_recovery_mode() { - format!("http://{server_name}") + if let Some(port) = bp.registry.https_port() { + format!("https://{server_name}:{port}") + } else { + format!("https://{server_name}") + } } else { String::new() }, diff --git a/crates/http-proto/src/context.rs b/crates/http-proto/src/context.rs index d4c72e88..672ad3e5 100644 --- a/crates/http-proto/src/context.rs +++ b/crates/http-proto/src/context.rs @@ -18,15 +18,6 @@ impl<'x> HttpContext<'x> { Self { session, req } } - #[allow(unused_variables)] - pub fn resolve_response_url<'y>(&self, server: &'y Server) -> &'y str { - if self.session.is_tls { - &server.core.network.http.url_https - } else { - &server.core.network.http.url_http - } - } - pub async fn has_endpoint_access(&self, server: &Server) -> StatusCode { server .eval_if( diff --git a/crates/http/src/api/mod.rs b/crates/http/src/api/mod.rs index 6fdcec55..5fb712b5 100644 --- a/crates/http/src/api/mod.rs +++ b/crates/http/src/api/mod.rs @@ -81,7 +81,7 @@ impl ManagementApi for Server { if let Some(email) = path.get(1).copied() { self.is_http_anonymous_request_allowed(session.remote_ip) .await?; - self.handle_discover_request(req, session, decode_path_element(email).as_ref()) + self.handle_discover_request(decode_path_element(email).as_ref()) .await } else { Err(trc::ResourceEvent::NotFound.into_err()) diff --git a/crates/http/src/auth/oauth/auth.rs b/crates/http/src/auth/oauth/auth.rs index 45748bb2..705f79d8 100644 --- a/crates/http/src/auth/oauth/auth.rs +++ b/crates/http/src/auth/oauth/auth.rs @@ -49,8 +49,6 @@ pub struct OAuthMetadata { pub trait OAuthApiHandler: Sync + Send { fn handle_discover_request( &self, - req: &HttpRequest, - session: &HttpSessionData, account_name: &str, ) -> impl Future> + Send; @@ -66,11 +64,7 @@ pub trait OAuthApiHandler: Sync + Send { session: &HttpSessionData, ) -> impl Future> + Send; - fn handle_oauth_metadata( - &self, - req: HttpRequest, - session: &HttpSessionData, - ) -> impl Future> + Send; + fn handle_oauth_metadata(&self) -> impl Future> + Send; } #[derive(Debug, serde::Serialize, serde::Deserialize)] @@ -120,12 +114,7 @@ pub enum LoginResponse { } impl OAuthApiHandler for Server { - async fn handle_discover_request( - &self, - req: &HttpRequest, - session: &HttpSessionData, - account_name: &str, - ) -> trc::Result { + async fn handle_discover_request(&self, account_name: &str) -> trc::Result { let account_name = account_name.trim().to_lowercase(); if let Some(domain_name) = account_name.try_domain_part() && let Some(endpoint) = self @@ -137,7 +126,7 @@ impl OAuthApiHandler for Server { .no_cache() .into_http_response()) } else { - self.handle_oidc_metadata(req, session).await + self.handle_oidc_metadata().await } } @@ -423,7 +412,7 @@ impl OAuthApiHandler for Server { .await?; // Build response - let base_url = HttpContext::new(session, req).resolve_response_url(self); + let base_url = &self.core.network.http.url_https; Ok(JsonResponse::new(DeviceAuthResponse { verification_uri: format!("{base_url}/device"), verification_uri_complete: format!("{base_url}/device/?code={user_code}"), @@ -436,12 +425,8 @@ impl OAuthApiHandler for Server { .into_http_response()) } - async fn handle_oauth_metadata( - &self, - req: HttpRequest, - session: &HttpSessionData, - ) -> trc::Result { - let base_url = HttpContext::new(session, &req).resolve_response_url(self); + async fn handle_oauth_metadata(&self) -> trc::Result { + let base_url = &self.core.network.http.url_https; Ok(JsonResponse::new(OAuthMetadata { authorization_endpoint: format!("{base_url}/login",), diff --git a/crates/http/src/auth/oauth/openid.rs b/crates/http/src/auth/oauth/openid.rs index d4488582..0dad6ca0 100644 --- a/crates/http/src/auth/oauth/openid.rs +++ b/crates/http/src/auth/oauth/openid.rs @@ -33,11 +33,7 @@ pub trait OpenIdHandler: Sync + Send { account_id: u32, ) -> impl Future> + Send; - fn handle_oidc_metadata( - &self, - req: &HttpRequest, - session: &HttpSessionData, - ) -> impl Future> + Send; + fn handle_oidc_metadata(&self) -> impl Future> + Send; } impl OpenIdHandler for Server { @@ -56,12 +52,8 @@ impl OpenIdHandler for Server { .into_http_response()) } - async fn handle_oidc_metadata( - &self, - req: &HttpRequest, - session: &HttpSessionData, - ) -> trc::Result { - let base_url = HttpContext::new(session, req).resolve_response_url(self); + async fn handle_oidc_metadata(&self) -> trc::Result { + let base_url = &self.core.network.http.url_https; Ok(JsonResponse::new(OpenIdMetadata { authorization_endpoint: format!("{base_url}/login",), diff --git a/crates/http/src/auth/oauth/token.rs b/crates/http/src/auth/oauth/token.rs index 8842ee68..02c2301d 100644 --- a/crates/http/src/auth/oauth/token.rs +++ b/crates/http/src/auth/oauth/token.rs @@ -64,9 +64,7 @@ impl TokenHandler for Server { let mut response = TokenResponse::error(ErrorType::InvalidGrant); - let issuer = HttpContext::new(&session, req) - .resolve_response_url(self) - .to_string(); + let issuer = self.core.network.http.url_https.to_string(); if grant_type.eq_ignore_ascii_case("authorization_code") { response = if let (Some(code), Some(client_id), Some(redirect_uri)) = ( diff --git a/crates/http/src/request.rs b/crates/http/src/request.rs index 44a7b76b..e6b518aa 100644 --- a/crates/http/src/request.rs +++ b/crates/http/src/request.rs @@ -197,14 +197,14 @@ impl ParseHttp for Server { self.authenticate_headers(&req, &session).await?; self.handle_session_resource( - ctx.resolve_response_url(self).to_string(), + self.core.network.http.url_https.to_string(), &access_token, ) .await .map(|s| s.into_http_response()) } else { Ok(Session::new( - ctx.resolve_response_url(self), + &self.core.network.http.url_https, &self.core.jmap.capabilities, ) .into_http_response()) @@ -271,14 +271,14 @@ impl ParseHttp for Server { self.is_http_anonymous_request_allowed(session.remote_ip) .await?; - return self.handle_oauth_metadata(req, &session).await; + return self.handle_oauth_metadata().await; } ("openid-configuration", &Method::GET) => { // Limit anonymous requests self.is_http_anonymous_request_allowed(session.remote_ip) .await?; - return self.handle_oidc_metadata(&req, &session).await; + return self.handle_oidc_metadata().await; } ("acme-challenge", &Method::GET) if self.has_acme_http_providers() => { if let Some(token) = path.next() { diff --git a/crates/store/src/build/registry.rs b/crates/store/src/build/registry.rs index 1efdab35..2c6a2ba5 100644 --- a/crates/store/src/build/registry.rs +++ b/crates/store/src/build/registry.rs @@ -275,6 +275,11 @@ impl RegistryStore { &self.0.env_hostname } + #[inline(always)] + pub fn https_port(&self) -> Option { + self.0.env_https_port + } + #[inline(always)] pub fn is_recovery_mode(&self) -> bool { self.0.env_recovery_mode @@ -318,6 +323,7 @@ impl RegistryStore { env_cluster_role: cluster_role, env_push_shard_id: push_shard_id, env_hostname: hostname, + env_https_port: None, id_generator: utils::snowflake::SnowflakeIdGenerator::new(), }) .await diff --git a/crates/store/src/lib.rs b/crates/store/src/lib.rs index 9c3829f9..2a909fde 100644 --- a/crates/store/src/lib.rs +++ b/crates/store/src/lib.rs @@ -217,6 +217,7 @@ pub struct RegistryStoreInner { pub(crate) env_cluster_role: Option, pub(crate) env_push_shard_id: u32, pub(crate) env_hostname: String, + pub(crate) env_https_port: Option, pub(crate) id_generator: SnowflakeIdGenerator, } diff --git a/crates/store/src/registry/local.rs b/crates/store/src/registry/local.rs index ae18ec14..a8123d1a 100644 --- a/crates/store/src/registry/local.rs +++ b/crates/store/src/registry/local.rs @@ -52,6 +52,9 @@ impl RegistryStoreInner { "localhost".to_string() } }), + env_https_port: std::env::var("STALWART_HTTPS_PORT") + .ok() + .and_then(|p| p.parse::().ok()), } } diff --git a/install.sh b/install.sh index 22a283a0..a3b95801 100644 --- a/install.sh +++ b/install.sh @@ -207,8 +207,8 @@ write_env_file() { # Override the hostname used in HTTP responses #STALWART_HOSTNAME=mail.example.com -# Override the HTTP port used in HTTP responses -#STALWART_HTTP_PORT=8080 +# Override the HTTPS port used in HTTPS responses +#STALWART_HTTPS_PORT=8080 # Enable bootstrap / recovery mode on startup. Accepted: 1, true. Default: false. #STALWART_RECOVERY_MODE=true