Skip to content

Commit eb44ffc

Browse files
dgirardijorgeluisrocha
authored andcommitted
Core: allow restriction of cookies / localStorage through bidderSettings.*.storageAllowed (prebid#9660)
* Core: allow restriction of cookies / localStorage through `bidderSettings.*.storageAllowed` * Add test cases
1 parent b7bc988 commit eb44ffc

File tree

3 files changed

+119
-115
lines changed

3 files changed

+119
-115
lines changed

modules/userId/index.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -133,22 +133,23 @@ import adapterManager, {gdprDataHandler} from '../../src/adapterManager.js';
133133
import CONSTANTS from '../../src/constants.json';
134134
import {hook, module, ready as hooksReady} from '../../src/hook.js';
135135
import {buildEidPermissions, createEidsArray, USER_IDS_CONFIG} from './eids.js';
136-
import {getCoreStorageManager} from '../../src/storageManager.js';
136+
import {getCoreStorageManager, STORAGE_TYPE_COOKIES, STORAGE_TYPE_LOCALSTORAGE} from '../../src/storageManager.js';
137137
import {
138138
cyrb53Hash,
139139
deepAccess,
140+
deepSetValue,
140141
delayExecution,
141142
getPrebidInternal,
142143
isArray,
144+
isEmpty,
143145
isEmptyStr,
144146
isFn,
145147
isGptPubadsDefined,
146148
isNumber,
147149
isPlainObject,
148150
logError,
149151
logInfo,
150-
logWarn,
151-
isEmpty, deepSetValue
152+
logWarn
152153
} from '../../src/utils.js';
153154
import {getPPID as coreGetPPID} from '../../src/adserver.js';
154155
import {defer, GreedyPromise} from '../../src/utils/promise.js';
@@ -158,8 +159,8 @@ import {newMetrics, timedAuctionHook, useMetrics} from '../../src/utils/perfMetr
158159
import {findRootDomain} from '../../src/fpd/rootDomain.js';
159160

160161
const MODULE_NAME = 'User ID';
161-
const COOKIE = 'cookie';
162-
const LOCAL_STORAGE = 'html5';
162+
const COOKIE = STORAGE_TYPE_COOKIES;
163+
const LOCAL_STORAGE = STORAGE_TYPE_LOCALSTORAGE;
163164
const DEFAULT_SYNC_DELAY = 500;
164165
const NO_AUCTION_DELAY = 0;
165166
const CONSENT_DATA_COOKIE_STORAGE_CONFIG = {

src/storageManager.js

+29-76
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ const moduleTypeWhiteList = ['core', 'prebid-module'];
77

88
export let storageCallbacks = [];
99

10+
export const STORAGE_TYPE_LOCALSTORAGE = 'html5';
11+
export const STORAGE_TYPE_COOKIES = 'cookie';
12+
1013
/**
1114
* Storage options
1215
* @typedef {Object} storageOptions
@@ -26,20 +29,22 @@ export let storageCallbacks = [];
2629
* @param {storageOptions} options
2730
*/
2831
export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} = {}, {bidderSettings = defaultBidderSettings} = {}) {
29-
function isBidderAllowed() {
32+
function isBidderAllowed(storageType) {
3033
if (bidderCode == null) {
3134
return true;
3235
}
3336
const storageAllowed = bidderSettings.get(bidderCode, 'storageAllowed');
34-
return storageAllowed == null ? false : storageAllowed;
37+
if (!storageAllowed || storageAllowed === true) return !!storageAllowed;
38+
if (Array.isArray(storageAllowed)) return storageAllowed.some((e) => e === storageType);
39+
return storageAllowed === storageType;
3540
}
3641

3742
if (moduleTypeWhiteList.includes(moduleType)) {
3843
gvlid = gvlid || VENDORLESS_GVLID;
3944
}
4045

41-
function isValid(cb) {
42-
if (!isBidderAllowed()) {
46+
function isValid(cb, storageType) {
47+
if (!isBidderAllowed(storageType)) {
4348
logInfo(`bidderSettings denied access to device storage for bidder '${bidderCode}'`);
4449
const result = {valid: false};
4550
return cb(result);
@@ -63,6 +68,17 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
6368
}
6469
}
6570

71+
function schedule(operation, storageType, done) {
72+
if (done && typeof done === 'function') {
73+
storageCallbacks.push(function() {
74+
let result = isValid(operation, storageType);
75+
done(result);
76+
});
77+
} else {
78+
return isValid(operation, storageType);
79+
}
80+
}
81+
6682
/**
6783
* @param {string} key
6884
* @param {string} value
@@ -83,14 +99,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
8399
document.cookie = `${key}=${encodeURIComponent(value)}${expiresPortion}; path=/${domainPortion}${sameSite ? `; SameSite=${sameSite}` : ''}${secure}`;
84100
}
85101
}
86-
if (done && typeof done === 'function') {
87-
storageCallbacks.push(function() {
88-
let result = isValid(cb);
89-
done(result);
90-
});
91-
} else {
92-
return isValid(cb);
93-
}
102+
return schedule(cb, STORAGE_TYPE_COOKIES, done);
94103
};
95104

96105
/**
@@ -105,14 +114,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
105114
}
106115
return null;
107116
}
108-
if (done && typeof done === 'function') {
109-
storageCallbacks.push(function() {
110-
let result = isValid(cb);
111-
done(result);
112-
});
113-
} else {
114-
return isValid(cb);
115-
}
117+
return schedule(cb, STORAGE_TYPE_COOKIES, done);
116118
};
117119

118120
/**
@@ -133,14 +135,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
133135
}
134136
return false;
135137
}
136-
if (done && typeof done === 'function') {
137-
storageCallbacks.push(function() {
138-
let result = isValid(cb);
139-
done(result);
140-
});
141-
} else {
142-
return isValid(cb);
143-
}
138+
return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done);
144139
}
145140

146141
/**
@@ -153,14 +148,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
153148
}
154149
return false;
155150
}
156-
if (done && typeof done === 'function') {
157-
storageCallbacks.push(function() {
158-
let result = isValid(cb);
159-
done(result);
160-
});
161-
} else {
162-
return isValid(cb);
163-
}
151+
return schedule(cb, STORAGE_TYPE_COOKIES, done);
164152
}
165153

166154
/**
@@ -173,14 +161,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
173161
window.localStorage.setItem(key, value);
174162
}
175163
}
176-
if (done && typeof done === 'function') {
177-
storageCallbacks.push(function() {
178-
let result = isValid(cb);
179-
done(result);
180-
});
181-
} else {
182-
return isValid(cb);
183-
}
164+
return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done);
184165
}
185166

186167
/**
@@ -194,14 +175,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
194175
}
195176
return null;
196177
}
197-
if (done && typeof done === 'function') {
198-
storageCallbacks.push(function() {
199-
let result = isValid(cb);
200-
done(result);
201-
});
202-
} else {
203-
return isValid(cb);
204-
}
178+
return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done);
205179
}
206180

207181
/**
@@ -213,14 +187,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
213187
window.localStorage.removeItem(key);
214188
}
215189
}
216-
if (done && typeof done === 'function') {
217-
storageCallbacks.push(function() {
218-
let result = isValid(cb);
219-
done(result);
220-
});
221-
} else {
222-
return isValid(cb);
223-
}
190+
return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done);
224191
}
225192

226193
/**
@@ -237,14 +204,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
237204
}
238205
return false;
239206
}
240-
if (done && typeof done === 'function') {
241-
storageCallbacks.push(function() {
242-
let result = isValid(cb);
243-
done(result);
244-
});
245-
} else {
246-
return isValid(cb);
247-
}
207+
return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done);
248208
}
249209

250210
/**
@@ -273,14 +233,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} =
273233
}
274234
}
275235

276-
if (done && typeof done === 'function') {
277-
storageCallbacks.push(function() {
278-
let result = isValid(cb);
279-
done(result);
280-
});
281-
} else {
282-
return isValid(cb);
283-
}
236+
return schedule(cb, STORAGE_TYPE_COOKIES, done);
284237
}
285238

286239
return {

test/spec/unit/core/storageManager_spec.js

+84-34
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,11 @@ describe('storage manager', function() {
142142
const COOKIE = 'test-cookie';
143143
const LS_KEY = 'test-localstorage';
144144

145-
function mockBidderSettings() {
145+
function mockBidderSettings(val) {
146146
return {
147147
get(bidder, key) {
148148
if (bidder === ALLOWED_BIDDER && key === ALLOW_KEY) {
149-
return true;
149+
return val;
150150
} else {
151151
return undefined;
152152
}
@@ -157,39 +157,89 @@ describe('storage manager', function() {
157157
Object.entries({
158158
disallowed: ['denied_bidder', false],
159159
allowed: [ALLOWED_BIDDER, true]
160-
}).forEach(([test, [bidderCode, shouldWork]]) => {
161-
describe(`for ${test} bidders`, () => {
162-
let mgr;
163-
164-
beforeEach(() => {
165-
mgr = newStorageManager({bidderCode: bidderCode}, {bidderSettings: mockBidderSettings()});
166-
})
167-
168-
afterEach(() => {
169-
mgr.setCookie(COOKIE, 'delete', new Date().toUTCString());
170-
mgr.removeDataFromLocalStorage(LS_KEY);
171-
})
172-
173-
const testDesc = (desc) => `should ${shouldWork ? '' : 'not'} ${desc}`;
174-
175-
it(testDesc('allow cookies'), () => {
176-
mgr.setCookie(COOKIE, 'value');
177-
expect(mgr.getCookie(COOKIE)).to.equal(shouldWork ? 'value' : null);
178-
});
179-
180-
it(testDesc('allow localStorage'), () => {
181-
mgr.setDataInLocalStorage(LS_KEY, 'value');
182-
expect(mgr.getDataFromLocalStorage(LS_KEY)).to.equal(shouldWork ? 'value' : null);
183-
});
184-
185-
it(testDesc('report localStorage as available'), () => {
186-
expect(mgr.hasLocalStorage()).to.equal(shouldWork);
187-
});
188-
189-
it(testDesc('report cookies as available'), () => {
190-
expect(mgr.cookiesAreEnabled()).to.equal(shouldWork);
160+
}).forEach(([t, [bidderCode, isBidderAllowed]]) => {
161+
describe(`for ${t} bidders`, () => {
162+
Object.entries({
163+
'all': {
164+
configValues: [
165+
true,
166+
['html5', 'cookie']
167+
],
168+
shouldWork: {
169+
html5: true,
170+
cookie: true
171+
}
172+
},
173+
'none': {
174+
configValues: [
175+
false,
176+
[]
177+
],
178+
shouldWork: {
179+
html5: false,
180+
cookie: false
181+
}
182+
},
183+
'localStorage': {
184+
configValues: [
185+
'html5',
186+
['html5']
187+
],
188+
shouldWork: {
189+
html5: true,
190+
cookie: false
191+
}
192+
},
193+
'cookies': {
194+
configValues: [
195+
'cookie',
196+
['cookie']
197+
],
198+
shouldWork: {
199+
html5: false,
200+
cookie: true
201+
}
202+
}
203+
}).forEach(([t, {configValues, shouldWork: {cookie, html5}}]) => {
204+
describe(`when ${t} is allowed`, () => {
205+
configValues.forEach(configValue => describe(`storageAllowed = ${configValue}`, () => {
206+
let mgr;
207+
208+
beforeEach(() => {
209+
mgr = newStorageManager({bidderCode: bidderCode}, {bidderSettings: mockBidderSettings(configValue)});
210+
})
211+
212+
afterEach(() => {
213+
mgr.setCookie(COOKIE, 'delete', new Date().toUTCString());
214+
mgr.removeDataFromLocalStorage(LS_KEY);
215+
})
216+
217+
function scenario(type, desc, fn) {
218+
const shouldWork = isBidderAllowed && ({html5, cookie})[type];
219+
it(`${shouldWork ? '' : 'NOT'} ${desc}`, () => fn(shouldWork));
220+
}
221+
222+
scenario('cookie', 'allow cookies', (shouldWork) => {
223+
mgr.setCookie(COOKIE, 'value');
224+
expect(mgr.getCookie(COOKIE)).to.equal(shouldWork ? 'value' : null);
225+
});
226+
227+
scenario('html5', 'allow localStorage', (shouldWork) => {
228+
mgr.setDataInLocalStorage(LS_KEY, 'value');
229+
expect(mgr.getDataFromLocalStorage(LS_KEY)).to.equal(shouldWork ? 'value' : null);
230+
});
231+
232+
scenario('html5', 'report localStorage as available', (shouldWork) => {
233+
expect(mgr.hasLocalStorage()).to.equal(shouldWork);
234+
});
235+
236+
scenario('cookie', 'report cookies as available', (shouldWork) => {
237+
expect(mgr.cookiesAreEnabled()).to.equal(shouldWork);
238+
});
239+
}));
240+
});
191241
});
192242
});
193243
});
194-
})
244+
});
195245
});

0 commit comments

Comments
 (0)