Skip to content

Commit

Permalink
Prebid 8: introduce new transmitTid activity control (#10034)
Browse files Browse the repository at this point in the history
* Core: introduce new `transmitTid` activity control and turn off TIDs by default

* enable TIDs for e2e tests
  • Loading branch information
dgirardi authored Jun 2, 2023
1 parent 76d8e57 commit 4320362
Show file tree
Hide file tree
Showing 15 changed files with 199 additions and 36 deletions.
13 changes: 2 additions & 11 deletions libraries/objectGuard/objectGuard.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {isData, objectTransformer} from '../../src/activities/redactor.js';
import {isData, objectTransformer, sessionedApplies} from '../../src/activities/redactor.js';
import {deepAccess, deepClone, deepEqual, deepSetValue} from '../../src/utils.js';

/**
Expand Down Expand Up @@ -41,15 +41,6 @@ export function objectGuard(rules) {

const wpTransformer = objectTransformer(writeRules);

function mkApplies(session, args) {
return function applies(rule) {
if (!session.hasOwnProperty(rule.name)) {
session[rule.name] = rule.applies(...args);
}
return session[rule.name];
}
}

function mkGuard(obj, tree, applies) {
return new Proxy(obj, {
get(target, prop, receiver) {
Expand All @@ -76,7 +67,7 @@ export function objectGuard(rules) {
return function guard(obj, ...args) {
const session = {};
return {
obj: mkGuard(obj, root.children || {}, mkApplies(session, args)),
obj: mkGuard(obj, root.children || {}, sessionedApplies(session, ...args)),
verify: mkVerify(wpTransformer(session, obj, ...args))
}
};
Expand Down
5 changes: 5 additions & 0 deletions src/activities/activities.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,8 @@ export const ACTIVITY_TRANSMIT_UFPD = 'transmitUfpd';
* transmitPreciseGeo: some component wants access to (and send along) geolocation info
*/
export const ACTIVITY_TRANSMIT_PRECISE_GEO = 'transmitPreciseGeo';

/**
* transmit TID: some component wants access ot (and send along) transaction IDs
*/
export const ACTIVITY_TRANSMIT_TID = 'transmitTid';
44 changes: 36 additions & 8 deletions src/activities/redactor.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import {deepAccess} from '../utils.js';
import {isActivityAllowed} from './rules.js';
import {ACTIVITY_TRANSMIT_EIDS, ACTIVITY_TRANSMIT_PRECISE_GEO, ACTIVITY_TRANSMIT_UFPD} from './activities.js';
import {config} from '../config.js';
import {isActivityAllowed, registerActivityControl} from './rules.js';
import {
ACTIVITY_TRANSMIT_EIDS,
ACTIVITY_TRANSMIT_PRECISE_GEO,
ACTIVITY_TRANSMIT_TID,
ACTIVITY_TRANSMIT_UFPD
} from './activities.js';

export const ORTB_UFPD_PATHS = ['user.data', 'user.ext.data'];
export const ORTB_EIDS_PATHS = ['user.eids', 'user.ext.eids'];
Expand Down Expand Up @@ -74,23 +80,28 @@ export function objectTransformer(rules) {
})
return function applyTransform(session, obj, ...args) {
const result = [];
const applies = sessionedApplies(session, ...args);
rules.forEach(rule => {
if (session[rule.name] === false) return;
for (const [head, tail] of rule.paths) {
const parent = head == null ? obj : deepAccess(obj, head);
result.push(rule.run(obj, head, parent, tail, () => {
if (!session.hasOwnProperty(rule.name)) {
session[rule.name] = !!rule.applies(...args);
}
return session[rule.name]
}))
result.push(rule.run(obj, head, parent, tail, applies.bind(null, rule)));
if (session[rule.name] === false) return;
}
})
return result.filter(el => el != null);
}
}

export function sessionedApplies(session, ...args) {
return function applies(rule) {
if (!session.hasOwnProperty(rule.name)) {
session[rule.name] = !!rule.applies(...args);
}
return session[rule.name];
}
}

