Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

consentManagementTcf: add flag to set dsarequired #11824

Merged
merged 2 commits into from
Jun 25, 2024
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
38 changes: 38 additions & 0 deletions libraries/consentManagement/cmUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {timedAuctionHook} from '../../src/utils/perfMetrics.js';
import {logError, logInfo, logWarn} from '../../src/utils.js';

export function consentManagementHook(name, getConsent, loadConsentData) {
function loadIfMissing(cb) {
if (getConsent()) {
logInfo('User consent information already known. Pulling internally stored information...');
// eslint-disable-next-line standard/no-callback-literal
cb(false);
} else {
loadConsentData(cb);
}
}

return timedAuctionHook(name, function requestBidsHook(fn, reqBidsConfigObj) {
loadIfMissing(function (shouldCancelAuction, errMsg, ...extraArgs) {
if (errMsg) {
let log = logWarn;
if (shouldCancelAuction) {
log = logError;
errMsg = `${errMsg} Canceling auction as per consentManagement config.`;
}
log(errMsg, ...extraArgs);
}

if (shouldCancelAuction) {
fn.stopTiming();
if (typeof reqBidsConfigObj.bidsBackHandler === 'function') {
reqBidsConfigObj.bidsBackHandler();
} else {
logError('Error executing bidsBackHandler');
}
} else {
fn.call(this, reqBidsConfigObj);
}
});
});
}
40 changes: 2 additions & 38 deletions modules/consentManagementGpp.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
import {deepSetValue, isEmpty, isNumber, isPlainObject, isStr, logError, logInfo, logWarn} from '../src/utils.js';
import {config} from '../src/config.js';
import {gppDataHandler} from '../src/adapterManager.js';
import {timedAuctionHook} from '../src/utils/perfMetrics.js';
import {enrichFPD} from '../src/fpd/enrichment.js';
import {getGlobal} from '../src/prebidGlobal.js';
import {cmpClient, MODE_CALLBACK} from '../libraries/cmp/cmpClient.js';
import {GreedyPromise} from '../src/utils/promise.js';
import {buildActivityParams} from '../src/activities/params.js';
import {consentManagementHook} from '../libraries/consentManagement/cmUtils.js';

const DEFAULT_CMP = 'iab';
const DEFAULT_CONSENT_TIMEOUT = 10000;
Expand Down Expand Up @@ -228,20 +228,6 @@ function loadConsentData(cb) {
}
}

/**
* Like `loadConsentData`, but cache and re-use previously loaded data.
* @param cb
*/
function loadIfMissing(cb) {
if (consentData) {
logInfo('User consent information already known. Pulling internally stored information...');
// eslint-disable-next-line standard/no-callback-literal
cb(false);
} else {
loadConsentData(cb);
}
}

/**
* If consentManagement module is enabled (ie included in setConfig), this hook function will attempt to fetch the
* user's encoded consent string from the supported CMP. Once obtained, the module will store this
Expand All @@ -250,29 +236,7 @@ function loadIfMissing(cb) {
* @param {object} reqBidsConfigObj required; This is the same param that's used in pbjs.requestBids.
* @param {function} fn required; The next function in the chain, used by hook.js
*/
export const requestBidsHook = timedAuctionHook('gpp', function requestBidsHook(fn, reqBidsConfigObj) {
loadIfMissing(function (shouldCancelAuction, errMsg, ...extraArgs) {
if (errMsg) {
let log = logWarn;
if (shouldCancelAuction) {
log = logError;
errMsg = `${errMsg} Canceling auction as per consentManagement config.`;
}
log(errMsg, ...extraArgs);
}

if (shouldCancelAuction) {
fn.stopTiming();
if (typeof reqBidsConfigObj.bidsBackHandler === 'function') {
reqBidsConfigObj.bidsBackHandler();
} else {
logError('Error executing bidsBackHandler');
}
} else {
fn.call(this, reqBidsConfigObj);
}
});
});
export const requestBidsHook = consentManagementHook('gpp', () => consentData, loadConsentData);

