From 6b7dbf159ee590eb51c22c76a67c22276dd89d79 Mon Sep 17 00:00:00 2001 From: Jaimin Panchal <7393273+jaiminpanchal27@users.noreply.github.com> Date: Tue, 31 Mar 2020 18:46:58 -0400 Subject: [PATCH] TCF Purpose 1 enforcement (#5018) * TCF Purpose 1 enforcement * some more unit tests and revert hello world page * some updates and unit test * remove unwanted comment * Adapter updates and some refactoring * add expires for ie11 * adding withCredentials false to appnexus adapter * improving conditions * improving conditions * Including changes for liveintent Co-authored-by: Jaimin Panchal --- integrationExamples/gpt/hello_world.html | 2 +- modules/aardvarkBidAdapter.js | 1 + modules/adagioBidAdapter.js | 11 +- modules/adkernelAdnAnalyticsAdapter.js | 6 +- modules/adspendBidAdapter.js | 8 +- modules/adtelligentBidAdapter.js | 1 + modules/aolBidAdapter.js | 1 + modules/appnexusBidAdapter.js | 28 +- modules/audigentRtdProvider.js | 7 +- modules/bidfluenceBidAdapter.js | 5 +- modules/browsiRtdProvider.js | 5 +- modules/categoryTranslation.js | 10 +- modules/ccxBidAdapter.js | 5 +- modules/cedatoBidAdapter.js | 5 +- modules/conversantBidAdapter.js | 11 +- modules/criteoBidAdapter.js | 10 +- modules/criteoIdSystem.js | 19 +- modules/digiTrustIdSystem.js | 7 +- modules/emoteevBidAdapter.js | 8 +- modules/eplanningBidAdapter.js | 15 +- modules/fidelityBidAdapter.js | 1 + modules/fintezaAnalyticsAdapter.js | 24 +- modules/gdprEnforcement.js | 218 +++++++++ modules/gumgumBidAdapter.js | 5 +- modules/id5IdSystem.js | 5 + modules/invibesBidAdapter.js | 14 +- modules/ixBidAdapter.js | 1 + modules/kargoBidAdapter.js | 7 +- modules/liveIntentIdSystem.js | 4 +- modules/livewrappedBidAdapter.js | 5 +- modules/mantisBidAdapter.js | 12 +- modules/medianetBidAdapter.js | 1 + modules/mgidBidAdapter.js | 7 +- modules/nanointeractiveBidAdapter.js | 9 +- modules/newborntownWebBidAdapter.js | 9 +- modules/nobidBidAdapter.js | 7 +- modules/oneVideoBidAdapter.js | 1 + modules/openxBidAdapter.js | 1 + modules/orbidderBidAdapter.js | 6 +- modules/pubCommonId.js | 31 +- modules/pubmaticBidAdapter.js | 1 + modules/pubwiseAnalyticsAdapter.js | 7 +- modules/pulsepointBidAdapter.js | 2 + modules/quantcastBidAdapter.js | 1 + modules/realvuAnalyticsAdapter.js | 13 +- modules/reloadBidAdapter.js | 8 +- modules/roxotAnalyticsAdapter.js | 15 +- modules/rubiconBidAdapter.js | 2 + modules/sigmoidAnalyticsAdapter.js | 19 +- modules/spotxBidAdapter.js | 1 + modules/staqAnalyticsAdapter.js | 7 +- modules/sublimeBidAdapter.js | 1 + modules/trionBidAdapter.js | 11 +- modules/unicornBidAdapter.js | 7 +- modules/userId/index.js | 40 +- modules/widespaceBidAdapter.js | 26 +- src/adapters/bidderFactory.js | 49 +- src/config.js | 5 + src/prebid.js | 15 + src/storageManager.js | 309 +++++++++++++ src/userSync.js | 5 +- src/utils.js | 124 ------ test/spec/modules/appnexusBidAdapter_spec.js | 27 ++ .../spec/modules/bidfluenceBidAdapter_spec.js | 38 +- test/spec/modules/categoryTranslation_spec.js | 4 +- .../spec/modules/conversantBidAdapter_spec.js | 22 +- test/spec/modules/criteoIdSystem_spec.js | 12 +- test/spec/modules/emoteevBidAdapter_spec.js | 17 +- test/spec/modules/eplanningBidAdapter_spec.js | 87 ++-- test/spec/modules/gdprEnforcement_spec.js | 418 ++++++++++++++++++ test/spec/modules/liveIntentIdSystem_spec.js | 6 +- .../modules/livewrappedBidAdapter_spec.js | 42 +- .../modules/newborntownWebBidAdapter_spec.js | 2 +- test/spec/modules/parrableIdSystem_spec.js | 7 +- test/spec/modules/userId_spec.js | 165 +++---- test/spec/modules/widespaceBidAdapter_spec.js | 13 +- test/spec/unit/core/bidderFactory_spec.js | 4 +- test/spec/unit/core/storageManager_spec.js | 46 ++ test/spec/utils_spec.js | 8 - 79 files changed, 1598 insertions(+), 521 deletions(-) create mode 100644 modules/gdprEnforcement.js create mode 100644 src/storageManager.js create mode 100644 test/spec/modules/gdprEnforcement_spec.js create mode 100644 test/spec/unit/core/storageManager_spec.js diff --git a/integrationExamples/gpt/hello_world.html b/integrationExamples/gpt/hello_world.html index d68e65011be..47ba5b8f18a 100755 --- a/integrationExamples/gpt/hello_world.html +++ b/integrationExamples/gpt/hello_world.html @@ -88,4 +88,4 @@
Div-1
- + \ No newline at end of file diff --git a/modules/aardvarkBidAdapter.js b/modules/aardvarkBidAdapter.js index 848a6672022..0b864286868 100644 --- a/modules/aardvarkBidAdapter.js +++ b/modules/aardvarkBidAdapter.js @@ -15,6 +15,7 @@ export function resetUserSync() { export const spec = { code: BIDDER_CODE, + gvlid: 52, aliases: ['adsparc', 'safereach'], isBidRequestValid: function(bid) { diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index d24350542a4..8b593c34270 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -4,6 +4,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import { loadExternalScript } from '../src/adloader.js' import JSEncrypt from 'jsencrypt/bin/jsencrypt.js'; import sha256 from 'crypto-js/sha256.js'; +import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'adagio'; const VERSION = '2.1.0'; @@ -12,6 +13,8 @@ const ENDPOINT = 'https://mp.4dex.io/prebid'; const SUPPORTED_MEDIA_TYPES = ['banner']; const ADAGIO_TAG_URL = 'https://script.4dex.io/localstore.js'; const ADAGIO_LOCALSTORAGE_KEY = 'adagioScript'; +const GVLID = 617; +const storage = getStorageManager(GVLID); export const ADAGIO_PUBKEY = `-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9el0+OEn6fvEh1RdVHQu4cnT0 @@ -22,7 +25,7 @@ pV6EP3MTLosuUEpLaQIDAQAB export function getAdagioScript() { try { - const ls = utils.getDataFromLocalStorage(ADAGIO_LOCALSTORAGE_KEY); + const ls = storage.getDataFromLocalStorage(ADAGIO_LOCALSTORAGE_KEY); if (!ls) { utils.logWarn('Adagio Script not found'); @@ -33,7 +36,7 @@ export function getAdagioScript() { if (!hashRgx.test(ls)) { utils.logWarn('No hash found in Adagio script'); - utils.removeDataFromLocalStorage(ADAGIO_LOCALSTORAGE_KEY); + storage.removeDataFromLocalStorage(ADAGIO_LOCALSTORAGE_KEY); } else { const r = ls.match(hashRgx); const hash = r[2]; @@ -47,7 +50,7 @@ export function getAdagioScript() { Function(ls)(); // eslint-disable-line no-new-func } else { utils.logWarn('Invalid Adagio script found'); - utils.removeDataFromLocalStorage(ADAGIO_LOCALSTORAGE_KEY); + storage.removeDataFromLocalStorage(ADAGIO_LOCALSTORAGE_KEY); } } } catch (err) { @@ -341,7 +344,7 @@ function _getGdprConsent(bidderRequest) { export const spec = { code: BIDDER_CODE, - + gvlid: GVLID, supportedMediaType: SUPPORTED_MEDIA_TYPES, isBidRequestValid: function (bid) { diff --git a/modules/adkernelAdnAnalyticsAdapter.js b/modules/adkernelAdnAnalyticsAdapter.js index 43cda60b0ec..b0bdadf57d0 100644 --- a/modules/adkernelAdnAnalyticsAdapter.js +++ b/modules/adkernelAdnAnalyticsAdapter.js @@ -4,10 +4,12 @@ import adapterManager from '../src/adapterManager.js'; import {parse} from '../src/url.js'; import * as utils from '../src/utils.js'; import {ajax} from '../src/ajax.js'; +import { getStorageManager } from '../src/storageManager.js'; const ANALYTICS_VERSION = '1.0.1'; const DEFAULT_QUEUE_TIMEOUT = 4000; const DEFAULT_HOST = 'tag.adkernel.com'; +const storageObj = getStorageManager(); const ADK_HB_EVENTS = { AUCTION_INIT: 'auctionInit', @@ -175,10 +177,10 @@ const ORGANIC = '(organic)'; export let storage = { getItem: (name) => { - return utils.getDataFromLocalStorage(name); + return storageObj.getDataFromLocalStorage(name); }, setItem: (name, value) => { - utils.setDataInLocalStorage(name, value); + storageObj.setDataInLocalStorage(name, value); } }; diff --git a/modules/adspendBidAdapter.js b/modules/adspendBidAdapter.js index 3342a1934cc..9fe70885eeb 100644 --- a/modules/adspendBidAdapter.js +++ b/modules/adspendBidAdapter.js @@ -3,7 +3,9 @@ import { ajax } from '../src/ajax.js' import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; +const storage = getStorageManager(); const BIDDER_CODE = 'adspend'; const BID_URL = 'https://rtb.com.ru/headerbidding-bid'; const SYNC_URL = 'https://rtb.com.ru/headerbidding-sync?uid={UUID}'; @@ -40,7 +42,7 @@ export const spec = { bid.params.bidfloor && bid.crumbs.pubcid && utils.checkCookieSupport() && - utils.cookiesAreEnabled() + storage.cookiesAreEnabled() ); }, @@ -145,11 +147,11 @@ export const spec = { } const getUserID = () => { - const i = utils.getCookie(COOKIE_NAME); + const i = storage.getCookie(COOKIE_NAME); if (i === null) { const uuid = utils.generateUUID(); - utils.setCookie(COOKIE_NAME, uuid); + storage.setCookie(COOKIE_NAME, uuid); return uuid; } return i; diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index 5749866b65b..82601117017 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -13,6 +13,7 @@ const syncsCache = {}; export const spec = { code: BIDDER_CODE, + gvlid: 410, aliases: ['onefiftytwomedia', 'selectmedia'], supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid: function (bid) { diff --git a/modules/aolBidAdapter.js b/modules/aolBidAdapter.js index d5fb2970628..d7ff7453870 100644 --- a/modules/aolBidAdapter.js +++ b/modules/aolBidAdapter.js @@ -100,6 +100,7 @@ function resolveEndpointCode(bid) { export const spec = { code: AOL_BIDDERS_CODES.AOL, + gvlid: 25, aliases: [AOL_BIDDERS_CODES.ONEMOBILE, AOL_BIDDERS_CODES.ONEDISPLAY], supportedMediaTypes: [BANNER], isBidRequestValid(bid) { diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index c411bacd3ec..f6a5e1d3eb9 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -7,6 +7,7 @@ import { auctionManager } from '../src/auctionManager.js'; import find from 'core-js/library/fn/array/find.js'; import includes from 'core-js/library/fn/array/includes.js'; import { OUTSTREAM, INSTREAM } from '../src/video.js'; +import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'appnexus'; const URL = 'https://ib.adnxs.com/ut/v3/prebid'; @@ -38,9 +39,12 @@ const mappingFileUrl = 'https://acdn.adnxs.com/prebid/appnexus-mapping/mappings. const SCRIPT_TAG_START = ' MAX_IMPS_PER_REQUEST) { const clonedPayload = utils.deepClone(payload); @@ -417,7 +437,8 @@ function formatRequest(payload, bidderRequest) { method: 'POST', url: URL, data: payloadString, - bidderRequest + bidderRequest, + options }); }); } else { @@ -426,7 +447,8 @@ function formatRequest(payload, bidderRequest) { method: 'POST', url: URL, data: payloadString, - bidderRequest + bidderRequest, + options }; } diff --git a/modules/audigentRtdProvider.js b/modules/audigentRtdProvider.js index 8b45287bddc..0f32c84962f 100644 --- a/modules/audigentRtdProvider.js +++ b/modules/audigentRtdProvider.js @@ -20,6 +20,9 @@ import {getGlobal} from '../src/prebidGlobal.js'; import * as utils from '../src/utils.js'; import {submodule} from '../src/hook.js'; import {ajax} from '../src/ajax.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); /** @type {string} */ const MODULE_NAME = 'realTimeData'; @@ -33,12 +36,12 @@ let _moduleParams = {}; */ export function setData(data) { - utils.setDataInLocalStorage('__adgntseg', JSON.stringify(data)); + storage.setDataInLocalStorage('__adgntseg', JSON.stringify(data)); } function getSegments(adUnits, onDone) { try { - let jsonData = utils.getDataFromLocalStorage('__adgntseg'); + let jsonData = storage.getDataFromLocalStorage('__adgntseg'); if (jsonData) { let data = JSON.parse(jsonData); if (data.audigent_segments) { diff --git a/modules/bidfluenceBidAdapter.js b/modules/bidfluenceBidAdapter.js index 6857a0f833f..f8a1f9ac92f 100644 --- a/modules/bidfluenceBidAdapter.js +++ b/modules/bidfluenceBidAdapter.js @@ -1,5 +1,8 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); const BIDDER_CODE = 'bidfluence'; function stdTimezoneOffset(t) { @@ -46,7 +49,7 @@ export const spec = { var payload = { v: '2.0', azr: true, - ck: utils.cookiesAreEnabled(), + ck: storage.cookiesAreEnabled(), re: refInfo ? refInfo.referer : '', st: refInfo ? refInfo.stack : [], tz: getBdfTz(new Date()), diff --git a/modules/browsiRtdProvider.js b/modules/browsiRtdProvider.js index d3fe3176cda..3765b6603af 100644 --- a/modules/browsiRtdProvider.js +++ b/modules/browsiRtdProvider.js @@ -22,6 +22,9 @@ import * as utils from '../src/utils.js'; import {submodule} from '../src/hook.js'; import {ajaxBuilder} from '../src/ajax.js'; import {loadExternalScript} from '../src/adloader.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); /** @type {string} */ const MODULE_NAME = 'realTimeData'; @@ -62,7 +65,7 @@ function collectData() { const doc = win.document; let browsiData = null; try { - browsiData = utils.getDataFromLocalStorage('__brtd'); + browsiData = storage.getDataFromLocalStorage('__brtd'); } catch (e) { utils.logError('unable to parse __brtd'); } diff --git a/modules/categoryTranslation.js b/modules/categoryTranslation.js index 722fc59c921..5342220d13a 100644 --- a/modules/categoryTranslation.js +++ b/modules/categoryTranslation.js @@ -14,9 +14,11 @@ import { config } from '../src/config.js'; import { setupBeforeHookFnOnce, hook } from '../src/hook.js'; import { ajax } from '../src/ajax.js'; -import { timestamp, logError, setDataInLocalStorage, getDataFromLocalStorage } from '../src/utils.js'; +import { timestamp, logError } from '../src/utils.js'; import { addBidResponse } from '../src/auction.js'; +import { getCoreStorageManager } from '../src/storageManager.js'; +export const storage = getCoreStorageManager('categoryTranslation'); const DEFAULT_TRANSLATION_FILE_URL = 'https://cdn.jsdelivr.net/gh/prebid/category-mapping-file@1/freewheel-mapping.json'; const DEFAULT_IAB_TO_FW_MAPPING_KEY = 'iabToFwMappingkey'; const DEFAULT_IAB_TO_FW_MAPPING_KEY_PUB = 'iabToFwMappingkeyPub'; @@ -43,7 +45,7 @@ export function getAdserverCategoryHook(fn, adUnitCode, bid) { let localStorageKey = (config.getConfig('brandCategoryTranslation.translationFile')) ? DEFAULT_IAB_TO_FW_MAPPING_KEY_PUB : DEFAULT_IAB_TO_FW_MAPPING_KEY; if (bid.meta && !bid.meta.adServerCatId) { - let mapping = getDataFromLocalStorage(localStorageKey); + let mapping = storage.getDataFromLocalStorage(localStorageKey); if (mapping) { try { mapping = JSON.parse(mapping); @@ -65,7 +67,7 @@ export function getAdserverCategoryHook(fn, adUnitCode, bid) { export function initTranslation(url, localStorageKey) { setupBeforeHookFnOnce(addBidResponse, getAdserverCategoryHook, 50); - let mappingData = getDataFromLocalStorage(localStorageKey); + let mappingData = storage.getDataFromLocalStorage(localStorageKey); if (!mappingData || timestamp() < mappingData.lastUpdated + refreshInDays * 24 * 60 * 60 * 1000) { ajax(url, { @@ -73,7 +75,7 @@ export function initTranslation(url, localStorageKey) { try { response = JSON.parse(response); response['lastUpdated'] = timestamp(); - setDataInLocalStorage(localStorageKey, JSON.stringify(response)); + storage.setDataInLocalStorage(localStorageKey, JSON.stringify(response)); } catch (error) { logError('Failed to parse translation mapping file'); } diff --git a/modules/ccxBidAdapter.js b/modules/ccxBidAdapter.js index 5532ec6ca1e..ee15d6bb3ec 100644 --- a/modules/ccxBidAdapter.js +++ b/modules/ccxBidAdapter.js @@ -1,6 +1,9 @@ import * as utils from '../src/utils.js' import { registerBidder } from '../src/adapters/bidderFactory.js' import { config } from '../src/config.js' +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); const BIDDER_CODE = 'ccx' const BID_URL = 'https://delivery.clickonometrics.pl/ortb/prebid/bid' const SUPPORTED_VIDEO_PROTOCOLS = [2, 3, 5, 6] @@ -170,7 +173,7 @@ export const spec = { requestBody.site = _getSiteObj(bidderRequest) requestBody.device = _getDeviceObj() requestBody.id = bidderRequest.bids[0].auctionId - requestBody.ext = {'ce': (utils.cookiesAreEnabled() ? 1 : 0)} + requestBody.ext = {'ce': (storage.cookiesAreEnabled() ? 1 : 0)} // Attaching GDPR Consent Params if (bidderRequest && bidderRequest.gdprConsent) { diff --git a/modules/cedatoBidAdapter.js b/modules/cedatoBidAdapter.js index 42a9baa514a..ab381698f01 100644 --- a/modules/cedatoBidAdapter.js +++ b/modules/cedatoBidAdapter.js @@ -1,6 +1,9 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); const BIDDER_CODE = 'cedato'; const BID_URL = 'https://h.cedatoplayer.com/hb'; @@ -19,7 +22,7 @@ export const spec = { bid.params && bid.params.player_id && utils.checkCookieSupport() && - utils.cookiesAreEnabled() + storage.cookiesAreEnabled() ); }, diff --git a/modules/conversantBidAdapter.js b/modules/conversantBidAdapter.js index b1f3a1a0a06..25eed9c0e99 100644 --- a/modules/conversantBidAdapter.js +++ b/modules/conversantBidAdapter.js @@ -1,12 +1,17 @@ import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const GVLID = 24; +export const storage = getStorageManager(GVLID); const BIDDER_CODE = 'conversant'; const URL = 'https://web.hb.ad.cpe.dotomi.com/s2s/header/24'; export const spec = { code: BIDDER_CODE, + gvlid: GVLID, aliases: ['cnvr'], // short code supportedMediaTypes: [BANNER, VIDEO], @@ -343,13 +348,13 @@ function readStoredValue(key) { let storedValue; try { // check cookies first - storedValue = utils.getCookie(key); + storedValue = storage.getCookie(key); if (!storedValue) { // check expiration time before reading local storage - const storedValueExp = utils.getDataFromLocalStorage(`${key}_exp`); + const storedValueExp = storage.getDataFromLocalStorage(`${key}_exp`); if (storedValueExp === '' || (storedValueExp && (new Date(storedValueExp)).getTime() - Date.now() > 0)) { - storedValue = utils.getDataFromLocalStorage(key); + storedValue = storage.getDataFromLocalStorage(key); storedValue = storedValue ? decodeURIComponent(storedValue) : storedValue; } } diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index caebb12c717..65675248208 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -6,13 +6,16 @@ import { parse } from '../src/url.js'; import * as utils from '../src/utils.js'; import find from 'core-js/library/fn/array/find.js'; import { verify } from 'criteo-direct-rsa-validate/build/verify.js'; +import { getStorageManager } from '../src/storageManager.js'; +const GVLID = 91; export const ADAPTER_VERSION = 26; const BIDDER_CODE = 'criteo'; const CDB_ENDPOINT = 'https://bidder.criteo.com/cdb'; const CRITEO_VENDOR_ID = 91; const PROFILE_ID_INLINE = 207; export const PROFILE_ID_PUBLISHERTAG = 185; +const storage = getStorageManager(GVLID); // Unminified source code can be found in: https://github.com/Prebid-org/prebid-js-external-js-criteo/blob/master/dist/prod.js const PUBLISHER_TAG_URL = 'https://static.criteo.net/js/ld/publishertag.prebid.js'; @@ -23,6 +26,7 @@ const FAST_BID_PUBKEY_N = 'ztQYwCE5BU7T9CDM5he6rKoabstXRmkzx54zFPZkWbK530dwtLBDe /** @type {BidderSpec} */ export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: [ BANNER, VIDEO, NATIVE ], /** @@ -423,7 +427,7 @@ export function tryGetCriteoFastBid() { try { const fastBidStorageKey = 'criteo_fast_bid'; const hashPrefix = '// Hash: '; - const fastBidFromStorage = utils.getDataFromLocalStorage(fastBidStorageKey); + const fastBidFromStorage = storage.getDataFromLocalStorage(fastBidStorageKey); if (fastBidFromStorage !== null) { // The value stored must contain the file's encrypted hash as first line @@ -432,7 +436,7 @@ export function tryGetCriteoFastBid() { if (firstLine.substr(0, hashPrefix.length) !== hashPrefix) { utils.logWarn('No hash found in FastBid'); - utils.removeDataFromLocalStorage(fastBidStorageKey); + storage.removeDataFromLocalStorage(fastBidStorageKey); } else { // Remove the hash part from the locally stored value const publisherTagHash = firstLine.substr(hashPrefix.length); @@ -446,7 +450,7 @@ export function tryGetCriteoFastBid() { utils.insertElement(script); } else { utils.logWarn('Invalid Criteo FastBid found'); - utils.removeDataFromLocalStorage(fastBidStorageKey); + storage.removeDataFromLocalStorage(fastBidStorageKey); } } } diff --git a/modules/criteoIdSystem.js b/modules/criteoIdSystem.js index 469532e4aa7..0103db8ef80 100644 --- a/modules/criteoIdSystem.js +++ b/modules/criteoIdSystem.js @@ -10,6 +10,9 @@ import * as ajax from '../src/ajax.js' import * as urlLib from '../src/url.js' import { getRefererInfo } from '../src/refererDetection.js' import { submodule } from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; + +export const storage = getStorageManager(); const bididStorageKey = 'cto_bidid'; const bundleStorageKey = 'cto_bundle'; @@ -20,9 +23,9 @@ const pastDateString = new Date(0).toString(); const expirationString = new Date(utils.timestamp() + cookiesMaxAge).toString(); function areCookiesWriteable() { - utils.setCookie(cookieWriteableKey, '1'); - const canWrite = utils.getCookie(cookieWriteableKey) === '1'; - utils.setCookie(cookieWriteableKey, '', pastDateString); + storage.setCookie(cookieWriteableKey, '1'); + const canWrite = storage.getCookie(cookieWriteableKey) === '1'; + storage.setCookie(cookieWriteableKey, '', pastDateString); return canWrite; } @@ -34,19 +37,19 @@ function extractProtocolHost (url, returnOnlyHost = false) { } function getFromAllStorages(key) { - return utils.getCookie(key) || utils.getDataFromLocalStorage(key); + return storage.getCookie(key) || storage.getDataFromLocalStorage(key); } function saveOnAllStorages(key, value) { if (key && value) { - utils.setCookie(key, value, expirationString); - utils.setDataInLocalStorage(key, value); + storage.setCookie(key, value, expirationString); + storage.setDataInLocalStorage(key, value); } } function deleteFromAllStorages(key) { - utils.setCookie(key, '', pastDateString); - utils.removeDataFromLocalStorage(key); + storage.setCookie(key, '', pastDateString); + storage.removeDataFromLocalStorage(key); } function getCriteoDataFromAllStorages() { diff --git a/modules/digiTrustIdSystem.js b/modules/digiTrustIdSystem.js index eeb1a4f3b60..0b964c43d57 100644 --- a/modules/digiTrustIdSystem.js +++ b/modules/digiTrustIdSystem.js @@ -12,6 +12,10 @@ import * as utils from '../src/utils.js' import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const DT_VENDOR_ID = 64; // cmp gvlVendorId +const storage = getStorageManager(DT_VENDOR_ID); var fallbackTimeout = 1550; // timeout value that allows userId system to execute first var fallbackTimer = 0; // timer Id for fallback init so we don't double call @@ -40,7 +44,6 @@ var noop = function () { const MAX_RETRIES = 2; const DT_ID_SVC = 'https://prebid.digitru.st/id/v1'; -const DT_VENDOR_ID = 64; // cmp gvlVendorId var isFunc = function (fn) { return typeof (fn) === 'function'; @@ -88,7 +91,7 @@ function writeDigiId(id) { var key = 'DigiTrust.v1.identity'; var date = new Date(); date.setTime(date.getTime() + 604800000); - utils.setCookie(key, encId(id), date.toUTCString(), 'none'); + storage.setCookie(key, encId(id), date.toUTCString(), 'none'); } /** diff --git a/modules/emoteevBidAdapter.js b/modules/emoteevBidAdapter.js index 254373b354a..0bdf4cda58e 100644 --- a/modules/emoteevBidAdapter.js +++ b/modules/emoteevBidAdapter.js @@ -23,11 +23,13 @@ import { deepAccess, isArray, isInteger, - getParameterByName, - getCookie + getParameterByName } from '../src/utils.js'; import {config} from '../src/config.js'; import * as url from '../src/url.js'; +import { getStorageManager } from '../src/storageManager.js'; + +export const storage = getStorageManager(); export const BIDDER_CODE = 'emoteev'; @@ -508,7 +510,7 @@ export const spec = { onBidWon: (bidObject) => triggerPixel(url.format(onBidWon( resolveEnv(config.getConfig(), getParameterByName('emoteevEnv')), - getCookie('_pubcid'), + storage.getCookie('_pubcid'), bidObject))), onTimeout: (bidRequest) => triggerPixel(url.format(onTimeout( diff --git a/modules/eplanningBidAdapter.js b/modules/eplanningBidAdapter.js index 979f6dbf464..a4b4f2d6728 100644 --- a/modules/eplanningBidAdapter.js +++ b/modules/eplanningBidAdapter.js @@ -1,5 +1,8 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getStorageManager } from '../src/storageManager.js'; + +export const storage = getStorageManager(); const BIDDER_CODE = 'eplanning'; const rnd = Math.random(); @@ -43,7 +46,7 @@ export const spec = { url = 'https://' + (urlConfig.sv || DEFAULT_SV) + '/hb/1/' + urlConfig.ci + '/' + dfpClientId + '/' + getDomain(pageUrl) + '/' + sec; const referrerUrl = bidderRequest.refererInfo.referer.reachedTop ? encodeURIComponent(window.top.document.referrer) : encodeURIComponent(bidderRequest.refererInfo.referer); - if (utils.hasLocalStorage()) { + if (storage.hasLocalStorage()) { registerViewabilityAllBids(bidRequests); } @@ -204,7 +207,7 @@ function getSpaces(bidRequests, ml) { function getVs(bid) { let s; let vs = ''; - if (utils.hasLocalStorage()) { + if (storage.hasLocalStorage()) { s = getViewabilityData(bid); vs += s.render >= 4 ? s.ratio.toString(16) : 'F'; } else { @@ -214,8 +217,8 @@ function getVs(bid) { } function getViewabilityData(bid) { - let r = utils.getDataFromLocalStorage(STORAGE_RENDER_PREFIX + bid.adUnitCode) || 0; - let v = utils.getDataFromLocalStorage(STORAGE_VIEW_PREFIX + bid.adUnitCode) || 0; + let r = storage.getDataFromLocalStorage(STORAGE_RENDER_PREFIX + bid.adUnitCode) || 0; + let v = storage.getDataFromLocalStorage(STORAGE_VIEW_PREFIX + bid.adUnitCode) || 0; let ratio = r > 0 ? (v / r) : 0; return { render: r, @@ -408,9 +411,9 @@ function visibilityHandler(obj) { function registerAuction(storageID) { let value; try { - value = utils.getDataFromLocalStorage(storageID); + value = storage.getDataFromLocalStorage(storageID); value = value ? window.parseInt(value, 10) + 1 : 1; - utils.setDataInLocalStorage(storageID, value); + storage.setDataInLocalStorage(storageID, value); } catch (exc) { return false; } diff --git a/modules/fidelityBidAdapter.js b/modules/fidelityBidAdapter.js index a0589986af3..7bca0745cc5 100644 --- a/modules/fidelityBidAdapter.js +++ b/modules/fidelityBidAdapter.js @@ -6,6 +6,7 @@ const BIDDER_SERVER = 'x.fidelity-media.com'; const FIDELITY_VENDOR_ID = 408; export const spec = { code: BIDDER_CODE, + gvlid: 408, isBidRequestValid: function isBidRequestValid(bid) { return !!(bid && bid.params && bid.params.zoneid); }, diff --git a/modules/fintezaAnalyticsAdapter.js b/modules/fintezaAnalyticsAdapter.js index 41343cdf371..f454e6b3acb 100644 --- a/modules/fintezaAnalyticsAdapter.js +++ b/modules/fintezaAnalyticsAdapter.js @@ -3,7 +3,9 @@ import adapter from '../src/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import * as utils from '../src/utils.js'; import { parse as parseURL } from '../src/url.js'; +import { getStorageManager } from '../src/storageManager.js'; +const storage = getStorageManager(); const CONSTANTS = require('../src/constants.json'); const ANALYTICS_TYPE = 'endpoint'; @@ -46,8 +48,8 @@ function getUniqId() { let uniq = cookies[ UNIQ_ID_KEY ]; if (!uniq) { try { - if (utils.hasLocalStorage()) { - uniq = utils.getDataFromLocalStorage(UNIQ_ID_KEY) || ''; + if (storage.hasLocalStorage()) { + uniq = storage.getDataFromLocalStorage(UNIQ_ID_KEY) || ''; isUniqFromLS = true; } } catch (b) {} @@ -62,7 +64,7 @@ function getUniqId() { expires.setFullYear(expires.getFullYear() + 10); try { - utils.setCookie(UNIQ_ID_KEY, uniq, expires.toUTCString()); + storage.setCookie(UNIQ_ID_KEY, uniq, expires.toUTCString()); } catch (e) {} } @@ -90,7 +92,7 @@ function initFirstVisit() { now.setFullYear(now.getFullYear() + 20); try { - utils.setCookie(FIRST_VISIT_DATE, visitDate, now.toUTCString()); + storage.setCookie(FIRST_VISIT_DATE, visitDate, now.toUTCString()); } catch (e) {} } @@ -110,7 +112,7 @@ function parseCookies(cookie) { let param, value; let i, j; - if (!cookie || !utils.cookiesAreEnabled()) { + if (!cookie || !storage.cookiesAreEnabled()) { return {}; } @@ -203,7 +205,7 @@ function initSession() { } try { - utils.setCookie(SESSION_ID, sessionId, expires.toUTCString()); + storage.setCookie(SESSION_ID, sessionId, expires.toUTCString()); } catch (e) {} return { @@ -249,10 +251,10 @@ function saveTrackRequestTime() { const expires = new Date(now + SESSION_DURATION); try { - if (utils.hasLocalStorage()) { - utils.setDataInLocalStorage(TRACK_TIME_KEY, now.toString()); + if (storage.hasLocalStorage()) { + storage.setDataInLocalStorage(TRACK_TIME_KEY, now.toString()); } else { - utils.setCookie(TRACK_TIME_KEY, now.toString(), expires.toUTCString()); + storage.setCookie(TRACK_TIME_KEY, now.toString(), expires.toUTCString()); } } catch (a) {} } @@ -261,9 +263,9 @@ function getTrackRequestLastTime() { let cookie; try { - if (utils.hasLocalStorage()) { + if (storage.hasLocalStorage()) { return parseInt( - utils.getDataFromLocalStorage(TRACK_TIME_KEY) || 0, + storage.getDataFromLocalStorage(TRACK_TIME_KEY) || 0, 10, ); } diff --git a/modules/gdprEnforcement.js b/modules/gdprEnforcement.js new file mode 100644 index 00000000000..363d0e396f8 --- /dev/null +++ b/modules/gdprEnforcement.js @@ -0,0 +1,218 @@ +/** + * This module gives publishers extra set of features to enforce individual purposes of TCF v2 + */ + +import * as utils from '../src/utils.js'; +import { config } from '../src/config.js'; +import { hasDeviceAccess } from '../src/utils.js'; +import adapterManager, { gdprDataHandler } from '../src/adapterManager.js'; +import find from 'core-js/library/fn/array/find.js'; +import includes from 'core-js/library/fn/array/includes.js'; +import { registerSyncInner } from '../src/adapters/bidderFactory.js'; +import { getHook } from '../src/hook.js'; +import { validateStorageEnforcement } from '../src/storageManager.js'; + +const purpose1 = 'storage'; + +let addedDeviceAccessHook = false; +let enforcementRules; + +function getGvlid() { + let gvlid; + const bidderCode = config.getCurrentBidder(); + if (bidderCode) { + const bidder = adapterManager.getBidAdapter(bidderCode); + gvlid = bidder.getSpec().gvlid; + } else { + utils.logWarn('Current module not found'); + } + return gvlid; +} + +/** + * This function takes in rules and consentData as input and validates against the consentData provided. If it returns true Prebid will allow the next call else it will log a warning + * @param {Object} rules enforcement rules set in config + * @param {Object} consentData gdpr consent data + * @returns {boolean} + */ +function validateRules(rule, consentData, currentModule, gvlid) { + let isAllowed = false; + if (!rule.vendorExceptions) rule.vendorExceptions = []; + if (rule.enforcePurpose && rule.enforceVendor) { + if ( + includes(rule.vendorExceptions, currentModule) || + ( + utils.deepAccess(consentData, 'vendorData.purpose.consents.1') === true && + utils.deepAccess(consentData, `vendorData.vendor.consents.${gvlid}`) === true + ) + ) { + isAllowed = true; + } + } else if (rule.enforcePurpose === false && rule.enforceVendor === true) { + if ( + includes(rule.vendorExceptions, currentModule) || + ( + utils.deepAccess(consentData, `vendorData.vendor.consents.${gvlid}`) === true + ) + ) { + isAllowed = true; + } + } else if (rule.enforcePurpose === false && rule.enforceVendor === false) { + if ( + !includes(rule.vendorExceptions, currentModule) || + ( + (utils.deepAccess(consentData, 'vendorData.purpose.consents.1') === true) && + (utils.deepAccess(consentData, `vendorData.vendor.consents.${gvlid}`) === true) + ) + ) { + isAllowed = true; + } + } else if (rule.enforcePurpose === true && rule.enforceVendor === false) { + if ( + (utils.deepAccess(consentData, 'vendorData.purpose.consents.1') === true) && + ( + !includes(rule.vendorExceptions, currentModule) || + (utils.deepAccess(consentData, `vendorData.vendor.consents.${gvlid}`) === true) + ) + ) { + isAllowed = true; + } + } + return isAllowed; +} + +/** + * This hook checks whether module has permission to access device or not. Device access include cookie and local storage + * @param {Function} fn reference to original function (used by hook logic) + * @param {Number=} gvlid gvlid of the module + * @param {string=} moduleName name of the module + */ +export function deviceAccessHook(fn, gvlid, moduleName, result) { + result = Object.assign({}, { + hasEnforcementHook: true + }); + if (!hasDeviceAccess()) { + utils.logWarn('Device access is disabled by Publisher'); + result.valid = false; + fn.call(this, gvlid, moduleName, result); + } else { + const consentData = gdprDataHandler.getConsentData(); + if (consentData && consentData.gdprApplies) { + if (consentData.apiVersion === 2) { + if (!gvlid) { + gvlid = getGvlid(); + } + const curModule = moduleName || config.getCurrentBidder(); + const purpose1Rule = find(enforcementRules, hasPurpose1); + let isAllowed = validateRules(purpose1Rule, consentData, curModule, gvlid); + if (isAllowed) { + result.valid = true; + fn.call(this, gvlid, moduleName, result); + } else { + utils.logWarn(`User denied Permission for Device access for ${curModule}`); + result.valid = false; + fn.call(this, gvlid, moduleName, result); + } + } else { + utils.logInfo('Enforcing TCF2 only'); + result.valid = true; + fn.call(this, gvlid, moduleName, result); + } + } else { + result.valid = true; + fn.call(this, gvlid, moduleName, result); + } + } +} + +/** + * This hook checks if a bidder has consent for user sync or not + * @param {Function} fn reference to original function (used by hook logic) + * @param {...any} args args + */ +export function userSyncHook(fn, ...args) { + const consentData = gdprDataHandler.getConsentData(); + if (consentData && consentData.gdprApplies) { + if (consentData.apiVersion === 2) { + const gvlid = getGvlid(); + const curBidder = config.getCurrentBidder(); + if (gvlid) { + const purpose1Rule = find(enforcementRules, hasPurpose1); + let isAllowed = validateRules(purpose1Rule, consentData, curBidder, gvlid); + if (isAllowed) { + fn.call(this, ...args); + } else { + utils.logWarn(`User sync not allowed for ${curBidder}`); + } + } else { + utils.logWarn(`User sync not allowed for ${curBidder}`); + } + } else { + utils.logInfo('Enforcing TCF2 only'); + fn.call(this, ...args); + } + } else { + fn.call(this, ...args); + } +} + +/** + * This hook checks if user id module is given consent or not + * @param {Function} fn reference to original function (used by hook logic) + * @param {Submodule[]} submodules Array of user id submodules + * @param {Object} consentData GDPR consent data + */ +export function userIdHook(fn, submodules, consentData) { + if (consentData && consentData.gdprApplies) { + if (consentData.apiVersion === 2) { + let userIdModules = submodules.map((submodule) => { + const gvlid = submodule.submodule.gvlid; + const moduleName = submodule.submodule.name; + if (gvlid) { + const purpose1Rule = find(enforcementRules, hasPurpose1); + let isAllowed = validateRules(purpose1Rule, consentData, moduleName, gvlid); + if (isAllowed) { + return submodule; + } else { + utils.logWarn(`User denied permission to fetch user id for ${moduleName} User id module`); + } + } else { + utils.logWarn(`User denied permission to fetch user id for ${moduleName} User id module`); + } + return undefined; + }).filter(module => module) + fn.call(this, userIdModules, consentData); + } else { + utils.logInfo('Enforcing TCF2 only'); + fn.call(this, submodules, consentData); + } + } else { + fn.call(this, submodules, consentData); + } +} + +const hasPurpose1 = (rule) => { return rule.purpose === purpose1 } + +/** + * A configuration function that initializes some module variables, as well as add hooks + * @param {Object} config GDPR enforcement config object + */ +export function setEnforcementConfig(config) { + const rules = utils.deepAccess(config, 'gdpr.rules'); + if (!rules) { + utils.logWarn('GDPR enforcement rules not defined, exiting enforcement module'); + return; + } + + enforcementRules = rules; + const hasDefinedPurpose1 = find(enforcementRules, hasPurpose1); + if (hasDefinedPurpose1 && !addedDeviceAccessHook) { + addedDeviceAccessHook = true; + validateStorageEnforcement.before(deviceAccessHook, 49); + registerSyncInner.before(userSyncHook, 48); + // Using getHook as user id and gdprEnforcement are both optional modules. Using import will auto include the file in build + getHook('validateGdprEnforcement').before(userIdHook, 47); + } +} + +config.getConfig('consentManagement', config => setEnforcementConfig(config.consentManagement)); diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index baa79ecfe04..3a347163e6d 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -4,6 +4,9 @@ import { config } from '../src/config.js' import { BANNER, VIDEO } from '../src/mediaTypes.js'; import includes from 'core-js/library/fn/array/includes.js'; import { registerBidder } from '../src/adapters/bidderFactory.js' +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); const BIDDER_CODE = 'gumgum' const ALIAS_BIDDER_CODE = ['gg'] @@ -55,7 +58,7 @@ function _getBrowserParams(topWindowUrl) { sw: topScreen.width, sh: topScreen.height, pu: topUrl, - ce: utils.cookiesAreEnabled(), + ce: storage.cookiesAreEnabled(), dpr: topWindow.devicePixelRatio || 1, jcsi: JSON.stringify({ t: 0, rq: 8 }), ogu: getOgURL() diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index 32617d2f328..1d55450239b 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -16,6 +16,11 @@ export const id5IdSubmodule = { * @type {string} */ name: 'id5Id', + /** + * Vendor id of ID5 + * @type {Number} + */ + gvlid: 131, /** * decode the stored id value for passing to bid requests * @function decode diff --git a/modules/invibesBidAdapter.js b/modules/invibesBidAdapter.js index 95737c15c3d..e839c173a93 100644 --- a/modules/invibesBidAdapter.js +++ b/modules/invibesBidAdapter.js @@ -1,5 +1,7 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getStorageManager } from '../src/storageManager.js'; +const storage = getStorageManager(); const CONSTANTS = { BIDDER_CODE: 'invibes', @@ -276,14 +278,14 @@ function getCappedCampaignsAsString() { let loadData = function () { try { - return JSON.parse(utils.getDataFromLocalStorage(key)) || {}; + return JSON.parse(storage.getDataFromLocalStorage(key)) || {}; } catch (e) { return {}; } }; let saveData = function (data) { - utils.setDataInLocalStorage(key, JSON.stringify(data)); + storage.setDataInLocalStorage(key, JSON.stringify(data)); }; let clearExpired = function () { @@ -319,7 +321,7 @@ function getCappedCampaignsAsString() { const noop = function () { }; function initLogger() { - if (utils.hasLocalStorage() && localStorage.InvibesDEBUG) { + if (storage.hasLocalStorage() && localStorage.InvibesDEBUG) { return window.console; } @@ -384,7 +386,7 @@ invibes.Uid = { let cookieDomain; invibes.getCookie = function (name) { - if (!utils.cookiesAreEnabled()) { return; } + if (!storage.cookiesAreEnabled()) { return; } let i, x, y; let cookies = document.cookie.split(';'); for (i = 0; i < cookies.length; i++) { @@ -398,7 +400,7 @@ invibes.getCookie = function (name) { }; invibes.setCookie = function (name, value, exdays, domain) { - if (!utils.cookiesAreEnabled()) { return; } + if (!storage.cookiesAreEnabled()) { return; } let whiteListed = name == 'ivNoCookie' || name == 'IvbsCampIdsLocal'; if (invibes.noCookies && !whiteListed && (exdays || 0) >= 0) { return; } if (exdays > 365) { exdays = 365; } @@ -406,7 +408,7 @@ invibes.setCookie = function (name, value, exdays, domain) { let exdate = new Date(); let exms = exdays * 24 * 60 * 60 * 1000; exdate.setTime(exdate.getTime() + exms); - utils.setCookie(name, value, exdate.toUTCString(), undefined, domain); + storage.setCookie(name, value, exdate.toUTCString(), undefined, domain); }; let detectTopmostCookieDomain = function () { diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 98896db9337..2725bafdff8 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -329,6 +329,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { export const spec = { code: BIDDER_CODE, + gvlid: 10, supportedMediaTypes: SUPPORTED_AD_TYPES, /** diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js index 6d86038e6bd..31c35f4afe3 100644 --- a/modules/kargoBidAdapter.js +++ b/modules/kargoBidAdapter.js @@ -1,6 +1,9 @@ import * as utils from '../src/utils.js'; import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); const BIDDER_CODE = 'kargo'; const HOST = 'https://krk.kargo.com'; const SYNC = 'https://crb.kargo.com/api/v1/initsyncrnd/{UUID}?seed={SEED}&idx={INDEX}'; @@ -98,7 +101,7 @@ export const spec = { // PRIVATE _readCookie(name) { - if (!utils.cookiesAreEnabled()) { + if (!storage.cookiesAreEnabled()) { return null; } let nameEquals = `${name}=`; @@ -173,7 +176,7 @@ export const spec = { _getLocalStorageSafely(key) { try { - return utils.getDataFromLocalStorage(key); + return storage.getDataFromLocalStorage(key); } catch (e) { return null; } diff --git a/modules/liveIntentIdSystem.js b/modules/liveIntentIdSystem.js index c020e80d2c6..3f5d0602166 100644 --- a/modules/liveIntentIdSystem.js +++ b/modules/liveIntentIdSystem.js @@ -9,8 +9,10 @@ import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; import { LiveConnect } from 'live-connect-js/cjs/live-connect.js'; import { uspDataHandler } from '../src/adapterManager.js'; +import { getStorageManager } from '../src/storageManager.js'; const MODULE_NAME = 'liveIntentId'; +export const storage = getStorageManager(null, MODULE_NAME); let eventFired = false; let liveConnect = null; @@ -83,7 +85,7 @@ function initializeLiveConnect(configParams) { } // The second param is the storage object, which means that all LS & Cookie manipulation will go through PBJS utils. - liveConnect = LiveConnect(liveConnectConfig, utils); + liveConnect = LiveConnect(liveConnectConfig, storage); return liveConnect; } diff --git a/modules/livewrappedBidAdapter.js b/modules/livewrappedBidAdapter.js index 0e478869b53..5a9ef302d51 100644 --- a/modules/livewrappedBidAdapter.js +++ b/modules/livewrappedBidAdapter.js @@ -3,6 +3,9 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import find from 'core-js/library/fn/array/find.js'; import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; + +export const storage = getStorageManager(); const BIDDER_CODE = 'livewrapped'; export const URL = 'https://lwadm.com/ad'; @@ -71,7 +74,7 @@ export const spec = { version: VERSION, gdprApplies: bidderRequest.gdprConsent ? bidderRequest.gdprConsent.gdprApplies : undefined, gdprConsent: bidderRequest.gdprConsent ? bidderRequest.gdprConsent.consentString : undefined, - cookieSupport: !utils.isSafariBrowser() && utils.cookiesAreEnabled(), + cookieSupport: !utils.isSafariBrowser() && storage.cookiesAreEnabled(), rcv: getAdblockerRecovered(), adRequests: [...adRequests], rtbData: handleEids(bidRequests) diff --git a/modules/mantisBidAdapter.js b/modules/mantisBidAdapter.js index 4e32f3f70ac..19e70a3e68b 100644 --- a/modules/mantisBidAdapter.js +++ b/modules/mantisBidAdapter.js @@ -1,5 +1,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; -import * as utils from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); function inIframe() { try { @@ -93,9 +95,9 @@ function storeUuid(uuid) { return false; } window.mantis_uuid = uuid; - if (utils.hasLocalStorage()) { + if (storage.hasLocalStorage()) { try { - utils.setDataInLocalStorage('mantis:uuid', uuid); + storage.setDataInLocalStorage('mantis:uuid', uuid); } catch (ex) { } } @@ -176,8 +178,8 @@ function buildMantisUrl(path, data, domain) { } if (window.mantis_uuid) { params.uuid = window.mantis_uuid; - } else if (utils.hasLocalStorage()) { - var localUuid = utils.getDataFromLocalStorage('mantis:uuid'); + } else if (storage.hasLocalStorage()) { + var localUuid = storage.getDataFromLocalStorage('mantis:uuid'); if (localUuid) { params.uuid = localUuid; } diff --git a/modules/medianetBidAdapter.js b/modules/medianetBidAdapter.js index f68f263a0d5..d944f354c83 100644 --- a/modules/medianetBidAdapter.js +++ b/modules/medianetBidAdapter.js @@ -297,6 +297,7 @@ function clearMnData() { export const spec = { code: BIDDER_CODE, + gvlid: 142, supportedMediaTypes: [BANNER, NATIVE], diff --git a/modules/mgidBidAdapter.js b/modules/mgidBidAdapter.js index f423f5d474b..194a8255641 100644 --- a/modules/mgidBidAdapter.js +++ b/modules/mgidBidAdapter.js @@ -3,6 +3,9 @@ import * as utils from '../src/utils.js'; import * as urlUtils from '../src/url.js'; import {BANNER, NATIVE} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); const DEFAULT_CUR = 'USD'; const BIDDER_CODE = 'mgid'; const ENDPOINT_URL = 'https://prebid.mgid.com/prebid/'; @@ -342,7 +345,7 @@ function getLanguage() { function getLocalStorageSafely(key) { try { - return utils.getDataFromLocalStorage(key); + return storage.getDataFromLocalStorage(key); } catch (e) { return null; } @@ -350,7 +353,7 @@ function getLocalStorageSafely(key) { function setLocalStorageSafely(key, val) { try { - return utils.setDataInLocalStorage(key, val); + return storage.setDataInLocalStorage(key, val); } catch (e) { return null; } diff --git a/modules/nanointeractiveBidAdapter.js b/modules/nanointeractiveBidAdapter.js index 3dfd4811bcd..42a343efc03 100644 --- a/modules/nanointeractiveBidAdapter.js +++ b/modules/nanointeractiveBidAdapter.js @@ -1,6 +1,9 @@ import * as utils from '../src/utils.js'; import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); export const BIDDER_CODE = 'nanointeractive'; export const END_POINT_URL = 'https://ad.audiencemanager.de'; @@ -97,7 +100,7 @@ function createSingleBidRequest(bid, bidderRequest) { function createSingleBidResponse(serverBid) { if (serverBid.userId) { - utils.setDataInLocalStorage('lsUserId', serverBid.userId); + storage.setDataInLocalStorage('lsUserId', serverBid.userId); } return { requestId: serverBid.id, @@ -147,8 +150,8 @@ function getEndpointUrl() { } function getLsUserId() { - if (utils.getDataFromLocalStorage('lsUserId') != null) { - return utils.getDataFromLocalStorage('lsUserId'); + if (storage.getDataFromLocalStorage('lsUserId') != null) { + return storage.getDataFromLocalStorage('lsUserId'); } return null; } diff --git a/modules/newborntownWebBidAdapter.js b/modules/newborntownWebBidAdapter.js index 0ad3e212089..56c63e2bb4d 100644 --- a/modules/newborntownWebBidAdapter.js +++ b/modules/newborntownWebBidAdapter.js @@ -1,6 +1,9 @@ import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE} from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); const BIDDER_CODE = 'newborntownWeb'; const REQUEST_URL = 'https://us-west.solortb.com/adx/api/rtb?from=4' @@ -53,10 +56,10 @@ export const spec = { return null; } var guid; - if (utils.getDataFromLocalStorage('sax_user_id') == null) { - utils.setDataInLocalStorage('sax_user_id', generateGUID()) + if (storage.getDataFromLocalStorage('sax_user_id') == null) { + storage.setDataInLocalStorage('sax_user_id', generateGUID()) } - guid = utils.getDataFromLocalStorage('sax_user_id') + guid = storage.getDataFromLocalStorage('sax_user_id') utils._each(validBidRequests, function(bidRequest) { const bidRequestObj = bidRequest.params var req = { diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index a845693425d..dead2d48ed4 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -2,6 +2,9 @@ import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); const BIDDER_CODE = 'nobid'; window.nobidVersion = '1.2.4'; window.nobid = window.nobid || {}; @@ -16,10 +19,10 @@ function nobidSetCookie(cname, cvalue, hours) { var d = new Date(); d.setTime(d.getTime() + (hours * 60 * 60 * 1000)); var expires = 'expires=' + d.toUTCString(); - utils.setCookie(cname, cvalue, expires); + storage.setCookie(cname, cvalue, expires); } function nobidGetCookie(cname) { - return utils.getCookie(cname); + return storage.getCookie(cname); } function nobidBuildRequests(bids, bidderRequest) { var serializeState = function(divIds, siteId, adunits) { diff --git a/modules/oneVideoBidAdapter.js b/modules/oneVideoBidAdapter.js index 9d3fabcd3b6..933f2d7a4c8 100644 --- a/modules/oneVideoBidAdapter.js +++ b/modules/oneVideoBidAdapter.js @@ -3,6 +3,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'oneVideo'; export const spec = { code: 'oneVideo', + gvlid: 25, VERSION: '3.0.0', ENDPOINT: 'https://ads.adaptv.advertising.com/rtb/openrtb?ext_id=', SYNC_ENDPOINT1: 'https://cm.g.doubleclick.net/pixel?google_nid=adaptv_dbm&google_cm&google_sc', diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index 8638dbd6d5d..a469a71ecb2 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -24,6 +24,7 @@ export const USER_ID_CODE_TO_QUERY_ARG = { export const spec = { code: BIDDER_CODE, + gvlid: 69, supportedMediaTypes: SUPPORTED_AD_TYPES, isBidRequestValid: function (bidRequest) { const hasDelDomainOrPlatform = bidRequest.params.delDomain || bidRequest.params.platform; diff --git a/modules/orbidderBidAdapter.js b/modules/orbidderBidAdapter.js index 3461aaf66a4..d7ce5aa859a 100644 --- a/modules/orbidderBidAdapter.js +++ b/modules/orbidderBidAdapter.js @@ -1,7 +1,9 @@ import {detectReferer} from '../src/refererDetection.js'; import {ajax} from '../src/ajax.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import * as utils from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); export const spec = { code: 'orbidder', @@ -9,7 +11,7 @@ export const spec = { orbidderHost: (() => { let ret = 'https://orbidder.otto.de'; try { - ret = utils.getDataFromLocalStorage('ov_orbidder_host') || ret; + ret = storage.getDataFromLocalStorage('ov_orbidder_host') || ret; } catch (e) { } return ret; diff --git a/modules/pubCommonId.js b/modules/pubCommonId.js index f476091ce2b..d69ce0f2086 100644 --- a/modules/pubCommonId.js +++ b/modules/pubCommonId.js @@ -8,6 +8,9 @@ import { config } from '../src/config.js'; import events from '../src/events.js'; import * as url from '../src/url.js'; import CONSTANTS from '../src/constants.json'; +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); const ID_NAME = '_pubcid'; const OPTOUT_NAME = '_pubcid_optout'; @@ -37,10 +40,10 @@ export function setStorageItem(key, val, expires) { try { if (expires !== undefined && expires != null) { const expStr = (new Date(Date.now() + (expires * 60 * 1000))).toUTCString(); - utils.setDataInLocalStorage(key + EXP_SUFFIX, expStr); + storage.setDataInLocalStorage(key + EXP_SUFFIX, expStr); } - utils.setDataInLocalStorage(key, val); + storage.setDataInLocalStorage(key, val); } catch (e) { utils.logMessage(e); } @@ -55,18 +58,18 @@ export function getStorageItem(key) { let val = null; try { - const expVal = utils.getDataFromLocalStorage(key + EXP_SUFFIX); + const expVal = storage.getDataFromLocalStorage(key + EXP_SUFFIX); if (!expVal) { // If there is no expiry time, then just return the item - val = utils.getDataFromLocalStorage(key); + val = storage.getDataFromLocalStorage(key); } else { // Only return the item if it hasn't expired yet. // Otherwise delete the item. const expDate = new Date(expVal); const isValid = (expDate.getTime() - Date.now()) > 0; if (isValid) { - val = utils.getDataFromLocalStorage(key); + val = storage.getDataFromLocalStorage(key); } else { removeStorageItem(key); } @@ -84,8 +87,8 @@ export function getStorageItem(key) { */ export function removeStorageItem(key) { try { - utils.removeDataFromLocalStorage(key + EXP_SUFFIX); - utils.removeDataFromLocalStorage(key); + storage.removeDataFromLocalStorage(key + EXP_SUFFIX); + storage.removeDataFromLocalStorage(key); } catch (e) { utils.logMessage(e); } @@ -101,7 +104,7 @@ function readValue(name, type) { let value; if (!type) { type = pubcidConfig.typeEnabled; } if (type === COOKIE) { - value = utils.getCookie(name); + value = storage.getCookie(name); } else if (type === LOCAL_STORAGE) { value = getStorageItem(name); } @@ -223,12 +226,12 @@ export function requestBidHook(next, config) { export function setCookie(name, value, expires, sameSite) { let expTime = new Date(); expTime.setTime(expTime.getTime() + expires * 1000 * 60); - utils.setCookie(name, value, expTime.toGMTString(), sameSite); + storage.setCookie(name, value, expTime.toGMTString(), sameSite); } // Helper to read a cookie export function getCookie(name) { - return utils.getCookie(name); + return storage.getCookie(name); } /** @@ -263,12 +266,12 @@ export function setConfig({ enable, expInterval, type = 'html5,cookie', create, for (let i = 0; i < typeArray.length; ++i) { const name = typeArray[i].trim(); if (name === COOKIE) { - if (utils.cookiesAreEnabled()) { + if (storage.cookiesAreEnabled()) { pubcidConfig.typeEnabled = COOKIE; break; } } else if (name === LOCAL_STORAGE) { - if (utils.hasLocalStorage()) { + if (storage.hasLocalStorage()) { pubcidConfig.typeEnabled = LOCAL_STORAGE; break; } @@ -282,8 +285,8 @@ export function setConfig({ enable, expInterval, type = 'html5,cookie', create, export function initPubcid() { config.getConfig('pubcid', config => setConfig(config.pubcid)); - const optout = (utils.cookiesAreEnabled() && readValue(OPTOUT_NAME, COOKIE)) || - (utils.hasLocalStorage() && readValue(OPTOUT_NAME, LOCAL_STORAGE)); + const optout = (storage.cookiesAreEnabled() && readValue(OPTOUT_NAME, COOKIE)) || + (storage.hasLocalStorage() && readValue(OPTOUT_NAME, LOCAL_STORAGE)); if (!optout) { $$PREBID_GLOBAL$$.requestBids.before(requestBidHook); diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 02c43e1c2a9..605ee69603f 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -817,6 +817,7 @@ function _handleDealCustomTargetings(payload, dctrArr, validBidRequests) { export const spec = { code: BIDDER_CODE, + gvlid: 76, supportedMediaTypes: [BANNER, VIDEO, NATIVE], /** * Determines whether or not the given bid request is valid. Valid bid request must have placementId and hbid diff --git a/modules/pubwiseAnalyticsAdapter.js b/modules/pubwiseAnalyticsAdapter.js index df38b3ab076..915aeb58f99 100644 --- a/modules/pubwiseAnalyticsAdapter.js +++ b/modules/pubwiseAnalyticsAdapter.js @@ -2,8 +2,11 @@ import {ajax} from '../src/ajax.js'; import adapter from '../src/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; +import { getStorageManager } from '../src/storageManager.js'; const utils = require('../src/utils.js'); +const storage = getStorageManager(); + /**** * PubWise.io Analytics * Contact: support@pubwise.io @@ -59,14 +62,14 @@ function enrichWithUTM(dataBag) { if (newUtm === false) { for (let prop in utmKeys) { - let itemValue = utils.getDataFromLocalStorage(`pw-${prop}`); + let itemValue = storage.getDataFromLocalStorage(`pw-${prop}`); if (itemValue.length !== 0) { dataBag[prop] = itemValue; } } } else { for (let prop in utmKeys) { - utils.setDataInLocalStorage(`pw-${prop}`, utmKeys[prop]); + storage.setDataInLocalStorage(`pw-${prop}`, utmKeys[prop]); } } } catch (e) { diff --git a/modules/pulsepointBidAdapter.js b/modules/pulsepointBidAdapter.js index d3655906cf0..7bfa8686728 100644 --- a/modules/pulsepointBidAdapter.js +++ b/modules/pulsepointBidAdapter.js @@ -28,6 +28,8 @@ export const spec = { code: 'pulsepoint', + gvlid: 81, + aliases: ['pulseLite', 'pulsepointLite'], supportedMediaTypes: ['banner', 'native', 'video'], diff --git a/modules/quantcastBidAdapter.js b/modules/quantcastBidAdapter.js index 4ffb84b8934..ab7ea54bfb6 100644 --- a/modules/quantcastBidAdapter.js +++ b/modules/quantcastBidAdapter.js @@ -78,6 +78,7 @@ function getDomain(url) { */ export const spec = { code: BIDDER_CODE, + GVLID: 11, supportedMediaTypes: ['banner', 'video'], /** diff --git a/modules/realvuAnalyticsAdapter.js b/modules/realvuAnalyticsAdapter.js index 4b05a998788..95e62397c2c 100644 --- a/modules/realvuAnalyticsAdapter.js +++ b/modules/realvuAnalyticsAdapter.js @@ -2,6 +2,9 @@ import adapter from '../src/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); const utils = require('../src/utils.js'); @@ -779,7 +782,7 @@ export let lib = { writePos: function (a) { try { let v = a.x + ',' + a.y + ',' + a.w + ',' + a.h; - utils.setDataInLocalStorage(this.keyPos(a), v); + storage.setDataInLocalStorage(this.keyPos(a), v); } catch (ex) { /* continue regardless of error */ } @@ -787,7 +790,7 @@ export let lib = { readPos: function (a) { try { - let s = utils.getDataFromLocalStorage(this.keyPos(a)); + let s = storage.getDataFromLocalStorage(this.keyPos(a)); if (s) { let v = s.split(','); a.x = parseInt(v[0], 10); @@ -806,7 +809,7 @@ export let lib = { incrMem: function(a, evt, name) { try { let k1 = this.keyPos(a) + '.' + name; - let vmem = utils.getDataFromLocalStorage(k1); + let vmem = storage.getDataFromLocalStorage(k1); if (vmem == null) vmem = '1:3'; let vr = vmem.split(':'); let nv = parseInt(vr[0], 10); @@ -819,7 +822,7 @@ export let lib = { if (evt == 'v') { nv |= 1; } - utils.setDataInLocalStorage(k1, nv + ':' + nr); + storage.setDataInLocalStorage(k1, nv + ':' + nr); } catch (ex) { /* do nothing */ } @@ -827,7 +830,7 @@ export let lib = { score: function (a, name) { try { - let vstr = utils.getDataFromLocalStorage(this.keyPos(a) + '.' + name); + let vstr = storage.getDataFromLocalStorage(this.keyPos(a) + '.' + name); if (vstr != null) { let vr = vstr.split(':'); let nv = parseInt(vr[0], 10); diff --git a/modules/reloadBidAdapter.js b/modules/reloadBidAdapter.js index dd4514c99c8..94ea4be281f 100644 --- a/modules/reloadBidAdapter.js +++ b/modules/reloadBidAdapter.js @@ -3,6 +3,10 @@ import { } from '../src/adapters/bidderFactory.js'; import * as utils from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); + const BIDDER_CODE = 'reload'; const VERSION_ADAPTER = '1.10'; export const spec = { @@ -390,14 +394,14 @@ function ReloadClientTool(args) { var stgFileStr = JSON.stringify(stgFileObj); - utils.setDataInLocalStorage(name, stgFileStr); + storage.setDataInLocalStorage(name, stgFileStr); return true; } function _getItem (name) { try { - var obStgFileStr = utils.getDataFromLocalStorage(name); + var obStgFileStr = storage.getDataFromLocalStorage(name); if (obStgFileStr === null) return null; var stgFileObj = JSON.parse(obStgFileStr); diff --git a/modules/roxotAnalyticsAdapter.js b/modules/roxotAnalyticsAdapter.js index a077c7ac7e0..0e9cda9b6bc 100644 --- a/modules/roxotAnalyticsAdapter.js +++ b/modules/roxotAnalyticsAdapter.js @@ -3,6 +3,9 @@ import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import includes from 'core-js/library/fn/array/includes.js'; import {ajaxBuilder} from '../src/ajax.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); const utils = require('../src/utils.js'); let ajax = ajaxBuilder(0); @@ -68,17 +71,17 @@ function detectDevice() { function checkIsNewFlag() { let key = buildLocalStorageKey(isNewKey); - let lastUpdate = Number(utils.getDataFromLocalStorage(key)); - utils.setDataInLocalStorage(key, Date.now()); + let lastUpdate = Number(storage.getDataFromLocalStorage(key)); + storage.setDataInLocalStorage(key, Date.now()); return Date.now() - lastUpdate > isNewTtl; } function updateUtmTimeout() { - utils.setDataInLocalStorage(buildLocalStorageKey(utmTtlKey), Date.now()); + storage.setDataInLocalStorage(buildLocalStorageKey(utmTtlKey), Date.now()); } function isUtmTimeoutExpired() { - let utmTimestamp = utils.getDataFromLocalStorage(buildLocalStorageKey(utmTtlKey)); + let utmTimestamp = storage.getDataFromLocalStorage(buildLocalStorageKey(utmTtlKey)); return (Date.now() - utmTimestamp) > utmTtl; } @@ -356,11 +359,11 @@ roxotAdapter.buildUtmTagData = function () { }); utmTags.forEach(function (utmTagKey) { if (utmTagsDetected) { - utils.setDataInLocalStorage(buildLocalStorageKey(utmTagKey), utmTagData[utmTagKey]); + storage.setDataInLocalStorage(buildLocalStorageKey(utmTagKey), utmTagData[utmTagKey]); updateUtmTimeout(); } else { if (!isUtmTimeoutExpired()) { - utmTagData[utmTagKey] = utils.getDataFromLocalStorage(buildLocalStorageKey(utmTagKey)) ? utils.getDataFromLocalStorage(buildLocalStorageKey(utmTagKey)) : ''; + utmTagData[utmTagKey] = storage.getDataFromLocalStorage(buildLocalStorageKey(utmTagKey)) ? storage.getDataFromLocalStorage(buildLocalStorageKey(utmTagKey)) : ''; updateUtmTimeout(); } } diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index f806dfdc468..616a17f470f 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -10,6 +10,7 @@ export const FASTLANE_ENDPOINT = 'https://fastlane.rubiconproject.com/a/api/fast export const VIDEO_ENDPOINT = 'https://prebid-server.rubiconproject.com/openrtb2/auction'; export const SYNC_ENDPOINT = 'https://eus.rubiconproject.com/usync.html'; +const GVLID = 52; const DIGITRUST_PROP_NAMES = { FASTLANE: { id: 'dt.id', @@ -113,6 +114,7 @@ utils._each(sizeMap, (item, key) => sizeMap[item] = key); export const spec = { code: 'rubicon', + gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO], /** * @param {object} bid diff --git a/modules/sigmoidAnalyticsAdapter.js b/modules/sigmoidAnalyticsAdapter.js index fac23730864..88bb5eb23a9 100644 --- a/modules/sigmoidAnalyticsAdapter.js +++ b/modules/sigmoidAnalyticsAdapter.js @@ -4,6 +4,9 @@ import includes from 'core-js/library/fn/array/includes.js'; import adapter from '../src/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); const utils = require('../src/utils.js'); @@ -54,31 +57,31 @@ function buildSessionIdTimeoutLocalStorageKey() { function updateSessionId() { if (isSessionIdTimeoutExpired()) { let newSessionId = utils.generateUUID(); - utils.setDataInLocalStorage(buildSessionIdLocalStorageKey(), newSessionId); + storage.setDataInLocalStorage(buildSessionIdLocalStorageKey(), newSessionId); } initOptions.sessionId = getSessionId(); updateSessionIdTimeout(); } function updateSessionIdTimeout() { - utils.setDataInLocalStorage(buildSessionIdTimeoutLocalStorageKey(), Date.now()); + storage.setDataInLocalStorage(buildSessionIdTimeoutLocalStorageKey(), Date.now()); } function isSessionIdTimeoutExpired() { - let cpmSessionTimestamp = utils.getDataFromLocalStorage(buildSessionIdTimeoutLocalStorageKey()); + let cpmSessionTimestamp = storage.getDataFromLocalStorage(buildSessionIdTimeoutLocalStorageKey()); return Date.now() - cpmSessionTimestamp > sessionTimeout; } function getSessionId() { - return utils.getDataFromLocalStorage(buildSessionIdLocalStorageKey()) ? utils.getDataFromLocalStorage(buildSessionIdLocalStorageKey()) : ''; + return storage.getDataFromLocalStorage(buildSessionIdLocalStorageKey()) ? storage.getDataFromLocalStorage(buildSessionIdLocalStorageKey()) : ''; } function updateUtmTimeout() { - utils.setDataInLocalStorage(buildUtmLocalStorageTimeoutKey(), Date.now()); + storage.setDataInLocalStorage(buildUtmLocalStorageTimeoutKey(), Date.now()); } function isUtmTimeoutExpired() { - let utmTimestamp = utils.getDataFromLocalStorage(buildUtmLocalStorageTimeoutKey()); + let utmTimestamp = storage.getDataFromLocalStorage(buildUtmLocalStorageTimeoutKey()); return (Date.now() - utmTimestamp) > utmTimeout; } @@ -219,11 +222,11 @@ sigmoidAdapter.buildUtmTagData = function () { }); utmTags.forEach(function(utmTagKey) { if (utmTagsDetected) { - utils.setDataInLocalStorage(buildUtmLocalStorageKey(utmTagKey), utmTagData[utmTagKey]); + storage.setDataInLocalStorage(buildUtmLocalStorageKey(utmTagKey), utmTagData[utmTagKey]); updateUtmTimeout(); } else { if (!isUtmTimeoutExpired()) { - utmTagData[utmTagKey] = utils.getDataFromLocalStorage(buildUtmLocalStorageKey(utmTagKey)) ? utils.getDataFromLocalStorage(buildUtmLocalStorageKey(utmTagKey)) : ''; + utmTagData[utmTagKey] = storage.getDataFromLocalStorage(buildUtmLocalStorageKey(utmTagKey)) ? storage.getDataFromLocalStorage(buildUtmLocalStorageKey(utmTagKey)) : ''; updateUtmTimeout(); } } diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js index fe42523e737..210153548b2 100644 --- a/modules/spotxBidAdapter.js +++ b/modules/spotxBidAdapter.js @@ -10,6 +10,7 @@ export const GOOGLE_CONSENT = { consented_providers: ['3', '7', '11', '12', '15' export const spec = { code: BIDDER_CODE, + gvlid: 165, aliases: ['spotx'], supportedMediaTypes: [VIDEO], diff --git a/modules/staqAnalyticsAdapter.js b/modules/staqAnalyticsAdapter.js index 2bcb175e531..31b231637f4 100644 --- a/modules/staqAnalyticsAdapter.js +++ b/modules/staqAnalyticsAdapter.js @@ -5,6 +5,9 @@ import { getRefererInfo } from '../src/refererDetection.js'; import { parse } from '../src/url.js'; import * as utils from '../src/utils.js'; import { ajax } from '../src/ajax.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const storageObj = getStorageManager(); const ANALYTICS_VERSION = '1.0.0'; const DEFAULT_QUEUE_TIMEOUT = 4000; @@ -212,10 +215,10 @@ const ORGANIC = '(organic)'; export let storage = { getItem: (name) => { - return utils.getDataFromLocalStorage(name); + return storageObj.getDataFromLocalStorage(name); }, setItem: (name, value) => { - utils.setDataInLocalStorage(name, value); + storageObj.setDataInLocalStorage(name, value); } }; diff --git a/modules/sublimeBidAdapter.js b/modules/sublimeBidAdapter.js index 63f851c5b4d..951b7b86ca9 100644 --- a/modules/sublimeBidAdapter.js +++ b/modules/sublimeBidAdapter.js @@ -10,6 +10,7 @@ const SUBLIME_VERSION = '0.4.0'; export const spec = { code: BIDDER_CODE, + gvlid: 114, aliases: [], /** diff --git a/modules/trionBidAdapter.js b/modules/trionBidAdapter.js index 846711a2721..37d54e60cd2 100644 --- a/modules/trionBidAdapter.js +++ b/modules/trionBidAdapter.js @@ -1,5 +1,8 @@ import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); const BID_REQUEST_BASE_URL = 'https://in-appadvertising.com/api/bidRequest'; const USER_SYNC_URL = 'https://in-appadvertising.com/api/userSync.html'; @@ -176,8 +179,8 @@ function handlePostMessage() { export function getStorageData(key) { var item = null; try { - if (utils.hasLocalStorage()) { - item = utils.getDataFromLocalStorage(key); + if (storage.hasLocalStorage()) { + item = storage.getDataFromLocalStorage(key); } } catch (e) { } @@ -186,8 +189,8 @@ export function getStorageData(key) { export function setStorageData(key, item) { try { - if (utils.hasLocalStorage()) { - utils.setDataInLocalStorage(key, item); + if (storage.hasLocalStorage()) { + storage.setDataInLocalStorage(key, item); } } catch (e) { } diff --git a/modules/unicornBidAdapter.js b/modules/unicornBidAdapter.js index b782c716323..cc320dc68a8 100644 --- a/modules/unicornBidAdapter.js +++ b/modules/unicornBidAdapter.js @@ -1,6 +1,9 @@ import * as utils from '../src/utils.js'; import { BANNER } from '../src/mediaTypes.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const storage = getStorageManager(); const BIDDER_CODE = 'unicorn'; const UNICORN_ENDPOINT = 'https://ds.uncn.jp/pb/0/bid.json'; const UNICORN_DEFAULT_CURRENCY = 'JPY'; @@ -119,7 +122,7 @@ const interpretResponse = (serverResponse, request) => { * Get or Create Uid for First Party Cookie */ const getUid = () => { - const ck = utils.getCookie(UNICORN_PB_COOKIE_KEY); + const ck = storage.getCookie(UNICORN_PB_COOKIE_KEY); if (ck) { return JSON.parse(ck)['uid']; } else { @@ -127,7 +130,7 @@ const getUid = () => { uid: utils.generateUUID() }; const expireIn = new Date(Date.now() + 24 * 60 * 60 * 10000).toUTCString(); - utils.setCookie(UNICORN_PB_COOKIE_KEY, JSON.stringify(newCk), expireIn); + storage.setCookie(UNICORN_PB_COOKIE_KEY, JSON.stringify(newCk), expireIn); return newCk.uid; } }; diff --git a/modules/userId/index.js b/modules/userId/index.js index 89d3ee9e5c7..cad513642a5 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -115,14 +115,16 @@ import * as utils from '../../src/utils.js'; import {getGlobal} from '../../src/prebidGlobal.js'; import {gdprDataHandler} from '../../src/adapterManager.js'; import CONSTANTS from '../../src/constants.json'; -import {module} from '../../src/hook.js'; +import {module, hook} from '../../src/hook.js'; import {createEidsArray} from './eids.js'; +import { getCoreStorageManager } from '../../src/storageManager.js'; const MODULE_NAME = 'User ID'; const COOKIE = 'cookie'; const LOCAL_STORAGE = 'html5'; const DEFAULT_SYNC_DELAY = 500; const NO_AUCTION_DELAY = 0; +export const coreStorage = getCoreStorageManager('userid'); /** @type {string[]} */ let validStorageTypes = []; @@ -162,15 +164,15 @@ function setStoredValue(storage, value) { const valueStr = utils.isPlainObject(value) ? JSON.stringify(value) : value; const expiresStr = (new Date(Date.now() + (storage.expires * (60 * 60 * 24 * 1000)))).toUTCString(); if (storage.type === COOKIE) { - utils.setCookie(storage.name, valueStr, expiresStr, 'Lax'); + coreStorage.setCookie(storage.name, valueStr, expiresStr, 'Lax'); if (typeof storage.refreshInSeconds === 'number') { - utils.setCookie(`${storage.name}_last`, new Date().toUTCString(), expiresStr); + coreStorage.setCookie(`${storage.name}_last`, new Date().toUTCString(), expiresStr); } } else if (storage.type === LOCAL_STORAGE) { - utils.setDataInLocalStorage(`${storage.name}_exp`, expiresStr); - utils.setDataInLocalStorage(storage.name, encodeURIComponent(valueStr)); + coreStorage.setDataInLocalStorage(`${storage.name}_exp`, expiresStr); + coreStorage.setDataInLocalStorage(storage.name, encodeURIComponent(valueStr)); if (typeof storage.refreshInSeconds === 'number') { - utils.setDataInLocalStorage(`${storage.name}_last`, new Date().toUTCString()); + coreStorage.setDataInLocalStorage(`${storage.name}_last`, new Date().toUTCString()); } } } catch (error) { @@ -188,15 +190,15 @@ function getStoredValue(storage, key = undefined) { let storedValue; try { if (storage.type === COOKIE) { - storedValue = utils.getCookie(storedKey); + storedValue = coreStorage.getCookie(storedKey); } else if (storage.type === LOCAL_STORAGE) { - const storedValueExp = utils.getDataFromLocalStorage(`${storage.name}_exp`); + const storedValueExp = coreStorage.getDataFromLocalStorage(`${storage.name}_exp`); // empty string means no expiration set if (storedValueExp === '') { - storedValue = utils.getDataFromLocalStorage(storedKey); + storedValue = coreStorage.getDataFromLocalStorage(storedKey); } else if (storedValueExp) { if ((new Date(storedValueExp)).getTime() - Date.now() > 0) { - storedValue = decodeURIComponent(utils.getDataFromLocalStorage(storedKey)); + storedValue = decodeURIComponent(coreStorage.getDataFromLocalStorage(storedKey)); } } } @@ -376,6 +378,13 @@ function getUserIds() { return getCombinedSubmoduleIds(initializedSubmodules); } +/** + * This hook returns updated list of submodules which are allowed to do get user id based on TCF 2 enforcement rules configured + */ +export const validateGdprEnforcement = hook('sync', function (submodules, consentData) { + return submodules; +}, 'validateGdprEnforcement'); + /** * @param {SubmoduleContainer[]} submodules * @param {ConsentData} consentData @@ -383,12 +392,13 @@ function getUserIds() { */ function initSubmodules(submodules, consentData) { // gdpr consent with purpose one is required, otherwise exit immediately + let userIdModules = validateGdprEnforcement(submodules, consentData); if (!hasGDPRConsent(consentData)) { utils.logWarn(`${MODULE_NAME} - gdpr permission not valid for local storage or cookies, exit module`); return []; } - return submodules.reduce((carry, submodule) => { + return userIdModules.reduce((carry, submodule) => { // There are two submodule configuration types to handle: storage or value // 1. storage: retrieve user id data from cookie/html storage or with the submodule's getId method // 2. value: pass directly to bids @@ -534,17 +544,17 @@ export function init(config) { // list of browser enabled storage types validStorageTypes = [ - utils.localStorageIsEnabled() ? LOCAL_STORAGE : null, - utils.cookiesAreEnabled() ? COOKIE : null + coreStorage.localStorageIsEnabled() ? LOCAL_STORAGE : null, + coreStorage.cookiesAreEnabled() ? COOKIE : null ].filter(i => i !== null); // exit immediately if opt out cookie or local storage keys exists. - if (validStorageTypes.indexOf(COOKIE) !== -1 && (utils.getCookie('_pbjs_id_optout') || utils.getCookie('_pubcid_optout'))) { + if (validStorageTypes.indexOf(COOKIE) !== -1 && (coreStorage.getCookie('_pbjs_id_optout') || coreStorage.getCookie('_pubcid_optout'))) { utils.logInfo(`${MODULE_NAME} - opt-out cookie found, exit module`); return; } // _pubcid_optout is checked for compatiblility with pubCommonId - if (validStorageTypes.indexOf(LOCAL_STORAGE) !== -1 && (utils.getDataFromLocalStorage('_pbjs_id_optout') || utils.getDataFromLocalStorage('_pubcid_optout'))) { + if (validStorageTypes.indexOf(LOCAL_STORAGE) !== -1 && (coreStorage.getDataFromLocalStorage('_pbjs_id_optout') || coreStorage.getDataFromLocalStorage('_pubcid_optout'))) { utils.logInfo(`${MODULE_NAME} - opt-out localStorage found, exit module`); return; } diff --git a/modules/widespaceBidAdapter.js b/modules/widespaceBidAdapter.js index 26ced2bb802..5165b00a98c 100644 --- a/modules/widespaceBidAdapter.js +++ b/modules/widespaceBidAdapter.js @@ -1,19 +1,17 @@ import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import { - cookiesAreEnabled, - hasLocalStorage, parseQueryStringParameters, parseSizesInput } from '../src/utils.js'; import includes from 'core-js/library/fn/array/includes.js'; import find from 'core-js/library/fn/array/find.js'; -import * as utils from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; + +export const storage = getStorageManager(); const BIDDER_CODE = 'widespace'; const WS_ADAPTER_VERSION = '2.0.1'; -const LOCAL_STORAGE_AVAILABLE = hasLocalStorage(); -const COOKIE_ENABLED = cookiesAreEnabled(); const LS_KEYS = { PERF_DATA: 'wsPerfData', LC_UID: 'wsLcuid', @@ -177,37 +175,37 @@ export const spec = { function storeData(data, name, stringify = true) { const value = stringify ? JSON.stringify(data) : data; - if (LOCAL_STORAGE_AVAILABLE) { - utils.setDataInLocalStorage(name, value); + if (storage.hasLocalStorage()) { + storage.setDataInLocalStorage(name, value); return true; - } else if (COOKIE_ENABLED) { + } else if (storage.cookiesAreEnabled()) { const theDate = new Date(); const expDate = new Date(theDate.setMonth(theDate.getMonth() + 12)).toGMTString(); - utils.setCookie(name, value, expDate); + storage.setCookie(name, value, expDate); return true; } } function getData(name, remove = true) { let data = []; - if (LOCAL_STORAGE_AVAILABLE) { + if (storage.hasLocalStorage()) { Object.keys(localStorage).filter((key) => { if (key.indexOf(name) > -1) { - data.push(utils.getDataFromLocalStorage(key)); + data.push(storage.getDataFromLocalStorage(key)); if (remove) { - utils.removeDataFromLocalStorage(key); + storage.removeDataFromLocalStorage(key); } } }); } - if (COOKIE_ENABLED) { + if (storage.cookiesAreEnabled()) { document.cookie.split(';').forEach((item) => { let value = item.split('='); if (value[0].indexOf(name) > -1) { data.push(value[1]); if (remove) { - utils.setCookie(value[0], '', 'Thu, 01 Jan 1970 00:00:01 GMT'); + storage.setCookie(value[0], '', 'Thu, 01 Jan 1970 00:00:01 GMT'); } } }); diff --git a/src/adapters/bidderFactory.js b/src/adapters/bidderFactory.js index 3e8771cd8df..af3ab5ed8c1 100644 --- a/src/adapters/bidderFactory.js +++ b/src/adapters/bidderFactory.js @@ -9,9 +9,12 @@ import CONSTANTS from '../constants.json'; import events from '../events.js'; import includes from 'core-js/library/fn/array/includes.js'; import { ajax } from '../ajax.js'; -import { logWarn, logError, parseQueryStringParameters, delayExecution, parseSizesInput, getBidderRequest, flatten, uniques, timestamp, setDataInLocalStorage, getDataFromLocalStorage, deepAccess, isArray } from '../utils.js'; +import { logWarn, logError, parseQueryStringParameters, delayExecution, parseSizesInput, getBidderRequest, flatten, uniques, timestamp, deepAccess, isArray } from '../utils.js'; import { ADPOD } from '../mediaTypes.js'; -import { getHook } from '../hook.js'; +import { getHook, hook } from '../hook.js'; +import { getCoreStorageManager } from '../storageManager.js'; + +export const storage = getCoreStorageManager('bidderFactory'); /** * This file aims to support Adapters during the Prebid 0.x -> 1.x transition. @@ -331,22 +334,7 @@ export function newBidder(spec) { }); function registerSyncs(responses, gdprConsent, uspConsent) { - const aliasSyncEnabled = config.getConfig('userSync.aliasSyncEnabled'); - if (spec.getUserSyncs && (aliasSyncEnabled || !adapterManager.aliasRegistry[spec.code])) { - let filterConfig = config.getConfig('userSync.filterSettings'); - let syncs = spec.getUserSyncs({ - iframeEnabled: !!(filterConfig && (filterConfig.iframe || filterConfig.all)), - pixelEnabled: !!(filterConfig && (filterConfig.image || filterConfig.all)), - }, responses, gdprConsent, uspConsent); - if (syncs) { - if (!Array.isArray(syncs)) { - syncs = [syncs]; - } - syncs.forEach((sync) => { - userSync.registerSync(sync.type, spec.code, sync.url) - }); - } - } + registerSyncInner(spec, responses, gdprConsent, uspConsent); } function filterAndWarn(bid) { @@ -358,6 +346,25 @@ export function newBidder(spec) { } } +export const registerSyncInner = hook('async', function(spec, responses, gdprConsent, uspConsent) { + const aliasSyncEnabled = config.getConfig('userSync.aliasSyncEnabled'); + if (spec.getUserSyncs && (aliasSyncEnabled || !adapterManager.aliasRegistry[spec.code])) { + let filterConfig = config.getConfig('userSync.filterSettings'); + let syncs = spec.getUserSyncs({ + iframeEnabled: !!(filterConfig && (filterConfig.iframe || filterConfig.all)), + pixelEnabled: !!(filterConfig && (filterConfig.image || filterConfig.all)), + }, responses, gdprConsent, uspConsent); + if (syncs) { + if (!Array.isArray(syncs)) { + syncs = [syncs]; + } + syncs.forEach((sync) => { + userSync.registerSync(sync.type, spec.code, sync.url) + }); + } + } +}, 'registerSyncs') + export function preloadBidderMappingFile(fn, adUnits) { if (!config.getConfig('adpod.brandCategoryExclusion')) { return fn.call(this, adUnits); @@ -374,7 +381,7 @@ export function preloadBidderMappingFile(fn, adUnits) { let info = bidderSpec.getSpec().getMappingFileInfo(); let refreshInDays = (info.refreshInDays) ? info.refreshInDays : DEFAULT_REFRESHIN_DAYS; let key = (info.localStorageKey) ? info.localStorageKey : bidderSpec.getSpec().code; - let mappingData = getDataFromLocalStorage(key); + let mappingData = storage.getDataFromLocalStorage(key); if (!mappingData || timestamp() < mappingData.lastUpdated + refreshInDays * 24 * 60 * 60 * 1000) { ajax(info.url, { @@ -385,7 +392,7 @@ export function preloadBidderMappingFile(fn, adUnits) { lastUpdated: timestamp(), mapping: response.mapping } - setDataInLocalStorage(key, JSON.stringify(mapping)); + storage.setDataInLocalStorage(key, JSON.stringify(mapping)); } catch (error) { logError(`Failed to parse ${bidder} bidder translation mapping file`); } @@ -413,7 +420,7 @@ export function getIabSubCategory(bidderCode, category) { if (bidderSpec.getSpec().getMappingFileInfo) { let info = bidderSpec.getSpec().getMappingFileInfo(); let key = (info.localStorageKey) ? info.localStorageKey : bidderSpec.getBidderCode(); - let data = getDataFromLocalStorage(key); + let data = storage.getDataFromLocalStorage(key); if (data) { try { data = JSON.parse(data); diff --git a/src/config.js b/src/config.js index 8484b87b77c..24962bdff37 100644 --- a/src/config.js +++ b/src/config.js @@ -450,9 +450,14 @@ export function newConfig() { } } + function getCurrentBidder() { + return currBidder; + } + resetConfig(); return { + getCurrentBidder, getConfig, setConfig, setDefaults, diff --git a/src/prebid.js b/src/prebid.js index aa2d2493e7b..480e3afa6fc 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -13,6 +13,7 @@ import includes from 'core-js/library/fn/array/includes.js'; import { adunitCounter } from './adUnits.js'; import { isRendererRequired, executeRenderer } from './Renderer.js'; import { createBid } from './bidfactory.js'; +import { storageCallbacks } from './storageManager.js'; const $$PREBID_GLOBAL$$ = getGlobal(); const CONSTANTS = require('./constants.json'); @@ -523,6 +524,20 @@ $$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeo return auction; }); +export function executeStorageCallbacks(fn, reqBidsConfigObj) { + runAll(storageCallbacks); + fn.call(this, reqBidsConfigObj); + function runAll(queue) { + var queued; + while ((queued = queue.shift())) { + queued(); + } + } +} + +// This hook will execute all storage callbacks which were registered before gdpr enforcement hook was added. Some bidders, user id modules use storage functions when module is parsed but gdpr enforcement hook is not added at that stage as setConfig callbacks are yet to be called. Hence for such calls we execute all the stored callbacks just before requestBids. At this hook point we will know for sure that gdprEnforcement module is added or not +$$PREBID_GLOBAL$$.requestBids.before(executeStorageCallbacks, 49); + /** * * Add adunit(s) diff --git a/src/storageManager.js b/src/storageManager.js new file mode 100644 index 00000000000..abda0ccb180 --- /dev/null +++ b/src/storageManager.js @@ -0,0 +1,309 @@ +import { hook } from './hook.js'; +import * as utils from './utils.js'; +import includes from 'core-js/library/fn/array/includes.js'; + +const moduleTypeWhiteList = ['core', 'prebid-module']; + +export let storageCallbacks = []; + +/** + * Storage options + * @typedef {Object} storageOptions + * @property {Number=} gvlid - Vendor id + * @property {string} moduleName - Module name + * @property {string=} moduleType - Module type, value can be anyone of core or prebid-module + */ + +/** + * Returns list of storage related functions with gvlid, module name and module type in its scope. + * All three argument are optional here. Below shows the usage of of these + * - GVL Id: Pass GVL id if you are a vendor + * - Module name: All modules need to pass module name + * - Module type: Some modules may need these functions but are not vendor. e.g prebid core files in src and modules like currency. + * @param {storageOptions} options + */ +export function newStorageManager({gvlid, moduleName, moduleType} = {}) { + function isValid(cb) { + if (includes(moduleTypeWhiteList, moduleType)) { + let result = { + valid: true + } + return cb(result); + } else { + let value; + let hookDetails = { + hasEnforcementHook: false + } + validateStorageEnforcement(gvlid, moduleName, hookDetails, function(result) { + if (result && result.hasEnforcementHook) { + value = cb(result); + } else { + let result = { + hasEnforcementHook: false, + valid: utils.hasDeviceAccess() + } + value = cb(result); + } + }); + return value; + } + } + + /** + * @param {string} key + * @param {string} value + * @param {string} [expires=''] + * @param {string} [sameSite='/'] + * @param {string} [domain] domain (e.g., 'example.com' or 'subdomain.example.com'). + * If not specified, defaults to the host portion of the current document location. + * If a domain is specified, subdomains are always included. + * Domain must match the domain of the JavaScript origin. Setting cookies to foreign domains will be silently ignored. + */ + const setCookie = function (key, value, expires, sameSite, domain, done) { + let cb = function (result) { + if (result && result.valid) { + const domainPortion = (domain && domain !== '') ? ` ;domain=${encodeURIComponent(domain)}` : ''; + const expiresPortion = (expires && expires !== '') ? ` ;expires=${expires}` : ''; + document.cookie = `${key}=${encodeURIComponent(value)}${expiresPortion}; path=/${domainPortion}${sameSite ? `; SameSite=${sameSite}` : ''}`; + } + } + if (done && typeof done === 'function') { + storageCallbacks.push(function() { + let result = isValid(cb); + done(result); + }); + } else { + return isValid(cb); + } + }; + + /** + * @param {string} name + * @returns {(string|null)} + */ + const getCookie = function(name, done) { + let cb = function (result) { + if (result && result.valid) { + let m = window.document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]*)\\s*(;|$)'); + return m ? decodeURIComponent(m[2]) : null; + } + return null; + } + if (done && typeof done === 'function') { + storageCallbacks.push(function() { + let result = isValid(cb); + done(result); + }); + } else { + return isValid(cb); + } + }; + + /** + * @returns {boolean} + */ + const localStorageIsEnabled = function (done) { + let cb = function (result) { + if (result && result.valid) { + try { + localStorage.setItem('prebid.cookieTest', '1'); + return localStorage.getItem('prebid.cookieTest') === '1'; + } catch (error) {} + } + return false; + } + if (done && typeof done === 'function') { + storageCallbacks.push(function() { + let result = isValid(cb); + done(result); + }); + } else { + return isValid(cb); + } + } + + /** + * @returns {boolean} + */ + const cookiesAreEnabled = function (done) { + let cb = function (result) { + if (result && result.valid) { + if (utils.checkCookieSupport()) { + return true; + } + window.document.cookie = 'prebid.cookieTest'; + return window.document.cookie.indexOf('prebid.cookieTest') !== -1; + } + return false; + } + if (done && typeof done === 'function') { + storageCallbacks.push(function() { + let result = isValid(cb); + done(result); + }); + } else { + return isValid(cb); + } + } + + /** + * @param {string} key + * @param {string} value + */ + const setDataInLocalStorage = function (key, value, done) { + let cb = function (result) { + if (result && result.valid) { + window.localStorage.setItem(key, value); + } + } + if (done && typeof done === 'function') { + storageCallbacks.push(function() { + let result = isValid(cb); + done(result); + }); + } else { + return isValid(cb); + } + } + + /** + * @param {string} key + * @returns {(string|null)} + */ + const getDataFromLocalStorage = function (key, done) { + let cb = function (result) { + if (result && result.valid) { + return window.localStorage.getItem(key); + } + return null; + } + if (done && typeof done === 'function') { + storageCallbacks.push(function() { + let result = isValid(cb); + done(result); + }); + } else { + return isValid(cb); + } + } + + /** + * @param {string} key + */ + const removeDataFromLocalStorage = function (key, done) { + let cb = function (result) { + if (result && result.valid) { + window.localStorage.removeItem(key); + } + } + if (done && typeof done === 'function') { + storageCallbacks.push(function() { + let result = isValid(cb); + done(result); + }); + } else { + return isValid(cb); + } + } + + /** + * @returns {boolean} + */ + const hasLocalStorage = function (done) { + let cb = function (result) { + if (result && result.valid) { + try { + return !!window.localStorage; + } catch (e) { + utils.logError('Local storage api disabled'); + } + } + return false; + } + if (done && typeof done === 'function') { + storageCallbacks.push(function() { + let result = isValid(cb); + done(result); + }); + } else { + return isValid(cb); + } + } + + /** + * Returns all cookie values from the jar whose names contain the `keyLike` + * Needs to exist in `utils.js` as it follows the StorageHandler interface defined in live-connect-js. If that module were to be removed, this function can go as well. + * @param {string} keyLike + * @return {[]} + */ + const findSimilarCookies = function(keyLike, done) { + let cb = function (result) { + if (result && result.valid) { + const all = []; + if (utils.hasDeviceAccess()) { + const cookies = document.cookie.split(';'); + while (cookies.length) { + const cookie = cookies.pop(); + let separatorIndex = cookie.indexOf('='); + separatorIndex = separatorIndex < 0 ? cookie.length : separatorIndex; + const cookieName = decodeURIComponent(cookie.slice(0, separatorIndex).replace(/^\s+/, '')); + if (cookieName.indexOf(keyLike) >= 0) { + all.push(decodeURIComponent(cookie.slice(separatorIndex + 1))); + } + } + } + return all; + } + } + + if (done && typeof done === 'function') { + storageCallbacks.push(function() { + let result = isValid(cb); + done(result); + }); + } else { + return isValid(cb); + } + } + + return { + setCookie, + getCookie, + localStorageIsEnabled, + cookiesAreEnabled, + setDataInLocalStorage, + getDataFromLocalStorage, + removeDataFromLocalStorage, + hasLocalStorage, + findSimilarCookies + } +} + +/** + * This hook validates the storage enforcement if gdprEnforcement module is included + */ +export const validateStorageEnforcement = hook('async', function(gvlid, moduleName, hookDetails, callback) { + callback(hookDetails); +}, 'validateStorageEnforcement'); + +/** + * This function returns storage functions to access cookies and localstorage. This function will bypass the gdpr enforcement requirement. Prebid as a software needs to use storage in some scenarios and is not a vendor so GDPR enforcement rules does not apply on Prebid. + * @param {string} moduleName Module name + */ +export function getCoreStorageManager(moduleName) { + return newStorageManager({moduleName: moduleName, moduleType: 'core'}); +} + +/** + * Note: Core modules or Prebid modules like Currency, SizeMapping should use getCoreStorageManager + * This function returns storage functions to access cookies and localstorage. Bidders and User id modules should import this and use it in their module if needed. GVL ID and Module name are optional param but gvl id is needed for when gdpr enforcement module is used. + * @param {Number=} gvlid Vendor id + * @param {string=} moduleName BidderCode or module name + */ +export function getStorageManager(gvlid, moduleName) { + return newStorageManager({gvlid: gvlid, moduleName: moduleName}); +} + +export function resetData() { + storageCallbacks = []; +} diff --git a/src/userSync.js b/src/userSync.js index e19f64d8082..5da22254f2c 100644 --- a/src/userSync.js +++ b/src/userSync.js @@ -1,6 +1,7 @@ import * as utils from './utils.js'; import { config } from './config.js'; import includes from 'core-js/library/fn/array/includes.js'; +import { getCoreStorageManager } from './storageManager.js'; export const USERSYNC_DEFAULT_CONFIG = { syncEnabled: true, @@ -20,6 +21,8 @@ config.setDefaults({ 'userSync': utils.deepClone(USERSYNC_DEFAULT_CONFIG) }); +const storage = getCoreStorageManager('usersync'); + /** * Factory function which creates a new UserSyncPool. * @@ -299,7 +302,7 @@ export function newUserSync(userSyncDependencies) { return publicApi; } -const browserSupportsCookies = !utils.isSafariBrowser() && utils.cookiesAreEnabled(); +const browserSupportsCookies = !utils.isSafariBrowser() && storage.cookiesAreEnabled(); export const userSync = newUserSync({ config: config.getConfig('userSync'), diff --git a/src/utils.js b/src/utils.js index 9b5b5b7bdac..d9c4e5ea0f8 100644 --- a/src/utils.js +++ b/src/utils.js @@ -844,86 +844,6 @@ export function checkCookieSupport() { } } -/** - * @returns {boolean} - */ -export function cookiesAreEnabled() { - if (hasDeviceAccess()) { - if (internal.checkCookieSupport()) { - return true; - } - window.document.cookie = 'prebid.cookieTest'; - return window.document.cookie.indexOf('prebid.cookieTest') !== -1; - } - return false; -} - -/** - * @param {string} name - * @returns {(string|null)} - */ -export function getCookie(name) { - if (hasDeviceAccess()) { - let m = window.document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]*)\\s*(;|$)'); - return m ? decodeURIComponent(m[2]) : null; - } - return null; -} - -/** - * @param {string} key - * @param {string} value - * @param {string} [expires=''] - * @param {string} [sameSite='/'] - * @param {string} [domain] domain (e.g., 'example.com' or 'subdomain.example.com'). - * If not specified, defaults to the host portion of the current document location. - * If a domain is specified, subdomains are always included. - * Domain must match the domain of the JavaScript origin. Setting cookies to foreign domains will be silently ignored. - */ -export function setCookie(key, value, expires, sameSite, domain) { - if (hasDeviceAccess()) { - const domainPortion = (domain && domain !== '') ? ` ;domain=${encodeURIComponent(domain)}` : ''; - const expiresPortion = (expires && expires !== '') ? ` ;expires=${expires}` : ''; - document.cookie = `${key}=${encodeURIComponent(value)}${expiresPortion}; path=/${domainPortion}${sameSite ? `; SameSite=${sameSite}` : ''}`; - } -} - -/** - * Returns all cookie values from the jar whose names contain the `keyLike` - * Needs to exist in `utils.js` as it follows the StorageHandler interface defined in live-connect-js. If that module were to be removed, this function can go as well. - * @param {string} keyLike - * @return {[]} - */ -export function findSimilarCookies(keyLike) { - const all = []; - if (hasDeviceAccess()) { - const cookies = document.cookie.split(';'); - while (cookies.length) { - const cookie = cookies.pop(); - let separatorIndex = cookie.indexOf('='); - separatorIndex = separatorIndex < 0 ? cookie.length : separatorIndex; - const cookieName = decodeURIComponent(cookie.slice(0, separatorIndex).replace(/^\s+/, '')); - if (cookieName.indexOf(keyLike) >= 0) { - all.push(decodeURIComponent(cookie.slice(separatorIndex + 1))); - } - } - } - return all; -} - -/** - * @returns {boolean} - */ -export function localStorageIsEnabled () { - if (hasDeviceAccess()) { - try { - localStorage.setItem('prebid.cookieTest', '1'); - return localStorage.getItem('prebid.cookieTest') === '1'; - } catch (error) {} - } - return false; -} - /** * Given a function, return a function which only executes the original after * it's been called numRequiredCalls times. @@ -1260,50 +1180,6 @@ export function convertTypes(types, params) { return params; } -/** - * @param {string} key - * @param {string} value - */ -export function setDataInLocalStorage(key, value) { - if (hasLocalStorage()) { - window.localStorage.setItem(key, value); - } -} - -/** - * @param {string} key - * @returns {(string|null)} - */ -export function getDataFromLocalStorage(key) { - if (hasLocalStorage()) { - return window.localStorage.getItem(key); - } - return null; -} - -/** - * @param {string} key - */ -export function removeDataFromLocalStorage(key) { - if (hasLocalStorage()) { - window.localStorage.removeItem(key); - } -} - -/** - * @returns {boolean} - */ -export function hasLocalStorage() { - if (hasDeviceAccess()) { - try { - return !!window.localStorage; - } catch (e) { - logError('Local storage api disabled'); - } - } - return false; -} - export function isArrayOfNums(val, size) { return (isArray(val)) && ((size) ? val.length === size : true) && (val.every(v => isInteger(v))); } diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index c61b1cf1979..6febb8ecc9a 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -624,6 +624,7 @@ describe('AppNexusAdapter', function () { bidderRequest.bids = bidRequests; const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.options).to.be.empty; const payload = JSON.parse(request.data); expect(payload.gdpr_consent).to.exist; @@ -774,6 +775,32 @@ describe('AppNexusAdapter', function () { config.getConfig.restore(); }); + + it('should set withCredentials to false if purpose 1 consent is not given', function () { + let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + let bidderRequest = { + 'bidderCode': 'appnexus', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'gdprConsent': { + consentString: consentString, + gdprApplies: true, + apiVersion: 2, + vendorData: { + vendor: { + consents: { + 32: false + } + } + } + } + }; + bidderRequest.bids = bidRequests; + + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.options).to.deep.equal({withCredentials: false}); + }); }) describe('interpretResponse', function () { diff --git a/test/spec/modules/bidfluenceBidAdapter_spec.js b/test/spec/modules/bidfluenceBidAdapter_spec.js index ff33715a176..6b3a0c2b044 100644 --- a/test/spec/modules/bidfluenceBidAdapter_spec.js +++ b/test/spec/modules/bidfluenceBidAdapter_spec.js @@ -51,30 +51,30 @@ describe('Bidfluence Adapter test', () => { }); describe('buildRequests', function () { - const request = spec.buildRequests(validBidRequests, bidderRequest); - it('sends bid request to our endpoint via POST', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); expect(request.method).to.equal('POST'); - }); + const payload = JSON.parse(request.data); - const payload = JSON.parse(request.data); - - expect(payload.bids[0].bid).to.equal(validBidRequests[0].bidId); - expect(payload.azr).to.equal(true); - expect(payload.ck).to.not.be.undefined; - expect(payload.bids[0].tid).to.equal(PLACEMENT_ID); - expect(payload.bids[0].pid).to.equal(PUB_ID); - expect(payload.bids[0].rp).to.be.a('number'); - expect(payload.re).to.not.be.undefined; - expect(payload.st).to.not.be.undefined; - expect(payload.tz).to.not.be.undefined; - expect(payload.sr).to.not.be.undefined; - expect(payload.vp).to.not.be.undefined; - expect(payload.sdt).to.not.be.undefined; - expect(payload.bids[0].w).to.equal('300'); - expect(payload.bids[0].h).to.equal('250'); + expect(payload.bids[0].bid).to.equal(validBidRequests[0].bidId); + expect(payload.azr).to.equal(true); + expect(payload.ck).to.not.be.undefined; + expect(payload.bids[0].tid).to.equal(PLACEMENT_ID); + expect(payload.bids[0].pid).to.equal(PUB_ID); + expect(payload.bids[0].rp).to.be.a('number'); + expect(payload.re).to.not.be.undefined; + expect(payload.st).to.not.be.undefined; + expect(payload.tz).to.not.be.undefined; + expect(payload.sr).to.not.be.undefined; + expect(payload.vp).to.not.be.undefined; + expect(payload.sdt).to.not.be.undefined; + expect(payload.bids[0].w).to.equal('300'); + expect(payload.bids[0].h).to.equal('250'); + }); it('sends gdpr info if exists', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const payload = JSON.parse(request.data); expect(payload.gdpr).to.equal(true); expect(payload.gdprc).to.equal(CONSENT_STRING); }); diff --git a/test/spec/modules/categoryTranslation_spec.js b/test/spec/modules/categoryTranslation_spec.js index f8684c0d7c0..555fe3d6357 100644 --- a/test/spec/modules/categoryTranslation_spec.js +++ b/test/spec/modules/categoryTranslation_spec.js @@ -1,4 +1,4 @@ -import { getAdserverCategoryHook, initTranslation } from 'modules/categoryTranslation.js'; +import { getAdserverCategoryHook, initTranslation, storage } from 'modules/categoryTranslation.js'; import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; import { expect } from 'chai'; @@ -9,7 +9,7 @@ describe('category translation', function () { beforeEach(function () { fakeTranslationServer = sinon.fakeServer.create(); - getLocalStorageStub = sinon.stub(utils, 'getDataFromLocalStorage'); + getLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); }); afterEach(function() { diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js index 7ad37a91afe..3d6a6be9fa1 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -1,5 +1,5 @@ import {expect} from 'chai'; -import {spec} from 'modules/conversantBidAdapter.js'; +import {spec, storage} from 'modules/conversantBidAdapter.js'; import * as utils from 'src/utils.js'; import { createEidsArray } from 'modules/userId/eids.js'; @@ -508,7 +508,7 @@ describe('Conversant adapter tests', function() { const requests = utils.deepClone(bidRequests); // add a pubcid cookie - utils.setCookie(ID_NAME, '12345', expStr(TIMEOUT)); + storage.setCookie(ID_NAME, '12345', expStr(TIMEOUT)); // construct http post payload const payload = spec.buildRequests(requests).data; @@ -521,7 +521,7 @@ describe('Conversant adapter tests', function() { requests[0].params.pubcid_name = CUSTOM_ID_NAME; // add a pubcid cookie - utils.setCookie(CUSTOM_ID_NAME, '12345', expStr(TIMEOUT)); + storage.setCookie(CUSTOM_ID_NAME, '12345', expStr(TIMEOUT)); // construct http post payload const payload = spec.buildRequests(requests).data; @@ -533,8 +533,8 @@ describe('Conversant adapter tests', function() { const requests = utils.deepClone(bidRequests); // add a pubcid in local storage - utils.setDataInLocalStorage(ID_NAME + EXP, ''); - utils.setDataInLocalStorage(ID_NAME, 'abcde'); + storage.setDataInLocalStorage(ID_NAME + EXP, ''); + storage.setDataInLocalStorage(ID_NAME, 'abcde'); // construct http post payload const payload = spec.buildRequests(requests).data; @@ -546,8 +546,8 @@ describe('Conversant adapter tests', function() { const requests = utils.deepClone(bidRequests); // add a pubcid in local storage - utils.setDataInLocalStorage(ID_NAME + EXP, expStr(TIMEOUT)); - utils.setDataInLocalStorage(ID_NAME, 'fghijk'); + storage.setDataInLocalStorage(ID_NAME + EXP, expStr(TIMEOUT)); + storage.setDataInLocalStorage(ID_NAME, 'fghijk'); // construct http post payload const payload = spec.buildRequests(requests).data; @@ -559,8 +559,8 @@ describe('Conversant adapter tests', function() { const requests = utils.deepClone(bidRequests); // add a pubcid in local storage - utils.setDataInLocalStorage(ID_NAME + EXP, expStr(-TIMEOUT)); - utils.setDataInLocalStorage(ID_NAME, 'lmnopq'); + storage.setDataInLocalStorage(ID_NAME + EXP, expStr(-TIMEOUT)); + storage.setDataInLocalStorage(ID_NAME, 'lmnopq'); // construct http post payload const payload = spec.buildRequests(requests).data; @@ -573,8 +573,8 @@ describe('Conversant adapter tests', function() { requests[0].params.pubcid_name = CUSTOM_ID_NAME; // add a pubcid in local storage - utils.setDataInLocalStorage(CUSTOM_ID_NAME + EXP, expStr(TIMEOUT)); - utils.setDataInLocalStorage(CUSTOM_ID_NAME, 'fghijk'); + storage.setDataInLocalStorage(CUSTOM_ID_NAME + EXP, expStr(TIMEOUT)); + storage.setDataInLocalStorage(CUSTOM_ID_NAME, 'fghijk'); // construct http post payload const payload = spec.buildRequests(requests).data; diff --git a/test/spec/modules/criteoIdSystem_spec.js b/test/spec/modules/criteoIdSystem_spec.js index c36852251f8..dee9a0563e2 100644 --- a/test/spec/modules/criteoIdSystem_spec.js +++ b/test/spec/modules/criteoIdSystem_spec.js @@ -1,4 +1,4 @@ -import { criteoIdSubmodule } from 'modules/criteoIdSystem.js'; +import { criteoIdSubmodule, storage } from 'modules/criteoIdSystem.js'; import * as utils from 'src/utils.js'; import * as ajaxLib from 'src/ajax.js'; import * as urlLib from 'src/url.js'; @@ -27,11 +27,11 @@ describe('CriteoId module', function () { let triggerPixelStub; beforeEach(function (done) { - getCookieStub = sinon.stub(utils, 'getCookie'); - setCookieStub = sinon.stub(utils, 'setCookie'); - getLocalStorageStub = sinon.stub(utils, 'getDataFromLocalStorage'); - setLocalStorageStub = sinon.stub(utils, 'setDataInLocalStorage'); - removeFromLocalStorageStub = sinon.stub(utils, 'removeDataFromLocalStorage'); + getCookieStub = sinon.stub(storage, 'getCookie'); + setCookieStub = sinon.stub(storage, 'setCookie'); + getLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); + setLocalStorageStub = sinon.stub(storage, 'setDataInLocalStorage'); + removeFromLocalStorageStub = sinon.stub(storage, 'removeDataFromLocalStorage'); timeStampStub = sinon.stub(utils, 'timestamp').returns(nowTimestamp); ajaxBuilderStub = sinon.stub(ajaxLib, 'ajaxBuilder').callsFake(mockResponse('{}')); parseUrlStub = sinon.stub(urlLib, 'parse').returns({protocol: 'https', hostname: 'testdev.com'}) diff --git a/test/spec/modules/emoteevBidAdapter_spec.js b/test/spec/modules/emoteevBidAdapter_spec.js index b967eee33b6..bf5bcc9e063 100644 --- a/test/spec/modules/emoteevBidAdapter_spec.js +++ b/test/spec/modules/emoteevBidAdapter_spec.js @@ -47,6 +47,7 @@ import { validateExternalId, VENDOR_ID, WALLPAPER, + storage } from 'modules/emoteevBidAdapter.js'; import * as url from '../../../src/url.js'; import * as utils from '../../../src/utils.js'; @@ -749,7 +750,7 @@ describe('emoteevBidAdapter', function () { }); beforeEach(function () { triggerPixelStub = sinon.stub(utils, 'triggerPixel'); - getCookieSpy = sinon.spy(utils, 'getCookie'); + getCookieSpy = sinon.spy(storage, 'getCookie'); getConfigSpy = sinon.spy(config, 'getConfig'); getParameterByNameSpy = sinon.spy(utils, 'getParameterByName'); }); @@ -776,7 +777,7 @@ describe('emoteevBidAdapter', function () { }; spec.isBidRequestValid(validBidRequest); sinon.assert.notCalled(utils.triggerPixel); - sinon.assert.notCalled(utils.getCookie); + sinon.assert.notCalled(storage.getCookie); // sinon.assert.notCalled(config.getConfig); sinon.assert.notCalled(utils.getParameterByName); }); @@ -786,7 +787,7 @@ describe('emoteevBidAdapter', function () { const invalidBidRequest = {}; spec.isBidRequestValid(invalidBidRequest); sinon.assert.notCalled(utils.triggerPixel); - sinon.assert.notCalled(utils.getCookie); + sinon.assert.notCalled(storage.getCookie); // disabling these getConfig tests as they have been flaky in browserstack testing // sinon.assert.notCalled(config.getConfig); sinon.assert.notCalled(utils.getParameterByName); @@ -796,7 +797,7 @@ describe('emoteevBidAdapter', function () { it('has intended side-effects', function () { spec.buildRequests(cannedValidBidRequests, cannedBidderRequest); sinon.assert.notCalled(utils.triggerPixel); - sinon.assert.notCalled(utils.getCookie); + sinon.assert.notCalled(storage.getCookie); // sinon.assert.callCount(config.getConfig, 3); sinon.assert.callCount(utils.getParameterByName, 2); }); @@ -805,7 +806,7 @@ describe('emoteevBidAdapter', function () { it('has intended side-effects', function () { spec.interpretResponse(serverResponse); sinon.assert.notCalled(utils.triggerPixel); - sinon.assert.notCalled(utils.getCookie); + sinon.assert.notCalled(storage.getCookie); // sinon.assert.notCalled(config.getConfig); sinon.assert.notCalled(utils.getParameterByName); }); @@ -815,7 +816,7 @@ describe('emoteevBidAdapter', function () { const bidObject = serverResponse.body[0]; spec.onBidWon(bidObject); sinon.assert.calledOnce(utils.triggerPixel); - sinon.assert.calledOnce(utils.getCookie); + sinon.assert.calledOnce(storage.getCookie); // sinon.assert.calledOnce(config.getConfig); sinon.assert.calledOnce(utils.getParameterByName); }); @@ -824,7 +825,7 @@ describe('emoteevBidAdapter', function () { it('has intended side-effects', function () { spec.onTimeout(cannedValidBidRequests[0]); sinon.assert.calledOnce(utils.triggerPixel); - sinon.assert.notCalled(utils.getCookie); + sinon.assert.notCalled(storage.getCookie); // sinon.assert.calledOnce(config.getConfig); sinon.assert.calledOnce(utils.getParameterByName); }); @@ -833,7 +834,7 @@ describe('emoteevBidAdapter', function () { it('has intended side-effects', function () { spec.getUserSyncs({}); sinon.assert.notCalled(utils.triggerPixel); - sinon.assert.notCalled(utils.getCookie); + sinon.assert.notCalled(storage.getCookie); // sinon.assert.calledOnce(config.getConfig); sinon.assert.calledOnce(utils.getParameterByName); }); diff --git a/test/spec/modules/eplanningBidAdapter_spec.js b/test/spec/modules/eplanningBidAdapter_spec.js index e9d478f911f..ff03bf033af 100644 --- a/test/spec/modules/eplanningBidAdapter_spec.js +++ b/test/spec/modules/eplanningBidAdapter_spec.js @@ -1,7 +1,6 @@ import { expect } from 'chai'; -import { spec } from 'modules/eplanningBidAdapter.js'; +import { spec, storage } from 'modules/eplanningBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; -import * as utils from 'src/utils.js'; describe('E-Planning Adapter', function () { const adapter = newBidder('spec'); @@ -559,10 +558,10 @@ describe('E-Planning Adapter', function () { }); } beforeEach(function () { - getLocalStorageSpy = sandbox.spy(utils, 'getDataFromLocalStorage'); - setDataInLocalStorageSpy = sandbox.spy(utils, 'setDataInLocalStorage'); + getLocalStorageSpy = sandbox.spy(storage, 'getDataFromLocalStorage'); + setDataInLocalStorageSpy = sandbox.spy(storage, 'setDataInLocalStorage'); - hasLocalStorageStub = sandbox.stub(utils, 'hasLocalStorage'); + hasLocalStorageStub = sandbox.stub(storage, 'hasLocalStorage'); hasLocalStorageStub.returns(true); clock = sandbox.useFakeTimers(); @@ -603,7 +602,7 @@ describe('E-Planning Adapter', function () { sinon.assert.calledWith(getLocalStorageSpy, storageIdRender); sinon.assert.calledWith(setDataInLocalStorageSpy, storageIdRender); - expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(storage.getDataFromLocalStorage(storageIdRender)).to.equal('1'); }); context('when element is fully in view', function() { @@ -617,29 +616,29 @@ describe('E-Planning Adapter', function () { expect(respuesta.data.vs).to.equal('F'); - expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); - expect(utils.getDataFromLocalStorage(storageIdView)).to.equal('1'); + expect(storage.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(storage.getDataFromLocalStorage(storageIdView)).to.equal('1'); }); it('when you have more than four render', function() { - utils.setDataInLocalStorage(storageIdRender, 4); + storage.setDataInLocalStorage(storageIdRender, 4); respuesta = spec.buildRequests(bidRequests, bidderRequest); clock.tick(1005); expect(respuesta.data.vs).to.equal('0'); - expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('5'); - expect(utils.getDataFromLocalStorage(storageIdView)).to.equal('1'); + expect(storage.getDataFromLocalStorage(storageIdRender)).to.equal('5'); + expect(storage.getDataFromLocalStorage(storageIdView)).to.equal('1'); }); it('when you have more than four render and already record visibility', function() { - utils.setDataInLocalStorage(storageIdRender, 4); - utils.setDataInLocalStorage(storageIdView, 4); + storage.setDataInLocalStorage(storageIdRender, 4); + storage.setDataInLocalStorage(storageIdView, 4); respuesta = spec.buildRequests(bidRequests, bidderRequest); clock.tick(1005); expect(respuesta.data.vs).to.equal('a'); - expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('5'); - expect(utils.getDataFromLocalStorage(storageIdView)).to.equal('5'); + expect(storage.getDataFromLocalStorage(storageIdRender)).to.equal('5'); + expect(storage.getDataFromLocalStorage(storageIdView)).to.equal('5'); }); }); @@ -654,17 +653,17 @@ describe('E-Planning Adapter', function () { clock.tick(1005); expect(respuesta.data.vs).to.equal('F'); - expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); - expect(utils.getDataFromLocalStorage(storageIdView)).to.equal(null); + expect(storage.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(storage.getDataFromLocalStorage(storageIdView)).to.equal(null); }); it('when you have more than four render', function() { - utils.setDataInLocalStorage(storageIdRender, 4); + storage.setDataInLocalStorage(storageIdRender, 4); respuesta = spec.buildRequests(bidRequests, bidderRequest); clock.tick(1005); expect(respuesta.data.vs).to.equal('0'); - expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('5'); - expect(utils.getDataFromLocalStorage(storageIdView)).to.equal(null); + expect(storage.getDataFromLocalStorage(storageIdRender)).to.equal('5'); + expect(storage.getDataFromLocalStorage(storageIdView)).to.equal(null); }); }); @@ -675,16 +674,16 @@ describe('E-Planning Adapter', function () { respuesta = spec.buildRequests(bidRequests, bidderRequest); clock.tick(1005); - expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); - expect(utils.getDataFromLocalStorage(storageIdView)).to.equal('1'); + expect(storage.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(storage.getDataFromLocalStorage(storageIdView)).to.equal('1'); }); it('you should not register visibility with less than 50%', function() { createPartiallyInvisibleElement(); respuesta = spec.buildRequests(bidRequests, bidderRequest); clock.tick(1005); - expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); - expect(utils.getDataFromLocalStorage(storageIdView)).to.equal(null); + expect(storage.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(storage.getDataFromLocalStorage(storageIdView)).to.equal(null); }); }); context('when width or height of the element is zero', function() { @@ -696,16 +695,16 @@ describe('E-Planning Adapter', function () { spec.buildRequests(bidRequests, bidderRequest) clock.tick(1005); - expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); - expect(utils.getDataFromLocalStorage(storageIdView)).to.equal(null); + expect(storage.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(storage.getDataFromLocalStorage(storageIdView)).to.equal(null); }); it('if the height is zero but the width is within the range', function() { element.style.height = '0px'; spec.buildRequests(bidRequests, bidderRequest) clock.tick(1005); - expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); - expect(utils.getDataFromLocalStorage(storageIdView)).to.equal(null); + expect(storage.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(storage.getDataFromLocalStorage(storageIdView)).to.equal(null); }); it('if both are zero', function() { element.style.height = '0px'; @@ -713,8 +712,8 @@ describe('E-Planning Adapter', function () { spec.buildRequests(bidRequests, bidderRequest) clock.tick(1005); - expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); - expect(utils.getDataFromLocalStorage(storageIdView)).to.equal(null); + expect(storage.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(storage.getDataFromLocalStorage(storageIdView)).to.equal(null); }); }); context('when tab is inactive', function() { @@ -723,8 +722,8 @@ describe('E-Planning Adapter', function () { focusStub.returns(false); spec.buildRequests(bidRequests, bidderRequest); clock.tick(1005); - expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); - expect(utils.getDataFromLocalStorage(storageIdView)).to.equal(null); + expect(storage.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(storage.getDataFromLocalStorage(storageIdView)).to.equal(null); }); }); context('segmentBeginsBeforeTheVisibleRange', function() { @@ -732,16 +731,16 @@ describe('E-Planning Adapter', function () { createElementOutOfRange(); spec.buildRequests(bidRequests, bidderRequest); clock.tick(1005); - expect(utils.getDataFromLocalStorage(storageIdRender)).to.equal('1'); - expect(utils.getDataFromLocalStorage(storageIdView)).to.equal(null); + expect(storage.getDataFromLocalStorage(storageIdRender)).to.equal('1'); + expect(storage.getDataFromLocalStorage(storageIdView)).to.equal(null); }); }); context('when there are multiple adunit', function() { let respuesta; beforeEach(function () { [ADUNIT_CODE_VIEW, ADUNIT_CODE_VIEW2, ADUNIT_CODE_VIEW3].forEach(ac => { - utils.setDataInLocalStorage('pbsr_' + ac, 5); - utils.setDataInLocalStorage('pbvi_' + ac, 5); + storage.setDataInLocalStorage('pbsr_' + ac, 5); + storage.setDataInLocalStorage('pbvi_' + ac, 5); }); }); afterEach(function () { @@ -761,8 +760,8 @@ describe('E-Planning Adapter', function () { respuesta = spec.buildRequests(bidRequestMultiple, bidderRequest); clock.tick(1005); [ADUNIT_CODE_VIEW, ADUNIT_CODE_VIEW2, ADUNIT_CODE_VIEW3].forEach(ac => { - expect(utils.getDataFromLocalStorage('pbsr_' + ac)).to.equal('6'); - expect(utils.getDataFromLocalStorage('pbvi_' + ac)).to.equal('6'); + expect(storage.getDataFromLocalStorage('pbsr_' + ac)).to.equal('6'); + expect(storage.getDataFromLocalStorage('pbvi_' + ac)).to.equal('6'); }); expect('aaa').to.equal(respuesta.data.vs); }); @@ -774,8 +773,8 @@ describe('E-Planning Adapter', function () { respuesta = spec.buildRequests(bidRequestMultiple, bidderRequest); clock.tick(1005); [ADUNIT_CODE_VIEW, ADUNIT_CODE_VIEW2, ADUNIT_CODE_VIEW3].forEach(ac => { - expect(utils.getDataFromLocalStorage('pbsr_' + ac)).to.equal('6'); - expect(utils.getDataFromLocalStorage('pbvi_' + ac)).to.equal('5'); + expect(storage.getDataFromLocalStorage('pbsr_' + ac)).to.equal('6'); + expect(storage.getDataFromLocalStorage('pbvi_' + ac)).to.equal('5'); }); expect('aaa').to.equal(respuesta.data.vs); @@ -787,11 +786,11 @@ describe('E-Planning Adapter', function () { respuesta = spec.buildRequests(bidRequestMultiple, bidderRequest); clock.tick(1005); - expect(utils.getDataFromLocalStorage('pbsr_' + ADUNIT_CODE_VIEW)).to.equal('6'); - expect(utils.getDataFromLocalStorage('pbvi_' + ADUNIT_CODE_VIEW)).to.equal('6'); + expect(storage.getDataFromLocalStorage('pbsr_' + ADUNIT_CODE_VIEW)).to.equal('6'); + expect(storage.getDataFromLocalStorage('pbvi_' + ADUNIT_CODE_VIEW)).to.equal('6'); [ADUNIT_CODE_VIEW2, ADUNIT_CODE_VIEW3].forEach(ac => { - expect(utils.getDataFromLocalStorage('pbsr_' + ac)).to.equal('6'); - expect(utils.getDataFromLocalStorage('pbvi_' + ac)).to.equal('5'); + expect(storage.getDataFromLocalStorage('pbsr_' + ac)).to.equal('6'); + expect(storage.getDataFromLocalStorage('pbvi_' + ac)).to.equal('5'); }); expect('aaa').to.equal(respuesta.data.vs); }); diff --git a/test/spec/modules/gdprEnforcement_spec.js b/test/spec/modules/gdprEnforcement_spec.js new file mode 100644 index 00000000000..5b46441cbbb --- /dev/null +++ b/test/spec/modules/gdprEnforcement_spec.js @@ -0,0 +1,418 @@ +import { deviceAccessHook, setEnforcementConfig, userSyncHook, userIdHook } from 'modules/gdprEnforcement.js'; +import { config } from 'src/config.js'; +import adapterManager, { gdprDataHandler } from 'src/adapterManager.js'; +import * as utils from 'src/utils.js'; +import { validateStorageEnforcement } from 'src/storageManager.js'; +import { executeStorageCallbacks } from 'src/prebid.js'; + +describe('gdpr enforcement', function() { + let nextFnSpy; + let logWarnSpy; + let gdprDataHandlerStub; + let staticConfig = { + cmpApi: 'static', + timeout: 7500, + allowAuctionWithoutConsent: false, + consentData: { + getTCData: { + 'tcString': 'COuqj-POu90rDBcBkBENAZCgAPzAAAPAACiQFwwBAABAA1ADEAbQC4YAYAAgAxAG0A', + 'cmpId': 92, + 'cmpVersion': 100, + 'tcfPolicyVersion': 2, + 'gdprApplies': true, + 'isServiceSpecific': true, + 'useNonStandardStacks': false, + 'purposeOneTreatment': false, + 'publisherCC': 'US', + 'cmpStatus': 'loaded', + 'eventStatus': 'tcloaded', + 'outOfBand': { + 'allowedVendors': {}, + 'discloseVendors': {} + }, + 'purpose': { + 'consents': { + '1': true, + '2': true, + '3': true + }, + 'legitimateInterests': { + '1': false, + '2': false, + '3': false + } + }, + 'vendor': { + 'consents': { + '1': true, + '2': true, + '3': false + }, + 'legitimateInterests': { + '1': false, + '2': true, + '3': false, + '4': false, + '5': false + } + }, + 'specialFeatureOptins': { + '1': false, + '2': false + }, + 'restrictions': {}, + 'publisher': { + 'consents': { + '1': false, + '2': false, + '3': false + }, + 'legitimateInterests': { + '1': false, + '2': false, + '3': false + }, + 'customPurpose': { + 'consents': {}, + 'legitimateInterests': {} + } + } + } + } + }; + + after(function() { + validateStorageEnforcement.getHooks({hook: deviceAccessHook}).remove(); + $$PREBID_GLOBAL$$.requestBids.getHooks({hook: executeStorageCallbacks}).remove(); + }) + + describe('deviceAccessHook', function() { + beforeEach(function() { + nextFnSpy = sinon.spy(); + gdprDataHandlerStub = sinon.stub(gdprDataHandler, 'getConsentData'); + logWarnSpy = sinon.spy(utils, 'logWarn'); + }); + afterEach(function() { + config.resetConfig(); + gdprDataHandler.getConsentData.restore(); + logWarnSpy.restore(); + }); + it('should not allow device access when device access flag is set to false', function() { + config.setConfig({ + deviceAccess: false, + consentManagement: { + gdpr: { + rules: [{ + purpose: 'storage', + enforcePurpose: false, + enforceVendor: false, + vendorExceptions: ['appnexus', 'rubicon'] + }] + } + } + }); + + deviceAccessHook(nextFnSpy); + expect(nextFnSpy.calledOnce).to.equal(true); + let result = { + hasEnforcementHook: true, + valid: false + } + expect(nextFnSpy.calledWith(undefined, result)); + }); + + it('should only check for consent for vendor exceptions when enforcePurpose and enforceVendor are false', function() { + setEnforcementConfig({ + gdpr: { + rules: [{ + purpose: 'storage', + enforcePurpose: false, + enforceVendor: false, + vendorExceptions: ['appnexus'] + }] + } + }); + let consentData = {} + consentData.vendorData = staticConfig.consentData.getTCData; + consentData.gdprApplies = true; + consentData.apiVersion = 2; + gdprDataHandlerStub.returns(consentData); + + deviceAccessHook(nextFnSpy, 1, 'appnexus'); + deviceAccessHook(nextFnSpy, 5, 'rubicon'); + expect(logWarnSpy.callCount).to.equal(0); + }); + + it('should check consent for all vendors when enforcePurpose and enforceVendor are true', function() { + setEnforcementConfig({ + gdpr: { + rules: [{ + purpose: 'storage', + enforcePurpose: true, + enforceVendor: true, + }] + } + }); + let consentData = {} + consentData.vendorData = staticConfig.consentData.getTCData; + consentData.gdprApplies = true; + consentData.apiVersion = 2; + gdprDataHandlerStub.returns(consentData); + + deviceAccessHook(nextFnSpy, 1, 'appnexus'); + deviceAccessHook(nextFnSpy, 3, 'rubicon'); + expect(logWarnSpy.callCount).to.equal(1); + }); + + it('should allow device access when gdprApplies is false and hasDeviceAccess flag is true', function() { + setEnforcementConfig({ + gdpr: { + rules: [{ + purpose: 'storage', + enforcePurpose: true, + enforceVendor: true, + vendorExceptions: [] + }] + } + }); + let consentData = {} + consentData.vendorData = staticConfig.consentData.getTCData; + consentData.gdprApplies = false; + consentData.apiVersion = 2; + gdprDataHandlerStub.returns(consentData); + + deviceAccessHook(nextFnSpy, 1, 'appnexus'); + expect(nextFnSpy.calledOnce).to.equal(true); + let result = { + hasEnforcementHook: true, + valid: true + } + expect(nextFnSpy.calledWith(undefined, result)); + }); + }); + + describe('userSyncHook', function() { + let curBidderStub; + let adapterManagerStub; + + beforeEach(function() { + gdprDataHandlerStub = sinon.stub(gdprDataHandler, 'getConsentData'); + logWarnSpy = sinon.spy(utils, 'logWarn'); + curBidderStub = sinon.stub(config, 'getCurrentBidder'); + adapterManagerStub = sinon.stub(adapterManager, 'getBidAdapter'); + nextFnSpy = sinon.spy(); + }); + + afterEach(function() { + config.getCurrentBidder.restore(); + config.resetConfig(); + gdprDataHandler.getConsentData.restore(); + adapterManager.getBidAdapter.restore(); + logWarnSpy.restore(); + }); + + it('should allow bidder to do user sync if consent is true', function() { + setEnforcementConfig({ + gdpr: { + rules: [{ + purpose: 'storage', + enforcePurpose: false, + enforceVendor: true, + vendorExceptions: ['sampleBidder2'] + }] + } + }); + let consentData = {} + consentData.vendorData = staticConfig.consentData.getTCData; + consentData.gdprApplies = true; + consentData.apiVersion = 2; + gdprDataHandlerStub.returns(consentData); + + curBidderStub.returns('sampleBidder1'); + adapterManagerStub.withArgs('sampleBidder1').returns({ + getSpec: function() { + return { + 'gvlid': 1 + } + } + }); + userSyncHook(nextFnSpy); + + curBidderStub.returns('sampleBidder2'); + adapterManagerStub.withArgs('sampleBidder2').returns({ + getSpec: function() { + return { + 'gvlid': 3 + } + } + }); + userSyncHook(nextFnSpy); + expect(nextFnSpy.calledTwice).to.equal(true); + }); + + it('should not allow bidder to do user sync if user has denied consent', function() { + setEnforcementConfig({ + gdpr: { + rules: [{ + purpose: 'storage', + enforcePurpose: false, + enforceVendor: true, + vendorExceptions: [] + }] + } + }); + let consentData = {} + consentData.vendorData = staticConfig.consentData.getTCData; + consentData.apiVersion = 2; + consentData.gdprApplies = true; + gdprDataHandlerStub.returns(consentData); + + curBidderStub.returns('sampleBidder1'); + adapterManagerStub.withArgs('sampleBidder1').returns({ + getSpec: function() { + return { + 'gvlid': 1 + } + } + }); + userSyncHook(nextFnSpy); + + curBidderStub.returns('sampleBidder2'); + adapterManagerStub.withArgs('sampleBidder2').returns({ + getSpec: function() { + return { + 'gvlid': 3 + } + } + }); + userSyncHook(nextFnSpy); + expect(nextFnSpy.calledOnce).to.equal(true); + expect(logWarnSpy.callCount).to.equal(1); + }); + + it('should not check vendor consent when enforceVendor is false', function() { + setEnforcementConfig({ + gdpr: { + rules: [{ + purpose: 'storage', + enforcePurpose: true, + enforceVendor: false, + vendorExceptions: ['sampleBidder1'] + }] + } + }); + let consentData = {} + consentData.vendorData = staticConfig.consentData.getTCData; + consentData.apiVersion = 2; + consentData.gdprApplies = true; + gdprDataHandlerStub.returns(consentData); + + curBidderStub.returns('sampleBidder1'); + adapterManagerStub.withArgs('sampleBidder1').returns({ + getSpec: function() { + return { + 'gvlid': 1 + } + } + }); + userSyncHook(nextFnSpy); + + curBidderStub.returns('sampleBidder2'); + adapterManagerStub.withArgs('sampleBidder2').returns({ + getSpec: function() { + return { + 'gvlid': 3 + } + } + }); + userSyncHook(nextFnSpy); + expect(nextFnSpy.calledTwice).to.equal(true); + expect(logWarnSpy.callCount).to.equal(0); + }); + }); + + describe('userIdHook', function() { + beforeEach(function() { + logWarnSpy = sinon.spy(utils, 'logWarn'); + nextFnSpy = sinon.spy(); + }); + afterEach(function() { + config.resetConfig(); + logWarnSpy.restore(); + }); + it('should allow user id module if consent is given', function() { + setEnforcementConfig({ + gdpr: { + rules: [{ + purpose: 'storage', + enforcePurpose: false, + enforceVendor: true, + vendorExceptions: [] + }] + } + }); + let consentData = {} + consentData.vendorData = staticConfig.consentData.getTCData; + consentData.apiVersion = 2; + consentData.gdprApplies = true; + let submodules = [{ + submodule: { + gvlid: 1, + name: 'sampleUserId' + } + }] + userIdHook(nextFnSpy, submodules, consentData); + expect(nextFnSpy.calledOnce).to.equal(true); + }); + + it('should allow userId module if gdpr not in scope', function() { + let submodules = [{ + submodule: { + gvlid: 1, + name: 'sampleUserId' + } + }]; + let consentData = null; + userIdHook(nextFnSpy, submodules, consentData); + expect(nextFnSpy.calledOnce).to.equal(true); + expect(nextFnSpy.calledWith(undefined, submodules, consentData)); + }); + + it('should not allow user id module if user denied consent', function() { + setEnforcementConfig({ + gdpr: { + rules: [{ + purpose: 'storage', + enforcePurpose: false, + enforceVendor: true, + vendorExceptions: [] + }] + } + }); + let consentData = {} + consentData.vendorData = staticConfig.consentData.getTCData; + consentData.apiVersion = 2; + consentData.gdprApplies = true; + let submodules = [{ + submodule: { + gvlid: 1, + name: 'sampleUserId' + } + }, { + submodule: { + gvlid: 3, + name: 'sampleUserId1' + } + }] + userIdHook(nextFnSpy, submodules, consentData); + expect(logWarnSpy.callCount).to.equal(1); + let expectedSubmodules = [{ + submodule: { + gvlid: 1, + name: 'sampleUserId' + } + }] + expect(nextFnSpy.calledWith(undefined, expectedSubmodules, consentData)); + }); + }); +}); diff --git a/test/spec/modules/liveIntentIdSystem_spec.js b/test/spec/modules/liveIntentIdSystem_spec.js index 3df1853c9f9..c3c9c8dc38d 100644 --- a/test/spec/modules/liveIntentIdSystem_spec.js +++ b/test/spec/modules/liveIntentIdSystem_spec.js @@ -1,4 +1,4 @@ -import {liveIntentIdSubmodule, reset as resetLiveIntentIdSubmodule} from 'modules/liveIntentIdSystem.js'; +import {liveIntentIdSubmodule, reset as resetLiveIntentIdSubmodule, storage} from 'modules/liveIntentIdSystem.js'; import * as utils from 'src/utils.js'; import {uspDataHandler} from '../../../src/adapterManager.js'; import {server} from 'test/mocks/xhr.js'; @@ -17,8 +17,8 @@ describe('LiveIntentId', function () { beforeEach(function () { imgStub = sinon.stub(window, 'Image').returns(pixel); - getCookieStub = sinon.stub(utils, 'getCookie'); - getDataFromLocalStorageStub = sinon.stub(utils, 'getDataFromLocalStorage'); + getCookieStub = sinon.stub(storage, 'getCookie'); + getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); logErrorStub = sinon.stub(utils, 'logError'); consentDataStub = sinon.stub(uspDataHandler, 'getConsentData'); }); diff --git a/test/spec/modules/livewrappedBidAdapter_spec.js b/test/spec/modules/livewrappedBidAdapter_spec.js index a48c00b6890..7b26f5b533d 100644 --- a/test/spec/modules/livewrappedBidAdapter_spec.js +++ b/test/spec/modules/livewrappedBidAdapter_spec.js @@ -1,5 +1,5 @@ import {expect} from 'chai'; -import {spec} from 'modules/livewrappedBidAdapter.js'; +import {spec, storage} from 'modules/livewrappedBidAdapter.js'; import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; import { BANNER, NATIVE } from 'src/mediaTypes.js'; @@ -88,7 +88,7 @@ describe('Livewrapped adapter tests', function () { describe('buildRequests', function() { it('should make a well-formed single request object', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); let result = spec.buildRequests(bidderRequest.bids, bidderRequest); let data = JSON.parse(result.data); @@ -116,7 +116,7 @@ describe('Livewrapped adapter tests', function () { it('should make a well-formed multiple request object', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); let multiplebidRequest = clone(bidderRequest); multiplebidRequest.bids.push(clone(bidderRequest.bids[0])); multiplebidRequest.bids[1].adUnitCode = 'box_d_1'; @@ -156,7 +156,7 @@ describe('Livewrapped adapter tests', function () { it('should make a well-formed single request object with AdUnitName', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); let testbidRequest = clone(bidderRequest); testbidRequest.bids[0].params.adUnitName = 'caller id 1'; delete testbidRequest.bids[0].params.adUnitId; @@ -186,7 +186,7 @@ describe('Livewrapped adapter tests', function () { it('should make a well-formed single request object with less parameters', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); let testbidRequest = clone(bidderRequest); delete testbidRequest.bids[0].params.userId; delete testbidRequest.bids[0].params.seats; @@ -215,7 +215,7 @@ describe('Livewrapped adapter tests', function () { it('should make a well-formed single request object with less parameters, no publisherId', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); let testbidRequest = clone(bidderRequest); delete testbidRequest.bids[0].params.userId; delete testbidRequest.bids[0].params.seats; @@ -244,7 +244,7 @@ describe('Livewrapped adapter tests', function () { it('should make a well-formed single request object with app parameters', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); let testbidRequest = clone(bidderRequest); delete testbidRequest.bids[0].params.userId; delete testbidRequest.bids[0].params.seats; @@ -275,7 +275,7 @@ describe('Livewrapped adapter tests', function () { it('should make a well-formed single request object with debug parameters', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); let testbidRequest = clone(bidderRequest); delete testbidRequest.bids[0].params.userId; delete testbidRequest.bids[0].params.seats; @@ -306,7 +306,7 @@ describe('Livewrapped adapter tests', function () { it('should make a well-formed single request object with optional parameters', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); let testbidRequest = clone(bidderRequest); delete testbidRequest.bids[0].params.userId; delete testbidRequest.bids[0].params.seats; @@ -336,7 +336,7 @@ describe('Livewrapped adapter tests', function () { it('should make a well-formed single request object with ad blocker revovered parameter', function() { sandbox.stub(utils, 'getWindowTop').returns({ I12C: { Morph: 1 } }); sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); let testbidRequest = clone(bidderRequest); delete testbidRequest.bids[0].params.userId; delete testbidRequest.bids[0].params.seats; @@ -364,7 +364,7 @@ describe('Livewrapped adapter tests', function () { it('should make a well-formed single request object with native only parameters', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); let testbidRequest = clone(bidderRequest); delete testbidRequest.bids[0].params.userId; delete testbidRequest.bids[0].params.seats; @@ -393,7 +393,7 @@ describe('Livewrapped adapter tests', function () { it('should make a well-formed single request object with native and banner parameters', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); let testbidRequest = clone(bidderRequest); delete testbidRequest.bids[0].params.userId; delete testbidRequest.bids[0].params.seats; @@ -423,7 +423,7 @@ describe('Livewrapped adapter tests', function () { it('should use mediaTypes.banner.sizes before legacy sizes', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); let testbidRequest = clone(bidderRequest); delete testbidRequest.bids[0].params.userId; delete testbidRequest.bids[0].params.seats; @@ -451,7 +451,7 @@ describe('Livewrapped adapter tests', function () { it('should pass gdpr true parameters', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); let testRequest = clone(bidderRequest); testRequest.gdprConsent = { gdprApplies: true, @@ -486,7 +486,7 @@ describe('Livewrapped adapter tests', function () { it('should pass gdpr false parameters', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); let testRequest = clone(bidderRequest); testRequest.gdprConsent = { gdprApplies: false @@ -518,7 +518,7 @@ describe('Livewrapped adapter tests', function () { }); it('should pass no cookie support', function() { - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => false); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => false); sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); let result = spec.buildRequests(bidderRequest.bids, bidderRequest); let data = JSON.parse(result.data); @@ -546,7 +546,7 @@ describe('Livewrapped adapter tests', function () { }); it('should pass no cookie support Safari', function() { - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); sandbox.stub(utils, 'isSafariBrowser').callsFake(() => true); let result = spec.buildRequests(bidderRequest.bids, bidderRequest); let data = JSON.parse(result.data); @@ -605,7 +605,7 @@ describe('Livewrapped adapter tests', function () { it('should make use of pubcid if available', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); let testbidRequest = clone(bidderRequest); delete testbidRequest.bids[0].params.userId; testbidRequest.bids[0].crumbs = {pubcid: 'pubcid 123'}; @@ -636,7 +636,7 @@ describe('Livewrapped adapter tests', function () { it('should make userId take precedence over pubcid', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); let testbidRequest = clone(bidderRequest); testbidRequest.bids[0].crumbs = {pubcid: 'pubcid 123'}; let result = spec.buildRequests(testbidRequest.bids, testbidRequest); @@ -667,7 +667,7 @@ describe('Livewrapped adapter tests', function () { it('should make use of Id5-Id if available', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); let testbidRequest = clone(bidderRequest); delete testbidRequest.bids[0].params.userId; testbidRequest.bids[0].userId = {}; @@ -686,7 +686,7 @@ describe('Livewrapped adapter tests', function () { it('should make use of publisher common Id if available', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); - sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); let testbidRequest = clone(bidderRequest); delete testbidRequest.bids[0].params.userId; testbidRequest.bids[0].userId = {}; diff --git a/test/spec/modules/newborntownWebBidAdapter_spec.js b/test/spec/modules/newborntownWebBidAdapter_spec.js index 84937d00012..3d3285328fe 100644 --- a/test/spec/modules/newborntownWebBidAdapter_spec.js +++ b/test/spec/modules/newborntownWebBidAdapter_spec.js @@ -54,8 +54,8 @@ describe('NewborntownWebAdapter', function() { 'timeout': 9000, 'start': 1573123289383 } - const request = spec.buildRequests(bidderRequest['bids'], bidderRequest); it('Returns POST method', function () { + const request = spec.buildRequests(bidderRequest['bids'], bidderRequest); expect(request[0].method).to.equal('POST'); expect(request[0].url.indexOf('//us-west.solortb.com/adx/api/rtb?from=4') !== -1).to.equal(true); expect(request[0].data).to.exist; diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 936b4c3a824..93415126a0a 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -3,6 +3,9 @@ import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; import { init, requestBidsHook, setSubmoduleRegistry } from 'modules/userId/index.js'; import { parrableIdSubmodule } from 'modules/parrableIdSystem.js'; +import { newStorageManager } from 'src/storageManager.js'; + +const storage = newStorageManager(); const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; const P_COOKIE_NAME = '_parrable_eid'; @@ -52,7 +55,7 @@ describe('Parrable ID System', function() { it('should append parrableid to bid request', function(done) { // simulate existing browser local storage values - utils.setCookie( + storage.setCookie( P_COOKIE_NAME, P_COOKIE_VALUE, (new Date(Date.now() + 5000).toUTCString()) @@ -69,7 +72,7 @@ describe('Parrable ID System', function() { expect(bid.userId.parrableid).to.equal(P_COOKIE_VALUE); }); }); - utils.setCookie(P_COOKIE_NAME, '', EXPIRED_COOKIE_DATE); + storage.setCookie(P_COOKIE_NAME, '', EXPIRED_COOKIE_DATE); done(); }, { adUnits }); }); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index f717ef3a714..6ff2095665d 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -4,7 +4,8 @@ import { init, requestBidsHook, setSubmoduleRegistry, - syncDelay + syncDelay, + coreStorage } from 'modules/userId/index.js'; import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; @@ -67,27 +68,27 @@ describe('User ID', function() { } before(function() { - utils.setCookie('_pubcid_optout', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('_pubcid_optout', '', EXPIRED_COOKIE_DATE); localStorage.removeItem('_pbjs_id_optout'); localStorage.removeItem('_pubcid_optout'); }); describe('Decorate Ad Units', function() { beforeEach(function() { - utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); - utils.setCookie('pubcid_alt', 'altpubcid200000', (new Date(Date.now() + 5000).toUTCString())); - sinon.spy(utils, 'setCookie'); + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('pubcid_alt', 'altpubcid200000', (new Date(Date.now() + 5000).toUTCString())); + sinon.spy(coreStorage, 'setCookie'); }); afterEach(function () { $$PREBID_GLOBAL$$.requestBids.removeAll(); config.resetConfig(); - utils.setCookie.restore(); + coreStorage.setCookie.restore(); }); after(function() { - utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); - utils.setCookie('pubcid_alt', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('pubcid_alt', '', EXPIRED_COOKIE_DATE); }); it('Check same cookie behavior', function () { @@ -96,7 +97,7 @@ describe('User ID', function() { let innerAdUnits1; let innerAdUnits2; - let pubcid = utils.getCookie('pubcid'); + let pubcid = coreStorage.getCookie('pubcid'); expect(pubcid).to.be.null; // there should be no cookie initially setSubmoduleRegistry([pubCommonIdSubmodule]); @@ -104,7 +105,7 @@ describe('User ID', function() { config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); requestBidsHook(config => { innerAdUnits1 = config.adUnits }, {adUnits: adUnits1}); - pubcid = utils.getCookie('pubcid'); // cookies is created after requestbidHook + pubcid = coreStorage.getCookie('pubcid'); // cookies is created after requestbidHook innerAdUnits1.forEach(unit => { unit.bids.forEach(bid => { @@ -133,8 +134,8 @@ describe('User ID', function() { init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); requestBidsHook((config) => { innerAdUnits1 = config.adUnits }, {adUnits: adUnits1}); - pubcid1 = utils.getCookie('pubcid'); // get first cookie - utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); // erase cookie + pubcid1 = coreStorage.getCookie('pubcid'); // get first cookie + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); // erase cookie innerAdUnits1.forEach((unit) => { unit.bids.forEach((bid) => { @@ -152,7 +153,7 @@ describe('User ID', function() { config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); requestBidsHook((config) => { innerAdUnits2 = config.adUnits }, {adUnits: adUnits2}); - pubcid2 = utils.getCookie('pubcid'); // get second cookie + pubcid2 = coreStorage.getCookie('pubcid'); // get second cookie innerAdUnits2.forEach((unit) => { unit.bids.forEach((bid) => { @@ -187,7 +188,7 @@ describe('User ID', function() { }); }); // Because the cookie exists already, there should be no setCookie call by default - expect(utils.setCookie.callCount).to.equal(0); + expect(coreStorage.setCookie.callCount).to.equal(0); }); it('Extend cookie', function () { @@ -211,7 +212,7 @@ describe('User ID', function() { }); }); // Because extend is true, the cookie will be updated even if it exists already - expect(utils.setCookie.callCount).to.equal(1); + expect(coreStorage.setCookie.callCount).to.equal(1); }); it('Disable auto create', function () { @@ -230,13 +231,13 @@ describe('User ID', function() { expect(bid).to.not.have.deep.nested.property('userIdAsEids'); }); }); - expect(utils.setCookie.callCount).to.equal(0); + expect(coreStorage.setCookie.callCount).to.equal(0); }); }); describe('Opt out', function () { before(function () { - utils.setCookie('_pbjs_id_optout', '1', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('_pbjs_id_optout', '1', (new Date(Date.now() + 5000).toUTCString())); }); beforeEach(function () { @@ -245,14 +246,14 @@ describe('User ID', function() { afterEach(function () { // removed cookie - utils.setCookie('_pbjs_id_optout', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('_pbjs_id_optout', '', EXPIRED_COOKIE_DATE); $$PREBID_GLOBAL$$.requestBids.removeAll(); utils.logInfo.restore(); config.resetConfig(); }); after(function () { - utils.setCookie('_pbjs_id_optout', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('_pbjs_id_optout', '', EXPIRED_COOKIE_DATE); }); it('fails initialization if opt out cookie exists', function () { @@ -422,7 +423,7 @@ describe('User ID', function() { sandbox.stub(events, 'on'); // remove cookie - utils.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); adUnits = [getAdUnitMock()]; @@ -436,7 +437,7 @@ describe('User ID', function() { }; }, getId: function() { - const storedId = utils.getCookie('MOCKID'); + const storedId = coreStorage.getCookie('MOCKID'); if (storedId) { return {id: {'MOCKID': storedId}}; } @@ -593,7 +594,7 @@ describe('User ID', function() { }); it('does not delay auction if there are no ids to fetch', function() { - utils.setCookie('MOCKID', JSON.stringify({'MOCKID': '123456778'}), new Date(Date.now() + 5000).toUTCString()); + coreStorage.setCookie('MOCKID', JSON.stringify({'MOCKID': '123456778'}), new Date(Date.now() + 5000).toUTCString()); config.setConfig({ userSync: { @@ -624,7 +625,7 @@ describe('User ID', function() { }); it('test hook from pubcommonid cookie', function(done) { - utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 100000).toUTCString())); + coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 100000).toUTCString())); setSubmoduleRegistry([pubCommonIdSubmodule]); init(config); @@ -641,7 +642,7 @@ describe('User ID', function() { }); }); }); - utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); @@ -715,7 +716,7 @@ describe('User ID', function() { }); it('test hook from identityLink cookie', function(done) { - utils.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', (new Date(Date.now() + 100000).toUTCString())); + coreStorage.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', (new Date(Date.now() + 100000).toUTCString())); setSubmoduleRegistry([identityLinkSubmodule]); init(config); @@ -732,7 +733,7 @@ describe('User ID', function() { }); }); }); - utils.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); @@ -763,7 +764,7 @@ describe('User ID', function() { }); it('test hook from liveIntentId cookie', function(done) { - utils.setCookie('_li_pbid', JSON.stringify({'unifiedId': 'random-cookie-identifier'}), (new Date(Date.now() + 100000).toUTCString())); + coreStorage.setCookie('_li_pbid', JSON.stringify({'unifiedId': 'random-cookie-identifier'}), (new Date(Date.now() + 100000).toUTCString())); setSubmoduleRegistry([liveIntentIdSubmodule]); init(config); @@ -780,15 +781,15 @@ describe('User ID', function() { }); }); }); - utils.setCookie('_li_pbid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('_li_pbid', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); it('test hook from id5id cookies when refresh needed', function(done) { // simulate existing browser local storage values - utils.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); - utils.setCookie('id5id_last', (new Date(Date.now() - 7200 * 1000)).toUTCString(), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('id5id_last', (new Date(Date.now() - 7200 * 1000)).toUTCString(), (new Date(Date.now() + 5000).toUTCString())); sinon.stub(utils, 'logError'); // getId should failed with a logError as it has no partnerId @@ -808,7 +809,7 @@ describe('User ID', function() { }); }); sinon.assert.calledOnce(utils.logError); - utils.setCookie('id5id', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('id5id', '', EXPIRED_COOKIE_DATE); utils.logError.restore(); done(); }, {adUnits}); @@ -862,7 +863,7 @@ describe('User ID', function() { }); it('test hook from liveIntentId cookie', function(done) { - utils.setCookie('_li_pbid', JSON.stringify({'unifiedId': 'random-cookie-identifier', 'segments': ['123']}), (new Date(Date.now() + 100000).toUTCString())); + coreStorage.setCookie('_li_pbid', JSON.stringify({'unifiedId': 'random-cookie-identifier', 'segments': ['123']}), (new Date(Date.now() + 100000).toUTCString())); setSubmoduleRegistry([liveIntentIdSubmodule]); init(config); @@ -881,14 +882,14 @@ describe('User ID', function() { }); }); }); - utils.setCookie('_li_pbid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('_li_pbid', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); it('test hook from britepoolid cookies', function(done) { // simulate existing browser local storage values - utils.setCookie('britepoolid', JSON.stringify({'primaryBPID': '279c0161-5152-487f-809e-05d7f7e653fd'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': '279c0161-5152-487f-809e-05d7f7e653fd'}), (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([britepoolIdSubmodule]); init(config); @@ -905,14 +906,14 @@ describe('User ID', function() { }); }); }); - utils.setCookie('britepoolid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('britepoolid', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); it('test hook from netId cookies', function(done) { // simulate existing browser local storage values - utils.setCookie('netId', JSON.stringify({'netId': 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('netId', JSON.stringify({'netId': 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg'}), (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([netIdSubmodule]); init(config); @@ -929,18 +930,18 @@ describe('User ID', function() { }); }); }); - utils.setCookie('netId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('netId', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); it('test hook when pubCommonId, unifiedId, id5Id, identityLink, britepoolId and netId have data to pass', function(done) { - utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); - utils.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); - utils.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); - utils.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', (new Date(Date.now() + 5000).toUTCString())); - utils.setCookie('britepoolid', JSON.stringify({'primaryBPID': 'testbritepoolid'}), (new Date(Date.now() + 5000).toUTCString())); - utils.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': 'testbritepoolid'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule]); init(config); @@ -975,23 +976,23 @@ describe('User ID', function() { expect(bid.userIdAsEids.length).to.equal(6); }); }); - utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); - utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); - utils.setCookie('id5id', '', EXPIRED_COOKIE_DATE); - utils.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); - utils.setCookie('britepoolid', '', EXPIRED_COOKIE_DATE); - utils.setCookie('netId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('id5id', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('britepoolid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('netId', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); it('test hook when pubCommonId, unifiedId, id5Id, britepoolId and netId have their modules added before and after init', function(done) { - utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); - utils.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); - utils.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); - utils.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', new Date(Date.now() + 5000).toUTCString()); - utils.setCookie('britepoolid', JSON.stringify({'primaryBPID': 'testbritepoolid'}), (new Date(Date.now() + 5000).toUTCString())); - utils.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); + coreStorage.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', new Date(Date.now() + 5000).toUTCString()); + coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': 'testbritepoolid'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([]); @@ -1038,24 +1039,24 @@ describe('User ID', function() { expect(bid.userIdAsEids.length).to.equal(6); }); }); - utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); - utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); - utils.setCookie('id5id', '', EXPIRED_COOKIE_DATE); - utils.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); - utils.setCookie('britepoolid', '', EXPIRED_COOKIE_DATE); - utils.setCookie('netId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('id5id', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('britepoolid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('netId', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); it('should add new id system ', function(done) { - utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); - utils.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); - utils.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); - utils.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', new Date(Date.now() + 5000).toUTCString()); - utils.setCookie('britepoolid', JSON.stringify({'primaryBPID': 'testbritepoolid'}), (new Date(Date.now() + 5000).toUTCString())); - utils.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), new Date(Date.now() + 5000).toUTCString()); - utils.setCookie('MOCKID', JSON.stringify({'MOCKID': '123456778'}), new Date(Date.now() + 5000).toUTCString()); + coreStorage.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); + coreStorage.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('idl_env', 'AiGNC8Z5ONyZKSpIPf', new Date(Date.now() + 5000).toUTCString()); + coreStorage.setCookie('britepoolid', JSON.stringify({'primaryBPID': 'testbritepoolid'}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('netId', JSON.stringify({'netId': 'testnetId'}), new Date(Date.now() + 5000).toUTCString()); + coreStorage.setCookie('MOCKID', JSON.stringify({'MOCKID': '123456778'}), new Date(Date.now() + 5000).toUTCString()); setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, britepoolIdSubmodule, netIdSubmodule]); init(config); @@ -1122,13 +1123,13 @@ describe('User ID', function() { expect(bid.userIdAsEids.length).to.equal(6);// mid is unknown for eids.js }); }); - utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); - utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); - utils.setCookie('id5id', '', EXPIRED_COOKIE_DATE); - utils.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); - utils.setCookie('britepoolid', '', EXPIRED_COOKIE_DATE); - utils.setCookie('netId', '', EXPIRED_COOKIE_DATE); - utils.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('id5id', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('idl_env', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('britepoolid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('netId', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); @@ -1138,17 +1139,17 @@ describe('User ID', function() { beforeEach(function() { sinon.stub(events, 'getEvents').returns([]); sinon.stub(utils, 'triggerPixel'); - utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); - utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); - utils.setCookie('_parrable_eid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('_parrable_eid', '', EXPIRED_COOKIE_DATE); }); afterEach(function() { events.getEvents.restore(); utils.triggerPixel.restore(); - utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); - utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); - utils.setCookie('_parrable_eid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('_parrable_eid', '', EXPIRED_COOKIE_DATE); }); it('pubcid callback with url', function () { @@ -1204,7 +1205,7 @@ describe('User ID', function() { let innerAdUnits; const parrableStoredId = '01.1111111111.test-eid'; const parrableRefreshedId = '02.2222222222.test-eid'; - utils.setCookie('_parrable_eid', parrableStoredId, (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('_parrable_eid', parrableStoredId, (new Date(Date.now() + 5000).toUTCString())); const parrableIdSubmoduleMock = { name: 'parrableId', diff --git a/test/spec/modules/widespaceBidAdapter_spec.js b/test/spec/modules/widespaceBidAdapter_spec.js index c7d4f3bdaf9..95f0117e5d1 100644 --- a/test/spec/modules/widespaceBidAdapter_spec.js +++ b/test/spec/modules/widespaceBidAdapter_spec.js @@ -1,5 +1,5 @@ import {expect} from 'chai'; -import {spec} from 'modules/widespaceBidAdapter.js'; +import {spec, storage} from 'modules/widespaceBidAdapter.js'; import includes from 'core-js/library/fn/array/includes.js'; describe('+widespaceAdatperTest', function () { @@ -141,8 +141,11 @@ describe('+widespaceAdatperTest', function () { }); describe('+bidRequest', function () { - const request = spec.buildRequests(bidRequest, bidderRequest); + let request; const UrlRegExp = /^((ftp|http|https):)?\/\/[^ "]+$/; + before(function() { + request = spec.buildRequests(bidRequest, bidderRequest); + }) let fakeLocalStorage = {}; let lsSetStub; @@ -150,15 +153,15 @@ describe('+widespaceAdatperTest', function () { let lsRemoveStub; beforeEach(function () { - lsSetStub = sinon.stub(window.localStorage, 'setItem').callsFake(function (name, value) { + lsSetStub = sinon.stub(storage, 'setDataInLocalStorage').callsFake(function (name, value) { fakeLocalStorage[name] = value; }); - lsGetStub = sinon.stub(window.localStorage, 'getItem').callsFake(function (key) { + lsGetStub = sinon.stub(storage, 'getDataFromLocalStorage').callsFake(function (key) { return fakeLocalStorage[key] || null; }); - lsRemoveStub = sinon.stub(window.localStorage, 'removeItem').callsFake(function (key) { + lsRemoveStub = sinon.stub(storage, 'removeDataFromLocalStorage').callsFake(function (key) { if (key && (fakeLocalStorage[key] !== null || fakeLocalStorage[key] !== undefined)) { delete fakeLocalStorage[key]; } diff --git a/test/spec/unit/core/bidderFactory_spec.js b/test/spec/unit/core/bidderFactory_spec.js index 03635803b23..ccca9bdd0dc 100644 --- a/test/spec/unit/core/bidderFactory_spec.js +++ b/test/spec/unit/core/bidderFactory_spec.js @@ -1,4 +1,4 @@ -import { newBidder, registerBidder, preloadBidderMappingFile } from 'src/adapters/bidderFactory.js'; +import { newBidder, registerBidder, preloadBidderMappingFile, storage } from 'src/adapters/bidderFactory.js'; import adapterManager from 'src/adapterManager.js'; import * as ajax from 'src/ajax.js'; import { expect } from 'chai'; @@ -844,7 +844,7 @@ describe('preload mapping url hook', function() { beforeEach(function () { fakeTranslationServer = server; - getLocalStorageStub = sinon.stub(utils, 'getDataFromLocalStorage'); + getLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); adapterManagerStub = sinon.stub(adapterManager, 'getBidAdapter'); }); diff --git a/test/spec/unit/core/storageManager_spec.js b/test/spec/unit/core/storageManager_spec.js new file mode 100644 index 00000000000..0b406242f90 --- /dev/null +++ b/test/spec/unit/core/storageManager_spec.js @@ -0,0 +1,46 @@ +import { resetData, getCoreStorageManager, storageCallbacks, getStorageManager } from 'src/storageManager.js'; +import { config } from 'src/config.js'; +import * as utils from 'src/utils.js'; + +describe('storage manager', function() { + beforeEach(function() { + resetData(); + }); + + afterEach(function() { + config.resetConfig(); + }) + + it('should allow to set cookie for core modules without checking gdpr enforcements', function() { + const coreStorage = getCoreStorageManager(); + let date = new Date(); + date.setTime(date.getTime() + (24 * 60 * 60 * 1000)); + let expires = date.toUTCString(); + coreStorage.setCookie('hello', 'world', expires); + expect(coreStorage.getCookie('hello')).to.equal('world'); + }); + + it('should add done callbacks to storageCallbacks array', function() { + let noop = sinon.spy(); + const coreStorage = getStorageManager(); + + coreStorage.setCookie('foo', 'bar', null, null, null, noop); + coreStorage.getCookie('foo', noop); + coreStorage.localStorageIsEnabled(noop); + coreStorage.cookiesAreEnabled(noop); + coreStorage.setDataInLocalStorage('foo', 'bar', noop); + coreStorage.getDataFromLocalStorage('foo', noop); + coreStorage.removeDataFromLocalStorage('foo', noop); + coreStorage.hasLocalStorage(noop); + + expect(storageCallbacks.length).to.equal(8); + }); + + it('should allow bidder to access device if gdpr enforcement module is not included', function() { + let deviceAccessSpy = sinon.spy(utils, 'hasDeviceAccess'); + const storage = getStorageManager(); + storage.setCookie('foo1', 'baz1'); + expect(deviceAccessSpy.calledOnce).to.equal(true); + deviceAccessSpy.restore(); + }) +}); diff --git a/test/spec/utils_spec.js b/test/spec/utils_spec.js index d98e7492e52..e7be6880444 100755 --- a/test/spec/utils_spec.js +++ b/test/spec/utils_spec.js @@ -356,14 +356,6 @@ describe('Utils', function () { }); }); - describe('cookie functions', function() { - it('returns an array of cookies in a jar that have a similar name', function() { - utils.setCookie('cookie-a', 'cookie-value-a'); - utils.setCookie('cookie-b', 'cookie-value-b'); - expect(utils.findSimilarCookies('cookie')).to.include.members(['cookie-value-a', 'cookie-value-b']); - }); - }); - describe('isStr', function () { it('should return true with input string', function () { var output = utils.isStr(obj_string);