Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
- Added basic unit tests
- Added publisher config

### Changed
- Upgrade to rust 1.87.0
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ tokio = { version = "1.46", features = ["sync", "macros", "io-util", "rt", "time
url = "2.4.1"

[dev-dependencies]
regex = "1.1.1"
temp-env = "0.3.6"
18 changes: 13 additions & 5 deletions crates/common/src/cookies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use cookie::{Cookie, CookieJar};
use fastly::http::header;
use fastly::Request;

use crate::settings::Settings;

const COOKIE_MAX_AGE: i32 = 365 * 24 * 60 * 60; // 1 year

// return empty cookie jar for unparsable cookies
Expand Down Expand Up @@ -31,15 +33,17 @@ pub fn handle_request_cookies(req: &Request) -> Option<CookieJar> {
}
}

pub fn create_synthetic_cookie(synthetic_id: &str) -> String {
pub fn create_synthetic_cookie(settings: &Settings, synthetic_id: &str) -> String {
format!(
"synthetic_id={}; Domain=.auburndao.com; Path=/; Secure; SameSite=Lax; Max-Age={}",
synthetic_id, COOKIE_MAX_AGE,
"synthetic_id={}; Domain={}; Path=/; Secure; SameSite=Lax; Max-Age={}",
synthetic_id, settings.publisher.cookie_domain, COOKIE_MAX_AGE,
)
}

#[cfg(test)]
mod tests {
use crate::test_support::tests::create_test_settings;

use super::*;

#[test]
Expand Down Expand Up @@ -113,10 +117,14 @@ mod tests {

#[test]
fn test_create_synthetic_cookie() {
let result = create_synthetic_cookie("12345");
let settings = create_test_settings();
let result = create_synthetic_cookie(&settings, "12345");
assert_eq!(
result,
"synthetic_id=12345; Domain=.auburndao.com; Path=/; Secure; SameSite=Lax; Max-Age=31536000"
format!(
"synthetic_id=12345; Domain={}; Path=/; Secure; SameSite=Lax; Max-Age={}",
settings.publisher.cookie_domain, COOKIE_MAX_AGE,
)
);
}
}
42 changes: 17 additions & 25 deletions crates/common/src/gdpr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,15 @@ pub fn get_consent_from_request(req: &Request) -> Option<GdprConsent> {
None
}

pub fn create_consent_cookie(consent: &GdprConsent) -> String {
pub fn create_consent_cookie(settings: &Settings, consent: &GdprConsent) -> String {
format!(
"gdpr_consent={}; Domain=.auburndao.com; Path=/; Secure; SameSite=Lax; Max-Age=31536000",
serde_json::to_string(consent).unwrap_or_default()
"gdpr_consent={}; Domain={}; Path=/; Secure; SameSite=Lax; Max-Age=31536000",
serde_json::to_string(consent).unwrap_or_default(),
settings.publisher.cookie_domain,
)
}

pub fn handle_consent_request(_settings: &Settings, req: Request) -> Result<Response, Error> {
pub fn handle_consent_request(settings: &Settings, req: Request) -> Result<Response, Error> {
match *req.get_method() {
Method::GET => {
// Return current consent status
Expand All @@ -81,7 +82,10 @@ pub fn handle_consent_request(_settings: &Settings, req: Request) -> Result<Resp
.with_header(header::CONTENT_TYPE, "application/json")
.with_body(serde_json::to_string(&consent)?);

response.set_header(header::SET_COOKIE, create_consent_cookie(&consent));
response.set_header(
header::SET_COOKIE,
create_consent_cookie(settings, &consent),
);
Ok(response)
}
_ => {
Expand Down Expand Up @@ -132,23 +136,7 @@ mod tests {
use super::*;
use fastly::{Body, Request};

fn create_test_settings() -> Settings {
Settings {
ad_server: crate::settings::AdServer {
ad_partner_url: "https://test.com".to_string(),
sync_url: "https://sync.test.com".to_string(),
},
prebid: crate::settings::Prebid {
server_url: "https://prebid.test.com".to_string(),
},
synthetic: crate::settings::Synthetic {
counter_store: "test-counter".to_string(),
opid_store: "test-opid".to_string(),
secret_key: "test-secret".to_string(),
template: "{{test}}".to_string(),
},
}
}
use crate::test_support::tests::create_test_settings;

#[test]
fn test_gdpr_consent_default() {
Expand Down Expand Up @@ -196,6 +184,7 @@ mod tests {

#[test]
fn test_create_consent_cookie() {
let settings = create_test_settings();
let consent = GdprConsent {
analytics: true,
advertising: true,
Expand All @@ -204,9 +193,9 @@ mod tests {
version: "1.0".to_string(),
};

let cookie = create_consent_cookie(&consent);
let cookie = create_consent_cookie(&settings, &consent);
assert!(cookie.starts_with("gdpr_consent="));
assert!(cookie.contains("Domain=.auburndao.com"));
assert!(cookie.contains(format!("Domain={}", settings.publisher.cookie_domain).as_str()));
assert!(cookie.contains("Path=/"));
assert!(cookie.contains("Secure"));
assert!(cookie.contains("SameSite=Lax"));
Expand Down Expand Up @@ -297,7 +286,10 @@ mod tests {
let set_cookie = response.get_header_str(header::SET_COOKIE);
assert!(set_cookie.is_some());
assert!(set_cookie.unwrap().contains("gdpr_consent="));
assert!(set_cookie.unwrap().contains("Domain=.auburndao.com"));

assert!(set_cookie
.unwrap()
.contains(format!("Domain={}", settings.publisher.cookie_domain).as_str()));

// Check response body
let body = response.into_body_str();
Expand Down
1 change: 1 addition & 0 deletions crates/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ pub mod privacy;
pub mod settings;
pub mod synthetic;
pub mod templates;
pub mod test_support;
pub mod why;
42 changes: 15 additions & 27 deletions crates/common/src/prebid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl PrebidRequest {
.and_then(|o| url::Url::parse(o).ok())
.and_then(|u| u.host_str().map(|h| h.to_string()))
})
.unwrap_or_else(|| "auburndao.com".to_string());
.unwrap_or_else(|| settings.publisher.domain.clone());

log::info!("Detected domain: {}", domain);

Expand Down Expand Up @@ -192,23 +192,7 @@ mod tests {
use super::*;
use fastly::Request;

fn create_test_settings() -> Settings {
Settings {
ad_server: crate::settings::AdServer {
ad_partner_url: "https://test.com".to_string(),
sync_url: "https://sync.test.com".to_string(),
},
prebid: crate::settings::Prebid {
server_url: "https://prebid.test.com/openrtb2/auction".to_string(),
},
synthetic: crate::settings::Synthetic {
counter_store: "test-counter".to_string(),
opid_store: "test-opid".to_string(),
secret_key: "test-secret-key".to_string(),
template: "{{client_ip}}:{{user_agent}}:{{first_party_id}}:{{auth_user_id}}:{{publisher_domain}}:{{accept_language}}".to_string(),
},
}
}
use crate::test_support::tests::create_test_settings;

#[test]
fn test_prebid_request_new_with_full_headers() {
Expand Down Expand Up @@ -258,31 +242,34 @@ mod tests {
#[test]
fn test_prebid_request_domain_fallback() {
let settings = create_test_settings();
let req = Request::get("https://example.com/test");
let url = format!("https://{}", settings.publisher.domain);
let req = Request::get(url.clone());
// No referer or origin headers

let prebid_req = PrebidRequest::new(&settings, &req).unwrap();

assert_eq!(prebid_req.domain, "auburndao.com");
assert_eq!(prebid_req.origin, "https://auburndao.com");
assert_eq!(prebid_req.domain, settings.publisher.domain);
assert_eq!(prebid_req.origin, url);
}

#[test]
fn test_prebid_request_invalid_url_in_referer() {
let settings = create_test_settings();
let mut req = Request::get("https://example.com/test");
let url = format!("https://{}/test", settings.publisher.domain);
let mut req = Request::get(url);
req.set_header(header::REFERER, "not-a-valid-url");

let prebid_req = PrebidRequest::new(&settings, &req).unwrap();

// Should fallback to default domain
assert_eq!(prebid_req.domain, "auburndao.com");
assert_eq!(prebid_req.domain, settings.publisher.domain);
}

#[test]
fn test_prebid_request_x_forwarded_for_parsing() {
let settings = create_test_settings();
let mut req = Request::get("https://example.com/test");
let url = format!("https://{}/test", settings.publisher.domain);
let mut req = Request::get(url);
req.set_header(HEADER_X_FORWARDED_FOR, "192.168.1.1, 10.0.0.1, 172.16.0.1");

let prebid_req = PrebidRequest::new(&settings, &req).unwrap();
Expand Down Expand Up @@ -330,18 +317,19 @@ mod tests {
#[test]
fn test_prebid_request_edge_cases() {
let settings = create_test_settings();
let url = format!("https://{}/test", settings.publisher.domain);

// Test with empty X-Forwarded-For
let mut req = Request::get("https://example.com/test");
let mut req = Request::get(url.clone());
req.set_header(HEADER_X_FORWARDED_FOR, "");
let prebid_req = PrebidRequest::new(&settings, &req).unwrap();
assert!(!prebid_req.client_ip.is_empty() || prebid_req.client_ip.is_empty());

// Test with malformed origin
let mut req2 = Request::get("https://example.com/test");
let mut req2 = Request::get(url.clone());
req2.set_header(header::ORIGIN, "://invalid");
let prebid_req2 = PrebidRequest::new(&settings, &req2).unwrap();
assert_eq!(prebid_req2.domain, "auburndao.com");
assert_eq!(prebid_req2.domain, settings.publisher.domain);
}

// Note: Testing send_bid_request would require mocking the Fastly backend,
Expand Down
Loading