services/sync/tests/unit/test_hmac_error.js

Wed, 31 Dec 2014 07:53:36 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:53:36 +0100
branch
TOR_BUG_3246
changeset 5
4ab42b5ab56c
permissions
-rw-r--r--

Correct small whitespace inconsistency, lost while renaming variables.

michael@0 1 /* Any copyright is dedicated to the Public Domain.
michael@0 2 http://creativecommons.org/publicdomain/zero/1.0/ */
michael@0 3
michael@0 4 Cu.import("resource://services-sync/engines.js");
michael@0 5 Cu.import("resource://services-sync/service.js");
michael@0 6 Cu.import("resource://services-sync/util.js");
michael@0 7 Cu.import("resource://testing-common/services/sync/rotaryengine.js");
michael@0 8 Cu.import("resource://testing-common/services/sync/utils.js");
michael@0 9
michael@0 10 // Track HMAC error counts.
michael@0 11 let hmacErrorCount = 0;
michael@0 12 (function () {
michael@0 13 let hHE = Service.handleHMACEvent;
michael@0 14 Service.handleHMACEvent = function () {
michael@0 15 hmacErrorCount++;
michael@0 16 return hHE.call(Service);
michael@0 17 };
michael@0 18 })();
michael@0 19
michael@0 20 function shared_setup() {
michael@0 21 hmacErrorCount = 0;
michael@0 22
michael@0 23 // Do not instantiate SyncTestingInfrastructure; we need real crypto.
michael@0 24 ensureLegacyIdentityManager();
michael@0 25 setBasicCredentials("foo", "foo", "aabcdeabcdeabcdeabcdeabcde");
michael@0 26
michael@0 27 // Make sure RotaryEngine is the only one we sync.
michael@0 28 Service.engineManager._engines = {};
michael@0 29 Service.engineManager.register(RotaryEngine);
michael@0 30 let engine = Service.engineManager.get("rotary");
michael@0 31 engine.enabled = true;
michael@0 32 engine.lastSync = 123; // Needs to be non-zero so that tracker is queried.
michael@0 33 engine._store.items = {flying: "LNER Class A3 4472",
michael@0 34 scotsman: "Flying Scotsman"};
michael@0 35 engine._tracker.addChangedID('scotsman', 0);
michael@0 36 do_check_eq(1, Service.engineManager.getEnabled().length);
michael@0 37
michael@0 38 let engines = {rotary: {version: engine.version,
michael@0 39 syncID: engine.syncID},
michael@0 40 clients: {version: Service.clientsEngine.version,
michael@0 41 syncID: Service.clientsEngine.syncID}};
michael@0 42
michael@0 43 // Common server objects.
michael@0 44 let global = new ServerWBO("global", {engines: engines});
michael@0 45 let keysWBO = new ServerWBO("keys");
michael@0 46 let rotaryColl = new ServerCollection({}, true);
michael@0 47 let clientsColl = new ServerCollection({}, true);
michael@0 48
michael@0 49 return [engine, rotaryColl, clientsColl, keysWBO, global];
michael@0 50 }
michael@0 51
michael@0 52 add_test(function hmac_error_during_404() {
michael@0 53 _("Attempt to replicate the HMAC error setup.");
michael@0 54 let [engine, rotaryColl, clientsColl, keysWBO, global] = shared_setup();
michael@0 55
michael@0 56 // Hand out 404s for crypto/keys.
michael@0 57 let keysHandler = keysWBO.handler();
michael@0 58 let key404Counter = 0;
michael@0 59 let keys404Handler = function (request, response) {
michael@0 60 if (key404Counter > 0) {
michael@0 61 let body = "Not Found";
michael@0 62 response.setStatusLine(request.httpVersion, 404, body);
michael@0 63 response.bodyOutputStream.write(body, body.length);
michael@0 64 key404Counter--;
michael@0 65 return;
michael@0 66 }
michael@0 67 keysHandler(request, response);
michael@0 68 };
michael@0 69
michael@0 70 let collectionsHelper = track_collections_helper();
michael@0 71 let upd = collectionsHelper.with_updated_collection;
michael@0 72 let collections = collectionsHelper.collections;
michael@0 73 let handlers = {
michael@0 74 "/1.1/foo/info/collections": collectionsHelper.handler,
michael@0 75 "/1.1/foo/storage/meta/global": upd("meta", global.handler()),
michael@0 76 "/1.1/foo/storage/crypto/keys": upd("crypto", keys404Handler),
michael@0 77 "/1.1/foo/storage/clients": upd("clients", clientsColl.handler()),
michael@0 78 "/1.1/foo/storage/rotary": upd("rotary", rotaryColl.handler())
michael@0 79 };
michael@0 80
michael@0 81 let server = sync_httpd_setup(handlers);
michael@0 82 Service.serverURL = server.baseURI;
michael@0 83
michael@0 84 try {
michael@0 85 _("Syncing.");
michael@0 86 Service.sync();
michael@0 87 _("Partially resetting client, as if after a restart, and forcing redownload.");
michael@0 88 Service.collectionKeys.clear();
michael@0 89 engine.lastSync = 0; // So that we redownload records.
michael@0 90 key404Counter = 1;
michael@0 91 _("---------------------------");
michael@0 92 Service.sync();
michael@0 93 _("---------------------------");
michael@0 94
michael@0 95 // Two rotary items, one client record... no errors.
michael@0 96 do_check_eq(hmacErrorCount, 0)
michael@0 97 } finally {
michael@0 98 Svc.Prefs.resetBranch("");
michael@0 99 Service.recordManager.clearCache();
michael@0 100 server.stop(run_next_test);
michael@0 101 }
michael@0 102 });
michael@0 103
michael@0 104 add_test(function hmac_error_during_node_reassignment() {
michael@0 105 _("Attempt to replicate an HMAC error during node reassignment.");
michael@0 106 let [engine, rotaryColl, clientsColl, keysWBO, global] = shared_setup();
michael@0 107
michael@0 108 let collectionsHelper = track_collections_helper();
michael@0 109 let upd = collectionsHelper.with_updated_collection;
michael@0 110
michael@0 111 // We'll provide a 401 mid-way through the sync. This function
michael@0 112 // simulates shifting to a node which has no data.
michael@0 113 function on401() {
michael@0 114 _("Deleting server data...");
michael@0 115 global.delete();
michael@0 116 rotaryColl.delete();
michael@0 117 keysWBO.delete();
michael@0 118 clientsColl.delete();
michael@0 119 delete collectionsHelper.collections.rotary;
michael@0 120 delete collectionsHelper.collections.crypto;
michael@0 121 delete collectionsHelper.collections.clients;
michael@0 122 _("Deleted server data.");
michael@0 123 }
michael@0 124
michael@0 125 let should401 = false;
michael@0 126 function upd401(coll, handler) {
michael@0 127 return function (request, response) {
michael@0 128 if (should401 && (request.method != "DELETE")) {
michael@0 129 on401();
michael@0 130 should401 = false;
michael@0 131 let body = "\"reassigned!\"";
michael@0 132 response.setStatusLine(request.httpVersion, 401, "Node reassignment.");
michael@0 133 response.bodyOutputStream.write(body, body.length);
michael@0 134 return;
michael@0 135 }
michael@0 136 handler(request, response);
michael@0 137 };
michael@0 138 }
michael@0 139
michael@0 140 function sameNodeHandler(request, response) {
michael@0 141 // Set this so that _setCluster will think we've really changed.
michael@0 142 let url = Service.serverURL.replace("localhost", "LOCALHOST");
michael@0 143 _("Client requesting reassignment; pointing them to " + url);
michael@0 144 response.setStatusLine(request.httpVersion, 200, "OK");
michael@0 145 response.bodyOutputStream.write(url, url.length);
michael@0 146 }
michael@0 147
michael@0 148 let handlers = {
michael@0 149 "/user/1.0/foo/node/weave": sameNodeHandler,
michael@0 150 "/1.1/foo/info/collections": collectionsHelper.handler,
michael@0 151 "/1.1/foo/storage/meta/global": upd("meta", global.handler()),
michael@0 152 "/1.1/foo/storage/crypto/keys": upd("crypto", keysWBO.handler()),
michael@0 153 "/1.1/foo/storage/clients": upd401("clients", clientsColl.handler()),
michael@0 154 "/1.1/foo/storage/rotary": upd("rotary", rotaryColl.handler())
michael@0 155 };
michael@0 156
michael@0 157 let server = sync_httpd_setup(handlers);
michael@0 158 Service.serverURL = server.baseURI;
michael@0 159 _("Syncing.");
michael@0 160 // First hit of clients will 401. This will happen after meta/global and
michael@0 161 // keys -- i.e., in the middle of the sync, but before RotaryEngine.
michael@0 162 should401 = true;
michael@0 163
michael@0 164 // Use observers to perform actions when our sync finishes.
michael@0 165 // This allows us to observe the automatic next-tick sync that occurs after
michael@0 166 // an abort.
michael@0 167 function onSyncError() {
michael@0 168 do_throw("Should not get a sync error!");
michael@0 169 }
michael@0 170 function onSyncFinished() {}
michael@0 171 let obs = {
michael@0 172 observe: function observe(subject, topic, data) {
michael@0 173 switch (topic) {
michael@0 174 case "weave:service:sync:error":
michael@0 175 onSyncError();
michael@0 176 break;
michael@0 177 case "weave:service:sync:finish":
michael@0 178 onSyncFinished();
michael@0 179 break;
michael@0 180 }
michael@0 181 }
michael@0 182 };
michael@0 183
michael@0 184 Svc.Obs.add("weave:service:sync:finish", obs);
michael@0 185 Svc.Obs.add("weave:service:sync:error", obs);
michael@0 186
michael@0 187 // This kicks off the actual test. Split into a function here to allow this
michael@0 188 // source file to broadly follow actual execution order.
michael@0 189 function onwards() {
michael@0 190 _("== Invoking first sync.");
michael@0 191 Service.sync();
michael@0 192 _("We should not simultaneously have data but no keys on the server.");
michael@0 193 let hasData = rotaryColl.wbo("flying") ||
michael@0 194 rotaryColl.wbo("scotsman");
michael@0 195 let hasKeys = keysWBO.modified;
michael@0 196
michael@0 197 _("We correctly handle 401s by aborting the sync and starting again.");
michael@0 198 do_check_true(!hasData == !hasKeys);
michael@0 199
michael@0 200 _("Be prepared for the second (automatic) sync...");
michael@0 201 }
michael@0 202
michael@0 203 _("Make sure that syncing again causes recovery.");
michael@0 204 onSyncFinished = function() {
michael@0 205 _("== First sync done.");
michael@0 206 _("---------------------------");
michael@0 207 onSyncFinished = function() {
michael@0 208 _("== Second (automatic) sync done.");
michael@0 209 hasData = rotaryColl.wbo("flying") ||
michael@0 210 rotaryColl.wbo("scotsman");
michael@0 211 hasKeys = keysWBO.modified;
michael@0 212 do_check_true(!hasData == !hasKeys);
michael@0 213
michael@0 214 // Kick off another sync. Can't just call it, because we're inside the
michael@0 215 // lock...
michael@0 216 Utils.nextTick(function() {
michael@0 217 _("Now a fresh sync will get no HMAC errors.");
michael@0 218 _("Partially resetting client, as if after a restart, and forcing redownload.");
michael@0 219 Service.collectionKeys.clear();
michael@0 220 engine.lastSync = 0;
michael@0 221 hmacErrorCount = 0;
michael@0 222
michael@0 223 onSyncFinished = function() {
michael@0 224 // Two rotary items, one client record... no errors.
michael@0 225 do_check_eq(hmacErrorCount, 0)
michael@0 226
michael@0 227 Svc.Obs.remove("weave:service:sync:finish", obs);
michael@0 228 Svc.Obs.remove("weave:service:sync:error", obs);
michael@0 229
michael@0 230 Svc.Prefs.resetBranch("");
michael@0 231 Service.recordManager.clearCache();
michael@0 232 server.stop(run_next_test);
michael@0 233 };
michael@0 234
michael@0 235 Service.sync();
michael@0 236 },
michael@0 237 this);
michael@0 238 };
michael@0 239 };
michael@0 240
michael@0 241 onwards();
michael@0 242 });
michael@0 243
michael@0 244 function run_test() {
michael@0 245 initTestLogging("Trace");
michael@0 246 run_next_test();
michael@0 247 }

mercurial