Skip to content

Commit

Permalink
Criteo Id Module: ensure all kind of privacy strings are sent to back…
Browse files Browse the repository at this point in the history
…end (prebid#9845)

Also adding missing gdpr applies flag
  • Loading branch information
leonardlabat authored and jorgeluisrocha committed May 18, 2023
1 parent 78db00e commit 2ab67a5
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 23 deletions.
41 changes: 27 additions & 14 deletions modules/criteoIdSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ import { timestamp, parseUrl, triggerPixel, logError } from '../src/utils.js';
import { ajax } from '../src/ajax.js';
import { getRefererInfo } from '../src/refererDetection.js';
import { submodule } from '../src/hook.js';
import {getStorageManager} from '../src/storageManager.js';
import {MODULE_TYPE_UID} from '../src/activities/modules.js';
import { getStorageManager } from '../src/storageManager.js';
import { MODULE_TYPE_UID } from '../src/activities/modules.js';
import { gdprDataHandler, uspDataHandler, gppDataHandler } from '../src/adapterManager.js';

const gvlid = 91;
const bidderCode = 'criteo';
export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: bidderCode});
export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: bidderCode });

const bididStorageKey = 'cto_bidid';
const bundleStorageKey = 'cto_bundle';
Expand Down Expand Up @@ -77,17 +78,33 @@ function getCriteoDataFromAllStorages() {
}
}

function buildCriteoUsersyncUrl(topUrl, domain, bundle, dnaBundle, areCookiesWriteable, isLocalStorageWritable, isPublishertagPresent, gdprString) {
const url = 'https://gum.criteo.com/sid/json?origin=prebid' +
function buildCriteoUsersyncUrl(topUrl, domain, bundle, dnaBundle, areCookiesWriteable, isLocalStorageWritable, isPublishertagPresent) {
let url = 'https://gum.criteo.com/sid/json?origin=prebid' +
`${topUrl ? '&topUrl=' + encodeURIComponent(topUrl) : ''}` +
`${domain ? '&domain=' + encodeURIComponent(domain) : ''}` +
`${bundle ? '&bundle=' + encodeURIComponent(bundle) : ''}` +
`${dnaBundle ? '&info=' + encodeURIComponent(dnaBundle) : ''}` +
`${gdprString ? '&gdprString=' + encodeURIComponent(gdprString) : ''}` +
`${areCookiesWriteable ? '&cw=1' : ''}` +
`${isPublishertagPresent ? '&pbt=1' : ''}` +
`${isLocalStorageWritable ? '&lsw=1' : ''}`;

const usPrivacyString = uspDataHandler.getConsentData();
if (usPrivacyString) {
url = url + `&us_privacy=${encodeURIComponent(usPrivacyString)}`;
}

const gdprConsent = gdprDataHandler.getConsentData()
if (gdprConsent) {
url = url + `${gdprConsent.consentString ? '&gdprString=' + encodeURIComponent(gdprConsent.consentString) : ''}`;
url = url + `&gdpr=${gdprConsent.gdprApplies === true ? 1 : 0}`;
}

const gppConsent = gppDataHandler.getConsentData();
if (gppConsent) {
url = url + `${gppConsent.gppString ? '&gpp=' + encodeURIComponent(gppConsent.gppString) : ''}`;
url = url + `${gppConsent.applicableSections ? '&gpp_sid=' + encodeURIComponent(gppConsent.applicableSections) : ''}`;
}

return url;
}

Expand Down Expand Up @@ -116,7 +133,7 @@ function callSyncPixel(domain, pixel) {
}
}

