Skip to content

Commit 2c34be4

Browse files
committed
Merge the Edge Cookie review follow-ups through split/3
Brings upd/split/3-permissions at e3bb4b9 into split/4-client-resolve. That carries the split/1 review follow-ups (0980b73, 0e7f7eb, fb1bc28, cb6f717 and 1b88e42), their merge into split/3 under that branch's permission gate, and split/3's be0ed2d, which lets the Cloudflare and Spin startup tests from main's #1016 load under the single-jurisdiction rule. This branch carries the same four settings blocks, so it takes that fix here. The merge had no conflicts. The resolve endpoint already applies a provider's response headers only with the 200 that sets the cookie or the 204 when the provider creates nothing, which is the rule cb6f717 brings to organic generation, so nothing in resolve.rs changes. Tests. The merged tree passes a native all-targets check, 2,885 core tests, the Axum, Cloudflare and Spin adapter tests, the permission signal crate tests, and the Fastly, Cloudflare and Spin wasm checks.
2 parents 69e7d57 + e3bb4b9 commit 2c34be4

11 files changed

Lines changed: 951 additions & 154 deletions

File tree

crates/trusted-server-adapter-cloudflare/src/app.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,9 @@ mod tests {
811811
812812
[ec]
813813
passphrase = "fictional-secret-key-32-bytes-minimum"
814+
815+
[geo]
816+
assume_single_jurisdiction = true
814817
"#,
815818
)
816819
.expect("should parse startup test settings");
@@ -859,6 +862,9 @@ mod tests {
859862
860863
[ec]
861864
passphrase = "fictional-secret-key-32-bytes-minimum"
865+
866+
[geo]
867+
assume_single_jurisdiction = true
862868
"#,
863869
)
864870
.expect("should parse startup test settings");
@@ -903,6 +909,9 @@ mod tests {
903909
904910
[ec]
905911
passphrase = "fictional-secret-key-32-bytes-minimum"
912+
913+
[geo]
914+
assume_single_jurisdiction = true
906915
"#,
907916
)
908917
.expect("should parse startup test settings");

crates/trusted-server-adapter-spin/src/app.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,9 @@ mod tests {
10211021
10221022
[ec]
10231023
passphrase = "fictional-secret-key-32-bytes-minimum"
1024+
1025+
[geo]
1026+
assume_single_jurisdiction = true
10241027
"#,
10251028
)
10261029
.expect("should parse startup test settings");

