Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 package org.mozilla.gecko.sync.middleware;
7 import java.io.UnsupportedEncodingException;
8 import java.util.concurrent.ExecutorService;
10 import org.mozilla.gecko.sync.CryptoRecord;
11 import org.mozilla.gecko.sync.crypto.CryptoException;
12 import org.mozilla.gecko.sync.crypto.KeyBundle;
13 import org.mozilla.gecko.sync.repositories.InactiveSessionException;
14 import org.mozilla.gecko.sync.repositories.NoStoreDelegateException;
15 import org.mozilla.gecko.sync.repositories.RecordFactory;
16 import org.mozilla.gecko.sync.repositories.RepositorySession;
17 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFetchRecordsDelegate;
18 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionStoreDelegate;
19 import org.mozilla.gecko.sync.repositories.domain.Record;
21 /**
22 * It's a RepositorySession that accepts Records as input, producing CryptoRecords
23 * for submission to a remote service.
24 * Takes a RecordFactory as a parameter. This is in charge of taking decrypted CryptoRecords
25 * as input and producing some expected kind of Record as output for local use.
26 *
27 *
31 +------------------------------------+
32 | Server11RepositorySession |
33 +-------------------------+----------+
34 ^ |
35 | |
36 Encrypted CryptoRecords
37 | |
38 | v
39 +---------+--------------------------+
40 | Crypto5MiddlewareRepositorySession |
41 +------------------------------------+
42 ^ |
43 | | Decrypted CryptoRecords
44 | |
45 | +---------------+
46 | | RecordFactory |
47 | +--+------------+
48 | |
49 Local Record instances
50 | |
51 | v
52 +---------+--------------------------+
53 | Local RepositorySession instance |
54 +------------------------------------+
57 * @author rnewman
58 *
59 */
60 public class Crypto5MiddlewareRepositorySession extends MiddlewareRepositorySession {
61 private KeyBundle keyBundle;
62 private RecordFactory recordFactory;
64 public Crypto5MiddlewareRepositorySession(RepositorySession session, Crypto5MiddlewareRepository repository, RecordFactory recordFactory) {
65 super(session, repository);
66 this.keyBundle = repository.keyBundle;
67 this.recordFactory = recordFactory;
68 }
70 public class DecryptingTransformingFetchDelegate implements RepositorySessionFetchRecordsDelegate {
71 private RepositorySessionFetchRecordsDelegate next;
72 private KeyBundle keyBundle;
73 private RecordFactory recordFactory;
75 DecryptingTransformingFetchDelegate(RepositorySessionFetchRecordsDelegate next, KeyBundle bundle, RecordFactory recordFactory) {
76 this.next = next;
77 this.keyBundle = bundle;
78 this.recordFactory = recordFactory;
79 }
81 @Override
82 public void onFetchFailed(Exception ex, Record record) {
83 next.onFetchFailed(ex, record);
84 }
86 @Override
87 public void onFetchedRecord(Record record) {
88 CryptoRecord r;
89 try {
90 r = (CryptoRecord) record;
91 } catch (ClassCastException e) {
92 next.onFetchFailed(e, record);
93 return;
94 }
95 r.keyBundle = keyBundle;
96 try {
97 r.decrypt();
98 } catch (Exception e) {
99 next.onFetchFailed(e, r);
100 return;
101 }
102 Record transformed;
103 try {
104 transformed = this.recordFactory.createRecord(r);
105 } catch (Exception e) {
106 next.onFetchFailed(e, r);
107 return;
108 }
109 next.onFetchedRecord(transformed);
110 }
112 @Override
113 public void onFetchCompleted(final long fetchEnd) {
114 next.onFetchCompleted(fetchEnd);
115 }
117 @Override
118 public RepositorySessionFetchRecordsDelegate deferredFetchDelegate(ExecutorService executor) {
119 // Synchronously perform *our* work, passing through appropriately.
120 RepositorySessionFetchRecordsDelegate deferredNext = next.deferredFetchDelegate(executor);
121 return new DecryptingTransformingFetchDelegate(deferredNext, keyBundle, recordFactory);
122 }
123 }
125 private DecryptingTransformingFetchDelegate makeUnwrappingDelegate(RepositorySessionFetchRecordsDelegate inner) {
126 if (inner == null) {
127 throw new IllegalArgumentException("Inner delegate cannot be null!");
128 }
129 return new DecryptingTransformingFetchDelegate(inner, this.keyBundle, this.recordFactory);
130 }
132 @Override
133 public void fetchSince(long timestamp,
134 RepositorySessionFetchRecordsDelegate delegate) {
135 inner.fetchSince(timestamp, makeUnwrappingDelegate(delegate));
136 }
138 @Override
139 public void fetch(String[] guids,
140 RepositorySessionFetchRecordsDelegate delegate) throws InactiveSessionException {
141 inner.fetch(guids, makeUnwrappingDelegate(delegate));
142 }
144 @Override
145 public void fetchAll(RepositorySessionFetchRecordsDelegate delegate) {
146 inner.fetchAll(makeUnwrappingDelegate(delegate));
147 }
149 @Override
150 public void setStoreDelegate(RepositorySessionStoreDelegate delegate) {
151 // TODO: it remains to be seen how this will work.
152 inner.setStoreDelegate(delegate);
153 this.delegate = delegate; // So we can handle errors without involving inner.
154 }
156 @Override
157 public void store(Record record) throws NoStoreDelegateException {
158 if (delegate == null) {
159 throw new NoStoreDelegateException();
160 }
161 CryptoRecord rec = record.getEnvelope();
162 rec.keyBundle = this.keyBundle;
163 try {
164 rec.encrypt();
165 } catch (UnsupportedEncodingException e) {
166 delegate.onRecordStoreFailed(e, record.guid);
167 return;
168 } catch (CryptoException e) {
169 delegate.onRecordStoreFailed(e, record.guid);
170 return;
171 }
172 // Allow the inner session to do delegate handling.
173 inner.store(rec);
174 }
175 }