Skip to content

Commit

Permalink
✨ I2I: <amp-consent> Allow receiving TCF 2.2 consent string via TCF p…
Browse files Browse the repository at this point in the history
…ostmessage API (new TCF policy version support) (#39411)

* feat: implement code to support new tcf policy version

feat: implement new logic to support new tcf policy versions

fix

feat: remove tcf policy version

fix

fix

Revert "feat: remove tcf policy version"

This reverts commit cedc20d.

Revert "feat: implement new logic to support new tcf policy versions"

This reverts commit eb2f1ec.

Revert "test: implement test to guarantee no bc and support new tcf policy version"

This reverts commit e3dfe73.

feat: implement tcfPolicyVersion as optional data

feat: impl client tcf policy version propagation

fix: implementation and docs

test: update tests and implement more

* fix: PR review tcf policy version on minimal ping
  • Loading branch information
marco-prontera authored Sep 29, 2023
1 parent f667ef8 commit 93c1e31
Show file tree
Hide file tree
Showing 13 changed files with 349 additions and 50 deletions.
66 changes: 56 additions & 10 deletions extensions/amp-consent/0.1/amp-consent.js
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ export class AmpConsent extends AMP.BaseElement {
}

let consentString;
let tcfPolicyVersion;
let metadata;
const data = getData(event);

Expand Down Expand Up @@ -337,6 +338,9 @@ export class AmpConsent extends AMP.BaseElement {
data['info'] = undefined;
}
consentString = data['info'];
tcfPolicyVersion = this.validateTCFPolicyVersion_(
data['tcfPolicyVersion']
);
metadata = this.validateMetadata_(data['consentMetadata']);
}

