Skip to content

Commit

Permalink
Core: allow restriction of cookies / localStorage through `bidderSett…
Browse files Browse the repository at this point in the history
…ings.*.storageAllowed` (prebid#9660)

* Core: allow restriction of cookies / localStorage through `bidderSettings.*.storageAllowed`

* Add test cases
  • Loading branch information
dgirardi authored and jorgeluisrocha committed May 18, 2023
1 parent b7bc988 commit eb44ffc
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 115 deletions.
11 changes: 6 additions & 5 deletions modules/userId/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,22 +133,23 @@ import adapterManager, {gdprDataHandler} from '../../src/adapterManager.js';
import CONSTANTS from '../../src/constants.json';
import {hook, module, ready as hooksReady} from '../../src/hook.js';
import {buildEidPermissions, createEidsArray, USER_IDS_CONFIG} from './eids.js';
import {getCoreStorageManager} from '../../src/storageManager.js';
import {getCoreStorageManager, STORAGE_TYPE_COOKIES, STORAGE_TYPE_LOCALSTORAGE} from '../../src/storageManager.js';
import {
cyrb53Hash,
deepAccess,
deepSetValue,
delayExecution,
getPrebidInternal,
isArray,
isEmpty,
isEmptyStr,
isFn,
isGptPubadsDefined,
isNumber,
isPlainObject,
logError,
logInfo,
logWarn,
isEmpty, deepSetValue
logWarn
} from '../../src/utils.js';
import {getPPID as coreGetPPID} from '../../src/adserver.js';
import {defer, GreedyPromise} from '../../src/utils/promise.js';
Expand All @@ -158,8 +159,8 @@ import {newMetrics, timedAuctionHook, useMetrics} from '../../src/utils/perfMetr
import {findRootDomain} from '../../src/fpd/rootDomain.js';

const MODULE_NAME = 'User ID';
const COOKIE = 'cookie';
const LOCAL_STORAGE = 'html5';
const COOKIE = STORAGE_TYPE_COOKIES;
const LOCAL_STORAGE = STORAGE_TYPE_LOCALSTORAGE;
const DEFAULT_SYNC_DELAY = 500;
const NO_AUCTION_DELAY = 0;
const CONSENT_DATA_COOKIE_STORAGE_CONFIG = {
Expand Down
105 changes: 29 additions & 76 deletions src/storageManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ const moduleTypeWhiteList = ['core', 'prebid-module'];

export let storageCallbacks = [];

export const STORAGE_TYPE_LOCALSTORAGE = 'html5';
export const STORAGE_TYPE_COOKIES = 'cookie';

/**
* Storage options
* @typedef {Object} storageOptions
Expand All @@ -26,20 +29,22 @@ export let storageCallbacks = [];
* @param {storageOptions} options
*/
export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} = {}, {bidderSettings = defaultBidderSettings} = {}) {
function isBidderAllowed() {
function isBidderAllowed(storageType) {
if (bidderCode == null) {
return true;
}
const storageAllowed = bidderSettings.get(bidderCode, 'storageAllowed');
return storageAllowed == null ? false : storageAllowed;
if (!storageAllowed || storageAllowed === true) return !!storageAllowed;
if (Array.isArray(storageAllowed)) return storageAllowed.some((e) => e === storageType);
return storageAllowed === storageType;
}

if (moduleTypeWhiteList.includes(moduleType)) {
gvlid = gvlid || VENDORLESS_GVLID;
}

function isValid(cb) {
if (!isBidderAllowed()) {
function isValid(cb, storageType) {
if (!isBidderAllowed(storageType)) {
logInfo(`bidderSettings denied access to device storage for bidder '${bidderCode}'`);
const result = {valid: false};
return cb(result);
Expand All @@ -63,6 +68,17 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
}
}

function schedule(operation, storageType, done) {
if (done && typeof done === 'function') {
storageCallbacks.push(function() {
let result = isValid(operation, storageType);
done(result);
});
} else {
return isValid(operation, storageType);
}
}

/**
* @param {string} key
* @param {string} value
Expand All @@ -83,14 +99,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
document.cookie = `${key}=${encodeURIComponent(value)}${expiresPortion}; path=/${domainPortion}${sameSite ? `; SameSite=${sameSite}` : ''}${secure}`;
}
}
if (done && typeof done === 'function') {
storageCallbacks.push(function() {
let result = isValid(cb);
done(result);
});
} else {
return isValid(cb);
}
return schedule(cb, STORAGE_TYPE_COOKIES, done);
};

/**
Expand All @@ -105,14 +114,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
}
return null;
}
if (done && typeof done === 'function') {
storageCallbacks.push(function() {
let result = isValid(cb);
done(result);
});
} else {
return isValid(cb);
}
return schedule(cb, STORAGE_TYPE_COOKIES, done);
};

/**
Expand All @@ -133,14 +135,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
}
return false;
}
if (done && typeof done === 'function') {
storageCallbacks.push(function() {
let result = isValid(cb);
done(result);
});
} else {
return isValid(cb);
}
return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done);
}

/**
Expand All @@ -153,14 +148,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
}
return false;
}
if (done && typeof done === 'function') {
storageCallbacks.push(function() {
let result = isValid(cb);
done(result);
});
} else {
return isValid(cb);
}
return schedule(cb, STORAGE_TYPE_COOKIES, done);
}

/**
Expand All @@ -173,14 +161,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
window.localStorage.setItem(key, value);
}
}
if (done && typeof done === 'function') {
storageCallbacks.push(function() {
let result = isValid(cb);
done(result);
});
} else {
return isValid(cb);
}
return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done);
}

/**
Expand All @@ -194,14 +175,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
}
return null;
}
if (done && typeof done === 'function') {
storageCallbacks.push(function() {
let result = isValid(cb);
done(result);
});
} else {
return isValid(cb);
}
return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done);
}

/**
Expand All @@ -213,14 +187,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
window.localStorage.removeItem(key);
}
}
if (done && typeof done === 'function') {
storageCallbacks.push(function() {
let result = isValid(cb);
done(result);
});
} else {
return isValid(cb);
}
return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done);
}

/**
Expand All @@ -237,14 +204,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
}
return false;
}
if (done && typeof done === 'function') {
storageCallbacks.push(function() {
let result = isValid(cb);
done(result);
});
} else {
return isValid(cb);
}
return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done);
}

/**
Expand Down Expand Up @@ -273,14 +233,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
}
}

if (done && typeof done === 'function') {
storageCallbacks.push(function() {
let result = isValid(cb);
done(result);
});
} else {
return isValid(cb);
}
return schedule(cb, STORAGE_TYPE_COOKIES, done);
}

return {
Expand Down
118 changes: 84 additions & 34 deletions test/spec/unit/core/storageManager_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,11 @@ describe('storage manager', function() {
const COOKIE = 'test-cookie';
const LS_KEY = 'test-localstorage';

function mockBidderSettings() {
function mockBidderSettings(val) {
return {
get(bidder, key) {
if (bidder === ALLOWED_BIDDER && key === ALLOW_KEY) {
return true;
return val;
} else {
return undefined;
}
Expand All @@ -157,39 +157,89 @@ describe('storage manager', function() {
Object.entries({
disallowed: ['denied_bidder', false],
allowed: [ALLOWED_BIDDER, true]
}).forEach(([test, [bidderCode, shouldWork]]) => {
describe(`for ${test} bidders`, () => {
let mgr;

beforeEach(() => {
mgr = newStorageManager({bidderCode: bidderCode}, {bidderSettings: mockBidderSettings()});
})

afterEach(() => {
mgr.setCookie(COOKIE, 'delete', new Date().toUTCString());
mgr.removeDataFromLocalStorage(LS_KEY);
})

const testDesc = (desc) => `should ${shouldWork ? '' : 'not'} ${desc}`;

it(testDesc('allow cookies'), () => {
mgr.setCookie(COOKIE, 'value');
expect(mgr.getCookie(COOKIE)).to.equal(shouldWork ? 'value' : null);
});

it(testDesc('allow localStorage'), () => {
mgr.setDataInLocalStorage(LS_KEY, 'value');
expect(mgr.getDataFromLocalStorage(LS_KEY)).to.equal(shouldWork ? 'value' : null);
});

it(testDesc('report localStorage as available'), () => {
expect(mgr.hasLocalStorage()).to.equal(shouldWork);
});

it(testDesc('report cookies as available'), () => {
expect(mgr.cookiesAreEnabled()).to.equal(shouldWork);
}).forEach(([t, [bidderCode, isBidderAllowed]]) => {
describe(`for ${t} bidders`, () => {
Object.entries({
'all': {
configValues: [
true,
['html5', 'cookie']
],
shouldWork: {
html5: true,
cookie: true
}
},
'none': {
configValues: [
false,
[]
],
shouldWork: {
html5: false,
cookie: false
}
},
'localStorage': {
configValues: [
'html5',
['html5']
],
shouldWork: {
html5: true,
cookie: false
}
},
'cookies': {
configValues: [
'cookie',
['cookie']
],
shouldWork: {
html5: false,
cookie: true
}
}
}).forEach(([t, {configValues, shouldWork: {cookie, html5}}]) => {
describe(`when ${t} is allowed`, () => {
configValues.forEach(configValue => describe(`storageAllowed = ${configValue}`, () => {
let mgr;

beforeEach(() => {
mgr = newStorageManager({bidderCode: bidderCode}, {bidderSettings: mockBidderSettings(configValue)});
})

afterEach(() => {
mgr.setCookie(COOKIE, 'delete', new Date().toUTCString());
mgr.removeDataFromLocalStorage(LS_KEY);
})

function scenario(type, desc, fn) {
const shouldWork = isBidderAllowed && ({html5, cookie})[type];
it(`${shouldWork ? '' : 'NOT'} ${desc}`, () => fn(shouldWork));
}

scenario('cookie', 'allow cookies', (shouldWork) => {
mgr.setCookie(COOKIE, 'value');
expect(mgr.getCookie(COOKIE)).to.equal(shouldWork ? 'value' : null);
});

scenario('html5', 'allow localStorage', (shouldWork) => {
mgr.setDataInLocalStorage(LS_KEY, 'value');
expect(mgr.getDataFromLocalStorage(LS_KEY)).to.equal(shouldWork ? 'value' : null);
});

scenario('html5', 'report localStorage as available', (shouldWork) => {
expect(mgr.hasLocalStorage()).to.equal(shouldWork);
});

scenario('cookie', 'report cookies as available', (shouldWork) => {
expect(mgr.cookiesAreEnabled()).to.equal(shouldWork);
});
}));
});
});
});
});
})
});
});

0 comments on commit eb44ffc

Please sign in to comment.