feat(push): Prepare codebase for data payloads
This commit is contained in:
Родитель
cf5be7f492
Коммит
b60c46491f
|
@ -9,7 +9,8 @@ node_js:
|
|||
- "0.10"
|
||||
- "4"
|
||||
|
||||
sudo: false
|
||||
dist: trusty
|
||||
sudo: required
|
||||
|
||||
addons:
|
||||
apt:
|
||||
|
@ -17,6 +18,9 @@ addons:
|
|||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
- mysql-server-5.6
|
||||
- mysql-client-core-5.6
|
||||
- mysql-client-5.6
|
||||
|
||||
env:
|
||||
global:
|
||||
|
@ -39,7 +43,7 @@ notifications:
|
|||
skip_join: false
|
||||
|
||||
before_install:
|
||||
- npm install -g npm@2
|
||||
- if [ "$TRAVIS_NODE_VERSION" == "0.10" ]; then npm install -g npm@2; fi
|
||||
- npm config set spin false
|
||||
|
||||
install:
|
||||
|
|
16
docs/api.md
16
docs/api.md
|
@ -1274,8 +1274,10 @@ or updates existing device details for this user/session
|
|||
If no device `id` is specified,
|
||||
both `name` and `type` must be provided.
|
||||
If a device `id` is specified,
|
||||
at least one of `name`, `type`, `pushCallback` and `pushPublicKey`
|
||||
at least one of `name`, `type`, `pushCallback` or the tuple (`pushCallback`, `pushPublicKey` and `pushAuthKey`)
|
||||
must be present.
|
||||
Beware that if you provide `pushCallback` without the couple (`pushPublicKey` and `pushAuthKey`), both of
|
||||
the keys will be reset to an empty string.
|
||||
|
||||
### Request
|
||||
|
||||
|
@ -1297,7 +1299,8 @@ https://api-accounts.dev.lcip.org/v1/account/device \
|
|||
"name": "My Phone",
|
||||
"type": "mobile",
|
||||
"pushCallback": "https://updates.push.services.mozilla.com/update/abcdef01234567890abcdefabcdef01234567890abcdef",
|
||||
"pushPublicKey": "468601214f60f4828b6cd5d51d9d99d212e7c73657978955f0f5a5b7e2fa1370"
|
||||
"pushPublicKey": "BCp93zru09_hab2Bg37LpTNG__Pw6eMPEP2hrQpwuytoj3h4chXpGc-3qqdKyqjuvAiEupsnOd_RLyc7erJHWgA",
|
||||
"pushAuthKey": "w3b14Zjc-Afj2SDOLOyong"
|
||||
}'
|
||||
```
|
||||
|
||||
|
@ -1328,7 +1331,8 @@ with an object that contains the device id in the JSON body:
|
|||
"name": "My Phone",
|
||||
"type": "mobile",
|
||||
"pushCallback": "https://updates.push.services.mozilla.com/update/abcdef01234567890abcdefabcdef01234567890abcdef",
|
||||
"pushPublicKey": "468601214f60f4828b6cd5d51d9d99d212e7c73657978955f0f5a5b7e2fa1370"
|
||||
"pushPublicKey": "BCp93zru09_hab2Bg37LpTNG__Pw6eMPEP2hrQpwuytoj3h4chXpGc-3qqdKyqjuvAiEupsnOd_RLyc7erJHWgA",
|
||||
"pushAuthKey": "w3b14Zjc-Afj2SDOLOyong"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -1373,7 +1377,8 @@ with an array of device details in the JSON body:
|
|||
"name": "My Phone",
|
||||
"type": "mobile",
|
||||
"pushCallback": "https://updates.push.services.mozilla.com/update/abcdef01234567890abcdefabcdef01234567890abcdef",
|
||||
"pushPublicKey": "468601214f60f4828b6cd5d51d9d99d212e7c73657978955f0f5a5b7e2fa1370"
|
||||
"pushPublicKey": "BCp93zru09_hab2Bg37LpTNG__Pw6eMPEP2hrQpwuytoj3h4chXpGc-3qqdKyqjuvAiEupsnOd_RLyc7erJHWgA",
|
||||
"pushAuthKey": "w3b14Zjc-Afj2SDOLOyong"
|
||||
},
|
||||
{
|
||||
"id": "0f7aa00356e5416e82b3bef7bc409eef",
|
||||
|
@ -1382,7 +1387,8 @@ with an array of device details in the JSON body:
|
|||
"name": "My Desktop",
|
||||
"type": null,
|
||||
"pushCallback": "https://updates.push.services.mozilla.com/update/d4c5b1e3f5791ef83896c27519979b93a45e6d0da34c75",
|
||||
"pushPublicKey": "468601214f60f4828b6cd5d51d9d99d212e7c73657978955f0f5a5b7e2fa1370"
|
||||
"pushPublicKey": "BCp93zru09_hab2Bg37LpTNG__Pw6eMPEP2hrQpwuytoj3h4chXpGc-3qqdKyqjuvAiEupsnOd_RLyc7erJHWgA",
|
||||
"pushAuthKey": "w3b14Zjc-Afj2SDOLOyong"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
|
|
@ -437,6 +437,7 @@
|
|||
* type
|
||||
* pushCallback
|
||||
* pushPublicKey
|
||||
* pushAuthKey
|
||||
* output
|
||||
* id
|
||||
* createdAt
|
||||
|
@ -444,6 +445,7 @@
|
|||
* type
|
||||
* pushCallback
|
||||
* pushPublicKey
|
||||
* pushAuthKey
|
||||
* db-write
|
||||
* Devices
|
||||
|
||||
|
@ -457,6 +459,7 @@
|
|||
* type
|
||||
* pushCallback
|
||||
* pushPublicKey
|
||||
* pushAuthKey
|
||||
|
||||
## /account/device
|
||||
|
||||
|
|
|
@ -95,4 +95,5 @@
|
|||
* type
|
||||
* pushCallback
|
||||
* pushPublicKey
|
||||
* pushAuthKey
|
||||
|
||||
|
|
11
lib/db.js
11
lib/db.js
|
@ -438,9 +438,10 @@ module.exports = function (
|
|||
name: item.name,
|
||||
type: item.type,
|
||||
pushCallback: item.callbackURL,
|
||||
pushPublicKey: item.callbackPublicKey
|
||||
pushPublicKey: item.callbackPublicKey,
|
||||
pushAuthKey: item.callbackAuthKey
|
||||
}, {
|
||||
ignore: [ 'name', 'type', 'pushCallback' ]
|
||||
ignore: [ 'name', 'type', 'pushCallback', 'pushPublicKey', 'pushAuthKey' ]
|
||||
})
|
||||
})
|
||||
},
|
||||
|
@ -521,7 +522,8 @@ module.exports = function (
|
|||
name: deviceInfo.name,
|
||||
type: deviceInfo.type,
|
||||
callbackURL: deviceInfo.pushCallback,
|
||||
callbackPublicKey: deviceInfo.pushPublicKey
|
||||
callbackPublicKey: deviceInfo.pushPublicKey,
|
||||
callbackAuthKey: deviceInfo.pushAuthKey
|
||||
})
|
||||
)
|
||||
.then(
|
||||
|
@ -565,7 +567,8 @@ module.exports = function (
|
|||
name: deviceInfo.name,
|
||||
type: deviceInfo.type,
|
||||
callbackURL: deviceInfo.pushCallback,
|
||||
callbackPublicKey: deviceInfo.pushPublicKey
|
||||
callbackPublicKey: deviceInfo.pushPublicKey,
|
||||
callbackAuthKey: deviceInfo.pushAuthKey
|
||||
})
|
||||
)
|
||||
.then(
|
||||
|
|
141
lib/push.js
141
lib/push.js
|
@ -2,40 +2,45 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var request = require('request')
|
||||
var webpush = require('web-push')
|
||||
var P = require('./promise')
|
||||
|
||||
var ERR_NO_PUSH_CALLBACK = 'No Push Callback'
|
||||
var ERR_DATA_BUT_NO_KEYS = 'Data payload present but missing key(s)'
|
||||
|
||||
var LOG_OP_NOTIFY_UPDATE = 'push.notifyUpdate'
|
||||
var LOG_OP_PUSH_TO_DEVICES = 'push.pushToDevices'
|
||||
|
||||
var reasonToEvents = {
|
||||
accountVerify: {
|
||||
send: 'push.send',
|
||||
success: 'push.success',
|
||||
resetSettings: 'push.reset_settings',
|
||||
failed: 'push.failed',
|
||||
noCallback: 'push.no_push_callback'
|
||||
send: 'push.account_verify.send',
|
||||
success: 'push.account_verify.success',
|
||||
resetSettings: 'push.account_verify.reset_settings',
|
||||
failed: 'push.account_verify.failed',
|
||||
noCallback: 'push.account_verify.no_push_callback',
|
||||
noKeys: 'push.account_verify.data_but_no_keys'
|
||||
},
|
||||
passwordReset: {
|
||||
send: 'push.password_reset.send',
|
||||
success: 'push.password_reset.success',
|
||||
resetSettings: 'push.password_reset.reset_settings',
|
||||
failed: 'push.password_reset.failed',
|
||||
noCallback: 'push.password_reset.no_push_callback'
|
||||
noCallback: 'push.password_reset.no_push_callback',
|
||||
noKeys: 'push.password_reset.data_but_no_keys'
|
||||
},
|
||||
passwordChange: {
|
||||
send: 'push.password_change.send',
|
||||
success: 'push.password_change.success',
|
||||
resetSettings: 'push.password_change.reset_settings',
|
||||
failed: 'push.password_change.failed',
|
||||
noCallback: 'push.password_change.no_push_callback'
|
||||
noCallback: 'push.password_change.no_push_callback',
|
||||
noKeys: 'push.password_change.data_but_no_keys'
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function (log, db) {
|
||||
function reportPushError(err, deviceId) {
|
||||
log.error({
|
||||
op: LOG_OP_NOTIFY_UPDATE,
|
||||
op: LOG_OP_PUSH_TO_DEVICES,
|
||||
deviceId: deviceId,
|
||||
err: err
|
||||
})
|
||||
|
@ -49,55 +54,83 @@ module.exports = function (log, db) {
|
|||
* @promise
|
||||
*/
|
||||
notifyUpdate: function notifyUpdate(uid, reason) {
|
||||
var events = reasonToEvents[reason] || reasonToEvents.accountVerify
|
||||
reason = reason || 'accountVerify'
|
||||
return this.pushToDevices(uid, reason)
|
||||
},
|
||||
|
||||
/**
|
||||
* Send a push notification with or without data to all the devices in the account (except the ones in the excludedDeviceIds)
|
||||
*
|
||||
* @param uid
|
||||
* @param reason
|
||||
* @param data
|
||||
* @param excludedDeviceIds
|
||||
* @promise
|
||||
*/
|
||||
pushToDevices: function pushToDevices(uid, reason, data, excludedDeviceIds) {
|
||||
var events = reasonToEvents[reason]
|
||||
return db.devices(uid).then(
|
||||
function (devices) {
|
||||
devices.forEach(function (device) {
|
||||
var deviceId = device.id.toString('hex')
|
||||
return P.all(
|
||||
devices.map(function(device) {
|
||||
var deviceId = device.id.toString('hex')
|
||||
|
||||
log.trace({
|
||||
op: LOG_OP_NOTIFY_UPDATE,
|
||||
deviceId: deviceId,
|
||||
pushCallback: device.pushCallback
|
||||
})
|
||||
if (excludedDeviceIds && excludedDeviceIds.indexOf(deviceId) !== -1) {
|
||||
return
|
||||
}
|
||||
|
||||
if (device.pushCallback) {
|
||||
// send the push notification
|
||||
log.increment(events.send)
|
||||
request.post({
|
||||
url: device.pushCallback,
|
||||
headers: {
|
||||
'ttl': '0'
|
||||
}
|
||||
}, function (err, response) {
|
||||
if (err) {
|
||||
// 404 or 410 error from the push servers means
|
||||
// the push settings need to be reset.
|
||||
// the clients will check this and re-register push endpoints
|
||||
if (response && (response.statusCode === 404 || response.statusCode === 410)) {
|
||||
// reset device push configuration
|
||||
// Warning: this method is called without any session tokens or auth validation.
|
||||
device.pushCallback = ''
|
||||
device.pushPublicKey = ''
|
||||
db.updateDevice(uid, device.id, device).catch(function (err) {
|
||||
reportPushError(err, deviceId)
|
||||
})
|
||||
log.increment(events.resetSettings)
|
||||
|
||||
} else {
|
||||
reportPushError(err, deviceId)
|
||||
log.increment(events.failed)
|
||||
}
|
||||
} else {
|
||||
log.increment(events.success)
|
||||
}
|
||||
log.trace({
|
||||
op: LOG_OP_PUSH_TO_DEVICES,
|
||||
deviceId: deviceId,
|
||||
pushCallback: device.pushCallback
|
||||
})
|
||||
} else {
|
||||
// keep track if there are any devices with no push urls.
|
||||
reportPushError(new Error(ERR_NO_PUSH_CALLBACK), deviceId)
|
||||
log.increment(events.noCallback)
|
||||
}
|
||||
})
|
||||
|
||||
if (device.pushCallback) {
|
||||
// send the push notification
|
||||
log.increment(events.send)
|
||||
var pushParams = { 'TTL': '0' }
|
||||
if (data) {
|
||||
if (!device.pushPublicKey || !device.pushAuthKey) {
|
||||
reportPushError(new Error(ERR_DATA_BUT_NO_KEYS), deviceId)
|
||||
log.increment(events.noKeys)
|
||||
return
|
||||
}
|
||||
pushParams.userPublicKey = device.pushPublicKey
|
||||
pushParams.userAuth = device.pushAuthKey
|
||||
pushParams.payload = data
|
||||
}
|
||||
return webpush.sendNotification(device.pushCallback, pushParams)
|
||||
.then(
|
||||
function () {
|
||||
log.increment(events.success)
|
||||
},
|
||||
function (err) {
|
||||
// 404 or 410 error from the push servers means
|
||||
// the push settings need to be reset.
|
||||
// the clients will check this and re-register push endpoints
|
||||
if (err.statusCode === 404 || err.statusCode === 410) {
|
||||
// reset device push configuration
|
||||
// Warning: this method is called without any session tokens or auth validation.
|
||||
device.pushCallback = ''
|
||||
device.pushPublicKey = ''
|
||||
device.pushAuthKey = ''
|
||||
return db.updateDevice(uid, device.id, device).catch(function (err) {
|
||||
reportPushError(err, deviceId)
|
||||
}).then(function() {
|
||||
log.increment(events.resetSettings)
|
||||
})
|
||||
} else {
|
||||
reportPushError(err, deviceId)
|
||||
log.increment(events.failed)
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
// keep track if there are any devices with no push urls.
|
||||
reportPushError(new Error(ERR_NO_PUSH_CALLBACK), deviceId)
|
||||
log.increment(events.noCallback)
|
||||
}
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ var validators = require('./validators')
|
|||
var HEX_STRING = validators.HEX_STRING
|
||||
var BASE64_JWT = validators.BASE64_JWT
|
||||
var DISPLAY_SAFE_UNICODE = validators.DISPLAY_SAFE_UNICODE
|
||||
var URLSAFEBASE64 = validators.URLSAFEBASE64
|
||||
|
||||
var butil = require('../crypto/butil')
|
||||
var openid = require('openid')
|
||||
|
@ -66,9 +67,9 @@ module.exports = function (
|
|||
name: isA.string().max(255).regex(DISPLAY_SAFE_UNICODE).required(),
|
||||
type: isA.string().max(16).required(),
|
||||
pushCallback: isA.string().uri({ scheme: 'https' }).max(255).optional().allow(''),
|
||||
// We're not yet ready to store pubkey values, don't let clients submit them.
|
||||
pushPublicKey: isA.string().length(64).regex(HEX_STRING).allow('').forbidden()
|
||||
})
|
||||
pushPublicKey: isA.string().max(88).regex(URLSAFEBASE64).optional().allow(''),
|
||||
pushAuthKey: isA.string().max(24).regex(URLSAFEBASE64).optional().allow('')
|
||||
}).and('pushPublicKey', 'pushAuthKey')
|
||||
.optional(),
|
||||
metricsContext: metricsContext.schema
|
||||
}
|
||||
|
@ -85,8 +86,9 @@ module.exports = function (
|
|||
name: isA.string().max(255).regex(DISPLAY_SAFE_UNICODE).required(),
|
||||
type: isA.string().max(16).required(),
|
||||
pushCallback: isA.string().uri({ scheme: 'https' }).max(255).optional().allow(''),
|
||||
pushPublicKey: isA.string().length(64).regex(HEX_STRING).optional().allow('')
|
||||
})
|
||||
pushPublicKey: isA.string().max(88).regex(URLSAFEBASE64).optional().allow(''),
|
||||
pushAuthKey: isA.string().max(24).regex(URLSAFEBASE64).optional().allow('')
|
||||
}).and('pushPublicKey', 'pushAuthKey')
|
||||
.optional()
|
||||
}
|
||||
}
|
||||
|
@ -305,9 +307,9 @@ module.exports = function (
|
|||
name: isA.string().max(255).regex(DISPLAY_SAFE_UNICODE).optional(),
|
||||
type: isA.string().max(16).optional(),
|
||||
pushCallback: isA.string().uri({ scheme: 'https' }).max(255).optional().allow(''),
|
||||
// We're not yet ready to store pubkey values, don't let clients submit them.
|
||||
pushPublicKey: isA.string().length(64).regex(HEX_STRING).allow('').forbidden()
|
||||
})
|
||||
pushPublicKey: isA.string().max(88).regex(URLSAFEBASE64).optional().allow(''),
|
||||
pushAuthKey: isA.string().max(24).regex(URLSAFEBASE64).optional().allow('')
|
||||
}).and('pushPublicKey', 'pushAuthKey')
|
||||
.optional(),
|
||||
metricsContext: metricsContext.schema
|
||||
}
|
||||
|
@ -325,8 +327,9 @@ module.exports = function (
|
|||
name: isA.string().max(255).regex(DISPLAY_SAFE_UNICODE).optional(),
|
||||
type: isA.string().max(16).optional(),
|
||||
pushCallback: isA.string().uri({ scheme: 'https' }).max(255).optional().allow(''),
|
||||
pushPublicKey: isA.string().length(64).regex(HEX_STRING).optional().allow('')
|
||||
})
|
||||
pushPublicKey: isA.string().max(88).regex(URLSAFEBASE64).optional().allow(''),
|
||||
pushAuthKey: isA.string().max(24).regex(URLSAFEBASE64).optional().allow('')
|
||||
}).and('pushPublicKey', 'pushAuthKey')
|
||||
.optional()
|
||||
}
|
||||
}
|
||||
|
@ -814,20 +817,20 @@ module.exports = function (
|
|||
name: isA.string().max(255).regex(DISPLAY_SAFE_UNICODE).optional(),
|
||||
type: isA.string().max(16).optional(),
|
||||
pushCallback: isA.string().uri({ scheme: 'https' }).max(255).optional().allow(''),
|
||||
// We're not yet ready to store pubkey values, don't let clients submit them.
|
||||
pushPublicKey: isA.string().length(64).regex(HEX_STRING).allow('').forbidden()
|
||||
}).or('name', 'type', 'pushCallback', 'pushPublicKey'),
|
||||
pushPublicKey: isA.string().max(88).regex(URLSAFEBASE64).optional().allow(''),
|
||||
pushAuthKey: isA.string().max(24).regex(URLSAFEBASE64).optional().allow('')
|
||||
}).or('name', 'type', 'pushCallback', 'pushPublicKey', 'pushAuthKey').and('pushPublicKey', 'pushAuthKey'),
|
||||
isA.object({
|
||||
name: isA.string().max(255).regex(DISPLAY_SAFE_UNICODE).required(),
|
||||
type: isA.string().max(16).required(),
|
||||
pushCallback: isA.string().uri({ scheme: 'https' }).max(255).optional().allow(''),
|
||||
// We're not yet ready to store pubkey values, don't let clients submit them.
|
||||
pushPublicKey: isA.string().length(64).regex(HEX_STRING).allow('').forbidden()
|
||||
})
|
||||
pushPublicKey: isA.string().max(88).regex(URLSAFEBASE64).optional().allow(''),
|
||||
pushAuthKey: isA.string().max(24).regex(URLSAFEBASE64).optional().allow('')
|
||||
}).and('pushPublicKey', 'pushAuthKey')
|
||||
)
|
||||
},
|
||||
response: {
|
||||
schema: {
|
||||
schema: isA.object({
|
||||
id: isA.string().length(32).regex(HEX_STRING).required(),
|
||||
createdAt: isA.number().positive().optional(),
|
||||
// We previously allowed devices to register with arbitrry unicode names,
|
||||
|
@ -835,8 +838,9 @@ module.exports = function (
|
|||
name: isA.string().max(255).optional(),
|
||||
type: isA.string().max(16).optional(),
|
||||
pushCallback: isA.string().uri({ scheme: 'https' }).max(255).optional().allow(''),
|
||||
pushPublicKey: isA.string().length(64).regex(HEX_STRING).optional().allow('')
|
||||
}
|
||||
pushPublicKey: isA.string().max(88).regex(URLSAFEBASE64).optional().allow(''),
|
||||
pushAuthKey: isA.string().max(24).regex(URLSAFEBASE64).optional().allow('')
|
||||
}).and('pushPublicKey', 'pushAuthKey')
|
||||
}
|
||||
},
|
||||
handler: function (request, reply) {
|
||||
|
@ -855,6 +859,10 @@ module.exports = function (
|
|||
throw error.featureNotEnabled()
|
||||
}
|
||||
}
|
||||
if (payload.pushCallback && (!payload.pushPublicKey || !payload.pushAuthKey)) {
|
||||
payload.pushPublicKey = ''
|
||||
payload.pushAuthKey = ''
|
||||
}
|
||||
var operation = payload.id ? 'updateDevice' : 'createDevice'
|
||||
db[operation](sessionToken.uid, sessionToken.tokenId, payload).then(
|
||||
function (device) {
|
||||
|
@ -910,8 +918,9 @@ module.exports = function (
|
|||
name: isA.string().max(255).required(),
|
||||
type: isA.string().max(16).required(),
|
||||
pushCallback: isA.string().uri({ scheme: 'https' }).max(255).optional().allow('').allow(null),
|
||||
pushPublicKey: isA.string().length(64).regex(HEX_STRING).optional().allow(null)
|
||||
}))
|
||||
pushPublicKey: isA.string().max(88).regex(URLSAFEBASE64).optional().allow('').allow(null),
|
||||
pushAuthKey: isA.string().max(24).regex(URLSAFEBASE64).optional().allow('').allow(null)
|
||||
}).and('pushPublicKey', 'pushAuthKey'))
|
||||
}
|
||||
},
|
||||
handler: function (request, reply) {
|
||||
|
|
|
@ -12,6 +12,8 @@ module.exports.HEX_STRING = /^(?:[a-fA-F0-9]{2})+$/
|
|||
// Match an encoded JWT.
|
||||
module.exports.BASE64_JWT = /^(?:[a-zA-Z0-9-_]+[=]{0,2}\.){2}[a-zA-Z0-9-_]+[=]{0,2}$/
|
||||
|
||||
module.exports.URLSAFEBASE64 = /^[a-zA-Z0-9-_]*$/
|
||||
|
||||
// Match display-safe unicode characters.
|
||||
// We're pretty liberal with what's allowed in a unicode string,
|
||||
// but we exclude the following classes of characters:
|
||||
|
|
|
@ -100,6 +100,7 @@ module.exports = function (log, inherits, Token) {
|
|||
this.deviceCreatedAt = data.deviceCreatedAt
|
||||
this.callbackURL = data.callbackURL
|
||||
this.callbackPublicKey = data.callbackPublicKey
|
||||
this.callbackAuthKey = data.callbackAuthKey
|
||||
}
|
||||
|
||||
return SessionToken
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -28,6 +28,7 @@
|
|||
"readmeFilename": "README.md",
|
||||
"dependencies": {
|
||||
"aws-sdk": "2.2.10",
|
||||
"base64url": "1.0.6",
|
||||
"binary-split": "0.1.2",
|
||||
"bluebird": "2.10.2",
|
||||
"convict": "1.3.0",
|
||||
|
@ -48,7 +49,8 @@
|
|||
"request": "2.65.0",
|
||||
"scrypt-hash": "1.1.13",
|
||||
"through": "2.3.8",
|
||||
"uuid": "1.4.1"
|
||||
"uuid": "1.4.1",
|
||||
"web-push": "2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ass": "git://github.com/jrgm/ass.git#5be99ee7abc9fcf63f9ebcc37b151b9c822146d1",
|
||||
|
|
|
@ -7,7 +7,7 @@ node ./scripts/gen_keys.js
|
|||
# Force install of mysql-patcher
|
||||
(cd node_modules/fxa-auth-db-mysql && npm install &>/var/tmp/db-mysql.out)
|
||||
|
||||
mysql -e 'DROP DATABASE IF EXISTS fxa'
|
||||
mysql -u root -e 'DROP DATABASE IF EXISTS fxa'
|
||||
node ./node_modules/fxa-auth-db-mysql/bin/db_patcher.js
|
||||
|
||||
# Start backgrounded fxa-auth-db-mysql server
|
||||
|
|
|
@ -18,7 +18,7 @@ var pushManager = new PushManager({
|
|||
})
|
||||
|
||||
test(
|
||||
'notifyUpdate sends notifications using a real push server',
|
||||
'pushToDevices sends notifications using a real push server',
|
||||
function (t) {
|
||||
|
||||
pushManager.getSubscription().then(function (subscription) {
|
||||
|
@ -31,7 +31,9 @@ test(
|
|||
'lastAccessTime': 1449235471335,
|
||||
'name': 'My Phone',
|
||||
'type': 'mobile',
|
||||
'pushCallback': subscription.endpoint
|
||||
'pushCallback': subscription.endpoint,
|
||||
'pushPublicKey': 'BBXOKjUb84pzws1wionFpfCBjDuCh4-s_1b52WA46K5wYL2gCWEOmFKWn_NkS5nmJwTBuO8qxxdjAIDtNeklvQc',
|
||||
'pushAuthKey': 'GSsIiaD2Mr83iPqwFNK4rw'
|
||||
}
|
||||
])
|
||||
}
|
||||
|
@ -39,14 +41,14 @@ test(
|
|||
|
||||
var thisMockLog = mockLog({
|
||||
increment: function (log) {
|
||||
if (log === 'push.success') {
|
||||
if (log === 'push.account_verify.success') {
|
||||
t.end()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
var push = proxyquire('../../lib/push', {})(thisMockLog, mockDbResult)
|
||||
push.notifyUpdate(mockUid)
|
||||
push.pushToDevices(mockUid, 'accountVerify', new Buffer('foodata'))
|
||||
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ require('ass')
|
|||
var test = require('../ptaptest')
|
||||
var uuid = require('uuid')
|
||||
var crypto = require('crypto')
|
||||
var base64url = require('base64url')
|
||||
var log = { trace: console.log, info: console.log }
|
||||
|
||||
var config = require('../../config').getProperties()
|
||||
|
@ -217,7 +218,8 @@ test(
|
|||
name: '',
|
||||
type: 'mobile',
|
||||
pushCallback: 'https://foo/bar',
|
||||
pushPublicKey: crypto.randomBytes(32)
|
||||
pushPublicKey: base64url(Buffer.concat([new Buffer('\x04'), crypto.randomBytes(64)])),
|
||||
pushAuthKey: base64url(crypto.randomBytes(16))
|
||||
}
|
||||
return dbConn.then(function (db) {
|
||||
return db.emailRecord(ACCOUNT.email)
|
||||
|
@ -263,7 +265,8 @@ test(
|
|||
t.equal(device.name, deviceInfo.name, 'device.name is correct')
|
||||
t.equal(device.type, deviceInfo.type, 'device.type is correct')
|
||||
t.equal(device.pushCallback, deviceInfo.pushCallback, 'device.pushCallback is correct')
|
||||
t.deepEqual(device.pushPublicKey, deviceInfo.pushPublicKey, 'device.pushPublicKey is correct')
|
||||
t.equal(device.pushPublicKey, deviceInfo.pushPublicKey, 'device.pushPublicKey is correct')
|
||||
t.equal(device.pushAuthKey, deviceInfo.pushAuthKey, 'device.pushAuthKey is correct')
|
||||
return db.createDevice(ACCOUNT.uid, sessionTokenId, deviceInfo)
|
||||
.then(function () {
|
||||
t.fail('adding a device with a duplicate session token should have failed')
|
||||
|
@ -285,12 +288,14 @@ test(
|
|||
t.equal(device.name, deviceInfo.name, 'device.name is correct')
|
||||
t.equal(device.type, deviceInfo.type, 'device.type is correct')
|
||||
t.equal(device.pushCallback, deviceInfo.pushCallback, 'device.pushCallback is correct')
|
||||
t.deepEqual(device.pushPublicKey, deviceInfo.pushPublicKey, 'device.pushPublicKey is correct')
|
||||
t.equal(device.pushPublicKey, deviceInfo.pushPublicKey, 'device.pushPublicKey is correct')
|
||||
t.equal(device.pushAuthKey, deviceInfo.pushAuthKey, 'device.pushAuthKey is correct')
|
||||
deviceInfo.id = device.id
|
||||
deviceInfo.name = 'wibble'
|
||||
deviceInfo.type = 'desktop'
|
||||
deviceInfo.pushCallback = ''
|
||||
deviceInfo.pushPublicKey = ''
|
||||
deviceInfo.pushAuthKey = ''
|
||||
return db.updateDevice(ACCOUNT.uid, sessionTokenId, deviceInfo)
|
||||
.catch(function (err) {
|
||||
t.fail('updating a new device should not have failed')
|
||||
|
@ -307,7 +312,8 @@ test(
|
|||
t.equal(device.name, deviceInfo.name, 'device.name is correct')
|
||||
t.equal(device.type, deviceInfo.type, 'device.type is correct')
|
||||
t.equal(device.pushCallback, deviceInfo.pushCallback, 'device.pushCallback is correct')
|
||||
t.deepEqual(device.pushPublicKey, zeroBuffer32, 'device.pushPublicKey is correct')
|
||||
t.equal(device.pushPublicKey, '', 'device.pushPublicKey is correct')
|
||||
t.equal(device.pushAuthKey, '', 'device.pushAuthKey is correct')
|
||||
return db.deleteDevice(ACCOUNT.uid, deviceInfo.id)
|
||||
.catch(function () {
|
||||
t.fail('deleting a device should not have failed')
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
require('ass')
|
||||
var tap = require('tap')
|
||||
var test = tap.test
|
||||
var P = require('../../lib/promise')
|
||||
var uuid = require('uuid')
|
||||
var crypto = require('crypto')
|
||||
var base64url = require('base64url')
|
||||
var proxyquire = require('proxyquire')
|
||||
var log = { trace: console.log, info: console.log }
|
||||
|
||||
|
@ -66,25 +68,26 @@ test(
|
|||
name: 'my push device',
|
||||
type: 'mobile',
|
||||
pushCallback: 'https://foo/bar',
|
||||
pushPublicKey: crypto.randomBytes(32)
|
||||
pushPublicKey: base64url(Buffer.concat([new Buffer('\x04'), crypto.randomBytes(64)])),
|
||||
pushAuthKey: base64url(crypto.randomBytes(16))
|
||||
}
|
||||
// two tests below, first for unknown 400 level error the device push info will stay the same
|
||||
// second, for a known 400 error we reset the device
|
||||
var mocksKnown400 = {
|
||||
request: {
|
||||
post: function (url, cb) {
|
||||
return cb(new Error('Failed 400 level'), {
|
||||
statusCode: 410
|
||||
})
|
||||
'web-push': {
|
||||
sendNotification: function (endpoint, params) {
|
||||
var err = new Error('Failed 400 level')
|
||||
err.statusCode = 410
|
||||
return P.reject(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
var mocksUnknown400 = {
|
||||
request: {
|
||||
post: function (url, cb) {
|
||||
return cb(new Error('Failed 429 level'), {
|
||||
statusCode: 429
|
||||
})
|
||||
'web-push': {
|
||||
sendNotification: function (endpoint, params) {
|
||||
var err = new Error('Failed 429 level')
|
||||
err.statusCode = 429
|
||||
return P.reject(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,6 +110,7 @@ test(
|
|||
t.equal(device.name, deviceInfo.name)
|
||||
t.equal(device.pushCallback, deviceInfo.pushCallback)
|
||||
t.equal(device.pushPublicKey, deviceInfo.pushPublicKey)
|
||||
t.equal(device.pushAuthKey, deviceInfo.pushAuthKey)
|
||||
})
|
||||
.then(function () {
|
||||
return db.devices(ACCOUNT.uid)
|
||||
|
@ -114,7 +118,7 @@ test(
|
|||
|
||||
.then(function () {
|
||||
var pushWithUnknown400 = proxyquire('../../lib/push', mocksUnknown400)(mockLog, db)
|
||||
return pushWithUnknown400.notifyUpdate(ACCOUNT.uid)
|
||||
return pushWithUnknown400.pushToDevices(ACCOUNT.uid, 'accountVerify')
|
||||
})
|
||||
.then(function () {
|
||||
return db.devices(ACCOUNT.uid)
|
||||
|
@ -123,12 +127,13 @@ test(
|
|||
var device = devices[0]
|
||||
t.equal(device.name, deviceInfo.name)
|
||||
t.equal(device.pushCallback, deviceInfo.pushCallback)
|
||||
t.deepEqual(device.pushPublicKey, deviceInfo.pushPublicKey, 'device.pushPublicKey is correct')
|
||||
t.equal(device.pushPublicKey, deviceInfo.pushPublicKey, 'device.pushPublicKey is correct')
|
||||
t.equal(device.pushAuthKey, deviceInfo.pushAuthKey, 'device.pushAuthKey is correct')
|
||||
})
|
||||
|
||||
.then(function () {
|
||||
var pushWithKnown400 = proxyquire('../../lib/push', mocksKnown400)(mockLog, db)
|
||||
return pushWithKnown400.notifyUpdate(ACCOUNT.uid)
|
||||
return pushWithKnown400.pushToDevices(ACCOUNT.uid, 'accountVerify')
|
||||
})
|
||||
.then(function () {
|
||||
return db.devices(ACCOUNT.uid)
|
||||
|
@ -137,7 +142,8 @@ test(
|
|||
var device = devices[0]
|
||||
t.equal(device.name, deviceInfo.name)
|
||||
t.equal(device.pushCallback, '')
|
||||
t.deepEqual(device.pushPublicKey, zeroBuffer32, 'device.pushPublicKey is correct')
|
||||
t.equal(device.pushPublicKey, '', 'device.pushPublicKey is correct')
|
||||
t.equal(device.pushAuthKey, '', 'device.pushAuthKey is correct')
|
||||
t.end()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
var tap = require('tap')
|
||||
var proxyquire = require('proxyquire')
|
||||
var sinon = require('sinon')
|
||||
|
||||
var test = tap.test
|
||||
var P = require('../../lib/promise')
|
||||
|
@ -26,27 +27,29 @@ var mockDbResult = {
|
|||
'name': 'My Phone',
|
||||
'type': 'mobile',
|
||||
'pushCallback': 'https://updates.push.services.mozilla.com/update/abcdef01234567890abcdefabcdef01234567890abcdef',
|
||||
'pushPublicKey': '468601214f60f4828b6cd5d51d9d99d212e7c73657978955f0f5a5b7e2fa1370'
|
||||
'pushPublicKey': 'BCp93zru09_hab2Bg37LpTNG__Pw6eMPEP2hrQpwuytoj3h4chXpGc-3qqdKyqjuvAiEupsnOd_RLyc7erJHWgA=',
|
||||
'pushAuthKey': 'w3b14Zjc-Afj2SDOLOyong=='
|
||||
},
|
||||
{
|
||||
'id': '0f7aa00356e5416e82b3bef7bc409eef',
|
||||
'id': '3a45e6d0dae543qqdKyqjuvAiEupsnOd',
|
||||
'isCurrentDevice': false,
|
||||
'lastAccessTime': 1417699471335,
|
||||
'name': 'My Desktop',
|
||||
'type': null,
|
||||
'pushCallback': 'https://updates.push.services.mozilla.com/update/d4c5b1e3f5791ef83896c27519979b93a45e6d0da34c75',
|
||||
'pushPublicKey': '468601214f60f4828b6cd5d51d9d99d212e7c73657978955f0f5a5b7e2fa1370'
|
||||
'pushPublicKey': 'BCp93zru09_hab2Bg37LpTNG__Pw6eMPEP2hrQpwuytoj3h4chXpGc-3qqdKyqjuvAiEupsnOd_RLyc7erJHWgA=',
|
||||
'pushAuthKey': 'w3b14Zjc-Afj2SDOLOyong=='
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
test(
|
||||
'notifyUpdate does not throw on empty device result',
|
||||
'pushToDevices does not throw on empty device result',
|
||||
function (t) {
|
||||
var thisMockLog = mockLog({
|
||||
increment: function (name) {
|
||||
if (name === 'push.success') {
|
||||
if (name === 'push.account_verify.success') {
|
||||
t.fail('must not call push.success')
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +57,7 @@ test(
|
|||
|
||||
try {
|
||||
var push = require('../../lib/push')(thisMockLog, mockDbEmpty)
|
||||
push.notifyUpdate(mockUid).catch(function (err) {
|
||||
push.pushToDevices(mockUid).catch(function (err) {
|
||||
t.fail('must not throw')
|
||||
throw err
|
||||
})
|
||||
|
@ -66,12 +69,12 @@ test(
|
|||
)
|
||||
|
||||
test(
|
||||
'notifyUpdate sends notifications',
|
||||
'pushToDevices sends notifications',
|
||||
function (t) {
|
||||
var successCalled = 0
|
||||
var thisMockLog = mockLog({
|
||||
increment: function (log) {
|
||||
if (log === 'push.success') {
|
||||
if (log === 'push.account_verify.success') {
|
||||
// notification sent
|
||||
successCalled++
|
||||
}
|
||||
|
@ -84,21 +87,92 @@ test(
|
|||
})
|
||||
|
||||
var mocks = {
|
||||
request: {
|
||||
post: function (url, cb) {
|
||||
t.equal(url.headers.ttl, '0', 'sends the proper ttl header')
|
||||
return cb()
|
||||
'web-push': {
|
||||
sendNotification: function (endpoint, params) {
|
||||
t.equal(params.TTL, '0', 'sends the proper ttl header')
|
||||
return P.resolve()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var push = proxyquire('../../lib/push', mocks)(thisMockLog, mockDbResult)
|
||||
push.notifyUpdate(mockUid, 'accountVerify')
|
||||
push.pushToDevices(mockUid, 'accountVerify')
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'notifyUpdate catches devices with no push callback',
|
||||
'pushToDevices does not send notification to an excluded device',
|
||||
function (t) {
|
||||
var mocks = {
|
||||
'web-push': {
|
||||
sendNotification: function (endpoint, params) {
|
||||
t.end()
|
||||
return P.resolve()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mockDbResult.devices().then(function(devices) {
|
||||
var push = proxyquire('../../lib/push', mocks)(mockLog(), mockDbResult)
|
||||
push.pushToDevices(mockUid, 'accountVerify', null, [devices[0].id])
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'pushToDevices sends data',
|
||||
function (t) {
|
||||
var count = 0
|
||||
var mocks = {
|
||||
'web-push': {
|
||||
sendNotification: function (endpoint, params) {
|
||||
count++
|
||||
t.ok(params.userPublicKey)
|
||||
t.ok(params.userAuth)
|
||||
t.deepEqual(params.payload, new Buffer('foobar'))
|
||||
if (count === 2) {
|
||||
t.end()
|
||||
}
|
||||
return P.resolve()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var push = proxyquire('../../lib/push', mocks)(mockLog(), mockDbResult)
|
||||
push.pushToDevices(mockUid, 'accountVerify', new Buffer('foobar'))
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'pushToDevices fails if data is present but both keys are not present',
|
||||
function (t) {
|
||||
var mockDbNoKeys = {
|
||||
devices: function () {
|
||||
return P.resolve([{
|
||||
'id': 'foo',
|
||||
'name': 'My Phone',
|
||||
'pushCallback': 'https://updates.push.services.mozilla.com/update/abcdef01234567890abcdefabcdef01234567890abcdef',
|
||||
'pushAuthKey': 'bogus'
|
||||
}])
|
||||
}
|
||||
}
|
||||
|
||||
var thisMockLog = mockLog({
|
||||
increment: function (log) {
|
||||
if (log === 'push.account_verify.data_but_no_keys') {
|
||||
// data detected but device had no keys
|
||||
t.end()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
var push = require('../../lib/push')(thisMockLog, mockDbNoKeys)
|
||||
push.pushToDevices(mockUid, 'accountVerify', new Buffer('foobar'))
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'pushToDevices catches devices with no push callback',
|
||||
function (t) {
|
||||
var mockDbNoCallback = {
|
||||
devices: function () {
|
||||
|
@ -111,7 +185,7 @@ test(
|
|||
|
||||
var thisMockLog = mockLog({
|
||||
increment: function (log) {
|
||||
if (log === 'push.no_push_callback') {
|
||||
if (log === 'push.account_verify.no_push_callback') {
|
||||
// device had no push callback
|
||||
t.end()
|
||||
}
|
||||
|
@ -119,12 +193,12 @@ test(
|
|||
})
|
||||
|
||||
var push = require('../../lib/push')(thisMockLog, mockDbNoCallback)
|
||||
push.notifyUpdate(mockUid, 'accountVerify')
|
||||
push.pushToDevices(mockUid, 'accountVerify')
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'notifyUpdate reports errors when requests fail',
|
||||
'pushToDevices reports errors when web-push fails',
|
||||
function (t) {
|
||||
var mockDb = {
|
||||
devices: function (/* uid */) {
|
||||
|
@ -139,28 +213,28 @@ test(
|
|||
|
||||
var thisMockLog = mockLog({
|
||||
increment: function (log) {
|
||||
if (log === 'push.failed') {
|
||||
// request failed
|
||||
if (log === 'push.account_verify.failed') {
|
||||
// web-push failed
|
||||
t.end()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
var mocks = {
|
||||
request: {
|
||||
post: function (url, cb) {
|
||||
return cb(new Error('Failed'))
|
||||
'web-push': {
|
||||
sendNotification: function (endpoint, params) {
|
||||
return P.reject(new Error('Failed'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var push = proxyquire('../../lib/push', mocks)(thisMockLog, mockDb)
|
||||
push.notifyUpdate(mockUid, 'accountVerify')
|
||||
push.pushToDevices(mockUid, 'accountVerify')
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'notifyUpdate resets device push data when push server responds with a 400 level error',
|
||||
'pushToDevices resets device push data when push server responds with a 400 level error',
|
||||
function (t) {
|
||||
var mockDb = {
|
||||
devices: function (/* uid */) {
|
||||
|
@ -178,25 +252,71 @@ test(
|
|||
|
||||
var thisMockLog = mockLog({
|
||||
increment: function (log) {
|
||||
if (log === 'push.reset_settings') {
|
||||
// request failed
|
||||
if (log === 'push.account_verify.reset_settings') {
|
||||
// web-push failed
|
||||
t.end()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
var mocks = {
|
||||
request: {
|
||||
post: function (url, cb) {
|
||||
return cb(new Error('Failed 400 level'), {
|
||||
statusCode: 410
|
||||
})
|
||||
'web-push': {
|
||||
sendNotification: function (endpoint, params) {
|
||||
var err = new Error('Failed')
|
||||
err.statusCode = 410
|
||||
return P.reject(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var push = proxyquire('../../lib/push', mocks)(thisMockLog, mockDb)
|
||||
push.notifyUpdate(mockUid, 'accountVerify')
|
||||
push.pushToDevices(mockUid, 'accountVerify')
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'notifyUpdate calls pushToDevices',
|
||||
function (t) {
|
||||
try {
|
||||
var push = require('../../lib/push')(mockLog(), mockDbEmpty)
|
||||
sinon.spy(push, 'pushToDevices')
|
||||
push.notifyUpdate(mockUid, 'passwordReset').catch(function (err) {
|
||||
t.fail('must not throw')
|
||||
throw err
|
||||
})
|
||||
.then(function() {
|
||||
t.ok(push.pushToDevices.calledOnce, 'pushToDevices was called')
|
||||
t.equal(push.pushToDevices.getCall(0).args[0], mockUid)
|
||||
t.equal(push.pushToDevices.getCall(0).args[1], 'passwordReset')
|
||||
push.pushToDevices.restore()
|
||||
t.end()
|
||||
})
|
||||
} catch (e) {
|
||||
t.fail('must not throw')
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'notifyUpdate without a 2nd arg calls pushToDevices with a accountVerify reason',
|
||||
function (t) {
|
||||
try {
|
||||
var push = require('../../lib/push')(mockLog(), mockDbEmpty)
|
||||
sinon.spy(push, 'pushToDevices')
|
||||
push.notifyUpdate(mockUid).catch(function (err) {
|
||||
t.fail('must not throw')
|
||||
throw err
|
||||
})
|
||||
.then(function() {
|
||||
t.ok(push.pushToDevices.calledOnce, 'pushToDevices was called')
|
||||
t.equal(push.pushToDevices.getCall(0).args[0], mockUid)
|
||||
t.equal(push.pushToDevices.getCall(0).args[1], 'accountVerify')
|
||||
push.pushToDevices.restore()
|
||||
t.end()
|
||||
})
|
||||
} catch (e) {
|
||||
t.fail('must not throw')
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ var TestServer = require('../test_server')
|
|||
var Client = require('../client')
|
||||
var config = require('../../config').getProperties()
|
||||
var crypto = require('crypto')
|
||||
var base64url = require('base64url')
|
||||
var P = require('../../lib/promise')
|
||||
|
||||
TestServer.start(config)
|
||||
|
@ -19,7 +20,9 @@ TestServer.start(config)
|
|||
var deviceInfo = {
|
||||
name: 'test device',
|
||||
type: 'desktop',
|
||||
pushCallback: 'https://foo/bar'
|
||||
pushCallback: 'https://foo/bar',
|
||||
pushPublicKey: base64url(Buffer.concat([new Buffer('\x04'), crypto.randomBytes(64)])),
|
||||
pushAuthKey: base64url(crypto.randomBytes(16))
|
||||
}
|
||||
return Client.create(config.publicUrl, email, password, { device: deviceInfo })
|
||||
.then(
|
||||
|
@ -30,6 +33,8 @@ TestServer.start(config)
|
|||
t.equal(client.device.name, deviceInfo.name, 'device.name is correct')
|
||||
t.equal(client.device.type, deviceInfo.type, 'device.type is correct')
|
||||
t.equal(client.device.pushCallback, deviceInfo.pushCallback, 'device.pushCallback is correct')
|
||||
t.equal(client.device.pushPublicKey, deviceInfo.pushPublicKey, 'device.pushPublicKey is correct')
|
||||
t.equal(client.device.pushAuthKey, deviceInfo.pushAuthKey, 'device.pushAuthKey is correct')
|
||||
return client.devices()
|
||||
.then(
|
||||
function (devices) {
|
||||
|
@ -39,6 +44,8 @@ TestServer.start(config)
|
|||
t.equal(devices[0].name, deviceInfo.name, 'devices returned correct name')
|
||||
t.equal(devices[0].type, deviceInfo.type, 'devices returned correct type')
|
||||
t.equal(devices[0].pushCallback, deviceInfo.pushCallback, 'devices returned correct pushCallback')
|
||||
t.equal(devices[0].pushPublicKey, deviceInfo.pushPublicKey, 'devices returned correct pushPublicKey')
|
||||
t.equal(devices[0].pushAuthKey, deviceInfo.pushAuthKey, 'devices returned correct pushAuthKey')
|
||||
return client.updateDevice({
|
||||
id: client.device.id,
|
||||
name: 'new name'
|
||||
|
@ -56,6 +63,8 @@ TestServer.start(config)
|
|||
t.equal(devices[0].name, 'new name', 'devices returned correct name')
|
||||
t.equal(devices[0].type, deviceInfo.type, 'devices returned correct type')
|
||||
t.equal(devices[0].pushCallback, deviceInfo.pushCallback, 'devices returned correct pushCallback')
|
||||
t.equal(devices[0].pushPublicKey, deviceInfo.pushPublicKey, 'devices returned correct pushPublicKey')
|
||||
t.equal(devices[0].pushAuthKey, deviceInfo.pushAuthKey, 'devices returned correct pushAuthKey')
|
||||
return client.destroyDevice(devices[0].id)
|
||||
}
|
||||
)
|
||||
|
@ -82,7 +91,9 @@ TestServer.start(config)
|
|||
var deviceInfo = {
|
||||
name: 'a different device name',
|
||||
type: 'mobile',
|
||||
pushCallback: ''
|
||||
pushCallback: '',
|
||||
pushPublicKey: '',
|
||||
pushAuthKey: ''
|
||||
}
|
||||
return Client.createAndVerify(config.publicUrl, email, password, server.mailbox)
|
||||
.then(
|
||||
|
@ -97,6 +108,8 @@ TestServer.start(config)
|
|||
t.equal(client.device.name, deviceInfo.name, 'device.name is correct')
|
||||
t.equal(client.device.type, deviceInfo.type, 'device.type is correct')
|
||||
t.equal(client.device.pushCallback, deviceInfo.pushCallback, 'device.pushCallback is correct')
|
||||
t.equal(client.device.pushPublicKey, deviceInfo.pushPublicKey, 'device.pushPublicKey is correct')
|
||||
t.equal(client.device.pushAuthKey, deviceInfo.pushAuthKey, 'device.pushAuthKey is correct')
|
||||
return client.devices()
|
||||
.then(
|
||||
function (devices) {
|
||||
|
@ -104,6 +117,8 @@ TestServer.start(config)
|
|||
t.equal(devices[0].name, deviceInfo.name, 'devices returned correct name')
|
||||
t.equal(devices[0].type, deviceInfo.type, 'devices returned correct type')
|
||||
t.equal(devices[0].pushCallback, deviceInfo.pushCallback, 'devices returned correct pushCallback')
|
||||
t.equal(devices[0].pushPublicKey, '', 'devices returned correct pushPublicKey')
|
||||
t.equal(devices[0].pushAuthKey, '', 'devices returned correct pushAuthKey')
|
||||
return client.destroyDevice(devices[0].id)
|
||||
}
|
||||
)
|
||||
|
@ -123,7 +138,9 @@ TestServer.start(config)
|
|||
var deviceInfo = {
|
||||
name: 'test device',
|
||||
type: 'mobile',
|
||||
pushCallback: ''
|
||||
pushCallback: '',
|
||||
pushPublicKey: '',
|
||||
pushAuthKey: ''
|
||||
}
|
||||
return client.devices()
|
||||
.then(
|
||||
|
@ -139,6 +156,8 @@ TestServer.start(config)
|
|||
t.equal(device.name, deviceInfo.name, 'device.name is correct')
|
||||
t.equal(device.type, deviceInfo.type, 'device.type is correct')
|
||||
t.equal(device.pushCallback, deviceInfo.pushCallback, 'device.pushCallback is correct')
|
||||
t.equal(device.pushPublicKey, deviceInfo.pushPublicKey, 'device.pushPublicKey is correct')
|
||||
t.equal(device.pushAuthKey, deviceInfo.pushAuthKey, 'device.pushAuthKey is correct')
|
||||
}
|
||||
)
|
||||
.then(
|
||||
|
@ -152,6 +171,8 @@ TestServer.start(config)
|
|||
t.equal(devices[0].name, deviceInfo.name, 'devices returned correct name')
|
||||
t.equal(devices[0].type, deviceInfo.type, 'devices returned correct type')
|
||||
t.equal(devices[0].pushCallback, '', 'devices returned empty pushCallback')
|
||||
t.equal(devices[0].pushPublicKey, '', 'devices returned correct pushPublicKey')
|
||||
t.equal(devices[0].pushAuthKey, '', 'devices returned correct pushAuthKey')
|
||||
return client.destroyDevice(devices[0].id)
|
||||
}
|
||||
)
|
||||
|
@ -186,6 +207,8 @@ TestServer.start(config)
|
|||
t.equal(device.name, deviceInfo.name, 'device.name is correct')
|
||||
t.equal(device.type, deviceInfo.type, 'device.type is correct')
|
||||
t.equal(device.pushCallback, undefined, 'device.pushCallback is undefined')
|
||||
t.equal(device.pushPublicKey, undefined, 'device.pushPublicKey is undefined')
|
||||
t.equal(device.pushAuthKey, undefined, 'device.pushAuthKey is undefined')
|
||||
}
|
||||
)
|
||||
.then(
|
||||
|
@ -199,6 +222,8 @@ TestServer.start(config)
|
|||
t.equal(devices[0].name, deviceInfo.name, 'devices returned correct name')
|
||||
t.equal(devices[0].type, deviceInfo.type, 'devices returned correct type')
|
||||
t.equal(devices[0].pushCallback, null, 'devices returned undefined pushCallback')
|
||||
t.equal(devices[0].pushPublicKey, null, 'devices returned undefined pushPublicKey')
|
||||
t.equal(devices[0].pushAuthKey, null, 'devices returned undefined pushAuthKey')
|
||||
return client.destroyDevice(devices[0].id)
|
||||
}
|
||||
)
|
||||
|
@ -257,39 +282,6 @@ TestServer.start(config)
|
|||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'device registration with the not-yet-accepted pushPublicKey field',
|
||||
function (t) {
|
||||
var email = server.uniqueEmail()
|
||||
var password = 'test password'
|
||||
return Client.create(config.publicUrl, email, password)
|
||||
.then(
|
||||
function (client) {
|
||||
var deviceInfo = {
|
||||
id: crypto.randomBytes(16).toString('hex'),
|
||||
name: 'test device',
|
||||
type: 'mobile',
|
||||
pushCallback: 'https://some.fake.url',
|
||||
pushPublicKey: crypto.randomBytes(32).toString('hex')
|
||||
}
|
||||
return client.updateDevice(deviceInfo)
|
||||
.then(
|
||||
function () {
|
||||
t.fail('request should have failed')
|
||||
}
|
||||
)
|
||||
.catch(
|
||||
function (err) {
|
||||
t.equal(err.code, 400, 'err.code was 400')
|
||||
t.equal(err.errno, 107, 'err.errno was 107')
|
||||
t.equal(err.validation.keys[0], 'pushPublicKey', 'pushPublicKey was rejected')
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'device registration with unsupported characters in the name',
|
||||
function (t) {
|
||||
|
@ -388,6 +380,50 @@ TestServer.start(config)
|
|||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'update device with callbackUrl but without keys resets the keys',
|
||||
function (t) {
|
||||
var email = server.uniqueEmail()
|
||||
var password = 'test password'
|
||||
var deviceInfo = {
|
||||
name: 'test device',
|
||||
type: 'desktop',
|
||||
pushCallback: 'https://foo/bar',
|
||||
pushPublicKey: base64url(Buffer.concat([new Buffer('\x04'), crypto.randomBytes(64)])),
|
||||
pushAuthKey: base64url(crypto.randomBytes(16))
|
||||
}
|
||||
return Client.create(config.publicUrl, email, password, { device: deviceInfo })
|
||||
.then(
|
||||
function (client) {
|
||||
return client.devices()
|
||||
.then(
|
||||
function (devices) {
|
||||
t.equal(devices[0].pushCallback, deviceInfo.pushCallback, 'devices returned correct pushCallback')
|
||||
t.equal(devices[0].pushPublicKey, deviceInfo.pushPublicKey, 'devices returned correct pushPublicKey')
|
||||
t.equal(devices[0].pushAuthKey, deviceInfo.pushAuthKey, 'devices returned correct pushAuthKey')
|
||||
return client.updateDevice({
|
||||
id: client.device.id,
|
||||
pushCallback: 'https://bar/foo'
|
||||
})
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function () {
|
||||
return client.devices()
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (devices) {
|
||||
t.equal(devices[0].pushCallback, 'https://bar/foo', 'devices returned correct pushCallback')
|
||||
t.equal(devices[0].pushPublicKey, '', 'devices returned newly empty pushPublicKey')
|
||||
t.equal(devices[0].pushAuthKey, '', 'devices returned newly empty pushAuthKey')
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
// Regression test for https://github.com/mozilla/fxa-auth-server/issues/1197
|
||||
'devices list, sessionToken.lastAccessTime === 0 (regression test for #1197)',
|
||||
|
|
|
@ -6,6 +6,9 @@ var test = require('../ptaptest')
|
|||
var url = require('url')
|
||||
var Client = require('../client')
|
||||
var TestServer = require('../test_server')
|
||||
var crypto = require('crypto')
|
||||
var base64url = require('base64url')
|
||||
|
||||
|
||||
var config = require('../../config').getProperties()
|
||||
|
||||
|
@ -368,7 +371,9 @@ TestServer.start(config)
|
|||
device: {
|
||||
name: 'baz',
|
||||
type: 'mobile',
|
||||
pushCallback: 'https://example.com/qux'
|
||||
pushCallback: 'https://example.com/qux',
|
||||
pushPublicKey: base64url(Buffer.concat([new Buffer('\x04'), crypto.randomBytes(64)])),
|
||||
pushAuthKey: base64url(crypto.randomBytes(16))
|
||||
}
|
||||
})
|
||||
.then(
|
||||
|
|
Загрузка…
Ссылка в новой задаче