Expand All @@ -362,7 +366,7 @@ export class AmpConsent extends AMP.BaseElement {
purposeConsents
);
}
this.handleAction_(action, consentString, metadata);
this.handleAction_(action, consentString, metadata, tcfPolicyVersion);
}
}
});
Expand Down Expand Up @@ -455,8 +459,14 @@ export class AmpConsent extends AMP.BaseElement {
* @param {string} action
* @param {string=} consentString
* @param {!ConsentMetadataDef=} opt_consentMetadata
* @param {number=} opt_tcfPolicyVersion
*/
handleAction_(action, consentString, opt_consentMetadata) {
handleAction_(
action,
consentString,
opt_consentMetadata,
opt_tcfPolicyVersion
) {
const setDirtyBitPromise =
isEnumValue(ACTION_TYPE, action) &&
this.consentConfig_['clearDirtyBitOnResponse_dontUseThisItMightBeRemoved']
Expand All @@ -466,7 +476,8 @@ export class AmpConsent extends AMP.BaseElement {
this.handleActionAfterClearingDirtyBit_(
action,
consentString,
opt_consentMetadata
opt_consentMetadata,
opt_tcfPolicyVersion
);
});
}
Expand All @@ -475,11 +486,13 @@ export class AmpConsent extends AMP.BaseElement {
* @param {string} action
* @param {string=} consentString
* @param {!ConsentMetadataDef=} opt_consentMetadata
* @param {number=} opt_tcfPolicyVersion
*/
handleActionAfterClearingDirtyBit_(
action,
consentString,
opt_consentMetadata
opt_consentMetadata,
opt_tcfPolicyVersion
) {
this.consentStateChangedViaPromptUI_ = true;

Expand All @@ -488,14 +501,16 @@ export class AmpConsent extends AMP.BaseElement {
this.consentStateManager_.updateConsentInstanceState(
CONSENT_ITEM_STATE.ACCEPTED,
consentString,
opt_consentMetadata
opt_consentMetadata,
opt_tcfPolicyVersion
);
} else if (action == ACTION_TYPE.REJECT) {
// reject
this.consentStateManager_.updateConsentInstanceState(
CONSENT_ITEM_STATE.REJECTED,
consentString,
opt_consentMetadata
opt_consentMetadata,
opt_tcfPolicyVersion
);
} else if (action == ACTION_TYPE.DISMISS) {
this.consentStateManager_.updateConsentInstanceState(
Expand Down Expand Up @@ -671,7 +686,8 @@ export class AmpConsent extends AMP.BaseElement {
response['consentStateValue'],
response['consentString'] || undefined,
response['consentMetadata'],
response['purposeConsents']
response['purposeConsents'],
response['tcfPolicyVersion']
);
}
});
Expand All @@ -684,12 +700,14 @@ export class AmpConsent extends AMP.BaseElement {
* @param {?string=} responseConsentString
* @param {?JsonObject=} responseMetadata
* @param {?JsonObject=} responsePurposeConsents
* @param {number=} responseTcfPolicyVersion
*/
updateCacheIfNotNull_(
responseStateValue,
responseConsentString,
responseMetadata,
responsePurposeConsents
responsePurposeConsents,
responseTcfPolicyVersion
) {
const consentStateValue = convertEnumValueToState(responseStateValue);
// consentStateValue and consentString are treated as a pair that will update together
Expand All @@ -704,10 +722,12 @@ export class AmpConsent extends AMP.BaseElement {
responsePurposeConsents
);
}

this.consentStateManager_.updateConsentInstanceState(
consentStateValue,
responseConsentString,
this.validateMetadata_(responseMetadata)
this.validateMetadata_(responseMetadata),
responseTcfPolicyVersion
);
}
}
Expand Down Expand Up @@ -735,6 +755,7 @@ export class AmpConsent extends AMP.BaseElement {
'consentStateValue': getConsentStateValue(storedInfo['consentState']),
'consentMetadata': storedInfo['consentMetadata'],
'consentString': storedInfo['consentString'],
'tcfPolicyVersion': storedInfo['tcfPolicyVersion'],
'isDirty': !!storedInfo['isDirty'],
'matchedGeoGroup': this.matchedGeoGroup_,
'purposeConsents': storedInfo['purposeConsents'],
Expand Down Expand Up @@ -945,7 +966,32 @@ export class AmpConsent extends AMP.BaseElement {
}

/**
* Convert valid opt_metadta into ConsentMetadataDef
* Check if the opt_tcfPolicyVersion provided is a valid number to use.
* @param {number=} opt_tcfPolicyVersion
* @return {number|undefined}
*/
validateTCFPolicyVersion_(opt_tcfPolicyVersion) {
if (typeof opt_tcfPolicyVersion !== 'number') {
return;
}

if (
isNaN(opt_tcfPolicyVersion) ||
!isFinite(opt_tcfPolicyVersion) ||
opt_tcfPolicyVersion.toString().split('.').length > 1
) {
user().error(
TAG,
'CMP tcfPolicyVersion must be a valid number (integer).'
);
return;
}

return opt_tcfPolicyVersion;
}

/**
* Convert valid opt_metadata into ConsentMetadataDef
* @param {JsonObject=} opt_metadata
* @return {ConsentMetadataDef|undefined}
*/
Expand Down
32 changes: 23 additions & 9 deletions extensions/amp-consent/0.1/consent-info.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,24 @@ const TAG = 'amp-consent';

/**
* Key values for retriving/storing consent info object.
* STATE: Set when user accept or reject consent.
* VERSION: Set when a consent string is provided to store its version.
* STRING: Set when a consent string is used to store more granular consent info
* on vendors.
* STATE: Set when user accept or reject consent.
* PURPOSE_CONSENTS: Set when consents for purposes are passed in for client side
* granular consent. Only values ACCEPT and REJECT signals are stored.
* METADATA: Set when consent metadata is passed in to store more granular consent info
* on vendors.
* DITRYBIT: Set when the stored consent info need to be revoked next time.
* PURPOSE_CONSENTS: Set when consents for purposes are passed in for client side
* granular consent. Only values ACCEPT and REJECT signals are stored.
* @enum {string}
*/
export const STORAGE_KEY = {
STATE: 's',
VERSION: 'e',
STRING: 'r',
IS_DIRTY: 'd',
METADATA: 'm',
STATE: 's',
PURPOSE_CONSENTS: 'pc',
METADATA: 'm',
IS_DIRTY: 'd',
};

/**
Expand Down Expand Up @@ -81,6 +83,7 @@ export const TCF_POST_MESSAGE_API_COMMANDS = {
* consentMetadata: (ConsentMetadataDef|undefined),
* purposeConsents: ({[key: string]: PURPOSE_CONSENT_STATE}|undefined),
* isDirty: (boolean|undefined),
* tcfPolicyVersion: (number|undefined),
* }}
*/
export let ConsentInfoDef;
Expand Down Expand Up @@ -119,7 +122,8 @@ export function getStoredConsentInfo(value) {
value[STORAGE_KEY.STRING],
convertStorageMetadata(value[STORAGE_KEY.METADATA]),
value[STORAGE_KEY.PURPOSE_CONSENTS],
value[STORAGE_KEY.IS_DIRTY] && value[STORAGE_KEY.IS_DIRTY] === 1
value[STORAGE_KEY.IS_DIRTY] && value[STORAGE_KEY.IS_DIRTY] === 1,
value[STORAGE_KEY.VERSION]
);
}

Expand Down Expand Up @@ -180,6 +184,10 @@ export function composeStoreValue(consentInfo) {
obj[STORAGE_KEY.STRING] = consentInfo['consentString'];
}

if (consentInfo['tcfPolicyVersion']) {
obj[STORAGE_KEY.VERSION] = consentInfo['tcfPolicyVersion'];
}

if (consentInfo['isDirty'] === true) {
obj[STORAGE_KEY.IS_DIRTY] = 1;
}
Expand Down Expand Up @@ -248,12 +256,15 @@ export function isConsentInfoStoredValueSame(infoA, infoB, opt_isDirty) {
infoA['purposeConsents'],
infoB['purposeConsents']
);
const tcfPolicyVersionEqual =
infoA['tcfPolicyVersion'] == infoB['tcfPolicyVersion'];
return (
stateEqual &&
stringEqual &&
metadataEqual &&
purposeConsentsEqual &&
isDirtyEqual
isDirtyEqual &&
tcfPolicyVersionEqual
);
}
return false;
Expand All @@ -277,21 +288,24 @@ function getLegacyStoredConsentInfo(value) {
* @param {ConsentMetadataDef=} opt_consentMetadata
* @param {{[key: string]: PURPOSE_CONSENT_STATE}=} opt_purposeConsents
* @param {boolean=} opt_isDirty
* @param {number=} opt_tcfPolicyVersion
* @return {!ConsentInfoDef}
*/
export function constructConsentInfo(
consentState,
opt_consentString,
opt_consentMetadata,
opt_purposeConsents,
opt_isDirty
opt_isDirty,
opt_tcfPolicyVersion
) {
return {
'consentState': consentState,
'consentString': opt_consentString,
'consentMetadata': opt_consentMetadata,
'purposeConsents': opt_purposeConsents,
'isDirty': opt_isDirty,
'tcfPolicyVersion': opt_tcfPolicyVersion,
};
}

Expand Down
19 changes: 19 additions & 0 deletions extensions/amp-consent/0.1/consent-policy-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ export class ConsentPolicyManager {
/** @private {?string} */
this.consentString_ = null;

/** @private {?number} */
this.tcfPolicyVersion_ = null;

/** @private {?Object|undefined} */
this.consentMetadata_ = null;

Expand Down Expand Up @@ -179,15 +182,18 @@ export class ConsentPolicyManager {
consentStateChangeHandler_(info) {
const state = info['consentState'];
const consentStr = info['consentString'];
const tcfPolicyVersion = info['tcfPolicyVersion'];
const consentMetadata = info['consentMetadata'];
const purposeConsents = info['purposeConsents'];
const {
consentMetadata_: prevConsentMetadata,
consentString_: prevConsentStr,
purposeConsents_: prevPurposeConsents,
tcfPolicyVersion_: prevTCFPolicyVersion,
} = this;

this.consentString_ = consentStr;
this.tcfPolicyVersion_ = tcfPolicyVersion;
this.consentMetadata_ = consentMetadata;
this.purposeConsents_ = purposeConsents;
if (state === CONSENT_ITEM_STATE.UNKNOWN) {
Expand All @@ -210,6 +216,7 @@ export class ConsentPolicyManager {
}
// None of the supplementary consent data changes with dismiss action
this.consentString_ = prevConsentStr;
this.tcfPolicyVersion_ = prevTCFPolicyVersion;
this.consentMetadata_ = prevConsentMetadata;
this.purposeConsents_ = prevPurposeConsents;
} else {
Expand Down Expand Up @@ -305,6 +312,18 @@ export class ConsentPolicyManager {
});
}

/**
* Get the tcf policy version of a policy. Return a promise that resolves
* when the policy resolves.
* @param {string} policyId
* @return {!Promise<?number>}
*/
getTcfPolicyVersion(policyId) {
return this.whenPolicyResolved(policyId).then(() => {
return this.tcfPolicyVersion_;
});
}

/**
* Get the consent metadata value of a policy. Return a promise that resolves
* when the policy resolves.
Expand Down
Loading

0 comments on commit 93c1e31

Please sign in to comment.