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

Use account information instead of access token for local storage #7703

Merged
merged 1 commit into from
Jan 29, 2019
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
65 changes: 48 additions & 17 deletions src/util/mapbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import config from './config';
import browser from './browser';
import window from './window';
import { version } from '../../package.json';
import { uuid, validateUuid, storageAvailable, warnOnce, extend } from './util';
import { uuid, validateUuid, storageAvailable, b64DecodeUnicode, b64EncodeUnicode, warnOnce, extend } from './util';
import { postData } from './ajax';

import type { RequestParameters } from './ajax';
Expand Down Expand Up @@ -158,10 +158,28 @@ function formatUrl(obj: UrlObject): string {
return `${obj.protocol}://${obj.authority}${obj.path}${params}`;
}

function parseAccessToken(accessToken: ?string) {
if (!accessToken) {
return null;
}

const parts = accessToken.split('.');
if (!parts || parts.length !== 3) {
return null;
}

try {
const jsonData = JSON.parse(b64DecodeUnicode(parts[1]));
return jsonData;
} catch (e) {
return null;
}
}

type TelemetryEventType = 'appUserTurnstile' | 'map.load';

class TelemetryEvent {
eventData: { lastSuccess: ?number, accessToken: ?string};
eventData: any;
anonId: ?string;
queue: Array<any>;
type: TelemetryEventType;
Expand All @@ -170,15 +188,28 @@ class TelemetryEvent {
constructor(type: TelemetryEventType) {
this.type = type;
this.anonId = null;
this.eventData = {lastSuccess: null, accessToken: config.ACCESS_TOKEN};
this.eventData = {};
this.queue = [];
this.pendingRequest = null;
}

getStorageKey(domain: ?string) {
const tokenData = parseAccessToken(config.ACCESS_TOKEN);
let u = '';
if (tokenData && tokenData['u']) {
u = b64EncodeUnicode(tokenData['u']);
} else {
u = config.ACCESS_TOKEN || '';
}
return domain ?
`${telemEventKey}.${domain}:${u}` :
`${telemEventKey}:${u}`;
}

fetchEventData() {
const isLocalStorageAvailable = storageAvailable('localStorage');
const storageKey = `${telemEventKey}:${config.ACCESS_TOKEN || ''}`;
const uuidKey = `${telemEventKey}.uuid:${config.ACCESS_TOKEN || ''}`;
const storageKey = this.getStorageKey();
const uuidKey = this.getStorageKey('uuid');

if (isLocalStorageAvailable) {
//Retrieve cached data
Expand All @@ -198,12 +229,12 @@ class TelemetryEvent {

saveEventData() {
const isLocalStorageAvailable = storageAvailable('localStorage');
const storageKey = `${telemEventKey}:${config.ACCESS_TOKEN || ''}`;
const uuidKey = `${telemEventKey}.uuid:${config.ACCESS_TOKEN || ''}`;
const storageKey = this.getStorageKey();
const uuidKey = this.getStorageKey('uuid');
if (isLocalStorageAvailable) {
try {
window.localStorage.setItem(uuidKey, this.anonId);
if (this.eventData.lastSuccess) {
if (Object.keys(this.eventData).length >= 1) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this change here? Isn't lastSuccess still set in the same place?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is unrelated, but it ensures that state is saved even if it doesn't include lastSuccess

window.localStorage.setItem(storageKey, JSON.stringify(this.eventData));
}
} catch (e) {
Expand Down Expand Up @@ -267,7 +298,7 @@ export class MapLoadEvent extends TelemetryEvent {
// mapbox tiles.
if (config.ACCESS_TOKEN &&
Array.isArray(tileUrls) &&
tileUrls.some(url => isMapboxHTTPURL(url))) {
tileUrls.some(url => isMapboxURL(url) || isMapboxHTTPURL(url))) {
ChrisLoer marked this conversation as resolved.
Show resolved Hide resolved
this.queueRequest({id: mapId, timestamp: Date.now()});
}
}
Expand Down Expand Up @@ -306,7 +337,7 @@ export class TurnstileEvent extends TelemetryEvent {
// mapbox tiles.
if (config.ACCESS_TOKEN &&
Array.isArray(tileUrls) &&
tileUrls.some(url => isMapboxHTTPURL(url))) {
tileUrls.some(url => isMapboxURL(url) || isMapboxHTTPURL(url))) {
this.queueRequest(Date.now());
}
}
Expand All @@ -317,16 +348,16 @@ export class TurnstileEvent extends TelemetryEvent {
return;
}

let dueForEvent = this.eventData.accessToken ? (this.eventData.accessToken !== config.ACCESS_TOKEN) : false;
//Reset event data cache if the access token changed.
if (dueForEvent) {
this.anonId = this.eventData.lastSuccess = null;
}
if (!this.anonId || !this.eventData.lastSuccess) {
if (!this.anonId || !this.eventData.lastSuccess || !this.eventData.tokenU) {
//Retrieve cached data
this.fetchEventData();
}

const tokenData = parseAccessToken(config.ACCESS_TOKEN);
const tokenU = tokenData ? tokenData['u'] : config.ACCESS_TOKEN;
//Reset event data cache if the access token owner changed.
let dueForEvent = tokenU !== this.eventData.tokenU;

if (!validateUuid(this.anonId)) {
this.anonId = uuid();
dueForEvent = true;
Expand All @@ -350,7 +381,7 @@ export class TurnstileEvent extends TelemetryEvent {
this.postEvent(nextUpdate, {"enabled.telemetry": false}, (err) => {
if (!err) {
this.eventData.lastSuccess = nextUpdate;
this.eventData.accessToken = config.ACCESS_TOKEN;
this.eventData.tokenU = tokenU;
}
});
}
Expand Down
20 changes: 20 additions & 0 deletions src/util/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -447,3 +447,23 @@ export function storageAvailable(type: string): boolean {
return false;
}
}

// The following methods are from https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem
//Unicode compliant base64 encoder for strings
export function b64EncodeUnicode(str: string) {
return window.btoa(
encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
(match, p1) => {
return String.fromCharCode(Number('0x' + p1)); //eslint-disable-line
}
)
);
}


// Unicode compliant decoder for base64-encoded strings
export function b64DecodeUnicode(str: string) {
return decodeURIComponent(window.atob(str).split('').map((c) => {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); //eslint-disable-line
}).join(''));
}
3 changes: 2 additions & 1 deletion test/unit/util/mapbox.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,8 @@ test("mapbox", (t) => {
const now = +Date.now();
window.localStorage.setItem(`mapbox.eventData.uuid:${config.ACCESS_TOKEN}`, uuid());
window.localStorage.setItem(`mapbox.eventData:${config.ACCESS_TOKEN}`, JSON.stringify({
lastSuccess: now
lastSuccess: now,
tokenU: 'key'
}));

// Post 5 seconds later
Expand Down