crates/trusted-server-core/src/auction/endpoints.rs

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,8 +305,13 @@ pub async fn handle_auction(
305305
// EC and both KV and partner stores are available. Gate the read on a
306306
// present registry: without one, `resolve_auction_eids` yields no
307307
// server-side EIDs, so the snapshot would be an unused billable KV read.
308+
// The row is read under the owning provider's canonical form of the
309+
// identifier, the key it is stored under, rather than under the identifier
310+
// as issued.
308311
let auction_kv_snapshot = match (kv, ec_id.as_deref(), registry) {
309-
(Some(graph), Some(ec_id), Some(_)) => graph.load_snapshot(ec_id),
312+
(Some(graph), Some(_), Some(_)) => ec_context
313+
.ec_kv_key()
314+
.map_or(EcKvSnapshot::NotRead, |kv_key| graph.load_snapshot(&kv_key)),
310315
_ => EcKvSnapshot::NotRead,
311316
};
312317
// Hand the loaded row to the request context so response finalization —
@@ -452,7 +457,13 @@ pub(crate) fn resolve_auction_eids(
452457

453458
let ec_id = ec_context.ec_value()?;
454459

455-
let Some(entry) = snapshot.entry_for(ec_id) else {
460+
// Callers read the snapshot under the identity-graph key, the owning
461+
// provider's canonical form of the identifier, so the entry is looked up
462+
// under that key rather than under the identifier as issued.
463+
let Some(entry) = ec_context
464+
.kv_key_for(ec_id)
465+
.and_then(|kv_key| snapshot.entry_for(&kv_key))
466+
else {
456467
return Some(Vec::new());
457468
};
458469

@@ -625,6 +636,7 @@ mod tests {
625636
use crate::auction::types::{AuctionRequest, AuctionResponse};
626637
use crate::consent::jurisdiction::Jurisdiction;
627638
use crate::consent::types::ConsentContext;
639+
use crate::ec::tests::{CANONICAL_COOKIE_VALUE, CANONICAL_KV_KEY, CanonicalizingProvider};
628640
use crate::error::IntoHttpResponse as _;
629641
use crate::openrtb::Uid;
630642
use crate::platform::test_support::{
@@ -809,6 +821,84 @@ mod tests {
809821
);
810822
}
811823

824+
#[tokio::test]
825+
async fn auction_endpoint_loads_the_row_under_the_canonical_key() {
826+
// The identity graph stores a row under the owning provider's
827+
// canonical form of the identifier. Loaded and resolved under the
828+
// identifier as issued, a provider whose canonical form differs from
829+
// the cookie value found no row, so the auction carried no server-side
830+
// EIDs and the context kept a snapshot bound to the wrong key.
831+
let settings = create_test_settings();
832+
let had_eids = Arc::new(std::sync::Mutex::new(None));
833+
let mut orchestrator = AuctionOrchestrator::new(AuctionConfig {
834+
enabled: true,
835+
providers: AuctionConfig::legacy_provider_map(&["eid_capturing_provider"]),
836+
timeout_ms: 2000,
837+
mediator: None,
838+
..Default::default()
839+
});
840+
orchestrator.register_provider(Arc::new(EidCapturingProvider {
841+
had_eids: Arc::clone(&had_eids),
842+
}));
843+
let registry = PartnerRegistry::from_config(&[counting_test_partner("ssp.example.com")])
844+
.expect("should build partner registry");
845+
let graph = KvIdentityGraph::in_memory("canonical-auction-store");
846+
graph
847+
.create(
848+
CANONICAL_KV_KEY,
849+
&crate::ec::kv_types::KvEntry::minimal(
850+
"ssp.example.com",
851+
"partner-uid-123",
852+
1_741_824_000,
853+
),
854+
)
855+
.expect("should seed the row under the canonical key");
856+
let mut ec_context = make_non_regulated_ec_context(Some(CANONICAL_COOKIE_VALUE))
857+
.with_provider_for_test(Arc::new(CanonicalizingProvider));
858+
let req = Request::builder()
859+
.method("POST")
860+
.uri("https://test-publisher.com/auction")
861+
.body(EdgeBody::from(
862+
serde_json::to_vec(&json!({
863+
"adUnits": [
864+
{
865+
"code": "div-gpt-ad-1",
866+
"mediaTypes": { "banner": { "sizes": [[300, 250]] } }
867+
}
868+
]
869+
}))
870+
.expect("should serialize body"),
871+
))
872+
.expect("should build auction request");
873+
874+
// The capturing provider records whether the request carried EIDs and
875+
// then fails its launch, which is all this test needs. The request
876+
// carries no client EIDs, so any EID it records came from the graph.
877+
let _ = handle_auction(
878+
&settings,
879+
&orchestrator,
880+
Some(&graph),
881+
Some(&registry),
882+
&mut ec_context,
883+
&noop_services(),
884+
req,
885+
)
886+
.await;
887+
888+
assert!(
889+
ec_context
890+
.kv_snapshot()
891+
.entry_for(CANONICAL_KV_KEY)
892+
.is_some(),
893+
"the endpoint should load the row stored under the canonical key"
894+
);
895+
assert_eq!(
896+
*had_eids.lock().expect("should lock captured eids"),
897+
Some(true),
898+
"the auction should carry the canonical row's partner ID as an EID"
899+
);
900+
}
901+
812902
/// Provider that fails the test if it is ever contacted. Used to prove the
813903
/// `/auction` consent gate short-circuits before any outbound bid request.
814904
struct PanicOnBidProvider;

0 commit comments

Comments
 (0)