This is an automated email from the ASF dual-hosted git repository.
bruno-roustant pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr-sandbox.git
The following commit(s) were added to refs/heads/main by this push:
new 0d007c3 Add isIndexEncrypted flag and encryption listener. (#128)
0d007c3 is described below
commit 0d007c3589011e1fab04d6d48e7f60786b287d99
Author: Bruno Roustant <[email protected]>
AuthorDate: Fri May 22 15:23:55 2026 +0200
Add isIndexEncrypted flag and encryption listener. (#128)
---
.../solr/encryption/EncryptionDirectory.java | 46 ++-
.../encryption/EncryptionDirectoryFactory.java | 29 +-
.../solr/encryption/EncryptionMergePolicy.java | 7 +-
.../solr/encryption/EncryptionUpdateLog.java | 2 +-
.../encryption/EncryptionBackupRepositoryTest.java | 16 +-
.../solr/encryption/EncryptionDirectoryTest.java | 14 +-
.../EncryptionMergePolicyFactoryTest.java | 22 +-
.../solr/encryption/EncryptionMergePolicyTest.java | 388 +++++++++++----------
.../encryption/EncryptionRequestHandlerTest.java | 15 +-
9 files changed, 325 insertions(+), 214 deletions(-)
diff --git
a/encryption/src/main/java/org/apache/solr/encryption/EncryptionDirectory.java
b/encryption/src/main/java/org/apache/solr/encryption/EncryptionDirectory.java
index fe4cd26..cb364b2 100644
---
a/encryption/src/main/java/org/apache/solr/encryption/EncryptionDirectory.java
+++
b/encryption/src/main/java/org/apache/solr/encryption/EncryptionDirectory.java
@@ -90,6 +90,8 @@ public class EncryptionDirectory extends FilterDirectory {
protected final KeySupplier keySupplier;
+ protected final EncryptionListener encryptionListener;
+
/** Cache of the latest commit user data. */
protected volatile CommitUserData commitUserData;
@@ -100,14 +102,20 @@ public class EncryptionDirectory extends FilterDirectory {
* Creates an {@link EncryptionDirectory} which wraps a delegate {@link
Directory} to encrypt/decrypt
* files on the fly.
*
- * @param encrypterFactory creates {@link AesCtrEncrypter}.
- * @param keySupplier provides the key secrets and determines which
files should be encrypted.
+ * @param encrypterFactory creates {@link AesCtrEncrypter}.
+ * @param keySupplier provides the key secrets and determines which
files should be encrypted.
+ * @param encryptionListener notified when the index is encrypted.
*/
- public EncryptionDirectory(Directory delegate, AesCtrEncrypterFactory
encrypterFactory, KeySupplier keySupplier)
+ public EncryptionDirectory(
+ Directory delegate,
+ AesCtrEncrypterFactory encrypterFactory,
+ KeySupplier keySupplier,
+ EncryptionListener encryptionListener)
throws IOException {
super(delegate);
this.encrypterFactory = encrypterFactory;
this.keySupplier = keySupplier;
+ this.encryptionListener = encryptionListener;
commitUserData = readLatestCommitUserData();
}
@@ -148,6 +156,7 @@ public class EncryptionDirectory extends FilterDirectory {
try {
String keyRef = getActiveKeyRefFromCommit(getLatestCommitData().data);
if (keyRef != null) {
+ encryptionListener.onEncryption();
// Get the key secret first. If it fails, we do not write anything.
byte[] keySecret = getKeySecret(keyRef);
// The IndexOutput has to be wrapped to be encrypted with the key.
@@ -173,10 +182,17 @@ public class EncryptionDirectory extends FilterDirectory {
/**
* Forces this {@link EncryptionDirectory} to read the user data of the
latest commit, to refresh its cache.
*/
- public void forceReadCommitUserData() {
+ public void clearCachedCommitUserData() {
shouldReadCommitUserData = true;
}
+ /**
+ * Clears the cached encryption status for logs.
+ */
+ public void clearCachedEncryptionStatus() {
+ encryptionListener.clearEncryptionStatus();
+ }
+
/**
* Gets the user data from the latest commit, potentially reading the latest
commit if the cache is stale.
*/
@@ -255,6 +271,7 @@ public class EncryptionDirectory extends FilterDirectory {
try {
String keyRef = getKeyRefForReading(indexInput);
if (keyRef != null) {
+ encryptionListener.onEncryption();
// The IndexInput has to be wrapped to be decrypted with the key.
indexInput = new DecryptingIndexInput(indexInput,
getKeySecret(keyRef), encrypterFactory);
}
@@ -371,4 +388,25 @@ public class EncryptionDirectory extends FilterDirectory {
keyCookies = getKeyCookiesFromCommit(data);
}
}
+
+ /**
+ * Notified when the index is encrypted.
+ */
+ public interface EncryptionListener {
+
+ EncryptionListener NO_LISTENER = new EncryptionListener() {
+
+ @Override
+ public void onEncryption() {
+ }
+
+ @Override
+ public void clearEncryptionStatus() {
+ }
+ };
+
+ void onEncryption();
+
+ void clearEncryptionStatus();
+ }
}
diff --git
a/encryption/src/main/java/org/apache/solr/encryption/EncryptionDirectoryFactory.java
b/encryption/src/main/java/org/apache/solr/encryption/EncryptionDirectoryFactory.java
index 4232723..5d12933 100644
---
a/encryption/src/main/java/org/apache/solr/encryption/EncryptionDirectoryFactory.java
+++
b/encryption/src/main/java/org/apache/solr/encryption/EncryptionDirectoryFactory.java
@@ -24,6 +24,7 @@ import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.DirectoryFactory;
import org.apache.solr.core.MMapDirectoryFactory;
import org.apache.solr.core.SolrCore;
+import org.apache.solr.encryption.EncryptionDirectory.EncryptionListener;
import org.apache.solr.encryption.crypto.AesCtrEncrypterFactory;
import org.apache.solr.encryption.crypto.CipherAesCtrEncrypter;
@@ -74,9 +75,20 @@ public class EncryptionDirectoryFactory extends
MMapDirectoryFactory {
*/
static final String PROPERTY_INNER_ENCRYPTION_DIRECTORY_FACTORY =
"innerEncryptionDirectoryFactory";
+ private final EncryptionListener encryptionListener = new
EncryptionListener() {
+ @Override
+ public void onEncryption() {
+ indexEncrypted = true;
+ }
+ @Override
+ public void clearEncryptionStatus() {
+ indexEncrypted = false;
+ }
+ };
private KeySupplier keySupplier;
private AesCtrEncrypterFactory encrypterFactory;
private InnerFactory innerFactory;
+ private volatile boolean indexEncrypted;
public EncryptionDirectoryFactory() {}
@@ -145,6 +157,15 @@ public class EncryptionDirectoryFactory extends
MMapDirectoryFactory {
return keySupplier;
}
+ /**
+ * Returns whether the index is encrypted.
+ * This flag is set when the {@link EncryptionDirectory} opens an
output/input stream that
+ * requires encryption.
+ */
+ public boolean isIndexEncrypted() {
+ return indexEncrypted;
+ }
+
public static EncryptionDirectoryFactory getFactory(SolrCore core) {
if (!(core.getDirectoryFactory() instanceof EncryptionDirectoryFactory)) {
throw new SolrException(SolrException.ErrorCode.SERVICE_UNAVAILABLE,
@@ -157,7 +178,7 @@ public class EncryptionDirectoryFactory extends
MMapDirectoryFactory {
@Override
protected Directory create(String path, LockFactory lockFactory, DirContext
dirContext) throws IOException {
- return innerFactory.create(super.create(path, lockFactory, dirContext),
getEncrypterFactory(), getKeySupplier());
+ return innerFactory.create(super.create(path, lockFactory, dirContext),
getEncrypterFactory(), getKeySupplier(), encryptionListener);
}
@Override
@@ -178,7 +199,11 @@ public class EncryptionDirectoryFactory extends
MMapDirectoryFactory {
* Visible for tests only - Inner factory that creates {@link
EncryptionDirectory} instances.
*/
interface InnerFactory {
- EncryptionDirectory create(Directory delegate, AesCtrEncrypterFactory
encrypterFactory, KeySupplier keySupplier)
+ EncryptionDirectory create(
+ Directory delegate,
+ AesCtrEncrypterFactory encrypterFactory,
+ KeySupplier keySupplier,
+ EncryptionListener encryptionListener)
throws IOException;
}
}
diff --git
a/encryption/src/main/java/org/apache/solr/encryption/EncryptionMergePolicy.java
b/encryption/src/main/java/org/apache/solr/encryption/EncryptionMergePolicy.java
index e915c31..75287c3 100644
---
a/encryption/src/main/java/org/apache/solr/encryption/EncryptionMergePolicy.java
+++
b/encryption/src/main/java/org/apache/solr/encryption/EncryptionMergePolicy.java
@@ -74,7 +74,12 @@ public class EncryptionMergePolicy extends FilterMergePolicy
{
// Make sure the EncryptionDirectory does not keep its cache for the
commit user data.
// It must read the latest commit user data to get the latest active key,
so below the
// segments with old key (to re-encrypt) are always accurate.
- encryptionDir.forceReadCommitUserData();
+ encryptionDir.clearCachedCommitUserData();
+ // Also clear the encryption status for logs if the index becomes
cleartext after rewriting
+ // the segments.
+ if (activeKeyId == null) {
+ encryptionDir.clearCachedEncryptionStatus();
+ }
List<SegmentCommitInfo> segmentsWithOldKeyId =
encryptionDir.getSegmentsWithOldKeyId(segmentInfos, activeKeyId);
if (segmentsWithOldKeyId.isEmpty()) {
return null;
diff --git
a/encryption/src/main/java/org/apache/solr/encryption/EncryptionUpdateLog.java
b/encryption/src/main/java/org/apache/solr/encryption/EncryptionUpdateLog.java
index db3a706..b67b0a4 100644
---
a/encryption/src/main/java/org/apache/solr/encryption/EncryptionUpdateLog.java
+++
b/encryption/src/main/java/org/apache/solr/encryption/EncryptionUpdateLog.java
@@ -98,7 +98,7 @@ public class EncryptionUpdateLog extends UpdateLog {
String activeKeyRef;
EncryptionDirectory directory = directorySupplier.get();
try {
- directory.forceReadCommitUserData();
+ directory.clearCachedCommitUserData();
latestCommitData = directory.getLatestCommitData().data;
activeKeyRef = getActiveKeyRefFromCommit(latestCommitData);
for (TransactionLog log : logs) {
diff --git
a/encryption/src/test/java/org/apache/solr/encryption/EncryptionBackupRepositoryTest.java
b/encryption/src/test/java/org/apache/solr/encryption/EncryptionBackupRepositoryTest.java
index 9f36a19..42ac87f 100644
---
a/encryption/src/test/java/org/apache/solr/encryption/EncryptionBackupRepositoryTest.java
+++
b/encryption/src/test/java/org/apache/solr/encryption/EncryptionBackupRepositoryTest.java
@@ -182,7 +182,11 @@ public class EncryptionBackupRepositoryTest extends
AbstractBackupRepositoryTest
AesCtrEncrypterFactory encrypterFactory = random().nextBoolean() ?
CipherAesCtrEncrypter.FACTORY : LightAesCtrEncrypter.FACTORY;
KeySupplier keySupplier = new TestingKeySupplier.Factory().create();
try (Directory fsSourceDir = FSDirectory.open(sourcePath);
- Directory encSourceDir = new TestEncryptionDirectory(fsSourceDir,
encrypterFactory, keySupplier);
+ Directory encSourceDir = new TestEncryptionDirectory(
+ fsSourceDir,
+ encrypterFactory,
+ keySupplier,
+ EncryptionDirectory.EncryptionListener.NO_LISTENER);
Directory destinationDir =
FSDirectory.open(createTempDir().toAbsolutePath())) {
String fileName = "source-file";
String content = "content";
@@ -331,9 +335,13 @@ public class EncryptionBackupRepositoryTest extends
AbstractBackupRepositoryTest
*/
private static class TestEncryptionDirectory extends EncryptionDirectory {
- TestEncryptionDirectory(Directory delegate, AesCtrEncrypterFactory
encrypterFactory, KeySupplier keySupplier)
- throws IOException {
- super(delegate, encrypterFactory, keySupplier);
+ TestEncryptionDirectory(
+ Directory delegate,
+ AesCtrEncrypterFactory encrypterFactory,
+ KeySupplier keySupplier,
+ EncryptionListener encryptionListener)
+ throws IOException {
+ super(delegate, encrypterFactory, keySupplier, encryptionListener);
}
@Override
diff --git
a/encryption/src/test/java/org/apache/solr/encryption/EncryptionDirectoryTest.java
b/encryption/src/test/java/org/apache/solr/encryption/EncryptionDirectoryTest.java
index f278701..709eb0c 100644
---
a/encryption/src/test/java/org/apache/solr/encryption/EncryptionDirectoryTest.java
+++
b/encryption/src/test/java/org/apache/solr/encryption/EncryptionDirectoryTest.java
@@ -262,8 +262,10 @@ public class EncryptionDirectoryTest extends
SolrCloudTestCase {
@Override
public EncryptionDirectory create(Directory delegate,
AesCtrEncrypterFactory encrypterFactory,
- KeySupplier keySupplier) throws
IOException {
- MockEncryptionDirectory mockDir = new MockEncryptionDirectory(delegate,
encrypterFactory, keySupplier);
+ KeySupplier keySupplier,
+ EncryptionDirectory.EncryptionListener
encryptionListener)
+ throws IOException {
+ MockEncryptionDirectory mockDir = new MockEncryptionDirectory(delegate,
encrypterFactory, keySupplier, encryptionListener);
mockDirs.add(mockDir);
return mockDir;
}
@@ -273,9 +275,13 @@ public class EncryptionDirectoryTest extends
SolrCloudTestCase {
final KeySupplier keySupplier;
- MockEncryptionDirectory(Directory delegate, AesCtrEncrypterFactory
encrypterFactory, KeySupplier keySupplier)
+ MockEncryptionDirectory(
+ Directory delegate,
+ AesCtrEncrypterFactory encrypterFactory,
+ KeySupplier keySupplier,
+ EncryptionListener encryptionListener)
throws IOException {
- super(delegate, encrypterFactory, keySupplier);
+ super(delegate, encrypterFactory, keySupplier, encryptionListener);
this.keySupplier = keySupplier;
}
diff --git
a/encryption/src/test/java/org/apache/solr/encryption/EncryptionMergePolicyFactoryTest.java
b/encryption/src/test/java/org/apache/solr/encryption/EncryptionMergePolicyFactoryTest.java
index 78d2ff2..7d517a2 100644
---
a/encryption/src/test/java/org/apache/solr/encryption/EncryptionMergePolicyFactoryTest.java
+++
b/encryption/src/test/java/org/apache/solr/encryption/EncryptionMergePolicyFactoryTest.java
@@ -30,6 +30,7 @@ import org.apache.lucene.store.MMapDirectory;
import org.apache.lucene.tests.util.LuceneTestCase;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.encryption.crypto.LightAesCtrEncrypter;
+import org.apache.solr.encryption.EncryptionDirectory.EncryptionListener;
import org.apache.solr.index.MergePolicyFactoryArgs;
import org.apache.solr.index.TieredMergePolicyFactory;
import org.junit.Test;
@@ -38,6 +39,7 @@ import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import static
org.apache.solr.encryption.TestingEncryptionRequestHandler.MOCK_COOKIE_PARAMS;
@@ -80,9 +82,22 @@ public class EncryptionMergePolicyFactoryTest extends
LuceneTestCase {
@Test
public void testSegmentReencryption() throws Exception {
KeySupplier keySupplier = new TestingKeySupplier.Factory().create();
- try (Directory dir = new EncryptionDirectory(new
MMapDirectory(createTempDir(), FSLockFactory.getDefault()),
- LightAesCtrEncrypter.FACTORY,
- keySupplier)) {
+ AtomicBoolean indexEncrypted = new AtomicBoolean(false);
+ EncryptionListener encryptionListener = new EncryptionListener() {
+ @Override
+ public void onEncryption() {
+ indexEncrypted.set(true);
+ }
+ @Override
+ public void clearEncryptionStatus() {
+ indexEncrypted.set(false);
+ }
+ };
+ try (Directory dir = new EncryptionDirectory(
+ new MMapDirectory(createTempDir(), FSLockFactory.getDefault()),
+ LightAesCtrEncrypter.FACTORY,
+ keySupplier,
+ encryptionListener)) {
IndexWriterConfig iwc = new IndexWriterConfig(new WhitespaceAnalyzer());
iwc.setMergeScheduler(new ConcurrentMergeScheduler());
iwc.setMergePolicy(createMergePolicy());
@@ -125,6 +140,7 @@ public class EncryptionMergePolicyFactoryTest extends
LuceneTestCase {
assertTrue(segmentNames.isEmpty());
}
}
+ assertTrue(indexEncrypted.get());
}
private void commit(IndexWriter writer, KeySupplier keySupplier, String...
keyIds) throws IOException {
diff --git
a/encryption/src/test/java/org/apache/solr/encryption/EncryptionMergePolicyTest.java
b/encryption/src/test/java/org/apache/solr/encryption/EncryptionMergePolicyTest.java
index 88cd3b2..0e61184 100644
---
a/encryption/src/test/java/org/apache/solr/encryption/EncryptionMergePolicyTest.java
+++
b/encryption/src/test/java/org/apache/solr/encryption/EncryptionMergePolicyTest.java
@@ -45,213 +45,219 @@ import static
org.apache.solr.encryption.TestingKeySupplier.KEY_ID_2;
*/
public class EncryptionMergePolicyTest extends LuceneTestCase {
- private final Path tempDir = createTempDir();
- private final KeySupplier keySupplier = new
TestingKeySupplier.Factory().create();
- private final AesCtrEncrypterFactory encrypterFactory =
LightAesCtrEncrypter.FACTORY;
-
- @Test
- public void testNoReencryptionWhenNoKeyChange() throws Exception {
- try (Directory dir = new EncryptionDirectory(
- new MMapDirectory(tempDir, FSLockFactory.getDefault()),
- encrypterFactory,
- keySupplier)) {
-
- IndexWriterConfig iwc = new IndexWriterConfig(new
WhitespaceAnalyzer());
- iwc.setMergePolicy(createMergePolicy());
-
- try (IndexWriter writer = new IndexWriter(dir, iwc)) {
- // Create initial segments with KEY_ID_1.
- commit(writer, keySupplier, KEY_ID_1);
- int numSegments = 3;
- for (int i = 0; i < numSegments; ++i) {
- writer.addDocument(new Document());
- commit(writer, keySupplier, KEY_ID_1);
- }
-
- Set<String> initialSegmentNames = readSegmentNames(dir);
- assertEquals(numSegments, initialSegmentNames.size());
-
- // Force merge with MAX_VALUE should not trigger reencryption.
- writer.forceMerge(Integer.MAX_VALUE);
- commit(writer, keySupplier, KEY_ID_1);
-
- // Verify segments remain unchanged.
- assertEquals(initialSegmentNames, readSegmentNames(dir));
- }
+ private final Path tempDir = createTempDir();
+ private final KeySupplier keySupplier = new
TestingKeySupplier.Factory().create();
+ private final AesCtrEncrypterFactory encrypterFactory =
LightAesCtrEncrypter.FACTORY;
+ private final EncryptionDirectory.EncryptionListener encryptionListener =
EncryptionDirectory.EncryptionListener.NO_LISTENER;
+
+ @Test
+ public void testNoReencryptionWhenNoKeyChange() throws Exception {
+ try (Directory dir = new EncryptionDirectory(
+ new MMapDirectory(tempDir, FSLockFactory.getDefault()),
+ encrypterFactory,
+ keySupplier,
+ encryptionListener)) {
+
+ IndexWriterConfig iwc = new IndexWriterConfig(new WhitespaceAnalyzer());
+ iwc.setMergePolicy(createMergePolicy());
+
+ try (IndexWriter writer = new IndexWriter(dir, iwc)) {
+ // Create initial segments with KEY_ID_1.
+ commit(writer, keySupplier, KEY_ID_1);
+ int numSegments = 3;
+ for (int i = 0; i < numSegments; ++i) {
+ writer.addDocument(new Document());
+ commit(writer, keySupplier, KEY_ID_1);
}
+
+ Set<String> initialSegmentNames = readSegmentNames(dir);
+ assertEquals(numSegments, initialSegmentNames.size());
+
+ // Force merge with MAX_VALUE should not trigger reencryption.
+ writer.forceMerge(Integer.MAX_VALUE);
+ commit(writer, keySupplier, KEY_ID_1);
+
+ // Verify segments remain unchanged.
+ assertEquals(initialSegmentNames, readSegmentNames(dir));
+ }
}
+ }
+
+ @Test
+ public void testReencryptionWithKeyChange() throws Exception {
+ try (Directory dir = new EncryptionDirectory(
+ new MMapDirectory(tempDir, FSLockFactory.getDefault()),
+ encrypterFactory,
+ keySupplier,
+ encryptionListener)) {
- @Test
- public void testReencryptionWithKeyChange() throws Exception {
- try (Directory dir = new EncryptionDirectory(
- new MMapDirectory(tempDir, FSLockFactory.getDefault()),
- encrypterFactory,
- keySupplier)) {
-
- IndexWriterConfig iwc = new IndexWriterConfig(new
WhitespaceAnalyzer());
- iwc.setMergePolicy(createMergePolicy());
-
- try (IndexWriter writer = new IndexWriter(dir, iwc)) {
- // Create initial segments with KEY_ID_1.
- commit(writer, keySupplier, KEY_ID_1);
- int numSegments = 3;
- for (int i = 0; i < numSegments; ++i) {
- writer.addDocument(new Document());
- commit(writer, keySupplier, KEY_ID_1);
- }
-
- Set<String> initialSegmentNames = readSegmentNames(dir);
- assertEquals(numSegments, initialSegmentNames.size());
-
- // Change active key to KEY_ID_2.
- commit(writer, keySupplier, KEY_ID_1, KEY_ID_2);
-
- // Force merge with MAX_VALUE should trigger reencryption.
- writer.forceMerge(Integer.MAX_VALUE);
- commit(writer, keySupplier, KEY_ID_1, KEY_ID_2);
-
- // Verify all segments have been rewritten.
- Set<String> newSegmentNames = readSegmentNames(dir);
- assertEquals(initialSegmentNames.size(),
newSegmentNames.size());
- assertNotEquals(initialSegmentNames, newSegmentNames);
- newSegmentNames.retainAll(initialSegmentNames);
- assertTrue(newSegmentNames.isEmpty());
- }
+ IndexWriterConfig iwc = new IndexWriterConfig(new WhitespaceAnalyzer());
+ iwc.setMergePolicy(createMergePolicy());
+
+ try (IndexWriter writer = new IndexWriter(dir, iwc)) {
+ // Create initial segments with KEY_ID_1.
+ commit(writer, keySupplier, KEY_ID_1);
+ int numSegments = 3;
+ for (int i = 0; i < numSegments; ++i) {
+ writer.addDocument(new Document());
+ commit(writer, keySupplier, KEY_ID_1);
}
+
+ Set<String> initialSegmentNames = readSegmentNames(dir);
+ assertEquals(numSegments, initialSegmentNames.size());
+
+ // Change active key to KEY_ID_2.
+ commit(writer, keySupplier, KEY_ID_1, KEY_ID_2);
+
+ // Force merge with MAX_VALUE should trigger reencryption.
+ writer.forceMerge(Integer.MAX_VALUE);
+ commit(writer, keySupplier, KEY_ID_1, KEY_ID_2);
+
+ // Verify all segments have been rewritten.
+ Set<String> newSegmentNames = readSegmentNames(dir);
+ assertEquals(initialSegmentNames.size(), newSegmentNames.size());
+ assertNotEquals(initialSegmentNames, newSegmentNames);
+ newSegmentNames.retainAll(initialSegmentNames);
+ assertTrue(newSegmentNames.isEmpty());
+ }
}
+ }
+
+ @Test
+ public void testNoReencryptionWithNonMaxValueForceMerge() throws Exception {
+ try (Directory dir = new EncryptionDirectory(
+ new MMapDirectory(tempDir, FSLockFactory.getDefault()),
+ encrypterFactory,
+ keySupplier,
+ encryptionListener)) {
+
+ IndexWriterConfig iwc = new IndexWriterConfig(new WhitespaceAnalyzer());
+ iwc.setMergePolicy(createMergePolicy());
- @Test
- public void testNoReencryptionWithNonMaxValueForceMerge() throws Exception
{
- try (Directory dir = new EncryptionDirectory(
- new MMapDirectory(tempDir, FSLockFactory.getDefault()),
- encrypterFactory,
- keySupplier)) {
-
- IndexWriterConfig iwc = new IndexWriterConfig(new
WhitespaceAnalyzer());
- iwc.setMergePolicy(createMergePolicy());
-
- try (IndexWriter writer = new IndexWriter(dir, iwc)) {
- // Create initial segments with KEY_ID_1.
- commit(writer, keySupplier, KEY_ID_1);
- int numSegments = 3;
- for (int i = 0; i < numSegments; ++i) {
- writer.addDocument(new Document());
- commit(writer, keySupplier, KEY_ID_1);
- }
-
- Set<String> initialSegmentNames = readSegmentNames(dir);
- assertEquals(numSegments, initialSegmentNames.size());
-
- // Change active key to KEY_ID_2.
- commit(writer, keySupplier, KEY_ID_1, KEY_ID_2);
-
- // Force merge with non-MAX_VALUE should not trigger
reencryption.
- writer.forceMerge(10);
- commit(writer, keySupplier, KEY_ID_1, KEY_ID_2);
-
- // Verify segments remain unchanged.
- assertEquals(initialSegmentNames, readSegmentNames(dir));
- }
+ try (IndexWriter writer = new IndexWriter(dir, iwc)) {
+ // Create initial segments with KEY_ID_1.
+ commit(writer, keySupplier, KEY_ID_1);
+ int numSegments = 3;
+ for (int i = 0; i < numSegments; ++i) {
+ writer.addDocument(new Document());
+ commit(writer, keySupplier, KEY_ID_1);
}
+
+ Set<String> initialSegmentNames = readSegmentNames(dir);
+ assertEquals(numSegments, initialSegmentNames.size());
+
+ // Change active key to KEY_ID_2.
+ commit(writer, keySupplier, KEY_ID_1, KEY_ID_2);
+
+ // Force merge with non-MAX_VALUE should not trigger reencryption.
+ writer.forceMerge(10);
+ commit(writer, keySupplier, KEY_ID_1, KEY_ID_2);
+
+ // Verify segments remain unchanged.
+ assertEquals(initialSegmentNames, readSegmentNames(dir));
+ }
}
+ }
- @Test
- public void testEmptyIndex() throws Exception {
- try (Directory dir = new EncryptionDirectory(
- new MMapDirectory(tempDir, FSLockFactory.getDefault()),
- encrypterFactory,
- keySupplier)) {
-
- IndexWriterConfig iwc = new IndexWriterConfig(new
WhitespaceAnalyzer());
- iwc.setMergePolicy(createMergePolicy());
-
- try (IndexWriter writer = new IndexWriter(dir, iwc)) {
- // Create empty index with KEY_ID_1.
- commit(writer, keySupplier, KEY_ID_1);
-
- // Change active key to KEY_ID_2.
- commit(writer, keySupplier, KEY_ID_1, KEY_ID_2);
-
- // Force merge with MAX_VALUE should not trigger any merges.
- writer.forceMerge(Integer.MAX_VALUE);
- commit(writer, keySupplier, KEY_ID_1, KEY_ID_2);
-
- // Verify no segments exist.
- assertEquals(0, readSegmentNames(dir).size());
- }
- }
+ @Test
+ public void testEmptyIndex() throws Exception {
+ try (Directory dir = new EncryptionDirectory(
+ new MMapDirectory(tempDir, FSLockFactory.getDefault()),
+ encrypterFactory,
+ keySupplier,
+ encryptionListener)) {
+
+ IndexWriterConfig iwc = new IndexWriterConfig(new WhitespaceAnalyzer());
+ iwc.setMergePolicy(createMergePolicy());
+
+ try (IndexWriter writer = new IndexWriter(dir, iwc)) {
+ // Create empty index with KEY_ID_1.
+ commit(writer, keySupplier, KEY_ID_1);
+
+ // Change active key to KEY_ID_2.
+ commit(writer, keySupplier, KEY_ID_1, KEY_ID_2);
+
+ // Force merge with MAX_VALUE should not trigger any merges.
+ writer.forceMerge(Integer.MAX_VALUE);
+ commit(writer, keySupplier, KEY_ID_1, KEY_ID_2);
+
+ // Verify no segments exist.
+ assertEquals(0, readSegmentNames(dir).size());
+ }
}
+ }
+
+ @Test
+ public void testPartiallyEncryptedSegments() throws Exception {
+ try (Directory dir = new EncryptionDirectory(
+ new MMapDirectory(tempDir, FSLockFactory.getDefault()),
+ encrypterFactory,
+ keySupplier,
+ encryptionListener)) {
+
+ IndexWriterConfig iwc = new IndexWriterConfig(new WhitespaceAnalyzer());
+ iwc.setMergePolicy(createMergePolicy());
+
+ try (IndexWriter writer = new IndexWriter(dir, iwc)) {
+ // Create segments with mixed encryption states.
+ commit(writer, keySupplier, KEY_ID_1);
- @Test
- public void testPartiallyEncryptedSegments() throws Exception {
- try (Directory dir = new EncryptionDirectory(
- new MMapDirectory(tempDir, FSLockFactory.getDefault()),
- encrypterFactory,
- keySupplier)) {
-
- IndexWriterConfig iwc = new IndexWriterConfig(new
WhitespaceAnalyzer());
- iwc.setMergePolicy(createMergePolicy());
-
- try (IndexWriter writer = new IndexWriter(dir, iwc)) {
- // Create segments with mixed encryption states.
- commit(writer, keySupplier, KEY_ID_1);
-
- // Add some documents with KEY_ID_1.
- for (int i = 0; i < 3; i++) {
- Document doc = new Document();
- doc.add(new StringField("id", String.valueOf(i),
Field.Store.YES));
- writer.addDocument(doc);
- commit(writer, keySupplier, KEY_ID_1);
- }
-
- Set<String> key1SegmentNames = readSegmentNames(dir);
- assertEquals(3, key1SegmentNames.size());
-
- // Change to KEY_ID_2.
- commit(writer, keySupplier, KEY_ID_1, KEY_ID_2);
-
- // Add more documents with KEY_ID_2.
- for (int i = 3; i < 6; i++) {
- Document doc = new Document();
- doc.add(new StringField("id", String.valueOf(i),
Field.Store.YES));
- writer.addDocument(doc);
- commit(writer, keySupplier, KEY_ID_1, KEY_ID_2);
- }
-
- Set<String> key2NewSegmentNames = readSegmentNames(dir);
- key2NewSegmentNames.removeAll(key1SegmentNames);
- assertEquals(3, key2NewSegmentNames.size());
-
- // Force merge with MAX_VALUE should trigger reencryption of
old segments.
- writer.forceMerge(Integer.MAX_VALUE);
- commit(writer, keySupplier, KEY_ID_1, KEY_ID_2);
-
- // Verify only and all key1 segments have been rewritten.
- Set<String> finalSegmentNames = readSegmentNames(dir);
- assertEquals(6, finalSegmentNames.size());
- assertTrue(finalSegmentNames.containsAll(key2NewSegmentNames));
-
assertTrue(finalSegmentNames.stream().noneMatch(key1SegmentNames::contains));
- }
+ // Add some documents with KEY_ID_1.
+ for (int i = 0; i < 3; i++) {
+ Document doc = new Document();
+ doc.add(new StringField("id", String.valueOf(i), Field.Store.YES));
+ writer.addDocument(doc);
+ commit(writer, keySupplier, KEY_ID_1);
}
- }
- private MergePolicy createMergePolicy() {
- return new EncryptionMergePolicy(new TieredMergePolicy());
- }
+ Set<String> key1SegmentNames = readSegmentNames(dir);
+ assertEquals(3, key1SegmentNames.size());
- private void commit(IndexWriter writer, KeySupplier keySupplier, String...
keyIds) throws IOException {
- Map<String, String> commitData = new HashMap<>();
- for (String keyId : keyIds) {
- EncryptionUtil.setNewActiveKeyIdInCommit(keyId,
keySupplier.getKeyCookie(keyId, MOCK_COOKIE_PARAMS), commitData);
+ // Change to KEY_ID_2.
+ commit(writer, keySupplier, KEY_ID_1, KEY_ID_2);
+
+ // Add more documents with KEY_ID_2.
+ for (int i = 3; i < 6; i++) {
+ Document doc = new Document();
+ doc.add(new StringField("id", String.valueOf(i), Field.Store.YES));
+ writer.addDocument(doc);
+ commit(writer, keySupplier, KEY_ID_1, KEY_ID_2);
}
- writer.setLiveCommitData(commitData.entrySet());
- writer.commit();
+
+ Set<String> key2NewSegmentNames = readSegmentNames(dir);
+ key2NewSegmentNames.removeAll(key1SegmentNames);
+ assertEquals(3, key2NewSegmentNames.size());
+
+ // Force merge with MAX_VALUE should trigger reencryption of old
segments.
+ writer.forceMerge(Integer.MAX_VALUE);
+ commit(writer, keySupplier, KEY_ID_1, KEY_ID_2);
+
+ // Verify only and all key1 segments have been rewritten.
+ Set<String> finalSegmentNames = readSegmentNames(dir);
+ assertEquals(6, finalSegmentNames.size());
+ assertTrue(finalSegmentNames.containsAll(key2NewSegmentNames));
+
assertTrue(finalSegmentNames.stream().noneMatch(key1SegmentNames::contains));
+ }
}
+ }
- private Set<String> readSegmentNames(Directory dir) throws IOException {
- SegmentInfos segmentInfos = SegmentInfos.readLatestCommit(dir);
- return segmentInfos.asList().stream()
- .map(sci -> sci.info.name)
- .collect(Collectors.toSet());
+ private MergePolicy createMergePolicy() {
+ return new EncryptionMergePolicy(new TieredMergePolicy());
+ }
+
+ private void commit(IndexWriter writer, KeySupplier keySupplier, String...
keyIds) throws IOException {
+ Map<String, String> commitData = new HashMap<>();
+ for (String keyId : keyIds) {
+ EncryptionUtil.setNewActiveKeyIdInCommit(keyId,
keySupplier.getKeyCookie(keyId, MOCK_COOKIE_PARAMS), commitData);
}
+ writer.setLiveCommitData(commitData.entrySet());
+ writer.commit();
+ }
+
+ private Set<String> readSegmentNames(Directory dir) throws IOException {
+ SegmentInfos segmentInfos = SegmentInfos.readLatestCommit(dir);
+ return segmentInfos.asList().stream()
+ .map(sci -> sci.info.name)
+ .collect(Collectors.toSet());
+ }
}
\ No newline at end of file
diff --git
a/encryption/src/test/java/org/apache/solr/encryption/EncryptionRequestHandlerTest.java
b/encryption/src/test/java/org/apache/solr/encryption/EncryptionRequestHandlerTest.java
index 0dcf9bc..bcabe01 100644
---
a/encryption/src/test/java/org/apache/solr/encryption/EncryptionRequestHandlerTest.java
+++
b/encryption/src/test/java/org/apache/solr/encryption/EncryptionRequestHandlerTest.java
@@ -27,6 +27,7 @@ import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.embedded.JettySolrRunner;
import org.apache.solr.encryption.crypto.AesCtrEncrypterFactory;
+import org.apache.solr.encryption.EncryptionDirectory.EncryptionListener;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrQueryRequestBase;
import org.apache.solr.response.SolrQueryResponse;
@@ -393,16 +394,22 @@ public class EncryptionRequestHandlerTest extends
SolrCloudTestCase {
@Override
public EncryptionDirectory create(Directory delegate,
AesCtrEncrypterFactory encrypterFactory,
- KeySupplier keySupplier) throws
IOException {
- return new MockEncryptionDirectory(delegate, encrypterFactory,
keySupplier);
+ KeySupplier keySupplier,
+ EncryptionListener encryptionListener)
+ throws IOException {
+ return new MockEncryptionDirectory(delegate, encrypterFactory,
keySupplier, encryptionListener);
}
}
private static class MockEncryptionDirectory extends EncryptionDirectory {
- MockEncryptionDirectory(Directory delegate, AesCtrEncrypterFactory
encrypterFactory, KeySupplier keySupplier)
+ MockEncryptionDirectory(
+ Directory delegate,
+ AesCtrEncrypterFactory encrypterFactory,
+ KeySupplier keySupplier,
+ EncryptionListener encryptionListener)
throws IOException {
- super(delegate, encrypterFactory, keySupplier);
+ super(delegate, encrypterFactory, keySupplier, encryptionListener);
}
@Override