Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 2 commits into from
Mar 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
});
}));
});
});
});
});
})
});
});