Skip to content

Commit

Permalink
šŸ›Check for consent before writing ADCID. (#39880)
Browse files Browse the repository at this point in the history
* Check for consent before writing ADCID.

* Add unit tests and fix some errors in code

* Fix comment in test

* Lint fixes
  • Loading branch information
zombifier authored Apr 9, 2024
1 parent c8fe081 commit ed24d5f
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 32 deletions.
16 changes: 14 additions & 2 deletions extensions/amp-a4a/0.1/amp-a4a.js
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,9 @@ export class AmpA4A extends AMP.BaseElement {
const consentStringType = consentMetadata
? consentMetadata['consentStringType']
: consentMetadata;
const purposeOne = consentMetadata
? consentMetadata['purposeOne']
: consentMetadata;
const gppSectionId = consentMetadata
? consentMetadata['gppSectionId']
: consentMetadata;
Expand All @@ -802,6 +805,7 @@ export class AmpA4A extends AMP.BaseElement {
gdprApplies,
additionalConsent,
consentSharedData,
purposeOne,
gppSectionId,
},
this.tryExecuteRealTimeConfig_(
Expand Down Expand Up @@ -2404,6 +2408,13 @@ export class AmpA4A extends AMP.BaseElement {
* @return {Promise<!Array<!rtcResponseDef>>|undefined}
*/
tryExecuteRealTimeConfig_(consentState, consentString, consentMetadata) {
const hasStorageConsent =
consentState != CONSENT_POLICY_STATE.UNKNOWN &&
consentState != CONSENT_POLICY_STATE.INSUFFICIENT &&
((consentMetadata?.gdprApplies &&
consentString &&
consentMetadata?.purposeOne) ||
!consentMetadata?.gdprApplies);
if (this.element.getAttribute('rtc-config')) {
installRealTimeConfigServiceForDoc(this.getAmpDoc());
return this.getBlockRtc_().then((shouldBlock) =>
Expand All @@ -2413,7 +2424,7 @@ export class AmpA4A extends AMP.BaseElement {
(realTimeConfig) =>
realTimeConfig.maybeExecuteRealTimeConfig(
this.element,
this.getCustomRealTimeConfigMacros_(),
this.getCustomRealTimeConfigMacros_(hasStorageConsent),
consentState,
consentString,
consentMetadata,
Expand All @@ -2427,10 +2438,11 @@ export class AmpA4A extends AMP.BaseElement {
/**
* To be overriden by network impl. Should return a mapping of macro keys
* to values for substitution in publisher-specified URLs for RTC.
* @param {?boolean} unusedHasStorageConsent
* @return {!Object<string,
* !../../../src/service/variable-source.AsyncResolverDef>}
*/
getCustomRealTimeConfigMacros_() {
getCustomRealTimeConfigMacros_(unusedHasStorageConsent) {
return {};
}

Expand Down
93 changes: 92 additions & 1 deletion extensions/amp-a4a/0.1/test/test-amp-a4a.js
Original file line number Diff line number Diff line change
Expand Up @@ -2811,6 +2811,7 @@ describes.realWin('amp-a4a', {amp: true}, (env) => {
gdprApplies,
'consentStringType': 1,
'additionalConsent': 'abc123',
purposeOne: true,
'gppSectionId': '1,2',
};
consentSharedData = {
Expand Down Expand Up @@ -2858,6 +2859,7 @@ describes.realWin('amp-a4a', {amp: true}, (env) => {
consentStringType: consentMetadata['consentStringType'],
additionalConsent: consentMetadata['additionalConsent'],
consentSharedData,
purposeOne: consentMetadata['purposeOne'],
gppSectionId: consentMetadata['gppSectionId'],
})
).calledOnce;
Expand Down Expand Up @@ -2915,6 +2917,7 @@ describes.realWin('amp-a4a', {amp: true}, (env) => {
consentStringType: consentMetadata['consentStringType'],
additionalConsent: consentMetadata['additionalConsent'],
consentSharedData,
purposeOne: consentMetadata['purposeOne'],
gppSectionId: consentMetadata['gppSectionId'],
})
).calledOnce;
Expand Down Expand Up @@ -2966,6 +2969,7 @@ describes.realWin('amp-a4a', {amp: true}, (env) => {
gdprApplies: null,
additionalConsent: null,
consentSharedData: null,
purposeOne: null,
gppSectionId: null,
})
).calledOnce;
Expand Down Expand Up @@ -3537,9 +3541,96 @@ describes.realWin('AmpA4a-RTC', {amp: true}, (env) => {
});
});

describe('#tryExecuteRealTimeConfig storage consent test', () => {
let getCustomRealTimeConfigMacrosSpy;

beforeEach(() => {
element.setAttribute('rtc-config', true);
getCustomRealTimeConfigMacrosSpy = env.sandbox.spy(
a4a,
'getCustomRealTimeConfigMacros_'
);
});

for (const {
consentState,
consentString,
gdprApplies,
hasStorageConsent,
purposeOne,
} of [
// Unknown consent
{
consentState: CONSENT_POLICY_STATE.UNKNOWN,
gdprApplies: true,
consentString: 'string',
purposeOne: true,
hasStorageConsent: false,
},
// Insufficient consent
{
consentState: CONSENT_POLICY_STATE.INSUFFICIENT,
gdprApplies: true,
consentString: 'string',
purposeOne: true,
hasStorageConsent: false,
},
// GDPR doesn't apply
{
consentState: CONSENT_POLICY_STATE.SUFFICIENT,
gdprApplies: false,
consentString: '',
purposeOne: false,
hasStorageConsent: true,
},
// No consent string
{
consentState: CONSENT_POLICY_STATE.SUFFICIENT,
gdprApplies: true,
consentString: '',
purposeOne: true,
hasStorageConsent: false,
},
// no purpose one consent
{
consentState: CONSENT_POLICY_STATE.SUFFICIENT,
gdprApplies: true,
consentString: 'string',
purposeOne: false,
hasStorageConsent: false,
},
// GDPR applies and all prerequisite satisfied
{
consentState: CONSENT_POLICY_STATE.SUFFICIENT,
gdprApplies: true,
consentString: 'string',
purposeOne: true,
hasStorageConsent: true,
},
]) {
it(
`storageConsent test - consentState=${consentState}, ` +
`consentString=${consentString}, gdprApplies=${gdprApplies},` +
`purposeOne=${purposeOne} -> hasStorageConsent=${hasStorageConsent}`,
() => {
return a4a
.tryExecuteRealTimeConfig_(consentState, consentString, {
gdprApplies,
purposeOne,
})
.then(() => {
expect(getCustomRealTimeConfigMacrosSpy).to.be.calledWith(
hasStorageConsent
);
});
}
);
}
});

describe('#getCustomRealTimeConfigMacros_', () => {
it('should return empty object', () => {
expect(a4a.getCustomRealTimeConfigMacros_()).to.deep.equal({});
expect(a4a.getCustomRealTimeConfigMacros_(true)).to.deep.equal({});
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -955,7 +955,7 @@ export class AmpAdNetworkDoubleclickImpl extends AmpA4A {
}

/** @override */
getCustomRealTimeConfigMacros_() {
getCustomRealTimeConfigMacros_(hasStorageConsent) {
/**
* This lists allowed attributes on the amp-ad element to be used as
* macros for constructing the RTC URL. Add attributes here, in lowercase,
Expand Down Expand Up @@ -983,12 +983,14 @@ export class AmpAdNetworkDoubleclickImpl extends AmpA4A {
(tryParseJson(this.element.getAttribute('json')) || {})['targeting']
),
ADCID: (opt_timeout) =>
getOrCreateAdCid(
this.getAmpDoc(),
'AMP_ECID_GOOGLE',
'_ga',
parseInt(opt_timeout, 10)
),
hasStorageConsent
? getOrCreateAdCid(
this.getAmpDoc(),
'AMP_ECID_GOOGLE',
'_ga',
parseInt(opt_timeout, 10)
)
: Promise.resolve(undefined),
ATTR: (name) => {
if (!allowlist[name.toLowerCase()]) {
dev().warn(TAG, `Invalid attribute ${name}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,8 +392,7 @@ describes.realWin('DoubleClick Fast Fetch RTC', {amp: true}, (env) => {
});

describe('getCustomRealTimeConfigMacros', () => {
// TODO(bradfrizzell, #18574): Fix failing referrer check and re-enable.
it.skip('should return correct macros', () => {
it('should return correct macros', () => {
const macros = {
'data-slot': '5678',
'height': '50',
Expand All @@ -419,18 +418,21 @@ describes.realWin('DoubleClick Fast Fetch RTC', {amp: true}, (env) => {
'json': JSON.stringify(json),
});
env.win.document.body.appendChild(element);
env.sandbox.defineProperty(env.win.document, 'referrer', {
value: 'https://www.google.com/',
});
// TODO(bradfrizzell, #18574): Fix failing referrer check and re-enable.
//env.sandbox.defineProperty(env.win.document, 'referrer', {
// value: 'https://www.google.com/',
//});
const docInfo = Services.documentInfoForDoc(element);
impl = new AmpAdNetworkDoubleclickImpl(
element,
env.win.document,
env.win
);
const docViewport = Services.viewportForDoc(this.getAmpDoc());
const docViewport = Services.viewportForDoc(impl.getAmpDoc());
impl.populateAdUrlState();
const customMacros = impl.getCustomRealTimeConfigMacros_();
const customMacros = impl.getCustomRealTimeConfigMacros_(
/*hasStorageConsent=*/ true
);
expect(customMacros.PAGEVIEWID()).to.equal(docInfo.pageViewId);
expect(customMacros.PAGEVIEWID_64()).to.equal(docInfo.pageViewId64);
expect(customMacros.HREF()).to.equal(env.win.location.href);
Expand All @@ -443,7 +445,7 @@ describes.realWin('DoubleClick Fast Fetch RTC', {amp: true}, (env) => {
docViewport.getScrollHeight()
);
expect(customMacros.BKG_STATE()).to.equal(
this.getAmpDoc().isVisible() ? 'visible' : 'hidden'
impl.getAmpDoc().isVisible() ? 'visible' : 'hidden'
);
Object.keys(macros).forEach((macro) => {
expect(customMacros.ATTR(macro)).to.equal(macros[macro]);
Expand All @@ -469,7 +471,9 @@ describes.realWin('DoubleClick Fast Fetch RTC', {amp: true}, (env) => {
env.win
);
impl.populateAdUrlState();
const customMacros = impl.getCustomRealTimeConfigMacros_();
const customMacros = impl.getCustomRealTimeConfigMacros_(
/*hasStorageConsent=*/ true
);
let adcid;
return customMacros.ADCID().then((adcid1) => {
adcid = adcid1;
Expand All @@ -480,6 +484,25 @@ describes.realWin('DoubleClick Fast Fetch RTC', {amp: true}, (env) => {
});
});

it('should not set adcid if no storage consent', () => {
element = createElementWithAttributes(env.win.document, 'amp-ad', {
type: 'doubleclick',
});
env.win.document.body.appendChild(element);
impl = new AmpAdNetworkDoubleclickImpl(
element,
env.win.document,
env.win
);
impl.populateAdUrlState();
const customMacros = impl.getCustomRealTimeConfigMacros_(
/*hasStorageConsent=*/ false
);
return customMacros.ADCID().then((adcid) => {
expect(adcid).to.be.undefined;
});
});

it('should respect timeout for adcid', () => {
element = createElementWithAttributes(env.win.document, 'amp-ad', {
type: 'doubleclick',
Expand All @@ -491,7 +514,9 @@ describes.realWin('DoubleClick Fast Fetch RTC', {amp: true}, (env) => {
env.win
);
impl.populateAdUrlState();
const customMacros = impl.getCustomRealTimeConfigMacros_();
const customMacros = impl.getCustomRealTimeConfigMacros_(
/*hasStorageConsent=*/ true
);
return customMacros.ADCID(0).then((adcid) => {
expect(adcid).to.be.undefined;
});
Expand All @@ -510,7 +535,9 @@ describes.realWin('DoubleClick Fast Fetch RTC', {amp: true}, (env) => {
impl.populateAdUrlState();
const viewer = Services.viewerForDoc(impl.getAmpDoc());
env.sandbox.stub(viewer, 'getReferrerUrl').returns(new Promise(() => {}));
const customMacros = impl.getCustomRealTimeConfigMacros_();
const customMacros = impl.getCustomRealTimeConfigMacros_(
/*hasStorageConsent=*/ true
);
return expect(customMacros.REFERRER(0)).to.eventually.be.undefined;
});

Expand All @@ -528,7 +555,9 @@ describes.realWin('DoubleClick Fast Fetch RTC', {amp: true}, (env) => {
env.win
);
impl.populateAdUrlState();
const customMacros = impl.getCustomRealTimeConfigMacros_();
const customMacros = impl.getCustomRealTimeConfigMacros_(
/*hasStorageConsent=*/ true
);
expect(customMacros.TGT()).to.equal(JSON.stringify(json['targeting']));
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export class AmpAdNetworkSmartadserverImpl extends AmpA4A {
}

/** @override */
getCustomRealTimeConfigMacros_() {
getCustomRealTimeConfigMacros_(hasStorageConsent) {
const allowed = {
'width': true,
'height': true,
Expand All @@ -156,12 +156,14 @@ export class AmpAdNetworkSmartadserverImpl extends AmpA4A {
(tryParseJson(this.element.getAttribute('json')) || {})['targeting']
),
ADCID: (opt_timeout) =>
getOrCreateAdCid(
this.getAmpDoc(),
'AMP_ECID_GOOGLE',
'_ga',
parseInt(opt_timeout, 10)
),
hasStorageConsent
? getOrCreateAdCid(
this.getAmpDoc(),
'AMP_ECID_GOOGLE',
'_ga',
parseInt(opt_timeout, 10)
)
: Promise.resolve(undefined),
ATTR: (name) => {
if (!allowed[name]) {
dev().warn(TAG, `Invalid attribute ${name}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@ describes.realWin('amp-ad-network-smartadserver-impl', realWinConfig, (env) => {

impl = new AmpAdNetworkSmartadserverImpl(element, env.win.doc, win);
const docInfo = Services.documentInfoForDoc(element);
const customMacros = impl.getCustomRealTimeConfigMacros_();
const customMacros = impl.getCustomRealTimeConfigMacros_(
/*hasStorageConsent=*/ false
);

expect(customMacros.PAGEVIEWID()).to.equal(docInfo.pageViewId);
expect(customMacros.PAGEVIEWID_64()).to.equal(docInfo.pageViewId64);
Expand Down Expand Up @@ -157,6 +159,18 @@ describes.realWin('amp-ad-network-smartadserver-impl', realWinConfig, (env) => {
);
expect(customMacros.ATTR('not-allowed')).to.equal('');
});

it('should not set adcid if no storage consent', () => {
element = createElementWithAttributes(doc, 'amp-ad');
impl = new AmpAdNetworkSmartadserverImpl(element);
const customMacros = impl.getCustomRealTimeConfigMacros_(
/*hasStorageConsent=*/ false
);

return customMacros.ADCID().then((adcid) => {
expect(adcid).to.be.undefined;
});
});
});

describe('getAdUrl', () => {
Expand Down
Loading

0 comments on commit ed24d5f

Please sign in to comment.