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

🐛Check for consent before writing ADCID. #39880

Merged
merged 6 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
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
75 changes: 74 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,78 @@ 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, gdprApplies, consentString, purposeOne, hasStorageConsent} 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 @@
}

/** @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 @@
(tryParseJson(this.element.getAttribute('json')) || {})['targeting']
),
ADCID: (opt_timeout) =>
getOrCreateAdCid(
this.getAmpDoc(),
'AMP_ECID_GOOGLE',
'_ga',
parseInt(opt_timeout, 10)
),
hasStorageConsent

Check warning on line 986 in extensions/amp-ad-network-doubleclick-impl/0.1/amp-ad-network-doubleclick-impl.js

View check run for this annotation

Codecov / codecov/patch

extensions/amp-ad-network-doubleclick-impl/0.1/amp-ad-network-doubleclick-impl.js#L986

Added line #L986 was not covered by tests
? 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,19 @@ 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 +443,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 +469,7 @@ 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 +480,23 @@ 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 +508,7 @@ 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 +527,7 @@ 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 +545,7 @@ 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,7 @@ 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 +157,16 @@ 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
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import '#service/real-time-config/real-time-config-impl';
import {CONSENT_POLICY_STATE} from '#core/constants/consent-state';
import {Deferred} from '#core/data-structures/promise';
import {domFingerprintPlain} from '#core/dom/fingerprint';
import {getPageLayoutBoxBlocking} from '#core/dom/layout/page-layout-box';
Expand Down Expand Up @@ -191,7 +192,7 @@ export class AmpAdNetworkValueimpressionImpl extends AmpA4A {
/** @override */
getAdUrl(opt_consentTuple, opt_rtcResponsesPromise, opt_serveNpaSignal) {
const consentTuple = opt_consentTuple || {};
const {consentString, gdprApplies} = consentTuple;
const {consentState, consentString, gdprApplies, purposeOne} = consentTuple;
const {win} = this;
const ampDoc = this.getAmpDoc();

Expand Down Expand Up @@ -229,10 +230,17 @@ export class AmpAdNetworkValueimpressionImpl extends AmpA4A {
});
const startTime = Date.now();

const hasStorageConsent =
consentState != CONSENT_POLICY_STATE.UNKNOWN &&
consentState != CONSENT_POLICY_STATE.INSUFFICIENT &&
((gdprApplies && consentString && purposeOne) || !gdprApplies);

Promise.all([
rtcParamsPromise,
referrerPromise,
getOrCreateAdCid(ampDoc, 'AMP_ECID_GOOGLE', '_ga'),
hasStorageConsent
? getOrCreateAdCid(ampDoc, 'AMP_ECID_GOOGLE', '_ga')
: Promise.resolve(undefined),
]).then((results) => {
const clientId = results[2];
const referrer = results[1];
Expand Down
Loading