Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/common/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ fn main() {
let toml_content = fs::read_to_string(init_config_path)
.unwrap_or_else(|_| panic!("Failed to read {init_config_path:?}"));

// Merge base TOML with environment variable overrides and write output
// Merge base TOML with environment variable overrides and write output.
// Panics if admin endpoints are not covered by a handler.
let settings = settings::Settings::from_toml_and_env(&toml_content)
.expect("Failed to parse settings at build time");

Expand Down
53 changes: 52 additions & 1 deletion crates/common/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,24 @@ use crate::settings::Settings;

const BASIC_AUTH_REALM: &str = r#"Basic realm="Trusted Server""#;

/// Enforces Basic-auth for incoming requests.
///
/// Authentication is required when a configured handler's `path` regex matches
/// the request path. Paths not covered by any handler pass through without
/// authentication.
///
/// Admin endpoints are protected by requiring a handler at build time — see
/// [`Settings::from_toml_and_env`].
///
/// # Returns
///
/// * `Some(Response)` — a `401 Unauthorized` response that should be sent back
/// to the client (credentials missing or incorrect).
/// * `None` — the request is allowed to proceed.
pub fn enforce_basic_auth(settings: &Settings, req: &Request) -> Option<Response> {
let handler = settings.handler_for_path(req.get_path())?;
let path = req.get_path();

let handler = settings.handler_for_path(path)?;

let (username, password) = match extract_credentials(req) {
Some(credentials) => credentials,
Expand Down Expand Up @@ -118,4 +134,39 @@ mod tests {
let response = enforce_basic_auth(&settings, &req).expect("should challenge");
assert_eq!(response.get_status(), StatusCode::UNAUTHORIZED);
}

#[test]
fn allow_admin_path_with_valid_credentials() {
let settings = settings_with_handlers();
let mut req = Request::new(Method::POST, "https://example.com/admin/keys/rotate");
let token = STANDARD.encode("admin:admin-pass");
req.set_header(header::AUTHORIZATION, format!("Basic {token}"));

assert!(
enforce_basic_auth(&settings, &req).is_none(),
"should allow admin path with correct credentials"
);
}

#[test]
fn challenge_admin_path_with_wrong_credentials() {
let settings = settings_with_handlers();
let mut req = Request::new(Method::POST, "https://example.com/admin/keys/rotate");
let token = STANDARD.encode("admin:wrong");
req.set_header(header::AUTHORIZATION, format!("Basic {token}"));

let response = enforce_basic_auth(&settings, &req)
.expect("should challenge admin path with wrong credentials");
assert_eq!(response.get_status(), StatusCode::UNAUTHORIZED);
}

#[test]
fn challenge_admin_path_with_missing_credentials() {
let settings = settings_with_handlers();
let req = Request::new(Method::POST, "https://example.com/admin/keys/rotate");

let response = enforce_basic_auth(&settings, &req)
.expect("should challenge admin path with missing credentials");
assert_eq!(response.get_status(), StatusCode::UNAUTHORIZED);
}
}
10 changes: 10 additions & 0 deletions crates/common/src/integrations/google_tag_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1297,6 +1297,11 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
#[test]
fn test_config_parsing() {
let toml_str = r#"
[[handlers]]
path = "^/admin"
username = "admin"
password = "admin-pass"

[publisher]
domain = "test-publisher.com"
cookie_domain = ".test-publisher.com"
Expand Down Expand Up @@ -1328,6 +1333,11 @@ upstream_url = "https://custom.gtm.example"
#[test]
fn test_config_defaults() {
let toml_str = r#"
[[handlers]]
path = "^/admin"
username = "admin"
password = "admin-pass"

[publisher]
domain = "test-publisher.com"
cookie_domain = ".test-publisher.com"
Expand Down
5 changes: 5 additions & 0 deletions crates/common/src/integrations/prebid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,11 @@ mod tests {

/// Shared TOML prefix for config-parsing tests (publisher + synthetic sections).
const TOML_BASE: &str = r#"
[[handlers]]
path = "^/admin"
username = "admin"
password = "admin-pass"

[publisher]
domain = "test-publisher.com"
cookie_domain = ".test-publisher.com"
Expand Down
Loading
Loading