From bbee4c87c46c333260c876ea3f15935db9b1f343 Mon Sep 17 00:00:00 2001 From: Maulin Vasavada Date: Fri, 21 Jul 2023 16:57:42 -0700 Subject: [PATCH] Add equals/hashCode override for ServerEncryptionOptions Patch by Maulin Vasavada; reviewed by brandonwilliams and smiklosovic for CASSANDRA-18428 --- CHANGES.txt | 1 + .../cassandra/config/EncryptionOptions.java | 37 +++++++++++ .../config/EncryptionOptionsEqualityTest.java | 61 +++++++++++++++++++ 3 files changed, 99 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 65b945a9eed3..153117ba856f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 5.0 + * Add equals/hashCode override for ServerEncryptionOptions (CASSANDRA-18428) * Upgrade ECJ to version 3.33.0 (CASSANDRA-18190) * Fix ClassCastException from jdk GaloisCounterMode when using JDK17 provider (CASSANDRA-18180) * Drop JDK8, add JDK17 (CASSANDRA-18255) diff --git a/src/java/org/apache/cassandra/config/EncryptionOptions.java b/src/java/org/apache/cassandra/config/EncryptionOptions.java index afa0d66bac3b..b223b6a89648 100644 --- a/src/java/org/apache/cassandra/config/EncryptionOptions.java +++ b/src/java/org/apache/cassandra/config/EncryptionOptions.java @@ -723,6 +723,43 @@ public boolean isExplicitlyOptional() return optional != null && optional; } + /** + * The method is being mainly used to cache SslContexts therefore, we only consider + * fields that would make a difference when the TrustStore or KeyStore files are updated + */ + @Override + public boolean equals(Object o) + { + if (o == this) + return true; + if (o == null || getClass() != o.getClass()) + return false; + if (!super.equals(o)) + return false; + + ServerEncryptionOptions opt = (ServerEncryptionOptions) o; + return internode_encryption == opt.internode_encryption && + legacy_ssl_storage_port_enabled == opt.legacy_ssl_storage_port_enabled && + Objects.equals(outbound_keystore, opt.outbound_keystore) && + Objects.equals(outbound_keystore_password, opt.outbound_keystore_password); + } + + /** + * The method is being mainly used to cache SslContexts therefore, we only consider + * fields that would make a difference when the TrustStore or KeyStore files are updated + */ + @Override + public int hashCode() + { + int result = 0; + result += 31 * super.hashCode(); + result += 31 * (internode_encryption == null ? 0 : internode_encryption.hashCode()); + result += 31 * Boolean.hashCode(legacy_ssl_storage_port_enabled); + result += 31 * (outbound_keystore == null ? 0 : outbound_keystore.hashCode()); + result += 31 * (outbound_keystore_password == null ? 0 : outbound_keystore_password.hashCode()); + return result; + } + public ServerEncryptionOptions withSslContextFactory(ParameterizedClass sslContextFactoryClass) { return new ServerEncryptionOptions(sslContextFactoryClass, keystore, keystore_password, diff --git a/test/unit/org/apache/cassandra/config/EncryptionOptionsEqualityTest.java b/test/unit/org/apache/cassandra/config/EncryptionOptionsEqualityTest.java index dbb309b39fef..e5aabc8ff38f 100644 --- a/test/unit/org/apache/cassandra/config/EncryptionOptionsEqualityTest.java +++ b/test/unit/org/apache/cassandra/config/EncryptionOptionsEqualityTest.java @@ -35,6 +35,21 @@ */ public class EncryptionOptionsEqualityTest { + private EncryptionOptions.ServerEncryptionOptions createServerEncryptionOptions() + { + return new EncryptionOptions.ServerEncryptionOptions() + .withStoreType("JKS") + .withKeyStore("test/conf/cassandra.keystore") + .withKeyStorePassword("cassandra") + .withTrustStore("test/conf/cassandra_ssl_test.truststore") + .withTrustStorePassword("cassandra") + .withOutboundKeystore("test/conf/cassandra_outbound.keystore") + .withOutboundKeystorePassword("cassandra") + .withProtocol("TLSv1.1") + .withRequireClientAuth(true) + .withRequireEndpointVerification(false); + } + @Test public void testKeystoreOptions() { EncryptionOptions encryptionOptions1 = @@ -139,4 +154,50 @@ public void testDifferentCustomSslContextFactoryParameters() { assertNotEquals(encryptionOptions1, encryptionOptions2); assertNotEquals(encryptionOptions1.hashCode(), encryptionOptions2.hashCode()); } + + @Test + public void testServerEncryptionOptions() + { + EncryptionOptions.ServerEncryptionOptions encryptionOptions1 = createServerEncryptionOptions(); + EncryptionOptions.ServerEncryptionOptions encryptionOptions2 = createServerEncryptionOptions(); + + assertEquals(encryptionOptions1, encryptionOptions2); + assertEquals(encryptionOptions1.hashCode(), encryptionOptions2.hashCode()); + } + + @Test + public void testServerEncryptionOptionsMismatchForOutboundKeystore() + { + EncryptionOptions.ServerEncryptionOptions encryptionOptions1 = createServerEncryptionOptions(); + EncryptionOptions.ServerEncryptionOptions encryptionOptions2 = createServerEncryptionOptions(); + + encryptionOptions1 = encryptionOptions1 + .withOutboundKeystore("test/conf/cassandra_outbound1.keystore") + .withOutboundKeystorePassword("cassandra1"); + + encryptionOptions2 = encryptionOptions2 + .withOutboundKeystore("test/conf/cassandra_outbound2.keystore") + .withOutboundKeystorePassword("cassandra2"); + + assertNotEquals(encryptionOptions1, encryptionOptions2); + assertNotEquals(encryptionOptions1.hashCode(), encryptionOptions2.hashCode()); + } + + @Test + public void testServerEncryptionOptionsMismatchForInboundKeystore() + { + EncryptionOptions.ServerEncryptionOptions encryptionOptions1 = createServerEncryptionOptions(); + EncryptionOptions.ServerEncryptionOptions encryptionOptions2 = createServerEncryptionOptions(); + + encryptionOptions1 = encryptionOptions1 + .withKeyStore("test/conf/cassandra1.keystore") + .withKeyStorePassword("cassandra1"); + + encryptionOptions2 = encryptionOptions2 + .withKeyStore("test/conf/cassandra2.keystore") + .withKeyStorePassword("cassandra2"); + + assertNotEquals(encryptionOptions1, encryptionOptions2); + assertNotEquals(encryptionOptions1.hashCode(), encryptionOptions2.hashCode()); + } }