Skip to content

Commit

Permalink
gpdrEnforcement : add purpose 4 check for transmitUfpd (#10199)
Browse files Browse the repository at this point in the history
* gdprEnforcement: add purpose 4 enforcement

* Guard additional user properties behind transmitUfpd
  • Loading branch information
dgirardi authored Jul 11, 2023
1 parent adfe8a7 commit 7e11273
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 24 deletions.
44 changes: 23 additions & 21 deletions modules/gdprEnforcement.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,16 @@ import {
ACTIVITY_ENRICH_EIDS,
ACTIVITY_FETCH_BIDS,
ACTIVITY_REPORT_ANALYTICS,
ACTIVITY_SYNC_USER
ACTIVITY_SYNC_USER, ACTIVITY_TRANSMIT_UFPD
} from '../src/activities/activities.js';

export const STRICT_STORAGE_ENFORCEMENT = 'strictStorageEnforcement';

const TCF2 = {
'purpose1': {id: 1, name: 'storage'},
'purpose2': {id: 2, name: 'basicAds'},
'purpose7': {id: 7, name: 'measurement'}
purpose1: {id: 1, name: 'storage'},
purpose2: {id: 2, name: 'basicAds'},
purpose4: {id: 4, name: 'personalizedAds'},
purpose7: {id: 7, name: 'measurement'},
};

/*
Expand All @@ -55,13 +56,15 @@ const DEFAULT_RULES = [{

export let purpose1Rule;
export let purpose2Rule;
export let purpose4Rule;
export let purpose7Rule;

export let enforcementRules;

const storageBlocked = new Set();
const biddersBlocked = new Set();
const analyticsBlocked = new Set();
const ufpdBlocked = new Set();

let hooksAdded = false;
let strictStorageEnforcement = false;
Expand Down Expand Up @@ -232,6 +235,8 @@ export const fetchBidsRule = ((rule) => {

export const reportAnalyticsRule = gdprRule(7, () => purpose7Rule, analyticsBlocked, (params) => getGvlidFromAnalyticsAdapter(params[ACTIVITY_PARAM_COMPONENT_NAME], params[ACTIVITY_PARAM_ANL_CONFIG]));

export const transmitUfpdRule = gdprRule(4, () => purpose4Rule, ufpdBlocked);

/**
* Compiles the TCF2.0 enforcement results into an object, which is emitted as an event payload to "tcf2Enforcement" event.
*/
Expand All @@ -243,27 +248,20 @@ function emitTCF2FinalResults() {
const tcf2FinalResults = {
storageBlocked: formatSet(storageBlocked),
biddersBlocked: formatSet(biddersBlocked),
analyticsBlocked: formatSet(analyticsBlocked)
analyticsBlocked: formatSet(analyticsBlocked),
ufpdBlocked: formatSet(ufpdBlocked),
};

events.emit(CONSTANTS.EVENTS.TCF2_ENFORCEMENT, tcf2FinalResults);
[storageBlocked, biddersBlocked, analyticsBlocked].forEach(el => el.clear());
[storageBlocked, biddersBlocked, analyticsBlocked, ufpdBlocked].forEach(el => el.clear());
}

events.on(CONSTANTS.EVENTS.AUCTION_END, emitTCF2FinalResults);

/*
Set of callback functions used to detect presence of a TCF rule, passed as the second argument to find().
*/
const hasPurpose1 = (rule) => {
return rule.purpose === TCF2.purpose1.name;
};
const hasPurpose2 = (rule) => {
return rule.purpose === TCF2.purpose2.name;
};
const hasPurpose7 = (rule) => {
return rule.purpose === TCF2.purpose7.name;
};
function hasPurpose(purposeNo) {
const pname = TCF2[`purpose${purposeNo}`].name;
return (rule) => rule.purpose === pname;
}

/**
* A configuration function that initializes some module variables, as well as adds hooks
Expand All @@ -279,9 +277,10 @@ export function setEnforcementConfig(config) {
}
strictStorageEnforcement = !!deepAccess(config, STRICT_STORAGE_ENFORCEMENT);

purpose1Rule = find(enforcementRules, hasPurpose1);
purpose2Rule = find(enforcementRules, hasPurpose2);
purpose7Rule = find(enforcementRules, hasPurpose7);
purpose1Rule = find(enforcementRules, hasPurpose(1));
purpose2Rule = find(enforcementRules, hasPurpose(2));
purpose4Rule = find(enforcementRules, hasPurpose(4))
purpose7Rule = find(enforcementRules, hasPurpose(7));

if (!purpose1Rule) {
purpose1Rule = DEFAULT_RULES[0];
Expand All @@ -301,6 +300,9 @@ export function setEnforcementConfig(config) {
if (purpose2Rule) {
RULE_HANDLES.push(registerActivityControl(ACTIVITY_FETCH_BIDS, RULE_NAME, fetchBidsRule));
}
if (purpose4Rule) {
RULE_HANDLES.push(registerActivityControl(ACTIVITY_TRANSMIT_UFPD, RULE_NAME, transmitUfpdRule));
}
if (purpose7Rule) {
RULE_HANDLES.push(registerActivityControl(ACTIVITY_REPORT_ANALYTICS, RULE_NAME, reportAnalyticsRule));
}
Expand Down
2 changes: 1 addition & 1 deletion src/activities/redactor.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
ACTIVITY_TRANSMIT_UFPD
} from './activities.js';

export const ORTB_UFPD_PATHS = ['user.data', 'user.ext.data'];
export const ORTB_UFPD_PATHS = ['user.data', 'user.ext.data', 'user.yob', 'user.gender', 'user.keywords', 'user.kwarray'];
export const ORTB_EIDS_PATHS = ['user.eids', 'user.ext.eids'];
export const ORTB_GEO_PATHS = ['user.geo.lat', 'user.geo.lon', 'device.geo.lat', 'device.geo.lon'];

Expand Down
45 changes: 43 additions & 2 deletions test/spec/modules/gdprEnforcement_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
reportAnalyticsRule,
setEnforcementConfig,
STRICT_STORAGE_ENFORCEMENT,
syncUserRule,
syncUserRule, transmitUfpdRule,
validateRules
} from 'modules/gdprEnforcement.js';
import {config} from 'src/config.js';
Expand Down Expand Up @@ -462,6 +462,46 @@ describe('gdpr enforcement', function () {
});
});

describe('transmitUfpdRule', () => {
it('should allow when purpose 3 consent is given', () => {
setEnforcementConfig({
gdpr: {
rules: [{
purpose: 'personalizedAds',
enforcePurpose: true,
enforceVendor: true,
}]
}
});
Object.assign(gvlids, {
mockBidder: 123
});
const consent = setupConsentData();
consent.vendorData.purpose.consents[4] = true;
consent.vendorData.vendor.consents[123] = true;
expectAllow(true, transmitUfpdRule(activityParams(MODULE_TYPE_BIDDER, 'mockBidder')));
});

it('should return deny when purpose 4 consent is withheld', () => {
setEnforcementConfig({
gdpr: {
rules: [{
purpose: 'personalizedAds',
enforcePurpose: true,
enforceVendor: true,
}]
}
});
Object.assign(gvlids, {
mockBidder: 123
});
const consent = setupConsentData();
consent.vendorData.purpose.consents[4] = true;
consent.vendorData.vendor.consents[123] = false;
expectAllow(false, transmitUfpdRule(activityParams(MODULE_TYPE_BIDDER, 'mockBidder')))
});
});

describe('validateRules', function () {
const createGdprRule = (purposeName = 'storage', enforcePurpose = true, enforceVendor = true, vendorExceptions = [], softVendorExceptions = []) => ({
purpose: purposeName,
Expand Down Expand Up @@ -599,7 +639,8 @@ describe('gdpr enforcement', function () {
Object.entries({
'storage': 1,
'basicAds': 2,
'measurement': 7
'measurement': 7,
'personalizedAds': 4,
}).forEach(([purpose, purposeNo]) => {
describe(`for purpose ${purpose}`, () => {
const rule = createGdprRule(purpose);
Expand Down

0 comments on commit 7e11273

Please sign in to comment.