export function isData(val) {
return val != null && (typeof val !== 'object' || Object.keys(val).length > 0)
}
Expand All @@ -107,6 +118,11 @@ function bidRequestTransmitRules(isAllowed = isActivityAllowed) {
name: ACTIVITY_TRANSMIT_EIDS,
paths: ['userId', 'userIdAsEids'],
applies: appliesWhenActivityDenied(ACTIVITY_TRANSMIT_EIDS, isAllowed),
},
{
name: ACTIVITY_TRANSMIT_TID,
paths: ['ortb2Imp.ext.tid'],
applies: appliesWhenActivityDenied(ACTIVITY_TRANSMIT_TID, isAllowed)
}
].map(redactRule)
}
Expand All @@ -130,6 +146,11 @@ export function ortb2TransmitRules(isAllowed = isActivityAllowed) {
get(val) {
return Math.round((val + Number.EPSILON) * 100) / 100;
}
},
{
name: ACTIVITY_TRANSMIT_TID,
paths: ['source.tid'],
applies: appliesWhenActivityDenied(ACTIVITY_TRANSMIT_TID, isAllowed),
}
].map(redactRule);
}
Expand All @@ -155,3 +176,10 @@ export function redactorFactory(isAllowed = isActivityAllowed) {
* that can redact disallowed data from ORTB2 and/or bid request objects.
*/
export const redactor = redactorFactory();

// by default, TIDs are off since version 8
registerActivityControl(ACTIVITY_TRANSMIT_TID, 'enableTIDs config', () => {
if (!config.getConfig('enableTIDs')) {
return {allow: false, reason: 'TIDs are disabled'}
}
});
43 changes: 40 additions & 3 deletions src/adapters/bidderFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
isArray,
isPlainObject,
logError,
logWarn,
logWarn, memoize,
parseQueryStringParameters,
parseSizesInput,
uniques
Expand All @@ -22,6 +22,10 @@ import {hook} from '../hook.js';
import {auctionManager} from '../auctionManager.js';
import {bidderSettings} from '../bidderSettings.js';
import {useMetrics} from '../utils/perfMetrics.js';
import {isActivityAllowed} from '../activities/rules.js';
import {activityParams} from '../activities/activityParams.js';
import {MODULE_TYPE_BIDDER} from '../activities/modules.js';
import {ACTIVITY_TRANSMIT_TID} from '../activities/activities.js';

/**
* This file aims to support Adapters during the Prebid 0.x -> 1.x transition.
Expand Down Expand Up @@ -180,6 +184,38 @@ export function registerBidder(spec) {
}
}

function guardTids(bidderCode) {
if (isActivityAllowed(ACTIVITY_TRANSMIT_TID, activityParams(MODULE_TYPE_BIDDER, bidderCode))) {
return {
bidRequest: (br) => br,
bidderRequest: (br) => br
};
}
function get(target, prop, receiver) {
if (['transactionId', 'auctionId'].includes(prop)) {
return null;
}
return Reflect.get(target, prop, receiver);
}
const bidRequest = memoize((br) => new Proxy(br, {get}), (arg) => arg.bidId)
/**
* Return a view on bidd(er) requests where auctionId/transactionId are nulled if the bidder is not allowed `transmitTid`.
*
* Because both auctionId and transactionId are used for Prebid's own internal bookkeeping, we cannot simply erase them
* from request objects; and because request objects are quite complex and not easily cloneable, we hide the IDs
* with a proxy instead. This should be used only around the adapter logic.
*/
return {
bidRequest,
bidderRequest: (br) => new Proxy(br, {
get(target, prop, receiver) {
if (prop === 'bids') return br.bids.map(bidRequest);
return get(target, prop, receiver);
}
})
}
}

