UNPKG

@iabtechlabtcf/core

Version:

Ensures consistent encoding and decoding of TC Signals for the iab. Transparency and Consent Framework (TCF).

513 lines (512 loc) 17.3 kB
import { Cloneable } from './Cloneable.js'; import { TCModelError } from './errors/index.js'; import { GVL } from './GVL.js'; import { PurposeRestrictionVector, Vector } from './model/index.js'; export class TCModel extends Cloneable { /** * Set of available consent languages published by the IAB */ static consentLanguages = GVL.consentLanguages; isServiceSpecific_ = false; supportOOB_ = true; useNonStandardTexts_ = false; purposeOneTreatment_ = false; publisherCountryCode_ = 'AA'; version_ = 2; consentScreen_ = 0; policyVersion_ = 5; consentLanguage_ = 'EN'; cmpId_ = 0; cmpVersion_ = 0; vendorListVersion_ = 0; numCustomPurposes_ = 0; // Member Variable for GVL gvl_; created; lastUpdated; /** * The TCF designates certain Features as special, that is, a CMP must afford * the user a means to opt in to their use. These Special Features are * published and numbered in the GVL separately from normal Features. * Provides for up to 12 special features. */ specialFeatureOptins = new Vector(); /** * Renamed from `PurposesAllowed` in TCF v1.1 * The user’s consent value for each Purpose established on the legal basis * of consent. Purposes are published in the Global Vendor List (see. [[GVL]]). */ purposeConsents = new Vector(); /** * The user’s permission for each Purpose established on the legal basis of * legitimate interest. If the user has exercised right-to-object for a * purpose. */ purposeLegitimateInterests = new Vector(); /** * The user’s consent value for each Purpose established on the legal basis * of consent, for the publisher. Purposes are published in the Global * Vendor List. */ publisherConsents = new Vector(); /** * The user’s permission for each Purpose established on the legal basis of * legitimate interest. If the user has exercised right-to-object for a * purpose. */ publisherLegitimateInterests = new Vector(); /** * The user’s consent value for each Purpose established on the legal basis * of consent, for the publisher. Purposes are published in the Global * Vendor List. */ publisherCustomConsents = new Vector(); /** * The user’s permission for each Purpose established on the legal basis of * legitimate interest. If the user has exercised right-to-object for a * purpose that is established in the publisher's custom purposes. */ publisherCustomLegitimateInterests = new Vector(); /** * set by a publisher if they wish to collect consent and LI Transparency for * purposes outside of the TCF */ customPurposes; /** * Each [[Vendor]] is keyed by id. Their consent value is true if it is in * the Vector */ vendorConsents = new Vector(); /** * Each [[Vendor]] is keyed by id. Whether their Legitimate Interests * Disclosures have been established is stored as boolean. * see: [[Vector]] */ vendorLegitimateInterests = new Vector(); /** * The value included for disclosed vendors signals which vendors have been * disclosed to the user in the interface surfaced by the CMP. This section * content is required when writing a TC string to the global (consensu) * scope. When a CMP has read from and is updating a TC string from the * global consensu.org storage, the CMP MUST retain the existing disclosure * information and only add information for vendors that it has disclosed * that had not been disclosed by other CMPs in prior interactions with this * device/user agent. */ vendorsDisclosed = new Vector(); /** * Signals which vendors the publisher permits to use OOB legal bases. */ vendorsAllowed = new Vector(); publisherRestrictions = new PurposeRestrictionVector(); /** * Constructs the TCModel. Passing a [[GVL]] is optional when constructing * as this TCModel may be constructed from decoding an existing encoded * TCString. * * @param {GVL} [gvl] */ constructor(gvl) { super(); if (gvl) { this.gvl = gvl; } this.updated(); } /** * sets the [[GVL]] with side effects of also setting the `vendorListVersion`, `policyVersion`, and `consentLanguage` * @param {GVL} gvl */ set gvl(gvl) { /** * set the reference, but make sure it's our GVL wrapper class. */ if (!(GVL.isInstanceOf(gvl))) { gvl = new GVL(gvl); } this.gvl_ = gvl; this.publisherRestrictions.gvl = gvl; } /** * @return {GVL} the gvl instance set on this TCModel instance */ get gvl() { return this.gvl_; } /** * @param {number} integer - A unique ID will be assigned to each Consent * Manager Provider (CMP) from the iab. * * @throws {TCModelError} if the value is not an integer greater than 1 as those are not valid. */ set cmpId(integer) { integer = Number(integer); if (Number.isInteger(integer) && integer > 1) { this.cmpId_ = integer; } else { throw new TCModelError('cmpId', integer); } } get cmpId() { return this.cmpId_; } /** * Each change to an operating CMP should receive a * new version number, for logging proof of consent. CmpVersion defined by * each CMP. * * @param {number} integer * * @throws {TCModelError} if the value is not an integer greater than 1 as those are not valid. */ set cmpVersion(integer) { integer = Number(integer); if (Number.isInteger(integer) && integer > -1) { this.cmpVersion_ = integer; } else { throw new TCModelError('cmpVersion', integer); } } get cmpVersion() { return this.cmpVersion_; } /** * The screen number is CMP and CmpVersion * specific, and is for logging proof of consent.(For example, a CMP could * keep records so that a publisher can request information about the context * in which consent was gathered.) * * @param {number} integer * * @throws {TCModelError} if the value is not an integer greater than 0 as those are not valid. */ set consentScreen(integer) { integer = Number(integer); if (Number.isInteger(integer) && integer > -1) { this.consentScreen_ = integer; } else { throw new TCModelError('consentScreen', integer); } } get consentScreen() { return this.consentScreen_; } /** * @param {string} lang - [two-letter ISO 639-1 language * code](http://www.loc.gov/standards/iso639-2/php/code_list.php) in which * the CMP UI was presented * * @throws {TCModelError} if the value is not a length-2 string of alpha characters */ set consentLanguage(lang) { this.consentLanguage_ = lang; } get consentLanguage() { return this.consentLanguage_; } /** * @param {string} countryCode - [two-letter ISO 3166-1 alpha-2 country * code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the publisher, * determined by the CMP-settings of the publisher. * * @throws {TCModelError} if the value is not a length-2 string of alpha characters */ set publisherCountryCode(countryCode) { if (/^([A-z]){2}$/.test(countryCode)) { this.publisherCountryCode_ = countryCode.toUpperCase(); } else { throw new TCModelError('publisherCountryCode', countryCode); } } get publisherCountryCode() { return this.publisherCountryCode_; } /** * Version of the GVL used to create this TCModel. Global * Vendor List versions will be released periodically. * * @param {number} integer * * @throws {TCModelError} if the value is not an integer greater than 0 as those are not valid. */ set vendorListVersion(integer) { /** * first coerce to a number via leading '+' then take the integer value by * bitshifting to the right. This works on all types in JavaScript and if * it's not valid then value will be 0. */ integer = Number(integer) >> 0; if (integer < 0) { throw new TCModelError('vendorListVersion', integer); } else { this.vendorListVersion_ = integer; } } get vendorListVersion() { if (this.gvl) { return this.gvl.vendorListVersion; } else { return this.vendorListVersion_; } } /** * From the corresponding field in the GVL that was * used for obtaining consent. A new policy version invalidates existing * strings and requires CMPs to re-establish transparency and consent from * users. * * If a TCF policy version number is different from the one from the latest * GVL, the CMP must re-establish transparency and consent. * * @param {number} num - You do not need to set this. This comes * directly from the [[GVL]]. * */ set policyVersion(num) { this.policyVersion_ = parseInt(num, 10); if (this.policyVersion_ < 0) { throw new TCModelError('policyVersion', num); } } get policyVersion() { if (this.gvl) { return this.gvl.tcfPolicyVersion; } else { return this.policyVersion_; } } set version(num) { this.version_ = parseInt(num, 10); } get version() { return this.version_; } /** * Whether the signals encoded in this TC String were from site-specific * storage `true` versus ‘global’ consensu.org shared storage `false`. A * string intended to be stored in global/shared scope but the CMP is unable * to store due to a user agent not accepting third-party cookies would be * considered site-specific `true`. * * @param {boolean} bool - value to set. Some changes to other fields in this * model will automatically change this value like adding publisher * restrictions. */ set isServiceSpecific(bool) { this.isServiceSpecific_ = bool; } get isServiceSpecific() { return this.isServiceSpecific_; } /** * Non-standard stacks means that a CMP is using publisher-customized stack * descriptions. Stacks (in terms of purposes in a stack) are pre-set by the * IAB. As are titles. Descriptions are pre-set, but publishers can customize * them. If they do, they need to set this bit to indicate that they've * customized descriptions. * * @param {boolean} bool - value to set */ set useNonStandardTexts(bool) { this.useNonStandardTexts_ = bool; } get useNonStandardTexts() { return this.useNonStandardTexts_; } /** * Whether or not this publisher supports OOB signaling. On Global TC String * OOB Vendors Disclosed will be included if the publish wishes to no allow * these vendors they should set this to false. * @param {boolean} bool - value to set */ set supportOOB(bool) { this.supportOOB_ = bool; } get supportOOB() { return this.supportOOB_; } /** * `false` There is no special Purpose 1 status. * Purpose 1 was disclosed normally (consent) as expected by Policy. `true` * Purpose 1 not disclosed at all. CMPs use PublisherCC to indicate the * publisher’s country of establishment to help Vendors determine whether the * vendor requires Purpose 1 consent. In global scope TC strings, this field * must always have a value of `false`. When a CMP encounters a global scope * string with `purposeOneTreatment=true` then that string should be * considered invalid and the CMP must re-establish transparency and consent. * * @param {boolean} bool */ set purposeOneTreatment(bool) { this.purposeOneTreatment_ = bool; } get purposeOneTreatment() { return this.purposeOneTreatment_; } /** * setAllVendorConsents - sets all vendors on the GVL Consent (true) * * @return {void} */ setAllVendorConsents() { this.vendorConsents.set(this.gvl.vendors); } /** * unsetAllVendorConsents - unsets all vendors on the GVL Consent (false) * * @return {void} */ unsetAllVendorConsents() { this.vendorConsents.empty(); } /** * setAllVendorsDisclosed - sets all vendors on the GVL Vendors Disclosed (true) * * @return {void} */ setAllVendorsDisclosed() { this.vendorsDisclosed.set(this.gvl.vendors); } /** * unsetAllVendorsDisclosed - unsets all vendors on the GVL Consent (false) * * @return {void} */ unsetAllVendorsDisclosed() { this.vendorsDisclosed.empty(); } /** * setAllVendorsAllowed - sets all vendors on the GVL Consent (true) * * @return {void} */ setAllVendorsAllowed() { this.vendorsAllowed.set(this.gvl.vendors); } /** * unsetAllVendorsAllowed - unsets all vendors on the GVL Consent (false) * * @return {void} */ unsetAllVendorsAllowed() { this.vendorsAllowed.empty(); } /** * setAllVendorLegitimateInterests - sets all vendors on the GVL LegitimateInterests (true) * * @return {void} */ setAllVendorLegitimateInterests() { this.vendorLegitimateInterests.set(this.gvl.vendors); } /** * unsetAllVendorLegitimateInterests - unsets all vendors on the GVL LegitimateInterests (false) * * @return {void} */ unsetAllVendorLegitimateInterests() { this.vendorLegitimateInterests.empty(); } /** * setAllPurposeConsents - sets all purposes on the GVL Consent (true) * * @return {void} */ setAllPurposeConsents() { this.purposeConsents.set(this.gvl.purposes); } /** * unsetAllPurposeConsents - unsets all purposes on the GVL Consent (false) * * @return {void} */ unsetAllPurposeConsents() { this.purposeConsents.empty(); } /** * setAllPurposeLegitimateInterests - sets all purposes on the GVL LI Transparency (true) * * @return {void} */ setAllPurposeLegitimateInterests() { this.purposeLegitimateInterests.set(this.gvl.purposes); } /** * unsetAllPurposeLegitimateInterests - unsets all purposes on the GVL LI Transparency (false) * * @return {void} */ unsetAllPurposeLegitimateInterests() { this.purposeLegitimateInterests.empty(); } /** * setAllSpecialFeatureOptins - sets all special featuresOptins on the GVL (true) * * @return {void} */ setAllSpecialFeatureOptins() { this.specialFeatureOptins.set(this.gvl.specialFeatures); } /** * unsetAllSpecialFeatureOptins - unsets all special featuresOptins on the GVL (true) * * @return {void} */ unsetAllSpecialFeatureOptins() { this.specialFeatureOptins.empty(); } setAll() { this.setAllVendorConsents(); this.setAllPurposeLegitimateInterests(); this.setAllSpecialFeatureOptins(); this.setAllPurposeConsents(); this.setAllVendorLegitimateInterests(); } unsetAll() { this.unsetAllVendorConsents(); this.unsetAllPurposeLegitimateInterests(); this.unsetAllSpecialFeatureOptins(); this.unsetAllPurposeConsents(); this.unsetAllVendorLegitimateInterests(); } get numCustomPurposes() { let len = this.numCustomPurposes_; if (typeof this.customPurposes === 'object') { /** * Keys are not guaranteed to be in order and likewise there is no * requirement that the customPurposes be non-sparse. So we have to sort * and take the highest value. Even if the set only contains 3 purposes * but goes to ID 6 we need to set the number to 6 for the encoding to * work properly since it's positional. */ const purposeIds = Object.keys(this.customPurposes) .sort((a, b) => Number(a) - Number(b)); len = parseInt(purposeIds.pop(), 10); } return len; } set numCustomPurposes(num) { this.numCustomPurposes_ = parseInt(num, 10); if (this.numCustomPurposes_ < 0) { throw new TCModelError('numCustomPurposes', num); } } /** * updated - updates the created and lastUpdated dates with a 'now' day-level UTC timestamp * * @return {void} */ updated() { const date = new Date(); const utcDate = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())); this.created = utcDate; this.lastUpdated = utcDate; } }