diff --git a/utils/src/main/java/com/cloud/utils/crypt/EncryptablePropertyPlaceholderConfigurer.java b/utils/src/main/java/com/cloud/utils/crypt/EncryptablePropertyPlaceholderConfigurer.java new file mode 100644 index 000000000000..88d973b8386e --- /dev/null +++ b/utils/src/main/java/com/cloud/utils/crypt/EncryptablePropertyPlaceholderConfigurer.java @@ -0,0 +1,62 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.utils.crypt; + +import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; + +/** + * Spring bean that resolves property placeholders, decrypting any value wrapped as + * {@code ENC(...)} with the management server's own secret key, via + * {@link EncryptionSecretKeyChecker} (the same AES-GCM based mechanism already used to + * encrypt {@code db.properties}). Values that are not wrapped are passed through unchanged, + * and if encryption has not been configured on the management server, decryption is skipped + * entirely and the raw (still-wrapped) value is returned. + * + * This replaces {@code org.jasypt.spring3.properties.EncryptablePropertyPlaceholderConfigurer} + * (from the jasypt-spring3 artifact), which is not on the classpath and is incompatible with + * Spring 5, so beans referencing it fail with a ClassNotFoundException. Unlike that class, this + * one needs no separate {@code StringEncryptor}/algorithm bean wired in: declare it as + *
{@code
+ *
+ *
+ *
+ * }
+ * and it reuses whichever secret key the management server was configured with (file/env/web,
+ * see {@code password.encryption.type} in db.properties).
+ */
+public class EncryptablePropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
+
+ private static final String ENC_PREFIX = "ENC(";
+ private static final String ENC_SUFFIX = ")";
+
+ @Override
+ protected String convertPropertyValue(String originalValue) {
+ if (originalValue == null || !EncryptionSecretKeyChecker.useEncryption()) {
+ return originalValue;
+ }
+
+ String trimmedValue = originalValue.trim();
+ if (trimmedValue.startsWith(ENC_PREFIX) && trimmedValue.endsWith(ENC_SUFFIX)) {
+ return EncryptionSecretKeyChecker.decryptPropertyIfNeeded(trimmedValue);
+ }
+
+ return originalValue;
+ }
+}
diff --git a/utils/src/test/java/com/cloud/utils/crypt/EncryptablePropertyPlaceholderConfigurerTest.java b/utils/src/test/java/com/cloud/utils/crypt/EncryptablePropertyPlaceholderConfigurerTest.java
new file mode 100644
index 000000000000..60a7e5db0f94
--- /dev/null
+++ b/utils/src/test/java/com/cloud/utils/crypt/EncryptablePropertyPlaceholderConfigurerTest.java
@@ -0,0 +1,78 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package com.cloud.utils.crypt;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class EncryptablePropertyPlaceholderConfigurerTest {
+
+ private static final String ENCRYPTED_VALUE = "ENC(iYVsCZXiGiC6SzZLMNBvBL93hoUpntxkuRjyaqC8L+JYKXw=)";
+
+ private final EncryptablePropertyPlaceholderConfigurer configurer = new EncryptablePropertyPlaceholderConfigurer();
+
+ @After
+ public void tearDown() {
+ EncryptionSecretKeyChecker.resetEncryptor();
+ }
+
+ @Test
+ public void convertPropertyValueDecryptsWrappedValueWhenEncryptionEnabled() {
+ EncryptionSecretKeyChecker.initEncryptor("managementkey");
+
+ String result = configurer.convertPropertyValue(ENCRYPTED_VALUE);
+
+ Assert.assertEquals("encthis", result);
+ }
+
+ @Test
+ public void convertPropertyValueTrimsSurroundingWhitespaceBeforeDecrypting() {
+ EncryptionSecretKeyChecker.initEncryptor("managementkey");
+
+ String result = configurer.convertPropertyValue(" " + ENCRYPTED_VALUE + " ");
+
+ Assert.assertEquals("encthis", result);
+ }
+
+ @Test
+ public void convertPropertyValuePassesThroughWrappedValueWhenEncryptionNotConfigured() {
+ String result = configurer.convertPropertyValue(ENCRYPTED_VALUE);
+
+ Assert.assertEquals(ENCRYPTED_VALUE, result);
+ }
+
+ @Test
+ public void convertPropertyValuePassesThroughPlainValues() {
+ EncryptionSecretKeyChecker.initEncryptor("managementkey");
+
+ String result = configurer.convertPropertyValue("guest");
+
+ Assert.assertEquals("guest", result);
+ }
+
+ @Test
+ public void convertPropertyValueHandlesNull() {
+ EncryptionSecretKeyChecker.initEncryptor("managementkey");
+
+ Assert.assertNull(configurer.convertPropertyValue(null));
+ }
+}