forked from prebid/Prebid.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconsentHandler.js
232 lines (210 loc) · 6 KB
/
consentHandler.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
import {cyrb53Hash, isStr, timestamp} from './utils.js';
import {defer, GreedyPromise} from './utils/promise.js';
import {config} from './config.js';
/**
* Placeholder gvlid for when vendor consent is not required. When this value is used as gvlid, the gdpr
* enforcement module will take it to mean "vendor consent was given".
*
* see https://github.com/prebid/Prebid.js/issues/8161
*/
export const VENDORLESS_GVLID = Object.freeze({});
export class ConsentHandler {
#enabled;
#data;
#defer;
#ready;
#dirty = true;
#hash;
generatedTime;
hashFields;
constructor() {
this.reset();
}
#resolve(data) {
this.#ready = true;
this.#data = data;
this.#defer.resolve(data);
}
/**
* reset this handler (mainly for tests)
*/
reset() {
this.#defer = defer();
this.#enabled = false;
this.#data = null;
this.#ready = false;
this.generatedTime = null;
}
/**
* Enable this consent handler. This should be called by the relevant consent management module
* on initialization.
*/
enable() {
this.#enabled = true;
}
/**
* @returns {boolean} true if the related consent management module is enabled.
*/
get enabled() {
return this.#enabled;
}
/**
* @returns {boolean} true if consent data has been resolved (it may be `null` if the resolution failed).
*/
get ready() {
return this.#ready;
}
/**
* @returns a promise than resolves to the consent data, or null if no consent data is available
*/
get promise() {
if (this.#ready) {
return GreedyPromise.resolve(this.#data);
}
if (!this.#enabled) {
this.#resolve(null);
}
return this.#defer.promise;
}
setConsentData(data, time = timestamp()) {
this.generatedTime = time;
this.#dirty = true;
this.#resolve(data);
}
getConsentData() {
return this.#data;
}
get hash() {
if (this.#dirty) {
this.#hash = cyrb53Hash(JSON.stringify(this.#data && this.hashFields ? this.hashFields.map(f => this.#data[f]) : this.#data))
this.#dirty = false;
}
return this.#hash;
}
}
class UspConsentHandler extends ConsentHandler {
getConsentMeta() {
const consentData = this.getConsentData();
if (consentData && this.generatedTime) {
return {
generatedAt: this.generatedTime
};
}
}
}
class GdprConsentHandler extends ConsentHandler {
hashFields = ['gdprApplies', 'consentString']
getConsentMeta() {
const consentData = this.getConsentData();
if (consentData && consentData.vendorData && this.generatedTime) {
return {
gdprApplies: consentData.gdprApplies,
consentStringSize: (isStr(consentData.vendorData.tcString)) ? consentData.vendorData.tcString.length : 0,
generatedAt: this.generatedTime,
apiVersion: consentData.apiVersion
}
}
}
}
class GppConsentHandler extends ConsentHandler {
hashFields = ['applicableSections', 'gppString'];
getConsentMeta() {
const consentData = this.getConsentData();
if (consentData && this.generatedTime) {
return {
generatedAt: this.generatedTime,
}
}
}
}
export function gvlidRegistry() {
const registry = {};
const flat = {};
const none = {};
return {
/**
* Register a module's GVL ID.
* @param {string} moduleType defined in `activities/modules.js`
* @param {string} moduleName
* @param {number} gvlid
*/
register(moduleType, moduleName, gvlid) {
if (gvlid) {
(registry[moduleName] = registry[moduleName] || {})[moduleType] = gvlid;
if (flat.hasOwnProperty(moduleName)) {
if (flat[moduleName] !== gvlid) flat[moduleName] = none;
} else {
flat[moduleName] = gvlid;
}
}
},
/**
* @typedef {Object} GvlIdResult
* @property {Object.<string, number>} modules - A map from module type to that module's GVL ID.
* @property {number} [gvlid] - The single GVL ID for this family of modules (only defined if all modules with this name declared the same ID).
*/
/**
* Get a module's GVL ID(s).
*
* @param {string} moduleName - The name of the module.
* @return {GvlIdResult} An object where:
* `modules` is a map from module type to that module's GVL ID;
* `gvlid` is the single GVL ID for this family of modules (only defined if all modules with this name declare the same ID).
*/
get(moduleName) {
const result = {modules: registry[moduleName] || {}};
if (flat.hasOwnProperty(moduleName) && flat[moduleName] !== none) {
result.gvlid = flat[moduleName];
}
return result;
}
}
}
export const gdprDataHandler = new GdprConsentHandler();
export const uspDataHandler = new UspConsentHandler();
export const gppDataHandler = new GppConsentHandler();
export const coppaDataHandler = (() => {
function getCoppa() {
return !!(config.getConfig('coppa'))
}
return {
getCoppa,
getConsentData: getCoppa,
getConsentMeta: getCoppa,
reset() {},
get promise() {
return GreedyPromise.resolve(getCoppa())
},
get hash() {
return getCoppa() ? '1' : '0'
}
}
})();
export const GDPR_GVLIDS = gvlidRegistry();
const ALL_HANDLERS = {
gdpr: gdprDataHandler,
usp: uspDataHandler,
gpp: gppDataHandler,
coppa: coppaDataHandler,
}
export function multiHandler(handlers = ALL_HANDLERS) {
handlers = Object.entries(handlers);
function collector(method) {
return function () {
return Object.fromEntries(handlers.map(([name, handler]) => [name, handler[method]()]))
}
}
return Object.assign(
{
get promise() {
return GreedyPromise.all(handlers.map(([name, handler]) => handler.promise.then(val => [name, val])))
.then(entries => Object.fromEntries(entries));
},
get hash() {
return cyrb53Hash(handlers.map(([_, handler]) => handler.hash).join(':'));
}
},
Object.fromEntries(['getConsentData', 'getConsentMeta', 'reset'].map(n => [n, collector(n)])),
)
}
export const allConsent = multiHandler();