feat(analytics): obfuscate room name (#11587)

* obfuscate room name

* fixed js-sha version

* add comma

* check for room change
This commit is contained in:
Andrei Gavrilescu 2022-06-21 09:53:07 +03:00 committed by GitHub
parent 3fb3be9727
commit c29e8bbdd1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 109 additions and 6 deletions

View File

@ -875,6 +875,10 @@ var config = {
// The Amplitude APP Key:
// amplitudeAPPKey: '<APP_KEY>'
// Obfuscates room name sent to analytics (amplitude, rtcstats)
// Default value is false.
// obfuscateRoomName: false,
// Configuration for the rtcstats server:
// By enabling rtcstats server every time a conference is joined the rtcstats
// module connects to the provided rtcstatsEndpoint and sends statistics regarding

11
package-lock.json generated
View File

@ -72,6 +72,7 @@
"jquery": "3.5.1",
"jquery-i18next": "1.2.1",
"js-md5": "0.6.1",
"js-sha512": "0.8.0",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1455.0.0+f3a2f61e/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
@ -11625,6 +11626,11 @@
"resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.6.1.tgz",
"integrity": "sha512-lyUTXOqMEaA9mm38mHxbTo83WsYAvMJm850kxJcRno3T2qL+e40B2G89E0/4r9TdAeB3jN0TdSVp/VHNI6/WyQ=="
},
"node_modules/js-sha512": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.8.0.tgz",
"integrity": "sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ=="
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -28898,6 +28904,11 @@
"resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.6.1.tgz",
"integrity": "sha512-lyUTXOqMEaA9mm38mHxbTo83WsYAvMJm850kxJcRno3T2qL+e40B2G89E0/4r9TdAeB3jN0TdSVp/VHNI6/WyQ=="
},
"js-sha512": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.8.0.tgz",
"integrity": "sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ=="
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",

View File

@ -77,6 +77,7 @@
"jquery": "3.5.1",
"jquery-i18next": "1.2.1",
"js-md5": "0.6.1",
"js-sha512": "0.8.0",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1455.0.0+f3a2f61e/lib-jitsi-meet.tgz",
"lodash": "4.17.21",

View File

