mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Bug 1247685 - Send subscription keys to the Push server. r=mt
MozReview-Commit-ID: 2i3UqgNGlEt --HG-- extra : rebase_source : 9c30f34c7de7c9fa7f04db5f224bad3ac0e68904
This commit is contained in:
parent
21365c8060
commit
4f645f9bfe
@ -330,6 +330,7 @@ SubscriptionListener.prototype = {
|
||||
scope: this._subInfo.record.scope,
|
||||
originAttributes: this._subInfo.record.originAttributes,
|
||||
systemRecord: this._subInfo.record.systemRecord,
|
||||
appServerKey: this._subInfo.record.appServerKey,
|
||||
ctime: Date.now(),
|
||||
});
|
||||
|
||||
|
@ -906,6 +906,7 @@ this.PushServiceWebSocket = {
|
||||
originAttributes: tmp.record.originAttributes,
|
||||
version: null,
|
||||
systemRecord: tmp.record.systemRecord,
|
||||
appServerKey: tmp.record.appServerKey,
|
||||
ctime: Date.now(),
|
||||
});
|
||||
Services.telemetry.getHistogramById("PUSH_API_SUBSCRIBE_WS_TIME").add(Date.now() - tmp.ctime);
|
||||
@ -1047,6 +1048,13 @@ this.PushServiceWebSocket = {
|
||||
let data = {channelID: this._generateID(),
|
||||
messageType: "register"};
|
||||
|
||||
if (record.appServerKey) {
|
||||
data.key = ChromeUtils.base64URLEncode(record.appServerKey, {
|
||||
// The Push server requires padding.
|
||||
pad: true,
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this._registerRequests.set(data.channelID, {
|
||||
record: record,
|
||||
|
@ -12,6 +12,7 @@ support-files =
|
||||
[test_has_permissions.html]
|
||||
[test_permissions.html]
|
||||
[test_register.html]
|
||||
[test_register_key.html]
|
||||
[test_multiple_register.html]
|
||||
[test_multiple_register_during_service_activation.html]
|
||||
[test_unregister.html]
|
||||
|
206
dom/push/test/test_register_key.html
Normal file
206
dom/push/test/test_register_key.html
Normal file
@ -0,0 +1,206 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Bug 1247685: Implement `applicationServerKey` for subscription association.
|
||||
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 1247685</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
|
||||
<script type="text/javascript" src="/tests/dom/push/test/test_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
|
||||
</head>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1247685">Mozilla Bug 1247685</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var isTestingMismatchedKey = false;
|
||||
var subscriptions = 0;
|
||||
var testKey; // Generated in `start`.
|
||||
|
||||
function generateKey() {
|
||||
return crypto.subtle.generateKey({
|
||||
name: "ECDSA",
|
||||
namedCurve: "P-256",
|
||||
}, true, ["sign", "verify"]).then(cryptoKey =>
|
||||
crypto.subtle.exportKey("raw", cryptoKey.publicKey)
|
||||
).then(publicKey => new Uint8Array(publicKey));
|
||||
}
|
||||
|
||||
var registration;
|
||||
add_task(function* start() {
|
||||
yield setupPrefsAndReplaceService({
|
||||
register(pageRecord) {
|
||||
ok(pageRecord.appServerKey.length > 0,
|
||||
"App server key should not be empty");
|
||||
if (pageRecord.appServerKey.length != 65) {
|
||||
throw { result:
|
||||
SpecialPowers.Cr.NS_ERROR_DOM_PUSH_INVALID_KEY_ERR };
|
||||
}
|
||||
if (isTestingMismatchedKey) {
|
||||
throw { result:
|
||||
SpecialPowers.Cr.NS_ERROR_DOM_PUSH_MISMATCHED_KEY_ERR };
|
||||
}
|
||||
return {
|
||||
endpoint: "https://example.com/push/" + (++subscriptions),
|
||||
appServerKey: pageRecord.appServerKey,
|
||||
};
|
||||
},
|
||||
|
||||
registration(pageRecord) {
|
||||
return {
|
||||
endpoint: "https://example.com/push/subWithKey",
|
||||
appServerKey: testKey,
|
||||
};
|
||||
},
|
||||
});
|
||||
yield setPushPermission(true);
|
||||
testKey = yield generateKey();
|
||||
|
||||
var url = "worker.js" + "?" + (Math.random());
|
||||
registration = yield navigator.serviceWorker.register(url, {scope: "."});
|
||||
});
|
||||
|
||||
var controlledFrame;
|
||||
add_task(function* createControlledIFrame() {
|
||||
controlledFrame = yield injectControlledFrame();
|
||||
});
|
||||
|
||||
add_task(function* emptyKey() {
|
||||
try {
|
||||
yield registration.pushManager.subscribe({
|
||||
applicationServerKey: new ArrayBuffer(0),
|
||||
});
|
||||
ok(false, "Should reject for empty app server keys");
|
||||
} catch (error) {
|
||||
ok(error instanceof DOMException,
|
||||
"Wrong exception type for empty key");
|
||||
is(error.name, "InvalidAccessError",
|
||||
"Wrong exception name for empty key");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* invalidKey() {
|
||||
try {
|
||||
yield registration.pushManager.subscribe({
|
||||
applicationServerKey: new Uint8Array([0]),
|
||||
});
|
||||
ok(false, "Should reject for invalid app server keys");
|
||||
} catch (error) {
|
||||
ok(error instanceof DOMException,
|
||||
"Wrong exception type for invalid key");
|
||||
is(error.name, "InvalidAccessError",
|
||||
"Wrong exception name for invalid key");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* validKey() {
|
||||
var pushSubscription = yield registration.pushManager.subscribe({
|
||||
applicationServerKey: yield generateKey(),
|
||||
});
|
||||
is(pushSubscription.endpoint, "https://example.com/push/1",
|
||||
"Wrong endpoint for subscription with key");
|
||||
});
|
||||
|
||||
add_task(function* retrieveKey() {
|
||||
var pushSubscription = yield registration.pushManager.getSubscription();
|
||||
is(pushSubscription.endpoint, "https://example.com/push/subWithKey",
|
||||
"Got wrong endpoint for subscription with key");
|
||||
isDeeply(
|
||||
new Uint8Array(pushSubscription.options.applicationServerKey),
|
||||
testKey,
|
||||
"Got wrong app server key"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(function* mismatchedKey() {
|
||||
isTestingMismatchedKey = true;
|
||||
try {
|
||||
yield registration.pushManager.subscribe({
|
||||
applicationServerKey: yield generateKey(),
|
||||
});
|
||||
ok(false, "Should reject for mismatched app server keys");
|
||||
} catch (error) {
|
||||
ok(error instanceof DOMException,
|
||||
"Wrong exception type for mismatched key");
|
||||
is(error.name, "InvalidStateError",
|
||||
"Wrong exception name for mismatched key");
|
||||
} finally {
|
||||
isTestingMismatchedKey = false;
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* emptyKeyInWorker() {
|
||||
var errorInfo = yield sendRequestToWorker({
|
||||
type: "subscribeWithKey",
|
||||
key: new ArrayBuffer(0),
|
||||
});
|
||||
ok(errorInfo.isDOMException,
|
||||
"Wrong exception type in worker for empty key");
|
||||
is(errorInfo.name, "InvalidAccessError",
|
||||
"Wrong exception name in worker for empty key");
|
||||
});
|
||||
|
||||
add_task(function* invalidKeyInWorker() {
|
||||
var errorInfo = yield sendRequestToWorker({
|
||||
type: "subscribeWithKey",
|
||||
key: new Uint8Array([1]),
|
||||
});
|
||||
ok(errorInfo.isDOMException,
|
||||
"Wrong exception type in worker for invalid key");
|
||||
is(errorInfo.name, "InvalidAccessError",
|
||||
"Wrong exception name in worker for invalid key");
|
||||
});
|
||||
|
||||
add_task(function* validKeyInWorker() {
|
||||
var key = yield generateKey();
|
||||
var data = yield sendRequestToWorker({
|
||||
type: "subscribeWithKey",
|
||||
key: key,
|
||||
});
|
||||
is(data.endpoint, "https://example.com/push/2",
|
||||
"Wrong endpoint for subscription with key created in worker");
|
||||
isDeeply(new Uint8Array(data.key), key,
|
||||
"Wrong app server key for subscription created in worker");
|
||||
});
|
||||
|
||||
add_task(function* mismatchedKeyInWorker() {
|
||||
isTestingMismatchedKey = true;
|
||||
try {
|
||||
var errorInfo = yield sendRequestToWorker({
|
||||
type: "subscribeWithKey",
|
||||
key: yield generateKey(),
|
||||
});
|
||||
ok(errorInfo.isDOMException,
|
||||
"Wrong exception type in worker for mismatched key");
|
||||
is(errorInfo.name, "InvalidStateError",
|
||||
"Wrong exception name in worker for mismatched key");
|
||||
} finally {
|
||||
isTestingMismatchedKey = false;
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* unsubscribe() {
|
||||
is(subscriptions, 2, "Wrong subscription count");
|
||||
controlledFrame.remove();
|
||||
});
|
||||
|
||||
add_task(function* unregister() {
|
||||
yield registration.unregister();
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -73,20 +73,20 @@ function handlePush(event) {
|
||||
broadcast(event, {type: "finished", okay: "no"});
|
||||
}
|
||||
|
||||
function handleMessage(event) {
|
||||
if (event.data.type == "publicKey") {
|
||||
reply(event, self.registration.pushManager.getSubscription().then(
|
||||
var testHandlers = {
|
||||
publicKey(data) {
|
||||
return self.registration.pushManager.getSubscription().then(
|
||||
subscription => ({
|
||||
p256dh: subscription.getKey("p256dh"),
|
||||
auth: subscription.getKey("auth"),
|
||||
})
|
||||
));
|
||||
return;
|
||||
}
|
||||
if (event.data.type == "resubscribe") {
|
||||
reply(event, self.registration.pushManager.getSubscription().then(
|
||||
);
|
||||
},
|
||||
|
||||
resubscribe(data) {
|
||||
return self.registration.pushManager.getSubscription().then(
|
||||
subscription => {
|
||||
assert(subscription.endpoint == event.data.endpoint,
|
||||
assert(subscription.endpoint == data.endpoint,
|
||||
"Wrong push endpoint in worker");
|
||||
return subscription.unsubscribe();
|
||||
}
|
||||
@ -100,11 +100,11 @@ function handleMessage(event) {
|
||||
return {
|
||||
endpoint: subscription.endpoint,
|
||||
};
|
||||
}));
|
||||
return;
|
||||
}
|
||||
if (event.data.type == "denySubscribe") {
|
||||
reply(event, self.registration.pushManager.getSubscription().then(
|
||||
});
|
||||
},
|
||||
|
||||
denySubscribe(data) {
|
||||
return self.registration.pushManager.getSubscription().then(
|
||||
subscription => {
|
||||
assert(!subscription,
|
||||
"Should not return worker subscription with revoked permission");
|
||||
@ -117,11 +117,34 @@ function handleMessage(event) {
|
||||
};
|
||||
});
|
||||
}
|
||||
));
|
||||
return;
|
||||
);
|
||||
},
|
||||
|
||||
subscribeWithKey(data) {
|
||||
return self.registration.pushManager.subscribe({
|
||||
applicationServerKey: data.key,
|
||||
}).then(subscription => {
|
||||
return {
|
||||
endpoint: subscription.endpoint,
|
||||
key: subscription.options.applicationServerKey,
|
||||
};
|
||||
}, error => {
|
||||
return {
|
||||
isDOMException: error instanceof DOMException,
|
||||
name: error.name,
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
function handleMessage(event) {
|
||||
var handler = testHandlers[event.data.type];
|
||||
if (handler) {
|
||||
reply(event, handler(event.data));
|
||||
} else {
|
||||
reply(event, Promise.reject(
|
||||
"Invalid message type: " + event.data.type));
|
||||
}
|
||||
reply(event, Promise.reject(
|
||||
"Invalid message type: " + event.data.type));
|
||||
}
|
||||
|
||||
function handlePushSubscriptionChange(event) {
|
||||
|
@ -455,6 +455,15 @@ var setUpServiceInParent = Task.async(function* (service, db) {
|
||||
}));
|
||||
},
|
||||
onRegister(request) {
|
||||
if (request.key) {
|
||||
let appServerKey = new Uint8Array(
|
||||
ChromeUtils.base64URLDecode(request.key, {
|
||||
padding: "require",
|
||||
})
|
||||
);
|
||||
equal(appServerKey.length, 65, 'Wrong app server key length');
|
||||
equal(appServerKey[0], 4, 'Wrong app server key format');
|
||||
}
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'register',
|
||||
uaid: userAgentID,
|
||||
|
@ -3,10 +3,26 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
Cu.importGlobalProperties(["crypto"]);
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
var db;
|
||||
|
||||
function done() {
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function generateKey() {
|
||||
return crypto.subtle.generateKey({
|
||||
name: "ECDSA",
|
||||
namedCurve: "P-256",
|
||||
}, true, ["sign", "verify"]).then(cryptoKey =>
|
||||
crypto.subtle.exportKey("raw", cryptoKey.publicKey)
|
||||
).then(publicKey => new Uint8Array(publicKey));
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
if (isParent) {
|
||||
do_get_profile();
|
||||
@ -40,6 +56,69 @@ add_test(function test_subscribe_success() {
|
||||
);
|
||||
});
|
||||
|
||||
add_test(function test_subscribeWithKey_error() {
|
||||
do_test_pending();
|
||||
|
||||
let invalidKey = [0, 1];
|
||||
PushServiceComponent.subscribeWithKey(
|
||||
'https://example.com/sub-key/invalid',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
invalidKey.length,
|
||||
invalidKey,
|
||||
(result, subscription) => {
|
||||
ok(!Components.isSuccessCode(result), 'Expected error creating subscription with invalid key');
|
||||
equal(result, Cr.NS_ERROR_DOM_PUSH_INVALID_KEY_ERR, 'Wrong error code for invalid key');
|
||||
strictEqual(subscription, null, 'Unexpected subscription');
|
||||
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_test(function test_subscribeWithKey_success() {
|
||||
do_test_pending();
|
||||
|
||||
generateKey().then(key => {
|
||||
PushServiceComponent.subscribeWithKey(
|
||||
'https://example.com/sub-key/ok',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
key.length,
|
||||
key,
|
||||
(result, subscription) => {
|
||||
ok(Components.isSuccessCode(result), 'Error creating subscription with key');
|
||||
notStrictEqual(subscription, null, 'Expected subscription');
|
||||
done();
|
||||
}
|
||||
);
|
||||
}, error => {
|
||||
ok(false, "Error generating app server key");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_subscribeWithKey_conflict() {
|
||||
do_test_pending();
|
||||
|
||||
generateKey().then(differentKey => {
|
||||
PushServiceComponent.subscribeWithKey(
|
||||
'https://example.com/sub-key/ok',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
differentKey.length,
|
||||
differentKey,
|
||||
(result, subscription) => {
|
||||
ok(!Components.isSuccessCode(result), 'Expected error creating subscription with conflicting key');
|
||||
equal(result, Cr.NS_ERROR_DOM_PUSH_MISMATCHED_KEY_ERR, 'Wrong error code for mismatched key');
|
||||
strictEqual(subscription, null, 'Unexpected subscription');
|
||||
done();
|
||||
}
|
||||
);
|
||||
}, error => {
|
||||
ok(false, "Error generating different app server key");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_subscribe_error() {
|
||||
do_test_pending();
|
||||
PushServiceComponent.subscribe(
|
||||
|
Loading…
Reference in New Issue
Block a user