/**
* Make a new bidder from the given spec. This is exported mainly for testing.
* Adapters will probably find it more convenient to use registerBidder instead.
Expand All @@ -196,6 +232,7 @@ export function newBidder(spec) {
if (!Array.isArray(bidderRequest.bids)) {
return;
}
const tidGuard = guardTids(bidderRequest.bidderCode);

const adUnitCodesHandled = {};
function addBidWithCode(adUnitCode, bid) {
Expand All @@ -221,7 +258,7 @@ export function newBidder(spec) {
}

const validBidRequests = adapterMetrics(bidderRequest)
.measureTime('validate', () => bidderRequest.bids.filter(filterAndWarn));
.measureTime('validate', () => bidderRequest.bids.filter((br) => filterAndWarn(tidGuard.bidRequest(br))));

if (validBidRequests.length === 0) {
afterAllResponses();
Expand All @@ -236,7 +273,7 @@ export function newBidder(spec) {
}
});

processBidderRequests(spec, validBidRequests, bidderRequest, ajax, configEnabledCallback, {
processBidderRequests(spec, validBidRequests.map(tidGuard.bidRequest), tidGuard.bidderRequest(bidderRequest), ajax, configEnabledCallback, {
onRequest: requestObject => events.emit(CONSTANTS.EVENTS.BEFORE_BIDDER_HTTP, bidderRequest, requestObject),
onResponse: (resp) => {
onTimelyResponse(spec.code);
Expand Down
1 change: 1 addition & 0 deletions test/pages/banner.html
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
});

pbjs.que.push(function () {
pbjs.setConfig({enableTIDs: true});
pbjs.addAdUnits(adUnits);
pbjs.requestBids({ bidsBackHandler: sendAdServerRequest });
});
Expand Down
1 change: 1 addition & 0 deletions test/pages/bidderSettings.html
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
});

pbjs.que.push(function () {
pbjs.setConfig({enableTIDs: true});
pbjs.addAdUnits(adUnits);
pbjs.bidderSettings = {
appnexus: {
Expand Down
1 change: 1 addition & 0 deletions test/pages/consent_mgt_gdpr.html
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
pbjs.que.push(function () {
pbjs.addAdUnits(adUnits);
pbjs.setConfig({
enableTIDs: true,
consentManagement: {
gdpr: {
cmpApi: 'static',
Expand Down
1 change: 1 addition & 0 deletions test/pages/currency.html
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@

pbjs.que.push(function() {
pbjs.setConfig({
enableTIDs: true,
"currency": {
"adServerCurrency": "GBP",
"granularityMultiplier": 1,
Expand Down
1 change: 1 addition & 0 deletions test/pages/instream.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@

pbjs.addAdUnits(videoAdUnit);
pbjs.setConfig({
enableTIDs: true,
debug: true,
cache: {
url: 'https://prebid.adnxs.com/pbc/v1/cache'
Expand Down
2 changes: 1 addition & 1 deletion test/pages/multiple_bidders.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
}
}]
}];

pbjs.setConfig({enableTIDs: true});
pbjs.addAdUnits(adUnits);
pbjs.requestBids({
timeout: PREBID_TIMEOUT,
Expand Down
1 change: 1 addition & 0 deletions test/pages/native.html
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
});

pbjs.que.push(function () {
pbjs.setConfig({enableTIDs: true});
pbjs.addAdUnits(adUnits);
pbjs.requestBids({ bidsBackHandler: sendAdServerRequest });
});
Expand Down
5 changes: 4 additions & 1 deletion test/pages/outstream.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@
googletag.cmd = googletag.cmd || [];

pbjs.que.push(function () {
pbjs.setConfig({ debug: true });
pbjs.setConfig({
enableTIDs: true,
debug: true
});
pbjs.addAdUnits(outstreamVideoAdUnit);
pbjs.bidderSettings = {
appnexus: {
Expand Down
12 changes: 10 additions & 2 deletions test/spec/activities/redactor_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
import {ACTIVITY_PARAM_COMPONENT_NAME, ACTIVITY_PARAM_COMPONENT_TYPE} from '../../../src/activities/params.js';
import {
ACTIVITY_TRANSMIT_EIDS,
ACTIVITY_TRANSMIT_PRECISE_GEO,
ACTIVITY_TRANSMIT_PRECISE_GEO, ACTIVITY_TRANSMIT_TID,
ACTIVITY_TRANSMIT_UFPD
} from '../../../src/activities/activities.js';
import {deepAccess, deepSetValue} from '../../../src/utils.js';
Expand Down Expand Up @@ -271,6 +271,10 @@ describe('redactor', () => {
testAllowDeny(ACTIVITY_TRANSMIT_EIDS, (allowed) => {
testPropertiesAreRemoved(() => redactor.bidRequest, ['userId', 'userIdAsEids'], allowed);
});

testAllowDeny(ACTIVITY_TRANSMIT_TID, (allowed) => {
testPropertiesAreRemoved(() => redactor.bidRequest, ['ortb2Imp.ext.tid'], allowed);
})
});

describe('.ortb2', () => {
Expand All @@ -282,6 +286,10 @@ describe('redactor', () => {
testPropertiesAreRemoved(() => redactor.ortb2, ORTB_UFPD_PATHS, allowed)
});

testAllowDeny(ACTIVITY_TRANSMIT_TID, (allowed) => {
testPropertiesAreRemoved(() => redactor.ortb2, ['source.tid'], allowed);
});

testAllowDeny(ACTIVITY_TRANSMIT_PRECISE_GEO, (allowed) => {
ORTB_GEO_PATHS.forEach(path => {
it(`should ${allowed ? 'NOT ' : ''} round down ${path}`, () => {
Expand All @@ -291,6 +299,6 @@ describe('redactor', () => {
expect(deepAccess(ortb2, path)).to.eql(allowed ? 1.2345 : 1.23);
})
})
})
});
});
})
20 changes: 16 additions & 4 deletions test/spec/unit/core/adapterManager_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1862,10 +1862,22 @@ describe('adapterManager tests', function () {
requests.appnexus.bids.forEach((bid) => expect(bid.ortb2).to.eql(requests.appnexus.ortb2));
});

it('should populate ortb2.source.tid with auctionId', () => {
const reqs = adapterManager.makeBidRequests(adUnits, 0, 'mockAuctionId', 1000, [], {global: {}});
expect(reqs[0].ortb2.source.tid).to.equal('mockAuctionId');
})
describe('source.tid', () => {
beforeEach(() => {
sinon.stub(dep, 'redact').returns({
ortb2: (o) => o,
bidRequest: (b) => b,
});
});
afterEach(() => {
dep.redact.restore();
});

it('should be populated with auctionId', () => {
const reqs = adapterManager.makeBidRequests(adUnits, 0, 'mockAuctionId', 1000, [], {global: {}});
expect(reqs[0].ortb2.source.tid).to.equal('mockAuctionId');
})
});

it('should merge in bid-level ortb2Imp with adUnit-level ortb2Imp', () => {
const adUnit = {
Expand Down
Loading

0 comments on commit 4320362

Please sign in to comment.