--- a/services/common/blocklist-clients.js
+++ b/services/common/blocklist-clients.js
@@ -63,64 +63,53 @@ function mergeChanges(collection, localR
return Object.values(records)
// Filter out deleted records.
.filter((record) => record.deleted != true)
// Sort list by record id.
.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
}
-function fetchCollectionMetadata(collection) {
- const client = new KintoHttpClient(collection.api.remote);
+function fetchCollectionMetadata(remote, collection) {
+ const client = new KintoHttpClient(remote);
return client.bucket(collection.bucket).collection(collection.name).getData()
.then(result => {
return result.signature;
});
}
-function fetchRemoteCollection(collection) {
- const client = new KintoHttpClient(collection.api.remote);
+function fetchRemoteCollection(remote, collection) {
+ const client = new KintoHttpClient(remote);
return client.bucket(collection.bucket)
.collection(collection.name)
.listRecords({sort: "id"});
}
-/**
- * Helper to instantiate a Kinto client based on preferences for remote server
- * URL and bucket name. It uses the `FirefoxAdapter` which relies on SQLite to
- * persist the local DB.
- */
-function kintoClient(connection, bucket) {
- const remote = Services.prefs.getCharPref(PREF_SETTINGS_SERVER);
-
- const config = {
- remote,
- bucket,
- adapter: FirefoxAdapter,
- adapterOptions: {sqliteHandle: connection},
- };
-
- return new Kinto(config);
-}
-
class BlocklistClient {
constructor(collectionName, lastCheckTimePref, processCallback, bucketName, signerName) {
this.collectionName = collectionName;
this.lastCheckTimePref = lastCheckTimePref;
this.processCallback = processCallback;
this.bucketName = bucketName;
this.signerName = signerName;
+
+ this._kinto = new Kinto({
+ bucket: bucketName,
+ adapter: FirefoxAdapter,
+ });
}
- validateCollectionSignature(payload, collection, ignoreLocal) {
+ validateCollectionSignature(remote, payload, collection, options = {}) {
+ const {ignoreLocal} = options;
+
return Task.spawn((function* () {
// this is a content-signature field from an autograph response.
- const {x5u, signature} = yield fetchCollectionMetadata(collection);
+ const {x5u, signature} = yield fetchCollectionMetadata(remote, collection);
const certChain = yield fetch(x5u).then((res) => res.text());
const verifier = Cc["@mozilla.org/security/contentsignatureverifier;1"]
.createInstance(Ci.nsIContentSignatureVerifier);
let toSerialize;
if (ignoreLocal) {
toSerialize = {
@@ -152,57 +141,63 @@ class BlocklistClient {
* Synchronize from Kinto server, if necessary.
*
* @param {int} lastModified the lastModified date (on the server) for
the remote collection.
* @param {Date} serverTime the current date return by the server.
* @return {Promise} which rejects on sync or process failure.
*/
maybeSync(lastModified, serverTime) {
- const opts = {};
- const enforceCollectionSigning =
+ const remote = Services.prefs.getCharPref(PREF_SETTINGS_SERVER);
+ let enforceCollectionSigning =
Services.prefs.getBoolPref(PREF_BLOCKLIST_ENFORCE_SIGNING);
// if there is a signerName and collection signing is enforced, add a
// hook for incoming changes that validates the signature
+ let hooks;
if (this.signerName && enforceCollectionSigning) {
- opts.hooks = {
- "incoming-changes": [this.validateCollectionSignature.bind(this)]
+ hooks = {
+ "incoming-changes": [(payload, collection) => {
+ return this.validateCollectionSignature(remote, payload, collection);
+ }]
}
}
-
return Task.spawn((function* syncCollection() {
- let connection;
+ let sqliteHandle;
try {
- connection = yield FirefoxAdapter.openConnection({path: KINTO_STORAGE_PATH});
- const db = kintoClient(connection, this.bucketName);
- const collection = db.collection(this.collectionName, opts);
+ // Synchronize remote data into a local Sqlite DB.
+ sqliteHandle = yield FirefoxAdapter.openConnection({path: KINTO_STORAGE_PATH});
+ const options = {
+ hooks,
+ adapterOptions: {sqliteHandle},
+ };
+ const collection = this._kinto.collection(this.collectionName, options);
const collectionLastModified = yield collection.db.getLastModified();
// If the data is up to date, there's no need to sync. We still need
// to record the fact that a check happened.
if (lastModified <= collectionLastModified) {
this.updateLastCheck(serverTime);
return;
}
// Fetch changes from server.
try {
- const {ok} = yield collection.sync();
+ const {ok} = yield collection.sync({remote});
if (!ok) {
throw new Error("Sync failed");
}
} catch (e) {
if (e.message == INVALID_SIGNATURE) {
// if sync fails with a signature error, it's likely that our
// local data has been modified in some way.
// We will attempt to fix this by retrieving the whole
// remote collection.
- const payload = yield fetchRemoteCollection(collection);
- yield this.validateCollectionSignature(payload, collection, true);
+ const payload = yield fetchRemoteCollection(remote, collection);
+ yield this.validateCollectionSignature(remote, payload, collection, {ignoreLocal: true});
// if the signature is good (we haven't thrown), and the remote
// last_modified is newer than the local last_modified, replace the
// local data
const localLastModified = yield collection.db.getLastModified();
if (payload.last_modified >= localLastModified) {
yield collection.clear();
yield collection.loadDump(payload.data);
}
@@ -213,17 +208,17 @@ class BlocklistClient {
// Read local collection of records.
const {data} = yield collection.list();
yield this.processCallback(data);
// Track last update.
this.updateLastCheck(serverTime);
} finally {
- yield connection.close();
+ yield sqliteHandle.close();
}
}).bind(this));
}
/**
* Save last time server was checked in users prefs.
*
* @param {Date} serverTime the current date return by server.