Skip to content

Commit

Permalink
Shared ID gdpr support (#6275)
Browse files Browse the repository at this point in the history
* SharedId gdpr support

* Reverted commented locally failing tests
  • Loading branch information
bjorn-lw authored Feb 8, 2021
1 parent 2640d08 commit 727bf20
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 10 deletions.
3 changes: 2 additions & 1 deletion modules/id5IdSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,11 @@ export const id5IdSubmodule = {
* It's permissible to return neither, one, or both fields.
* @function extendId
* @param {SubmoduleConfig} config
* @param {ConsentData|undefined} consentData
* @param {Object} cacheIdObj - existing id, if any
* @return {(IdResponse|function(callback:function))} A response object that contains id and/or callback.
*/
extendId(config, cacheIdObj) {
extendId(config, consentData, cacheIdObj) {
const partnerId = (config && config.params && config.params.partner) || 0;
incrementNb(partnerId);
return cacheIdObj;
Expand Down
20 changes: 16 additions & 4 deletions modules/pubCommonIdSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,17 @@ function handleResponse(pubcid, callback, config) {
}
}

/**
* Builds and returns the shared Id URL with attached consent data if applicable
* @param {Object} consentData
* @return {string}
*/
function sharedIdUrl(consentData) {
if (!consentData || typeof consentData.gdprApplies !== 'boolean' || !consentData.gdprApplies) return SHAREDID_URL;

return `${SHAREDID_URL}?gdpr=1&gdpr_consent=${consentData.consentString}`
}

/**
* Wraps pixelCallback in order to call sharedid sync
* @param {string} pubcid Pubcommon id value
Expand All @@ -144,12 +155,12 @@ function handleResponse(pubcid, callback, config) {
* @return {function(...[*]=)}
*/

function getIdCallback(pubcid, pixelCallback, config) {
function getIdCallback(pubcid, pixelCallback, config, consentData) {
return function (callback) {
if (typeof pixelCallback === 'function') {
pixelCallback();
}
ajax(SHAREDID_URL, handleResponse(pubcid, callback, config), undefined, {method: 'GET', withCredentials: true});
ajax(sharedIdUrl(consentData), handleResponse(pubcid, callback, config), undefined, {method: 'GET', withCredentials: true});
}
}

Expand Down Expand Up @@ -227,7 +238,7 @@ export const pubCommonIdSubmodule = {
}

const pixelCallback = this.makeCallback(pixelUrl, newId);
const combinedCallback = enableSharedId ? getIdCallback(newId, pixelCallback, config) : pixelCallback;
const combinedCallback = enableSharedId ? getIdCallback(newId, pixelCallback, config, consentData) : pixelCallback;

return {id: newId, callback: combinedCallback};
},
Expand All @@ -247,10 +258,11 @@ export const pubCommonIdSubmodule = {
*
* @function
* @param {SubmoduleParams} [config]
* @param {ConsentData|undefined} consentData
* @param {Object} storedId existing id
* @returns {IdResponse|undefined}
*/
extendId: function(config = {}, storedId) {
extendId: function(config = {}, consentData, storedId) {
const {params: {extend = false, pixelUrl, enableSharedId = SHAREDID_DEFAULT_STATE} = {}} = config;

if (extend) {
Expand Down
21 changes: 17 additions & 4 deletions modules/sharedIdSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,17 @@ function detectPrng(root) {
return () => Math.random();
}

/**
* Builds and returns the shared Id URL with attached consent data if applicable
* @param {Object} consentData
* @return {string}
*/
function sharedIdUrl(consentData) {
if (!consentData || typeof consentData.gdprApplies !== 'boolean' || !consentData.gdprApplies) return ID_SVC;

return `${ID_SVC}?gdpr=1&gdpr_consent=${consentData.consentString}`
}

/** @type {Submodule} */
export const sharedIdSubmodule = {
/**
Expand Down Expand Up @@ -303,23 +314,25 @@ export const sharedIdSubmodule = {
* performs action to obtain id and return a value.
* @function
* @param {SubmoduleConfig} [config]
* @param {ConsentData|undefined} consentData
* @returns {sharedId}
*/
getId(config) {
getId(config, consentData) {
const resp = function (callback) {
utils.logInfo('SharedId: Sharedid doesnt exists, new cookie creation');
ajax(ID_SVC, idGenerationCallback(callback), undefined, {method: 'GET', withCredentials: true});
ajax(sharedIdUrl(consentData), idGenerationCallback(callback), undefined, {method: 'GET', withCredentials: true});
};
return {callback: resp};
},

/**
* performs actions even if the id exists and returns a value
* @param config
* @param consentData
* @param storedId
* @returns {{callback: *}}
*/
extendId(config, storedId) {
extendId(config, consentData, storedId) {
const configParams = (config && config.params) || {};
utils.logInfo('SharedId: Existing shared id ' + storedId.id);
const resp = function (callback) {
Expand All @@ -329,7 +342,7 @@ export const sharedIdSubmodule = {
const sharedIdPayload = {};
sharedIdPayload.sharedId = storedId.id;
const payloadString = JSON.stringify(sharedIdPayload);
ajax(ID_SVC, existingIdCallback(storedId, callback), payloadString, {method: 'POST', withCredentials: true});
ajax(sharedIdUrl(consentData), existingIdCallback(storedId, callback), payloadString, {method: 'POST', withCredentials: true});
}
};
return {callback: resp};
Expand Down
3 changes: 2 additions & 1 deletion modules/userId/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
* It's permissible to return neither, one, or both fields.
* @name Submodule#extendId
* @param {SubmoduleConfig} config
* @param {ConsentData|undefined} consentData
* @param {Object} storedId - existing id, if any
* @return {(IdResponse|function(callback:function))} A response object that contains id and/or callback.
*/
Expand Down Expand Up @@ -621,7 +622,7 @@ function populateSubmoduleId(submodule, consentData, storedConsentData, forceRef
response = submodule.submodule.getId(submodule.config, consentData, storedId);
} else if (typeof submodule.submodule.extendId === 'function') {
// If the id exists already, give submodule a chance to decide additional actions that need to be taken
response = submodule.submodule.extendId(submodule.config, storedId);
response = submodule.submodule.extendId(submodule.config, consentData, storedId);
}

if (utils.isPlainObject(response)) {
Expand Down
55 changes: 55 additions & 0 deletions test/spec/modules/sharedIdSystem_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {
sharedIdSubmodule,
} from 'modules/sharedIdSystem.js';
import { server } from 'test/mocks/xhr.js';

let expect = require('chai').expect;

describe('SharedId System', function() {
const SHAREDID_RESPONSE = {sharedId: 'testsharedid'};

describe('Xhr Requests from getId()', function() {
let callbackSpy = sinon.spy();

beforeEach(function() {
callbackSpy.resetHistory();
});

afterEach(function () {

});

it('should call shared id endpoint without consent data and handle a valid response', function () {
let submoduleCallback = sharedIdSubmodule.getId(undefined, undefined).callback;
submoduleCallback(callbackSpy);

let request = server.requests[0];
expect(request.url).to.equal('https://id.sharedid.org/id');
expect(request.withCredentials).to.be.true;

request.respond(200, {}, JSON.stringify(SHAREDID_RESPONSE));

expect(callbackSpy.calledOnce).to.be.true;
expect(callbackSpy.lastCall.lastArg.id).to.equal(SHAREDID_RESPONSE.sharedId);
});

it('should call shared id endpoint with consent data and handle a valid response', function () {
let consentData = {
gdprApplies: true,
consentString: 'abc12345234',
};

let submoduleCallback = sharedIdSubmodule.getId(undefined, consentData).callback;
submoduleCallback(callbackSpy);

let request = server.requests[0];
expect(request.url).to.equal('https://id.sharedid.org/id?gdpr=1&gdpr_consent=abc12345234');
expect(request.withCredentials).to.be.true;

request.respond(200, {}, JSON.stringify(SHAREDID_RESPONSE));

expect(callbackSpy.calledOnce).to.be.true;
expect(callbackSpy.lastCall.lastArg.id).to.equal(SHAREDID_RESPONSE.sharedId);
});
});
});
53 changes: 53 additions & 0 deletions test/spec/modules/userId_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2037,6 +2037,8 @@ describe('User ID', function () {
coreStorage.setCookie('pubcid_sharedid', '', EXPIRED_COOKIE_DATE);
coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE);
coreStorage.setCookie('_parrable_eid', '', EXPIRED_COOKIE_DATE);
resetConsentData();
delete window.__tcfapi;
});

it('pubcid callback with url', function () {
Expand Down Expand Up @@ -2171,6 +2173,57 @@ describe('User ID', function () {
expect(server.requests[0].url).to.equal('https://id.sharedid.org/id');
expect(coreStorage.getCookie('pubcid_sharedid')).to.be.null;
});

it('verify sharedid called with consent data when gdpr applies', function () {
let adUnits = [getAdUnitMock()];
let customCfg = getConfigMock(['pubCommonId', 'pubcid', 'cookie']);
let consentConfig = {
cmpApi: 'iab',
timeout: 7500,
allowAuctionWithoutConsent: false
};
customCfg = addConfig(customCfg, 'params', {pixelUrl: '/any/pubcid/url', enableSharedId: true});

server.respondWith('https://id.sharedid.org/id?gdpr=1&gdpr_consent=abc12345234', function(xhr) {
xhr.respond(200, {}, '{"sharedId":"testsharedid"}');
});
server.respondImmediately = true;

let testConsentData = {
tcString: 'abc12345234',
gdprApplies: true,
purposeOneTreatment: false,
eventStatus: 'tcloaded',
vendor: {consents: {887: true}},
purpose: {
consents: {
1: true
}
}
};

window.__tcfapi = function () { };
sinon.stub(window, '__tcfapi').callsFake((...args) => {
args[2](testConsentData, true);
});

setSubmoduleRegistry([pubCommonIdSubmodule]);
init(config);
config.setConfig(customCfg);
setConsentConfig(consentConfig);

consentManagementRequestBidsHook(() => {
}, {});
requestBidsHook((config) => {
}, {adUnits});

expect(utils.triggerPixel.called).to.be.false;
events.emit(CONSTANTS.EVENTS.AUCTION_END, {});
expect(utils.triggerPixel.getCall(0).args[0]).to.include('/any/pubcid/url');

expect(server.requests[0].url).to.equal('https://id.sharedid.org/id?gdpr=1&gdpr_consent=abc12345234');
expect(coreStorage.getCookie('pubcid_sharedid')).to.equal('testsharedid');
});
});

describe('Set cookie behavior', function () {
Expand Down

0 comments on commit 727bf20

Please sign in to comment.