@ -2,6 +2,7 @@
import { API_ID } from '../../../modules/API/constants';
import { getName as getAppName } from '../app/functions';
import { getAnalyticsRoomName } from '../base/conference';
import {
checkChromeExtensionsInstalled,
isMobileBrowser
@ -155,7 +156,9 @@ export async function createHandlers({ getState }: { getState: Function }) {
* @param {Array<Object>} handlers - The analytics handlers.
* @returns {void}
*/
export function initAnalytics({ getState }: { getState: Function }, handlers: Array<Object>) {
export function initAnalytics(store: Store, handlers: Array<Object>) {
const { getState, dispatch } = store;
if (!isAnalyticsEnabled(getState) || handlers.length === 0) {
return;
}
@ -166,7 +169,6 @@ export function initAnalytics({ getState }: { getState: Function }, handlers: Ar
deploymentInfo
} = config;
const { group, server } = state['features/base/jwt'];
const roomName = state['features/base/conference'].room;
const { locationURL = {} } = state['features/base/connection'];
const { tenant } = parseURIString(locationURL.href) || {};
const permanentProperties = {};
@ -204,7 +206,7 @@ export function initAnalytics({ getState }: { getState: Function }, handlers: Ar
}
analytics.addPermanentProperties(permanentProperties);
analytics.setConferenceName(roomName);
analytics.setConferenceName(getAnalyticsRoomName(state, dispatch));
// Set the handlers last, since this triggers emptying of the cache
analytics.setAnalyticsHandlers(handlers);

View File

@ -206,6 +206,16 @@ export const SEND_TONES = 'SEND_TONES';
*/
export const SET_FOLLOW_ME = 'SET_FOLLOW_ME';
/**
* The type of (redux) action which sets the obfuscated room name.
*
* {
* type: SET_OBFUSCATED_ROOM,
* obfuscatedRoom: string
* }
*/
export const SET_OBFUSCATED_ROOM = 'SET_OBFUSCATED_ROOM';
/**
* The type of (redux) action which updates the current known status of the
* Mute Reactions Sound feature.

View File

@ -56,6 +56,7 @@ import {
P2P_STATUS_CHANGED,
SEND_TONES,
SET_FOLLOW_ME,
SET_OBFUSCATED_ROOM,
SET_PASSWORD,
SET_PASSWORD_FAILED,
SET_ROOM,
@ -804,6 +805,24 @@ export function setPassword(
};
}
/**
* Sets the obfuscated room name of the conference to be joined.
*
* @param {(string)} obfuscatedRoom - Obfuscated room name.
* @param {(string)} obfuscatedRoomSource - The room name that was obfuscated.
* @returns {{
* type: SET_OBFUSCATED_ROOM,
* room: string
* }}
*/
export function setObfuscatedRoom(obfuscatedRoom: string, obfuscatedRoomSource: string) {
return {
type: SET_OBFUSCATED_ROOM,
obfuscatedRoom,
obfuscatedRoomSource
};
}
/**
* Sets (the name of) the room of the conference to be joined.
*

View File

@ -1,5 +1,6 @@
// @flow
import { sha512_256 as sha512 } from 'js-sha512';
import _ from 'lodash';
import { getName } from '../../app/functions';
@ -19,6 +20,7 @@ import {
safeDecodeURIComponent
} from '../util';
import { setObfuscatedRoom } from './actions';
import {
AVATAR_URL_COMMAND,
EMAIL_COMMAND,
@ -298,6 +300,47 @@ export function getRoomName(state: Object): string {
return getConferenceState(state).room;
}
/**
* Get an obfuscated room name or create and persist it if it doesn't exists.
*
* @param {Object} state - The current state of the app.
* @param {Function} dispatch - The Redux dispatch function.
* @returns {string} - Obfuscated room name.
*/
export function getOrCreateObfuscatedRoomName(state: Object, dispatch: Function) {
let { obfuscatedRoom } = getConferenceState(state);
const { obfuscatedRoomSource } = getConferenceState(state);
const room = getRoomName(state);
// On native mobile the store doesn't clear when joining a new conference so we might have the obfuscatedRoom
// stored even though a different room was joined.
// Check if the obfuscatedRoom was already computed for the current room.
if (!obfuscatedRoom || (obfuscatedRoomSource !== room)) {
obfuscatedRoom = sha512(room);
dispatch(setObfuscatedRoom(obfuscatedRoom, room));
}
return obfuscatedRoom;
}
/**
* Analytics may require an obfuscated room name, this functions decides based on a config if the normal or
* obfuscated room name should be returned.
*
* @param {Object} state - The current state of the app.
* @param {Function} dispatch - The Redux dispatch function.
* @returns {string} - Analytics room name.
*/
export function getAnalyticsRoomName(state: Object, dispatch: Function) {
const { analysis: { obfuscateRoomName = false } = {} } = state['features/base/config'];
if (obfuscateRoomName) {
return getOrCreateObfuscatedRoomName(state, dispatch);
}
return getRoomName(state);
}
/**
* Returns the result of getWiFiStats from the global NS or does nothing
* (returns empty result).

View File

@ -18,6 +18,7 @@ import {
LOCK_STATE_CHANGED,
P2P_STATUS_CHANGED,
SET_FOLLOW_ME,
SET_OBFUSCATED_ROOM,
SET_PASSWORD,
SET_PENDING_SUBJECT_CHANGE,
SET_ROOM,
@ -88,6 +89,12 @@ ReducerRegistry.register(
case SET_LOCATION_URL:
return set(state, 'room', undefined);
case SET_OBFUSCATED_ROOM:
return { ...state,
obfuscatedRoom: action.obfuscatedRoom,
obfuscatedRoomSource: action.obfuscatedRoomSource
};
case SET_PASSWORD:
return _setPassword(state, action);

View File

@ -3,8 +3,13 @@
import { jitsiLocalStorage } from '@jitsi/js-utils';
import { getAmplitudeIdentity } from '../analytics';
import { CONFERENCE_UNIQUE_ID_SET, E2E_RTT_CHANGED, CONFERENCE_TIMESTAMP_CHANGED, getConferenceOptions, getRoomName }
from '../base/conference';
import {
CONFERENCE_UNIQUE_ID_SET,
E2E_RTT_CHANGED,
CONFERENCE_TIMESTAMP_CHANGED,
getConferenceOptions,
getAnalyticsRoomName
} from '../base/conference';
import { LIB_WILL_INIT } from '../base/lib-jitsi-meet/actionTypes';
import { DOMINANT_SPEAKER_CHANGED, getLocalParticipant } from '../base/participants';
import { MiddlewareRegistry } from '../base/redux';
@ -24,6 +29,7 @@ import logger from './logger';
*/
MiddlewareRegistry.register(store => next => action => {
const state = store.getState();
const { dispatch } = store;
const config = state['features/base/config'];
const { analytics, faceLandmarks } = config;
@ -118,7 +124,7 @@ MiddlewareRegistry.register(store => next => action => {
...getAmplitudeIdentity(),
...options,
endpointId: localParticipant?.id,
confName: getRoomName(state),
confName: getAnalyticsRoomName(state, dispatch),
displayName,
meetingUniqueId
});