30
30
import org .apache .solr .encryption .crypto .AesCtrEncrypterFactory ;
31
31
32
32
import java .io .IOException ;
33
+ import java .util .ArrayList ;
34
+ import java .util .Arrays ;
33
35
import java .util .HashMap ;
36
+ import java .util .List ;
34
37
import java .util .Map ;
35
38
import java .util .UUID ;
36
39
44
47
45
48
/**
46
49
* Tests {@link EncryptionDirectory}.
47
- * <p>
48
- * This test class ignores the DirectoryFactory defined in solrconfig.xml to use
49
- * {@link EncryptionDirectoryFactory}.
50
50
*/
51
51
public class EncryptionDirectoryTest extends SolrCloudTestCase {
52
52
53
53
private static final String COLLECTION_PREFIX = EncryptionDirectoryTest .class .getSimpleName () + "-collection-" ;
54
54
55
- private static MockEncryptionDirectory mockDir ;
56
-
57
55
private String collectionName ;
58
56
private CloudSolrClient solrClient ;
59
57
private EncryptionTestUtil testUtil ;
@@ -80,14 +78,13 @@ public void setUp() throws Exception {
80
78
solrClient = cluster .getSolrClient ();
81
79
CollectionAdminRequest .createCollection (collectionName , 2 , 2 ).process (solrClient );
82
80
cluster .waitForActiveCollection (collectionName , 2 , 4 );
83
- testUtil = new EncryptionTestUtil (solrClient , collectionName );
81
+ testUtil = new EncryptionTestUtil (solrClient , collectionName )
82
+ .setShouldDistributeRequests (false );
84
83
}
85
84
86
85
@ Override
87
86
public void tearDown () throws Exception {
88
- if (mockDir != null ) {
89
- mockDir .clearMockValues ();
90
- }
87
+ MockFactory .clearMockValues ();
91
88
CollectionAdminRequest .deleteCollection (collectionName ).process (solrClient );
92
89
super .tearDown ();
93
90
}
@@ -106,7 +103,7 @@ public void testEncryptionFromNoKeysToOneKey() throws Exception {
106
103
*/
107
104
private void indexAndEncryptOneSegment () throws Exception {
108
105
// Start with no key ids defined in the latest commit metadata.
109
- mockDir .clearMockValues ();
106
+ MockFactory .clearMockValues ();
110
107
// Create 2 index segments without encryption.
111
108
testUtil .indexDocsAndCommit ("weather broadcast" );
112
109
testUtil .indexDocsAndCommit ("sunny weather" );
@@ -120,23 +117,23 @@ private void indexAndEncryptOneSegment() throws Exception {
120
117
121
118
// Set the encryption key id in the commit user data,
122
119
// and run an optimized commit to rewrite the index, now encrypted.
123
- mockDir .setKeysInCommitUserData (KEY_ID_1 );
120
+ MockFactory .setKeysInCommitUserData (KEY_ID_1 );
124
121
optimizeCommit ();
125
122
126
123
// Verify that without key id, we cannot decrypt the index anymore.
127
- mockDir .forceClearText = true ;
124
+ MockFactory .forceClearText = true ;
128
125
testUtil .assertCannotReloadCores ();
129
126
// Verify that with a wrong key id, we cannot decrypt the index.
130
- mockDir .forceClearText = false ;
131
- mockDir .forceKeySecret = KEY_SECRET_2 ;
127
+ MockFactory .forceClearText = false ;
128
+ MockFactory .forceKeySecret = KEY_SECRET_2 ;
132
129
testUtil .assertCannotReloadCores ();
133
130
// Verify that with the right key id, we can decrypt the index and search it.
134
- mockDir .forceKeySecret = null ;
135
- mockDir . expectedKeySecret = KEY_SECRET_1 ;
131
+ MockFactory .forceKeySecret = null ;
132
+ MockFactory . expectedKeySecrets = List . of ( KEY_SECRET_1 ) ;
136
133
testUtil .reloadCores ();
137
134
testUtil .assertQueryReturns ("weather" , 2 );
138
135
testUtil .assertQueryReturns ("sunny" , 1 );
139
- mockDir .clearMockValues ();
136
+ MockFactory .clearMockValues ();
140
137
}
141
138
142
139
/**
@@ -156,24 +153,24 @@ private void indexAndEncryptTwoSegments() throws Exception {
156
153
indexAndEncryptOneSegment ();
157
154
158
155
// Create 1 new segment with the same encryption key id.
159
- mockDir .setKeysInCommitUserData (KEY_ID_1 );
156
+ MockFactory .setKeysInCommitUserData (KEY_ID_1 );
160
157
testUtil .indexDocsAndCommit ("foggy weather" );
161
158
testUtil .indexDocsAndCommit ("boo" );
162
159
163
160
// Verify that without key id, we cannot decrypt the index.
164
- mockDir .forceClearText = true ;
161
+ MockFactory .forceClearText = true ;
165
162
testUtil .assertCannotReloadCores ();
166
163
// Verify that with a wrong key id, we cannot decrypt the index.
167
- mockDir .forceClearText = false ;
168
- mockDir .forceKeySecret = KEY_SECRET_2 ;
164
+ MockFactory .forceClearText = false ;
165
+ MockFactory .forceKeySecret = KEY_SECRET_2 ;
169
166
testUtil .assertCannotReloadCores ();
170
167
// Verify that with the right key id, we can decrypt the index and search it.
171
- mockDir .forceKeySecret = null ;
172
- mockDir . expectedKeySecret = KEY_SECRET_1 ;
168
+ MockFactory .forceKeySecret = null ;
169
+ MockFactory . expectedKeySecrets = List . of ( KEY_SECRET_1 ) ;
173
170
testUtil .reloadCores ();
174
171
testUtil .assertQueryReturns ("weather" , 3 );
175
172
testUtil .assertQueryReturns ("sunny" , 1 );
176
- mockDir .clearMockValues ();
173
+ MockFactory .clearMockValues ();
177
174
}
178
175
179
176
/**
@@ -186,19 +183,19 @@ public void testReEncryptionFromOneKeyToAnotherKey() throws Exception {
186
183
187
184
// Set the new encryption key id in the commit user data,
188
185
// and run an optimized commit to rewrite the index, now encrypted with the new key.
189
- mockDir .setKeysInCommitUserData (KEY_ID_1 , KEY_ID_2 );
186
+ MockFactory .setKeysInCommitUserData (KEY_ID_1 , KEY_ID_2 );
190
187
optimizeCommit ();
191
188
192
189
// Verify that without key id, we cannot decrypt the index.
193
- mockDir .forceClearText = true ;
190
+ MockFactory .forceClearText = true ;
194
191
testUtil .assertCannotReloadCores ();
195
192
// Verify that with a wrong key id, we cannot decrypt the index.
196
- mockDir .forceClearText = false ;
197
- mockDir .forceKeySecret = KEY_SECRET_1 ;
193
+ MockFactory .forceClearText = false ;
194
+ MockFactory .forceKeySecret = KEY_SECRET_1 ;
198
195
testUtil .assertCannotReloadCores ();
199
196
// Verify that with the right key id, we can decrypt the index and search it.
200
- mockDir .forceKeySecret = null ;
201
- mockDir . expectedKeySecret = KEY_SECRET_2 ;
197
+ MockFactory .forceKeySecret = null ;
198
+ MockFactory . expectedKeySecrets = List . of ( KEY_SECRET_1 , KEY_SECRET_2 ) ;
202
199
testUtil .reloadCores ();
203
200
testUtil .assertQueryReturns ("weather" , 3 );
204
201
testUtil .assertQueryReturns ("sunny" , 1 );
@@ -214,11 +211,11 @@ public void testDecryptionFromOneKeyToNoKeys() throws Exception {
214
211
215
212
// Remove the active key parameter from the commit user data,
216
213
// and run an optimized commit to rewrite the index, now cleartext with no keys.
217
- mockDir .setKeysInCommitUserData (KEY_ID_1 , null );
214
+ MockFactory .setKeysInCommitUserData (KEY_ID_1 , null );
218
215
optimizeCommit ();
219
216
220
217
// Verify that without key id, we can reload the index because it is not encrypted.
221
- mockDir .forceClearText = true ;
218
+ MockFactory .forceClearText = true ;
222
219
testUtil .reloadCores ();
223
220
testUtil .assertQueryReturns ("weather" , 3 );
224
221
testUtil .assertQueryReturns ("sunny" , 1 );
@@ -232,28 +229,49 @@ public void testDecryptionFromOneKeyToNoKeys() throws Exception {
232
229
* {@link EncryptionRequestHandler}, but this test is designed to work independently.
233
230
*/
234
231
private void optimizeCommit () {
235
- testUtil .forAllReplicas (replica -> {
232
+ testUtil .forAllReplicas (false , replica -> {
236
233
UpdateRequest request = new UpdateRequest ();
237
234
request .setAction (UpdateRequest .ACTION .OPTIMIZE , true , true , 1 );
238
235
testUtil .requestCore (request , replica );
239
236
});
240
237
}
241
238
242
239
public static class MockFactory implements EncryptionDirectoryFactory .InnerFactory {
240
+
241
+ static final List <MockEncryptionDirectory > mockDirs = new ArrayList <>();
242
+
243
+ static boolean forceClearText ;
244
+ static byte [] forceKeySecret ;
245
+ static List <byte []> expectedKeySecrets ;
246
+
247
+ static void clearMockValues () {
248
+ forceClearText = false ;
249
+ forceKeySecret = null ;
250
+ expectedKeySecrets = null ;
251
+ for (MockEncryptionDirectory mockDir : mockDirs ) {
252
+ mockDir .clearMockValues ();
253
+ }
254
+ }
255
+
256
+ static void setKeysInCommitUserData (String ... keyIds ) throws IOException {
257
+ for (MockEncryptionDirectory mockDir : mockDirs ) {
258
+ mockDir .setKeysInCommitUserData (keyIds );
259
+ }
260
+ }
261
+
243
262
@ Override
244
263
public EncryptionDirectory create (Directory delegate ,
245
264
AesCtrEncrypterFactory encrypterFactory ,
246
265
KeySupplier keySupplier ) throws IOException {
247
- return mockDir = new MockEncryptionDirectory (delegate , encrypterFactory , keySupplier );
266
+ MockEncryptionDirectory mockDir = new MockEncryptionDirectory (delegate , encrypterFactory , keySupplier );
267
+ mockDirs .add (mockDir );
268
+ return mockDir ;
248
269
}
249
270
}
250
271
251
272
private static class MockEncryptionDirectory extends EncryptionDirectory {
252
273
253
274
final KeySupplier keySupplier ;
254
- boolean forceClearText ;
255
- byte [] forceKeySecret ;
256
- byte [] expectedKeySecret ;
257
275
258
276
MockEncryptionDirectory (Directory delegate , AesCtrEncrypterFactory encrypterFactory , KeySupplier keySupplier )
259
277
throws IOException {
@@ -263,9 +281,6 @@ private static class MockEncryptionDirectory extends EncryptionDirectory {
263
281
264
282
void clearMockValues () {
265
283
commitUserData = new CommitUserData (commitUserData .segmentFileName , Map .of ());
266
- forceClearText = false ;
267
- forceKeySecret = null ;
268
- expectedKeySecret = null ;
269
284
}
270
285
271
286
/**
@@ -285,7 +300,7 @@ void setKeysInCommitUserData(String... keyIds) throws IOException {
285
300
286
301
@ Override
287
302
public IndexInput openInput (String fileName , IOContext context ) throws IOException {
288
- return forceClearText ? in .openInput (fileName , context ) : super .openInput (fileName , context );
303
+ return MockFactory . forceClearText ? in .openInput (fileName , context ) : super .openInput (fileName , context );
289
304
}
290
305
291
306
@ Override
@@ -298,12 +313,19 @@ protected CommitUserData readLatestCommitUserData() {
298
313
299
314
@ Override
300
315
protected byte [] getKeySecret (String keyRef ) throws IOException {
301
- if (forceKeySecret != null ) {
302
- return forceKeySecret ;
316
+ if (MockFactory . forceKeySecret != null ) {
317
+ return MockFactory . forceKeySecret ;
303
318
}
304
319
byte [] keySecret = super .getKeySecret (keyRef );
305
- if (expectedKeySecret != null ) {
306
- assertArrayEquals (expectedKeySecret , keySecret );
320
+ if (MockFactory .expectedKeySecrets != null ) {
321
+ boolean keySecretMatches = false ;
322
+ for (byte [] expectedKeySecret : MockFactory .expectedKeySecrets ) {
323
+ if (Arrays .equals (expectedKeySecret , keySecret )) {
324
+ keySecretMatches = true ;
325
+ break ;
326
+ }
327
+ }
328
+ assertTrue (keySecretMatches );
307
329
}
308
330
return keySecret ;
309
331
}
0 commit comments