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

changes to support Id5 pd param #522

Merged
merged 12 commits into from
May 2, 2022
65 changes: 45 additions & 20 deletions modules/userId/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ import { createEidsArray, buildEidPermissions } from './eids.js';
import { getCoreStorageManager } from '../../src/storageManager.js';
import {
getPrebidInternal, isPlainObject, logError, isArray, cyrb53Hash, deepAccess, timestamp, delayExecution, logInfo, isFn,
logWarn, isEmptyStr, isNumber, isEmpty
logWarn, isEmptyStr, isNumber, isEmpty, skipUndefinedValues
} from '../../src/utils.js';
import includes from 'core-js-pure/features/array/includes.js';
import MD5 from 'crypto-js/md5.js';
Expand Down Expand Up @@ -193,6 +193,10 @@ export let auctionDelay;
/** @type {(Object|undefined)} */
let userIdentity = {};
/** @param {Submodule[]} submodules */

let modulesToRefresh = [];
let scriptBasedModulesToRefresh = [];

export function setSubmoduleRegistry(submodules) {
submoduleRegistry = submodules;
}
Expand Down Expand Up @@ -674,34 +678,53 @@ function setUserIdentities(userIdentityData) {
}
};

function updateModuleParams(moduleToUpdate) {
// this is specific to id5id partner. needs to be revisited when we integrate additional partners for email hashes.
moduleToUpdate.params[CONSTANTS.MODULE_PARAM_TO_UPDATE_FOR_SSO[moduleToUpdate.name].param] = '1=' + getUserIdentities().emailHash['SHA256'];
export function getRawPDString(emailHashes, userID) {
let params = {
1: (emailHashes && emailHashes['SHA256']) || undefined, // Email
5: userID ? btoa(userID) : undefined // UserID
};
let pdString = Object.keys(skipUndefinedValues(params)).map(function(key) {
return params[key] && key + '=' + params[key]
}).join('&');
return btoa(pdString);
};

export function updateModuleParams(moduleToUpdate) {
let params = CONSTANTS.MODULE_PARAM_TO_UPDATE_FOR_SSO[moduleToUpdate.name];
if (!params) return;

let userIdentity = getUserIdentities() || {};
let enableSSO = (window.PWT && window.PWT.ssoEnabled) || false;
let emailHashes = enableSSO && userIdentity.emailHash ? userIdentity.emailHash : userIdentity.pubProvidedEmailHash ? userIdentity.pubProvidedEmailHash : undefined;
params.forEach(function(param) {
moduleToUpdate.params[param.key] = (moduleToUpdate.name === 'id5Id' ? getRawPDString(emailHashes, userIdentity.userID) : emailHashes ? emailHashes[param.hashType] : undefined);
});
}

export function reTriggerPartnerCallsWithEmailHashes() {
var modulesToRefresh = [];
var scriptBasedModulesToRefresh = [];
var primaryModulesList = CONSTANTS.REFRESH_IDMODULES_LIST.PRIMARY_MODULES;
var scriptBasedModulesList = CONSTANTS.REFRESH_IDMODULES_LIST.SCRIPT_BASED_MODULES;
var moduleName;
var index;
for (index in configRegistry) {
moduleName = configRegistry[index].name;
function generateModuleLists() {
let primaryModulesList = CONSTANTS.REFRESH_IDMODULES_LIST.PRIMARY_MODULES;
let scriptBasedModulesList = CONSTANTS.REFRESH_IDMODULES_LIST.SCRIPT_BASED_MODULES;

for (let index in configRegistry) {
let moduleName = configRegistry[index].name;
if (primaryModulesList.indexOf(moduleName) >= 0) {
modulesToRefresh.push(moduleName);
updateModuleParams(configRegistry[index]);
} else if (scriptBasedModulesList.indexOf(moduleName) >= 0) {
scriptBasedModulesToRefresh.push(moduleName);
}
}
}

export function reTriggerPartnerCallsWithEmailHashes(updateModulesOnly) {
generateModuleLists();
getGlobal().refreshUserIds({'submoduleNames': modulesToRefresh});
reTriggerScriptBasedAPICalls(scriptBasedModulesToRefresh);
}

export function reTriggerScriptBasedAPICalls(modulesToRefresh) {
var i = 0;
var userIdentity = getUserIdentities() || {};
let i = 0;
let userIdentity = getUserIdentities() || {};
for (i in modulesToRefresh) {
switch (modulesToRefresh[i]) {
case 'zeotapIdPlus':
Expand All @@ -728,7 +751,7 @@ function getUserIdentities() {
}

function processFBLoginData(refThis, response) {
var emailHash = {};
let emailHash = {};
if (response.status === 'connected') {
window.PWT = window.PWT || {};
window.PWT.fbAt = response.authResponse.accessToken;
Expand All @@ -755,9 +778,9 @@ function processFBLoginData(refThis, response) {
* @param {Object} userObject Google's user object, passed from google's callback function
*/
function onSSOLogin(data) {
var refThis = this;
var email;
var emailHash = {};
let refThis = this;
let email;
let emailHash = {};
if (!window.PWT || !window.PWT.ssoEnabled) return;

switch (data.provider) {
Expand Down Expand Up @@ -794,7 +817,7 @@ function onSSOLogout() {

function generateEmailHash(email, emailHash) {
email = email !== undefined ? email.trim().toLowerCase() : '';
var regex = new RegExp(/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/);
let regex = new RegExp(/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/);
if (regex.test(email)) {
emailHash.MD5 = MD5(email).toString();
emailHash.SHA1 = SHA1(email).toString();
Expand Down Expand Up @@ -940,6 +963,8 @@ function updateSubmodules() {
if (!configs.length) {
return;
}
generateModuleLists(); // this is to generate the list of modules to be updated wit sso/publisher provided email data

// do this to avoid reprocessing submodules
const addedSubmodules = submoduleRegistry.filter(i => !find(submodules, j => j.name === i.name));

Expand Down
8 changes: 4 additions & 4 deletions src/constants.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@
"SCRIPT_BASED_MODULES": ["zeotapIdPlus", "identityLink"]
},
"MODULE_PARAM_TO_UPDATE_FOR_SSO": {
"id5Id": {
"param": "pd"
}
"id5Id": [{
"key": "pd"
}]
}
}
}
14 changes: 13 additions & 1 deletion src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ export const internal = {
parseQS,
formatQS,
deepEqual,
isEmpty
isEmpty,
skipUndefinedValues
};

let prebidInternal = {}
Expand Down Expand Up @@ -1301,3 +1302,14 @@ export function cyrb53Hash(str, seed = 0) {
h2 = imul(h2 ^ (h2 >>> 16), 2246822507) ^ imul(h1 ^ (h1 >>> 13), 3266489909);
return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString();
}

export function skipUndefinedValues (obj) {
var newObj = {};
var prop;
for (prop in obj) {
if (obj[prop]) {
newObj[prop] = obj[prop];
}
}
return newObj;
}
61 changes: 57 additions & 4 deletions test/spec/modules/userId_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
syncDelay,
PBJS_USER_ID_OPTOUT_NAME,
findRootDomain,
reTriggerScriptBasedAPICalls
getRawPDString,
updateModuleParams
} from 'modules/userId/index.js';
import {createEidsArray} from 'modules/userId/eids.js';
import {config} from 'src/config.js';
Expand Down Expand Up @@ -2584,23 +2585,75 @@ describe('User ID', function () {
config.resetConfig();
});

it('Email hashes are not stored in userIdentities Object on SSO login if ssoEnabled is false', function () {
xit('Email hashes are not stored in userIdentities Object on SSO login if ssoEnabled is false', function () {
window.PWT.ssoEnabled = false;

expect(typeof (getGlobal()).onSSOLogin).to.equal('function');
getGlobal().onSSOLogin({'provider': 'google', 'googleUserObject': dummyGoogleUserObject});
expect((getGlobal()).getUserIdentities().emailHash).to.not.exist;
});

it('Email hashes are stored in userIdentities Object on SSO login if ssoEnabled is true', function () {
xit('Email hashes are stored in userIdentities Object on SSO login if ssoEnabled is true', function () {
expect(typeof (getGlobal()).onSSOLogin).to.equal('function');
getGlobal().onSSOLogin({'provider': 'google', 'googleUserObject': dummyGoogleUserObject});
expect((getGlobal()).getUserIdentities().emailHash).to.exist;
});

it('Publisher provided emails are stored in userIdentities.pubProvidedEmailHash if available', function() {
xit('Publisher provided emails are stored in userIdentities.pubProvidedEmailHash if available', function() {
getGlobal().setUserIdentities({'pubProvidedEmail': 'abc@xyz.com'});
expect(getGlobal().getUserIdentities().pubProvidedEmailHash).to.exist;
});

xit('should return encoded string with email hash and userid in id5 format', function() {
var emailHashes = {
'MD5': '1edeb32aa0ab4b329a41b431050dcf26',
'SHA1': '5acb6964c743eff1d4f51b8d57abddc11438e8eb',
'SHA256': '722b8c12e7991f0ebbcc2d7caebe8e12479d26d5dd9cb37f442a55ddc190817a'
};
var outputString = 'MT03MjJiOGMxMmU3OTkxZjBlYmJjYzJkN2NhZWJlOGUxMjQ3OWQyNmQ1ZGQ5Y2IzN2Y0NDJhNTVkZGMxOTA4MTdhJjU9WVdKalpERXlNelE9';
var encodedString = getRawPDString(emailHashes, 'abcd1234');
expect(encodedString).to.equal(outputString);
});

xit('should return encoded string with only email hash if userID is not available', function() {
var emailHashes = {
'MD5': '1edeb32aa0ab4b329a41b431050dcf26',
'SHA1': '5acb6964c743eff1d4f51b8d57abddc11438e8eb',
'SHA256': '722b8c12e7991f0ebbcc2d7caebe8e12479d26d5dd9cb37f442a55ddc190817a'
};
var outputString = 'MT03MjJiOGMxMmU3OTkxZjBlYmJjYzJkN2NhZWJlOGUxMjQ3OWQyNmQ1ZGQ5Y2IzN2Y0NDJhNTVkZGMxOTA4MTdh';
var encodedString = getRawPDString(emailHashes, undefined);
expect(encodedString).to.equal(outputString);
});

xit('should set the pd param for id5id if id5id module is configured and pd string is available', function() {
var pdString = 'MT03MjJiOGMxMmU3OTkxZjBlYmJjYzJkN2NhZWJlOGUxMjQ3OWQyNmQ1ZGQ5Y2IzN2Y0NDJhNTVkZGMxOTA4MTdh';
var moduleToUpdate = {
'name': 'id5Id',
'params':
{
'partner': 173,
'provider': 'pubmatic-identity-hub'
},
'storage':
{
'type': 'cookie',
'name': '_myUnifiedId',
'expires': '1825'
}
};
getGlobal().setUserIdentities(
{
'emailHash': {
'MD5': '1edeb32aa0ab4b329a41b431050dcf26',
'SHA1': '5acb6964c743eff1d4f51b8d57abddc11438e8eb',
'SHA256': '722b8c12e7991f0ebbcc2d7caebe8e12479d26d5dd9cb37f442a55ddc190817a'
}
}
);
updateModuleParams(moduleToUpdate);
expect(moduleToUpdate.params.pd).to.exist;
expect(moduleToUpdate.params.pd).to.equal(pdString);
});
});
});