summaryrefslogtreecommitdiffstats
path: root/mobile/android/services/src/main/java/org/mozilla/gecko/sync/middleware
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/services/src/main/java/org/mozilla/gecko/sync/middleware')
-rw-r--r--mobile/android/services/src/main/java/org/mozilla/gecko/sync/middleware/Crypto5MiddlewareRepository.java76
-rw-r--r--mobile/android/services/src/main/java/org/mozilla/gecko/sync/middleware/Crypto5MiddlewareRepositorySession.java172
-rw-r--r--mobile/android/services/src/main/java/org/mozilla/gecko/sync/middleware/MiddlewareRepository.java22
-rw-r--r--mobile/android/services/src/main/java/org/mozilla/gecko/sync/middleware/MiddlewareRepositorySession.java185
4 files changed, 455 insertions, 0 deletions
diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/middleware/Crypto5MiddlewareRepository.java b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/middleware/Crypto5MiddlewareRepository.java
new file mode 100644
index 000000000..79319aff5
--- /dev/null
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/middleware/Crypto5MiddlewareRepository.java
@@ -0,0 +1,76 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+package org.mozilla.gecko.sync.middleware;
+
+import org.mozilla.gecko.sync.crypto.KeyBundle;
+import org.mozilla.gecko.sync.repositories.IdentityRecordFactory;
+import org.mozilla.gecko.sync.repositories.RecordFactory;
+import org.mozilla.gecko.sync.repositories.Repository;
+import org.mozilla.gecko.sync.repositories.RepositorySession;
+import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionCleanDelegate;
+import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionCreationDelegate;
+
+import android.content.Context;
+
+/**
+ * Wrap an existing repository in middleware that encrypts and decrypts records
+ * passing through.
+ *
+ * @author rnewman
+ *
+ */
+public class Crypto5MiddlewareRepository extends MiddlewareRepository {
+
+ public RecordFactory recordFactory = new IdentityRecordFactory();
+
+ public class Crypto5MiddlewareRepositorySessionCreationDelegate extends MiddlewareRepository.SessionCreationDelegate {
+ private final Crypto5MiddlewareRepository repository;
+ private final RepositorySessionCreationDelegate outerDelegate;
+
+ public Crypto5MiddlewareRepositorySessionCreationDelegate(Crypto5MiddlewareRepository repository, RepositorySessionCreationDelegate outerDelegate) {
+ this.repository = repository;
+ this.outerDelegate = outerDelegate;
+ }
+
+ @Override
+ public void onSessionCreateFailed(Exception ex) {
+ this.outerDelegate.onSessionCreateFailed(ex);
+ }
+
+ @Override
+ public void onSessionCreated(RepositorySession session) {
+ // Do some work, then report success with the wrapping session.
+ Crypto5MiddlewareRepositorySession cryptoSession;
+ try {
+ // Synchronous, baby.
+ cryptoSession = new Crypto5MiddlewareRepositorySession(session, this.repository, recordFactory);
+ } catch (Exception ex) {
+ this.outerDelegate.onSessionCreateFailed(ex);
+ return;
+ }
+ this.outerDelegate.onSessionCreated(cryptoSession);
+ }
+ }
+
+ public KeyBundle keyBundle;
+ private final Repository inner;
+
+ public Crypto5MiddlewareRepository(Repository inner, KeyBundle keys) {
+ super();
+ this.inner = inner;
+ this.keyBundle = keys;
+ }
+ @Override
+ public void createSession(RepositorySessionCreationDelegate delegate, Context context) {
+ Crypto5MiddlewareRepositorySessionCreationDelegate delegateWrapper = new Crypto5MiddlewareRepositorySessionCreationDelegate(this, delegate);
+ inner.createSession(delegateWrapper, context);
+ }
+
+ @Override
+ public void clean(boolean success, RepositorySessionCleanDelegate delegate,
+ Context context) {
+ this.inner.clean(success, delegate, context);
+ }
+}
diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/middleware/Crypto5MiddlewareRepositorySession.java b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/middleware/Crypto5MiddlewareRepositorySession.java
new file mode 100644
index 000000000..46de7a236
--- /dev/null
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/middleware/Crypto5MiddlewareRepositorySession.java
@@ -0,0 +1,172 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+package org.mozilla.gecko.sync.middleware;
+
+import java.io.UnsupportedEncodingException;
+import java.util.concurrent.ExecutorService;
+
+import org.mozilla.gecko.sync.CryptoRecord;
+import org.mozilla.gecko.sync.crypto.CryptoException;
+import org.mozilla.gecko.sync.crypto.KeyBundle;
+import org.mozilla.gecko.sync.repositories.InactiveSessionException;
+import org.mozilla.gecko.sync.repositories.NoStoreDelegateException;
+import org.mozilla.gecko.sync.repositories.RecordFactory;
+import org.mozilla.gecko.sync.repositories.RepositorySession;
+import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFetchRecordsDelegate;
+import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionStoreDelegate;
+import org.mozilla.gecko.sync.repositories.domain.Record;
+
+/**
+ * It's a RepositorySession that accepts Records as input, producing CryptoRecords
+ * for submission to a remote service.
+ * Takes a RecordFactory as a parameter. This is in charge of taking decrypted CryptoRecords
+ * as input and producing some expected kind of Record as output for local use.
+ *
+ *
+
+
+
+ +------------------------------------+
+ | Server11RepositorySession |
+ +-------------------------+----------+
+ ^ |
+ | |
+ Encrypted CryptoRecords
+ | |
+ | v
+ +---------+--------------------------+
+ | Crypto5MiddlewareRepositorySession |
+ +------------------------------------+
+ ^ |
+ | | Decrypted CryptoRecords
+ | |
+ | +---------------+
+ | | RecordFactory |
+ | +--+------------+
+ | |
+ Local Record instances
+ | |
+ | v
+ +---------+--------------------------+
+ | Local RepositorySession instance |
+ +------------------------------------+
+
+
+ * @author rnewman
+ *
+ */
+public class Crypto5MiddlewareRepositorySession extends MiddlewareRepositorySession {
+ private final KeyBundle keyBundle;
+ private final RecordFactory recordFactory;
+
+ public Crypto5MiddlewareRepositorySession(RepositorySession session, Crypto5MiddlewareRepository repository, RecordFactory recordFactory) {
+ super(session, repository);
+ this.keyBundle = repository.keyBundle;
+ this.recordFactory = recordFactory;
+ }
+
+ public class DecryptingTransformingFetchDelegate implements RepositorySessionFetchRecordsDelegate {
+ private final RepositorySessionFetchRecordsDelegate next;
+ private final KeyBundle keyBundle;
+ private final RecordFactory recordFactory;
+
+ DecryptingTransformingFetchDelegate(RepositorySessionFetchRecordsDelegate next, KeyBundle bundle, RecordFactory recordFactory) {
+ this.next = next;
+ this.keyBundle = bundle;
+ this.recordFactory = recordFactory;
+ }
+
+ @Override
+ public void onFetchFailed(Exception ex, Record record) {
+ next.onFetchFailed(ex, record);
+ }
+
+ @Override
+ public void onFetchedRecord(Record record) {
+ CryptoRecord r;
+ try {
+ r = (CryptoRecord) record;
+ } catch (ClassCastException e) {
+ next.onFetchFailed(e, record);
+ return;
+ }
+ r.keyBundle = keyBundle;
+ try {
+ r.decrypt();
+ } catch (Exception e) {
+ next.onFetchFailed(e, r);
+ return;
+ }
+ Record transformed;
+ try {
+ transformed = this.recordFactory.createRecord(r);
+ } catch (Exception e) {
+ next.onFetchFailed(e, r);
+ return;
+ }
+ next.onFetchedRecord(transformed);
+ }
+
+ @Override
+ public void onFetchCompleted(final long fetchEnd) {
+ next.onFetchCompleted(fetchEnd);
+ }
+
+ @Override
+ public RepositorySessionFetchRecordsDelegate deferredFetchDelegate(ExecutorService executor) {
+ // Synchronously perform *our* work, passing through appropriately.
+ RepositorySessionFetchRecordsDelegate deferredNext = next.deferredFetchDelegate(executor);
+ return new DecryptingTransformingFetchDelegate(deferredNext, keyBundle, recordFactory);
+ }
+ }
+
+ private DecryptingTransformingFetchDelegate makeUnwrappingDelegate(RepositorySessionFetchRecordsDelegate inner) {
+ if (inner == null) {
+ throw new IllegalArgumentException("Inner delegate cannot be null!");
+ }
+ return new DecryptingTransformingFetchDelegate(inner, this.keyBundle, this.recordFactory);
+ }
+
+ @Override
+ public void fetchSince(long timestamp,
+ RepositorySessionFetchRecordsDelegate delegate) {
+ inner.fetchSince(timestamp, makeUnwrappingDelegate(delegate));
+ }
+
+ @Override
+ public void fetch(String[] guids,
+ RepositorySessionFetchRecordsDelegate delegate) throws InactiveSessionException {
+ inner.fetch(guids, makeUnwrappingDelegate(delegate));
+ }
+
+ @Override
+ public void fetchAll(RepositorySessionFetchRecordsDelegate delegate) {
+ inner.fetchAll(makeUnwrappingDelegate(delegate));
+ }
+
+ @Override
+ public void setStoreDelegate(RepositorySessionStoreDelegate delegate) {
+ // TODO: it remains to be seen how this will work.
+ inner.setStoreDelegate(delegate);
+ this.delegate = delegate; // So we can handle errors without involving inner.
+ }
+
+ @Override
+ public void store(Record record) throws NoStoreDelegateException {
+ if (delegate == null) {
+ throw new NoStoreDelegateException();
+ }
+ CryptoRecord rec = record.getEnvelope();
+ rec.keyBundle = this.keyBundle;
+ try {
+ rec.encrypt();
+ } catch (UnsupportedEncodingException | CryptoException e) {
+ delegate.onRecordStoreFailed(e, record.guid);
+ return;
+ }
+ // Allow the inner session to do delegate handling.
+ inner.store(rec);
+ }
+}
diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/middleware/MiddlewareRepository.java b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/middleware/MiddlewareRepository.java
new file mode 100644
index 000000000..d807aa5c0
--- /dev/null
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/middleware/MiddlewareRepository.java
@@ -0,0 +1,22 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+package org.mozilla.gecko.sync.middleware;
+
+import org.mozilla.gecko.sync.repositories.Repository;
+import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionCreationDelegate;
+
+public abstract class MiddlewareRepository extends Repository {
+
+ public abstract class SessionCreationDelegate implements
+ RepositorySessionCreationDelegate {
+
+ // We call through to our inner repository, so we don't need our own
+ // deferral scheme.
+ @Override
+ public RepositorySessionCreationDelegate deferredCreationDelegate() {
+ return this;
+ }
+ }
+}
diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/middleware/MiddlewareRepositorySession.java b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/middleware/MiddlewareRepositorySession.java
new file mode 100644
index 000000000..e14ef5226
--- /dev/null
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/middleware/MiddlewareRepositorySession.java
@@ -0,0 +1,185 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+package org.mozilla.gecko.sync.middleware;
+
+import java.util.concurrent.ExecutorService;
+
+import org.mozilla.gecko.background.common.log.Logger;
+import org.mozilla.gecko.sync.repositories.InactiveSessionException;
+import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException;
+import org.mozilla.gecko.sync.repositories.RepositorySession;
+import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
+import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionBeginDelegate;
+import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFinishDelegate;
+import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionGuidsSinceDelegate;
+import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionWipeDelegate;
+
+public abstract class MiddlewareRepositorySession extends RepositorySession {
+ private static final String LOG_TAG = "MiddlewareSession";
+ protected final RepositorySession inner;
+
+ public MiddlewareRepositorySession(RepositorySession innerSession, MiddlewareRepository repository) {
+ super(repository);
+ this.inner = innerSession;
+ }
+
+ @Override
+ public void wipe(RepositorySessionWipeDelegate delegate) {
+ inner.wipe(delegate);
+ }
+
+ public class MiddlewareRepositorySessionBeginDelegate implements RepositorySessionBeginDelegate {
+
+ private final MiddlewareRepositorySession outerSession;
+ private final RepositorySessionBeginDelegate next;
+
+ public MiddlewareRepositorySessionBeginDelegate(MiddlewareRepositorySession outerSession, RepositorySessionBeginDelegate next) {
+ this.outerSession = outerSession;
+ this.next = next;
+ }
+
+ @Override
+ public void onBeginFailed(Exception ex) {
+ next.onBeginFailed(ex);
+ }
+
+ @Override
+ public void onBeginSucceeded(RepositorySession session) {
+ next.onBeginSucceeded(outerSession);
+ }
+
+ @Override
+ public RepositorySessionBeginDelegate deferredBeginDelegate(ExecutorService executor) {
+ final RepositorySessionBeginDelegate deferred = next.deferredBeginDelegate(executor);
+ return new RepositorySessionBeginDelegate() {
+ @Override
+ public void onBeginSucceeded(RepositorySession session) {
+ if (inner != session) {
+ Logger.warn(LOG_TAG, "Got onBeginSucceeded for session " + session + ", not our inner session!");
+ }
+ deferred.onBeginSucceeded(outerSession);
+ }
+
+ @Override
+ public void onBeginFailed(Exception ex) {
+ deferred.onBeginFailed(ex);
+ }
+
+ @Override
+ public RepositorySessionBeginDelegate deferredBeginDelegate(ExecutorService executor) {
+ return this;
+ }
+ };
+ }
+ }
+
+ @Override
+ public void begin(RepositorySessionBeginDelegate delegate) throws InvalidSessionTransitionException {
+ inner.begin(new MiddlewareRepositorySessionBeginDelegate(this, delegate));
+ }
+
+ public class MiddlewareRepositorySessionFinishDelegate implements RepositorySessionFinishDelegate {
+ private final MiddlewareRepositorySession outerSession;
+ private final RepositorySessionFinishDelegate next;
+
+ public MiddlewareRepositorySessionFinishDelegate(MiddlewareRepositorySession outerSession, RepositorySessionFinishDelegate next) {
+ this.outerSession = outerSession;
+ this.next = next;
+ }
+
+ @Override
+ public void onFinishFailed(Exception ex) {
+ next.onFinishFailed(ex);
+ }
+
+ @Override
+ public void onFinishSucceeded(RepositorySession session, RepositorySessionBundle bundle) {
+ next.onFinishSucceeded(outerSession, bundle);
+ }
+
+ @Override
+ public RepositorySessionFinishDelegate deferredFinishDelegate(ExecutorService executor) {
+ return this;
+ }
+ }
+
+ @Override
+ public void finish(RepositorySessionFinishDelegate delegate) throws InactiveSessionException {
+ inner.finish(new MiddlewareRepositorySessionFinishDelegate(this, delegate));
+ }
+
+
+ @Override
+ public synchronized void ensureActive() throws InactiveSessionException {
+ inner.ensureActive();
+ }
+
+ @Override
+ public synchronized boolean isActive() {
+ return inner.isActive();
+ }
+
+ @Override
+ public synchronized SessionStatus getStatus() {
+ return inner.getStatus();
+ }
+
+ @Override
+ public synchronized void setStatus(SessionStatus status) {
+ inner.setStatus(status);
+ }
+
+ @Override
+ public synchronized void transitionFrom(SessionStatus from, SessionStatus to)
+ throws InvalidSessionTransitionException {
+ inner.transitionFrom(from, to);
+ }
+
+ @Override
+ public void abort() {
+ inner.abort();
+ }
+
+ @Override
+ public void abort(RepositorySessionFinishDelegate delegate) {
+ inner.abort(new MiddlewareRepositorySessionFinishDelegate(this, delegate));
+ }
+
+ @Override
+ public void guidsSince(long timestamp, RepositorySessionGuidsSinceDelegate delegate) {
+ // TODO: need to do anything here?
+ inner.guidsSince(timestamp, delegate);
+ }
+
+ @Override
+ public void storeDone() {
+ inner.storeDone();
+ }
+
+ @Override
+ public void storeDone(long storeEnd) {
+ inner.storeDone(storeEnd);
+ }
+
+ @Override
+ public boolean shouldSkip() {
+ return inner.shouldSkip();
+ }
+
+ @Override
+ public boolean dataAvailable() {
+ return inner.dataAvailable();
+ }
+
+ @Override
+ public void unbundle(RepositorySessionBundle bundle) {
+ inner.unbundle(bundle);
+ }
+
+ @Override
+ public long getLastSyncTimestamp() {
+ return inner.getLastSyncTimestamp();
+ }
+}