function callCriteoUserSync(parsedCriteoData, gdprString, callback) {
function callCriteoUserSync(parsedCriteoData, callback) {
const cw = storage.cookiesAreEnabled();
const lsw = storage.localStorageIsEnabled();
const topUrl = extractProtocolHost(getRefererInfo().page);
Expand All @@ -131,8 +148,7 @@ function callCriteoUserSync(parsedCriteoData, gdprString, callback) {
parsedCriteoData.dnaBundle,
cw,
lsw,
isPublishertagPresent,
gdprString
isPublishertagPresent
);

const callbacks = {
Expand Down Expand Up @@ -191,13 +207,10 @@ export const criteoIdSubmodule = {
* @param {ConsentData} [consentData]
* @returns {{id: {criteoId: string} | undefined}}}
*/
getId(config, consentData) {
const hasGdprData = consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies;
const gdprConsentString = hasGdprData ? consentData.consentString : undefined;

getId() {
let localData = getCriteoDataFromAllStorages();

const result = (callback) => callCriteoUserSync(localData, gdprConsentString, callback);
const result = (callback) => callCriteoUserSync(localData, callback);

return {
id: localData.bidId ? { criteoId: localData.bidId } : undefined,
Expand Down
108 changes: 99 additions & 9 deletions test/spec/modules/criteoIdSystem_spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { criteoIdSubmodule, storage } from 'modules/criteoIdSystem.js';
import * as utils from 'src/utils.js';
import { gdprDataHandler, uspDataHandler, gppDataHandler } from '../../../src/adapterManager.js';
import { server } from '../../mocks/xhr';

const pastDateString = new Date(0).toString()
Expand All @@ -17,6 +18,9 @@ describe('CriteoId module', function () {
let timeStampStub;
let parseUrlStub;
let triggerPixelStub;
let gdprConsentDataStub;
let uspConsentDataStub;
let gppConsentDataStub;

beforeEach(function (done) {
getCookieStub = sinon.stub(storage, 'getCookie');
Expand All @@ -27,6 +31,9 @@ describe('CriteoId module', function () {
timeStampStub = sinon.stub(utils, 'timestamp').returns(nowTimestamp);
parseUrlStub = sinon.stub(utils, 'parseUrl').returns({ protocol: 'https', hostname: 'testdev.com' })
triggerPixelStub = sinon.stub(utils, 'triggerPixel');
gdprConsentDataStub = sinon.stub(gdprDataHandler, 'getConsentData');
uspConsentDataStub = sinon.stub(uspDataHandler, 'getConsentData');
gppConsentDataStub = sinon.stub(gppDataHandler, 'getConsentData');
done();
});

Expand All @@ -39,6 +46,9 @@ describe('CriteoId module', function () {
timeStampStub.restore();
triggerPixelStub.restore();
parseUrlStub.restore();
gdprConsentDataStub.restore();
uspConsentDataStub.restore();
gppConsentDataStub.restore();
});

const storageTestCases = [
Expand Down Expand Up @@ -136,11 +146,11 @@ describe('CriteoId module', function () {
}));

const gdprConsentTestCases = [
{ consentData: { gdprApplies: true, consentString: 'expectedConsentString' }, expected: 'expectedConsentString' },
{ consentData: { gdprApplies: false, consentString: 'expectedConsentString' }, expected: undefined },
{ consentData: { gdprApplies: true, consentString: undefined }, expected: undefined },
{ consentData: { gdprApplies: 'oui', consentString: 'expectedConsentString' }, expected: undefined },
{ consentData: undefined, expected: undefined }
{ consentData: { gdprApplies: true, consentString: 'expectedConsentString' }, expectedGdprConsent: 'expectedConsentString', expectedGdpr: '1' },
{ consentData: { gdprApplies: false, consentString: 'expectedConsentString' }, expectedGdprConsent: 'expectedConsentString', expectedGdpr: '0' },
{ consentData: { gdprApplies: true, consentString: undefined }, expectedGdprConsent: undefined, expectedGdpr: '1' },
{ consentData: { gdprApplies: 'oui', consentString: 'expectedConsentString' }, expectedGdprConsent: 'expectedConsentString', expectedGdpr: '0' },
{ consentData: undefined, expectedGdprConsent: undefined, expectedGdpr: undefined }
];

it('should call sync pixels if request by backend', function () {
Expand Down Expand Up @@ -225,14 +235,94 @@ describe('CriteoId module', function () {

gdprConsentTestCases.forEach(testCase => it('should call user sync url with the gdprConsent', function () {
let callBackSpy = sinon.spy();
let result = criteoIdSubmodule.getId(undefined, testCase.consentData);

gdprConsentDataStub.returns(testCase.consentData);

let result = criteoIdSubmodule.getId(undefined);
result.callback(callBackSpy);

let request = server.requests[0];

if (testCase.expectedGdprConsent) {
expect(request.url).to.have.string(`gdprString=${testCase.expectedGdprConsent}`);
} else {
expect(request.url).to.not.have.string('gdprString=');
}

if (testCase.expectedGdpr) {
expect(request.url).to.have.string(`gdpr=${testCase.expectedGdpr}`);
} else {
expect(request.url).to.not.have.string('gdpr=');
}

request.respond(
200,
{ 'Content-Type': 'application/json' },
JSON.stringify({})
);

expect(callBackSpy.calledOnce).to.be.true;
}));

[undefined, 'abc'].forEach(usPrivacy => it('should call user sync url with the us privacy string', function () {
let callBackSpy = sinon.spy();

uspConsentDataStub.returns(usPrivacy);

let result = criteoIdSubmodule.getId(undefined);
result.callback(callBackSpy);

let request = server.requests[0];
if (testCase.expected) {
expect(request.url).to.have.string(`gdprString=${testCase.expected}`);

if (usPrivacy) {
expect(request.url).to.have.string(`us_privacy=${usPrivacy}`);
} else {
expect(request.url).to.not.have.string('us_privacy=');
}

request.respond(
200,
{ 'Content-Type': 'application/json' },
JSON.stringify({})
);

expect(callBackSpy.calledOnce).to.be.true;
}));

[
{
consentData: {
gppString: 'abc',
applicableSections: [1]
},
expectedGpp: 'abc',
expectedGppSid: '1'
},
{
consentData: undefined,
expectedGpp: undefined,
expectedGppSid: undefined
}
].forEach(testCase => it('should call user sync url with the gpp string', function () {
let callBackSpy = sinon.spy();

gppConsentDataStub.returns(testCase.consentData);

let result = criteoIdSubmodule.getId(undefined);
result.callback(callBackSpy);

let request = server.requests[0];

if (testCase.expectedGpp) {
expect(request.url).to.have.string(`gpp=${testCase.expectedGpp}`);
} else {
expect(request.url).to.not.have.string('gpp=');
}

if (testCase.expectedGppSid) {
expect(request.url).to.have.string(`gpp_sid=${testCase.expectedGppSid}`);
} else {
expect(request.url).to.not.have.string('gdprString');
expect(request.url).to.not.have.string('gpp_sid=');
}

request.respond(
Expand Down

0 comments on commit 2ab67a5

Please sign in to comment.