/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ package org.mozilla.gecko.sync.stage.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.junit.Before; import org.junit.Test; import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper; import org.mozilla.android.sync.test.helpers.MockGlobalSession; import org.mozilla.android.sync.test.helpers.MockGlobalSessionCallback; import org.mozilla.android.sync.test.helpers.MockServer; import org.mozilla.android.sync.test.helpers.WaitHelper; import org.mozilla.gecko.sync.AlreadySyncingException; import org.mozilla.gecko.sync.CollectionKeys; import org.mozilla.gecko.sync.CryptoRecord; import org.mozilla.gecko.sync.ExtendedJSONObject; import org.mozilla.gecko.sync.GlobalSession; import org.mozilla.gecko.sync.InfoCollections; import org.mozilla.gecko.sync.crypto.KeyBundle; import org.mozilla.gecko.sync.stage.EnsureCrypto5KeysStage; import org.mozilla.gecko.sync.stage.GlobalSyncStage; import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage; import org.simpleframework.http.Request; import org.simpleframework.http.Response; public class TestEnsureCrypto5KeysStage { private int TEST_PORT = HTTPServerTestHelper.getTestPort(); private final String TEST_CLUSTER_URL = "http://localhost:" + TEST_PORT; private final String TEST_USERNAME = "johndoe"; private final String TEST_PASSWORD = "password"; private final String TEST_SYNC_KEY = "abcdeabcdeabcdeabcdeabcdea"; private final String TEST_JSON_NO_CRYPTO = "{\"history\":1.3319567131E9}"; private final String TEST_JSON_OLD_CRYPTO = "{\"history\":1.3319567131E9,\"crypto\":1.1E9}"; private final String TEST_JSON_NEW_CRYPTO = "{\"history\":1.3319567131E9,\"crypto\":3.1E9}"; private HTTPServerTestHelper data = new HTTPServerTestHelper(); private KeyBundle syncKeyBundle; private MockGlobalSessionCallback callback; private GlobalSession session; private boolean calledResetStages; private Collection<String> stagesReset; @Before public void setUp() throws Exception { syncKeyBundle = new KeyBundle(TEST_USERNAME, TEST_SYNC_KEY); callback = new MockGlobalSessionCallback(); session = new MockGlobalSession(TEST_CLUSTER_URL, TEST_USERNAME, TEST_PASSWORD, syncKeyBundle, callback) { @Override protected void prepareStages() { super.prepareStages(); Map<Stage, GlobalSyncStage> stages = new HashMap<Stage, GlobalSyncStage>(this.stages); stages.put(Stage.ensureKeysStage, new EnsureCrypto5KeysStage(this)); this.stages = stages; } @Override public void resetStagesByEnum(Collection<Stage> stages) { calledResetStages = true; stagesReset = new ArrayList<String>(); for (Stage stage : stages) { stagesReset.add(stage.name()); } } @Override public void resetStagesByName(Collection<String> names) { calledResetStages = true; stagesReset = names; } }; session.config.setClusterURL(new URI(TEST_CLUSTER_URL)); // Set info collections to not have crypto. final ExtendedJSONObject noCrypto = ExtendedJSONObject.parseJSONObject(TEST_JSON_NO_CRYPTO); session.config.infoCollections = new InfoCollections(noCrypto); calledResetStages = false; stagesReset = null; } public void doSession(MockServer server) { data.startHTTPServer(server); try { WaitHelper.getTestWaiter().performWait(new Runnable() { @Override public void run() { try { session.start(); } catch (AlreadySyncingException e) { WaitHelper.getTestWaiter().performNotify(e); } } }); } finally { data.stopHTTPServer(); } } @Test public void testDownloadUsesPersisted() throws Exception { session.config.infoCollections = new InfoCollections(ExtendedJSONObject.parseJSONObject(TEST_JSON_OLD_CRYPTO)); session.config.persistedCryptoKeys().persistLastModified(System.currentTimeMillis()); assertNull(session.config.collectionKeys); final CollectionKeys keys = CollectionKeys.generateCollectionKeys(); keys.setDefaultKeyBundle(syncKeyBundle); session.config.persistedCryptoKeys().persistKeys(keys); MockServer server = new MockServer() { public void handle(Request request, Response response) { this.handle(request, response, 404, "should not be called!"); } }; doSession(server); assertTrue(callback.calledSuccess); assertNotNull(session.config.collectionKeys); assertTrue(CollectionKeys.differences(session.config.collectionKeys, keys).isEmpty()); } @Test public void testDownloadFetchesNew() throws Exception { session.config.infoCollections = new InfoCollections(ExtendedJSONObject.parseJSONObject(TEST_JSON_NEW_CRYPTO)); session.config.persistedCryptoKeys().persistLastModified(System.currentTimeMillis()); assertNull(session.config.collectionKeys); final CollectionKeys keys = CollectionKeys.generateCollectionKeys(); keys.setDefaultKeyBundle(syncKeyBundle); session.config.persistedCryptoKeys().persistKeys(keys); MockServer server = new MockServer() { public void handle(Request request, Response response) { try { CryptoRecord rec = keys.asCryptoRecord(); rec.keyBundle = syncKeyBundle; rec.encrypt(); this.handle(request, response, 200, rec.toJSONString()); } catch (Exception e) { throw new RuntimeException(e); } } }; doSession(server); assertTrue(callback.calledSuccess); assertNotNull(session.config.collectionKeys); assertTrue(session.config.collectionKeys.equals(keys)); } /** * Change the default key but keep one collection key the same. Should reset * all but that one collection. */ @Test public void testDownloadResetsOnDifferentDefaultKey() throws Exception { String TEST_COLLECTION = "bookmarks"; session.config.infoCollections = new InfoCollections(ExtendedJSONObject.parseJSONObject(TEST_JSON_NEW_CRYPTO)); session.config.persistedCryptoKeys().persistLastModified(System.currentTimeMillis()); KeyBundle keyBundle = KeyBundle.withRandomKeys(); assertNull(session.config.collectionKeys); final CollectionKeys keys = CollectionKeys.generateCollectionKeys(); keys.setKeyBundleForCollection(TEST_COLLECTION, keyBundle); session.config.persistedCryptoKeys().persistKeys(keys); keys.setDefaultKeyBundle(syncKeyBundle); // Change the default key bundle, but keep "bookmarks" the same. MockServer server = new MockServer() { public void handle(Request request, Response response) { try { CryptoRecord rec = keys.asCryptoRecord(); rec.keyBundle = syncKeyBundle; rec.encrypt(); this.handle(request, response, 200, rec.toJSONString()); } catch (Exception e) { throw new RuntimeException(e); } } }; doSession(server); assertTrue(calledResetStages); Collection<String> allButCollection = new ArrayList<String>(); for (Stage stage : Stage.getNamedStages()) { allButCollection.add(stage.getRepositoryName()); } allButCollection.remove(TEST_COLLECTION); assertTrue(stagesReset.containsAll(allButCollection)); assertTrue(allButCollection.containsAll(stagesReset)); assertTrue(callback.calledError); } @Test public void testDownloadResetsEngineOnDifferentKey() throws Exception { final String TEST_COLLECTION = "history"; session.config.infoCollections = new InfoCollections(ExtendedJSONObject.parseJSONObject(TEST_JSON_NEW_CRYPTO)); session.config.persistedCryptoKeys().persistLastModified(System.currentTimeMillis()); assertNull(session.config.collectionKeys); final CollectionKeys keys = CollectionKeys.generateCollectionKeys(); session.config.persistedCryptoKeys().persistKeys(keys); keys.setKeyBundleForCollection(TEST_COLLECTION, syncKeyBundle); // Change one key bundle. CryptoRecord rec = keys.asCryptoRecord(); rec.keyBundle = syncKeyBundle; rec.encrypt(); MockServer server = new MockServer(200, rec.toJSONString()); doSession(server); assertTrue(calledResetStages); assertNotNull(stagesReset); assertEquals(1, stagesReset.size()); assertTrue(stagesReset.contains(TEST_COLLECTION)); assertTrue(callback.calledError); } }