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

Introduce device id v2 #362

Merged
merged 5 commits into from Dec 23, 2019
Merged
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

deviceIdV2 and SQS queue migration

  • Loading branch information
darkdh committed Nov 26, 2019
commit 9e96726e869b8d8e7b35d27e36c43055527cb9fe
@@ -1,9 +1,9 @@
'use strict'

const crypto = require('brave-crypto')
const libCrypto = require('../lib/crypto')
const messages = require('./constants/messages')
const {syncVersion} = require('./config')
const uuidv4 = require('uuid/v4')

/**
* Initializes crypto and device ID
@@ -13,14 +13,16 @@ module.exports.init = function () {
return new Promise((resolve, reject) => {
const ipc = window.chrome.ipcRenderer
ipc.send(messages.GET_INIT_DATA, syncVersion)
ipc.once(messages.GOT_INIT_DATA, (e, seed, deviceId, config, deviceUuid) => {
ipc.once(messages.GOT_INIT_DATA, (e, seed, deviceId, config, deviceIdV2) => {
// TODO(darkdh): save uuid for migrated devices
if (deviceIdV2.length === 0) {
deviceIdV2 = Buffer.from(libCrypto.randomBytes(3)).toString('hex')
}
if (seed === null) {
// Generate a new "persona"
seed = crypto.getSeed() // 32 bytes
deviceId = new Uint8Array([0])
// dash is reserved for s3 key delimiters
deviceUuid = uuidv4().replace(/(-)/g, '_')
ipc.send(messages.SAVE_INIT_DATA, seed, deviceId, deviceUuid)
ipc.send(messages.SAVE_INIT_DATA, seed, deviceId, deviceIdV2)
// TODO: The background process should listen for SAVE_INIT_DATA and emit
// GOT_INIT_DATA once the save is successful
}
@@ -31,7 +33,7 @@ module.exports.init = function () {
reject(new Error('Invalid crypto seed'))
return
}
resolve({seed, deviceId, config, deviceUuid})
resolve({seed, deviceId, config, deviceIdV2})
})
})
}
@@ -2,6 +2,7 @@

const awsSdk = require('aws-sdk')
const cryptoUtil = require('./cryptoUtil')
const deepEqual = require('deep-equal')
const recordUtil = require('./recordUtil')
const proto = require('./constants/proto')
const {limitConcurrency} = require('../lib/promiseHelper')
@@ -64,6 +65,7 @@ const RequestUtil = function (opts = {}) {
this.saveAWSCredentials(credentials)
}
this.SQSUrlByCat = []
this.oldSQSUrlByCat = []
this.missingObjectsCache = new LRUCache(50)
// This is used to keep the most recent records for each object id
this.latestRecordsCache = new LRUCache(100)
@@ -249,8 +251,27 @@ RequestUtil.prototype.list = function (category, startAt, maxRecords, nextContin
WaitTimeSeconds: CONFIG.SQS_MESSAGES_LONGPOLL_TIMEOUT
}

return s3Helper.listNotifications(this.sqs, notificationParams, category,
prefix)
if (this.oldSQSUrlByCat[category]) {
let oldNotificationParams = Object.assign({}, notificationParams)
oldNotificationParams.QueueUrl = `${this.oldSQSUrlByCat[category]}`

return s3Helper.listNotifications(this.sqs, notificationParams, category, prefix).then((values) => {
if (this.shouldRetireOldSQSQueue(parseInt(values.createdTimeStamp))) {
return this.deleteSQSQueue(this.oldSQSUrlByCat[category]).then(() => {

This comment has been minimized.

Copy link
@darkdh

darkdh Dec 12, 2019

Author Member

@AlexeyBarabash reminded me that when device id is duplicated, if there is an old Brave which doesn't contain the fix, its SQS queue will be unavailable until next relaunch because upgraded Brave deletes it after 24 hours

This comment has been minimized.

Copy link
@AlexeyBarabash

AlexeyBarabash Dec 12, 2019

Contributor

I was confused and I thought this.deleteSQSQueue can delete queues belonging to other devices. And like @darkdh mentioned that only can happen if device id was duplicated. Which is a separate case.

This comment has been minimized.

Copy link
@darkdh

darkdh Dec 13, 2019

Author Member

Non upgraded device will get reset anyway, so duplicate case won’t be an issue

delete this.oldSQSUrlByCat[category]
return values
})
}
return s3Helper.listNotifications(this.sqs, oldNotificationParams, category, prefix).then((oldValues) => {
if (deepEqual(values.contents, oldValues.contents, {strict: true})) {
return values
}
return {contents: values.contents.concat(oldValues.contents),
createdTimeStamp: values.createdTimeStamp}
})
})
}
return s3Helper.listNotifications(this.sqs, notificationParams, category, prefix)
})
}

@@ -275,6 +296,14 @@ RequestUtil.prototype.shouldListObject = function (startAt, category) {
this.listInProgress === true
}

RequestUtil.prototype.shouldRetireOldSQSQueue = function (createdTimestamp) {
let currentTime = new Date().getTime()
let newQueueCreatedTime =
this.normalizeTimestampToMs(createdTimestamp, currentTime)

return (currentTime - newQueueCreatedTime) > parseInt(s3Helper.SQS_RETENTION, 10) * 1000
}

/**
* Checks do we need to use s3 list Object or SQS notifications
* @param {number=} startAt return objects with timestamp >= startAt (e.g. 1482435340). Could be seconds or milliseconds
@@ -342,15 +371,16 @@ RequestUtil.prototype.sqsName = function (deviceId, category) {
/**
* Creates SQS for the current device.
* @param {string} deviceId
* @param {string} deviceUuid
* @param {string} deviceIdV2
* @returns {Promise}
*/
RequestUtil.prototype.createAndSubscribeSQS = function (deviceId, deviceUuid) {
RequestUtil.prototype.createAndSubscribeSQS = function (deviceId, deviceIdV2) {
// Creating a query for the current userId
if (!deviceId) {
throw new Error('createSQS failed. deviceId is null!')
if (!deviceIdV2) {
throw new Error('createSQS failed. deviceIdV2 is null!')
}
this.deviceId = deviceId
this.deviceIdV2 = deviceIdV2
const createAndSubscribeSQSforCategory = function (deviceId, category, thisRef) {
let newQueueParams = {
QueueName: thisRef.sqsName(deviceId, category),
@@ -370,10 +400,31 @@ RequestUtil.prototype.createAndSubscribeSQS = function (deviceId, deviceUuid) {
})
})
}

const subscribeOldSQSforCategory = function (deviceId, category, thisRef) {
return new Promise((resolve, reject) => {
let params = {
QueueName: thisRef.sqsName(deviceId, category)
}
thisRef.sqs.getQueueUrl(params, (error, data) => {
if (error) {
// queue doesn't exist
resolve()
} else if (data) {
thisRef.oldSQSUrlByCat[category] = data.QueueUrl
resolve()
}
})
})
}
var createSQSPromises = []
// Simple for loop instead foreach to capture 'this'
for (var i = 0; i < CATEGORIES_FOR_SQS.length; ++i) {
createSQSPromises.push(createAndSubscribeSQSforCategory(deviceId, CATEGORIES_FOR_SQS[i], this))
// Doesn't have to create about to deprecate quques
// createSQSPromises.push(createAndSubscribeSQSforCategory(deviceId, CATEGORIES_FOR_SQS[i], this))
createSQSPromises.push(subscribeOldSQSforCategory(deviceId, CATEGORIES_FOR_SQS[i], this))
createSQSPromises.push(createAndSubscribeSQSforCategory(deviceIdV2, CATEGORIES_FOR_SQS[i], this))
// TODO(darkdh): encode into base64
}

return Promise.all(createSQSPromises)
@@ -531,7 +582,7 @@ RequestUtil.prototype.deleteUser = function () {
RequestUtil.prototype.purgeUserCategoryQueue = function (category) {
return new Promise((resolve, reject) => {
let params = {
QueueName: this.sqsName(this.deviceId, category)
QueueName: this.sqsName(this.deviceIdV2, category)
}
this.sqs.getQueueUrl(params, (error, data) => {
if (error) {
@@ -554,6 +605,20 @@ RequestUtil.prototype.purgeUserCategoryQueue = function (category) {
})
}

RequestUtil.prototype.deleteSQSQueue = function (url) {
return new Promise((resolve, reject) => {
let params = {
QueueUrl: url
}
this.sqs.deleteQueue(params, (err, data) => {
if (err) {
console.log('SQS deleteQueue failed with error: ' + err)
}
resolve([])
})
})
}

RequestUtil.prototype.purgeUserQueue = function () {
var purgeQueuePromises = []
for (var i = 0; i < CATEGORIES_FOR_SQS.length; ++i) {
@@ -8,7 +8,6 @@ const messages = require('./constants/messages')
const proto = require('./constants/proto')
const serializer = require('../lib/serializer')
const {deriveKeys} = require('../lib/crypto')
const uuidv4 = require('uuid/v4')

let ipc = window.chrome.ipcRenderer

@@ -19,7 +18,7 @@ const ERROR = 2
const logElement = document.querySelector('#output')

var clientDeviceId = null
var clientDeviceUuid = null
var clientDeviceIdV2 = null
var clientUserId = null
var clientKeys = {}
var config = {}
@@ -73,9 +72,7 @@ const maybeSetDeviceId = (requester) => {
})
}
clientDeviceId = new Uint8Array([maxId + 1])
// dash is reserved for s3 key delimiters
clientDeviceUuid = uuidv4().replace(/(-)/g, '_')
ipc.send(messages.SAVE_INIT_DATA, seed, clientDeviceId, clientDeviceUuid)
ipc.send(messages.SAVE_INIT_DATA, seed, clientDeviceId, clientDeviceIdV2)
return Promise.resolve(requester)
})
}
@@ -256,8 +253,7 @@ const main = () => {
const clientSerializer = values[0]
const keys = deriveKeys(values[1].seed)
const deviceId = values[1].deviceId
const deviceUuid = values[1].deviceUuid
logSync(`deviceUUID ${deviceUuid}`)
clientDeviceIdV2 = values[1].deviceIdV2
seed = values[1].seed
clientKeys = keys
config = values[1].config
@@ -294,8 +290,8 @@ const main = () => {
})
.then((requester) => {
if (clientDeviceId !== null && requester && requester.s3) {
logSync('set device ID: ' + clientDeviceId + ' device UUID: ' + clientDeviceUuid)
requester.createAndSubscribeSQS(clientDeviceId, clientDeviceUuid).then(() => {
logSync('set device ID: ' + clientDeviceId + ' device ID V2: ' + clientDeviceIdV2)
requester.createAndSubscribeSQS(clientDeviceId, clientDeviceIdV2).then(() => {
startSync(requester)
})
.catch((e) => {
@@ -82,7 +82,7 @@ message SyncRecord {
}
message Device {
string name = 1;
string uuid = 2;
string deviceIdV2 = 2;
}
Action action = 1;
bytes deviceId = 2;
@@ -2970,7 +2970,7 @@
* @memberof api.SyncRecord
* @interface IDevice
* @property {string|null} [name] Device name
* @property {string|null} [uuid] Device uuid
* @property {string|null} [deviceIdV2] Device deviceIdV2
*/

/**
@@ -2997,12 +2997,12 @@
Device.prototype.name = "";

/**
* Device uuid.
* @member {string} uuid
* Device deviceIdV2.
* @member {string} deviceIdV2
* @memberof api.SyncRecord.Device
* @instance
*/
Device.prototype.uuid = "";
Device.prototype.deviceIdV2 = "";

/**
* Creates a new Device instance using the specified properties.
@@ -3030,8 +3030,8 @@
writer = $Writer.create();
if (message.name != null && message.hasOwnProperty("name"))
writer.uint32(/* id 1, wireType 2 =*/10).string(message.name);
if (message.uuid != null && message.hasOwnProperty("uuid"))
writer.uint32(/* id 2, wireType 2 =*/18).string(message.uuid);
if (message.deviceIdV2 != null && message.hasOwnProperty("deviceIdV2"))
writer.uint32(/* id 2, wireType 2 =*/18).string(message.deviceIdV2);
return writer;
};

@@ -3070,7 +3070,7 @@
message.name = reader.string();
break;
case 2:
message.uuid = reader.string();
message.deviceIdV2 = reader.string();
break;
default:
reader.skipType(tag & 7);
@@ -3110,9 +3110,9 @@
if (message.name != null && message.hasOwnProperty("name"))
if (!$util.isString(message.name))
return "name: string expected";
if (message.uuid != null && message.hasOwnProperty("uuid"))
if (!$util.isString(message.uuid))
return "uuid: string expected";
if (message.deviceIdV2 != null && message.hasOwnProperty("deviceIdV2"))
if (!$util.isString(message.deviceIdV2))
return "deviceIdV2: string expected";
return null;
};

@@ -3130,8 +3130,8 @@
var message = new $root.api.SyncRecord.Device();
if (object.name != null)
message.name = String(object.name);
if (object.uuid != null)
message.uuid = String(object.uuid);
if (object.deviceIdV2 != null)
message.deviceIdV2 = String(object.deviceIdV2);
return message;
};

@@ -3150,12 +3150,12 @@
var object = {};
if (options.defaults) {
object.name = "";
object.uuid = "";
object.deviceIdV2 = "";
}
if (message.name != null && message.hasOwnProperty("name"))
object.name = message.name;
if (message.uuid != null && message.hasOwnProperty("uuid"))
object.uuid = message.uuid;
if (message.deviceIdV2 != null && message.hasOwnProperty("deviceIdV2"))
object.deviceIdV2 = message.deviceIdV2;
return object;
};

@@ -116,7 +116,8 @@ module.exports.listNotifications = function (SQS, options, category, prefix) {
let queueAttributesParams = {
QueueUrl: options.QueueUrl,
AttributeNames: [
'ApproximateNumberOfMessages'
'ApproximateNumberOfMessages',
'CreatedTimestamp'
]
}
SQS.getQueueAttributes(queueAttributesParams, (errorAttr, dataAttr) => {
@@ -125,9 +126,11 @@ module.exports.listNotifications = function (SQS, options, category, prefix) {
reject(errorAttr)
} else if (dataAttr) {
let approximateNumberOfMessages = parseInt(dataAttr.Attributes.ApproximateNumberOfMessages)
let createdTimeStamp = dataAttr.Attributes.CreatedTimestamp
if (approximateNumberOfMessages === 0) {
resolve({
contents: content
contents: content,
createdTimeStamp
})

return
@@ -138,7 +141,8 @@ module.exports.listNotifications = function (SQS, options, category, prefix) {
reject(error)
}
resolve({
contents: data
contents: data,
createdTimeStamp
})
})
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.