function processCmpData(consentData) {
if (
Expand Down
45 changes: 7 additions & 38 deletions modules/consentManagementTcf.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import {deepSetValue, isNumber, isPlainObject, isStr, logError, logInfo, logWarn
import {config} from '../src/config.js';
import {gdprDataHandler} from '../src/adapterManager.js';
import {includes} from '../src/polyfill.js';
import {timedAuctionHook} from '../src/utils/perfMetrics.js';
import {registerOrtbProcessor, REQUEST} from '../src/pbjsORTB.js';
import {enrichFPD} from '../src/fpd/enrichment.js';
import {getGlobal} from '../src/prebidGlobal.js';
import {cmpClient} from '../libraries/cmp/cmpClient.js';
import {consentManagementHook} from '../libraries/consentManagement/cmUtils.js';

const DEFAULT_CMP = 'iab';
const DEFAULT_CONSENT_TIMEOUT = 10000;
Expand All @@ -22,6 +22,7 @@ export let userCMP;
export let consentTimeout;
export let gdprScope;
export let staticConsentData;
let dsaPlatform = false;
let actionTimeout;

let consentData;
Expand Down Expand Up @@ -156,20 +157,6 @@ function loadConsentData(cb) {
}
}

/**
* Like `loadConsentData`, but cache and re-use previously loaded data.
* @param cb
*/
function loadIfMissing(cb) {
if (consentData) {
logInfo('User consent information already known. Pulling internally stored information...');
// eslint-disable-next-line standard/no-callback-literal
cb(false);
} else {
loadConsentData(cb);
}
}

/**
* If consentManagement module is enabled (ie included in setConfig), this hook function will attempt to fetch the
* user's encoded consent string from the supported CMP. Once obtained, the module will store this
Expand All @@ -178,29 +165,7 @@ function loadIfMissing(cb) {
* @param {object} reqBidsConfigObj required; This is the same param that's used in pbjs.requestBids.
* @param {function} fn required; The next function in the chain, used by hook.js
*/
export const requestBidsHook = timedAuctionHook('gdpr', function requestBidsHook(fn, reqBidsConfigObj) {
loadIfMissing(function (shouldCancelAuction, errMsg, ...extraArgs) {
if (errMsg) {
let log = logWarn;
if (shouldCancelAuction) {
log = logError;
errMsg = `${errMsg} Canceling auction as per consentManagement config.`;
}
log(errMsg, ...extraArgs);
}

if (shouldCancelAuction) {
fn.stopTiming();
if (typeof reqBidsConfigObj.bidsBackHandler === 'function') {
reqBidsConfigObj.bidsBackHandler();
} else {
logError('Error executing bidsBackHandler');
}
} else {
fn.call(this, reqBidsConfigObj);
}
});
});
export const requestBidsHook = consentManagementHook('gdpr', () => consentData, loadConsentData);

/**
* This function checks the consent data provided by CMP to ensure it's in an expected state.
Expand Down Expand Up @@ -282,6 +247,7 @@ export function setConsentConfig(config) {

// if true, then gdprApplies should be set to true
gdprScope = config.defaultGdprScope === true;
dsaPlatform = !!config.dsaPlatform;

logInfo('consentManagement module has been activated...');

Expand Down Expand Up @@ -315,6 +281,9 @@ export function enrichFPDHook(next, fpd) {
}
deepSetValue(ortb2, 'user.ext.consent', consent.consentString);
}
if (dsaPlatform) {
deepSetValue(ortb2, 'regs.ext.dsa.dsarequired', 3);
}
return ortb2;
}));
}
Expand Down
26 changes: 24 additions & 2 deletions test/spec/fpd/gdpr_spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {gdprDataHandler} from '../../../src/adapterManager.js';
import {enrichFPDHook} from '../../../modules/consentManagementTcf.js';
import {config} from 'src/config.js';
import 'src/prebid.js';

describe('GDPR FPD enrichment', () => {
let sandbox, consent;
Expand All @@ -12,9 +14,9 @@ describe('GDPR FPD enrichment', () => {
sandbox.restore();
})

function callHook() {
function callHook(ortb2 = {}) {
let result;
enrichFPDHook((res) => { result = res }, Promise.resolve({}));
enrichFPDHook((res) => { result = res }, Promise.resolve(ortb2));
return result;
}

Expand Down Expand Up @@ -44,4 +46,24 @@ describe('GDPR FPD enrichment', () => {
})
})
});

describe('dsa', () => {
describe('when dsaPlaform is set', () => {
beforeEach(() => {
config.setConfig({
consentManagement: {
gdpr: {
dsaPlatform: true
}
}
});
});

it('sets dsarequired', () => {
return callHook().then(ortb2 => {
expect(ortb2.regs.ext.dsa.dsarequired).to.equal(3);
});
});
});
});
});