From a3cbc0ef67a5d561b1185df49286e8082c7eb98a Mon Sep 17 00:00:00 2001 From: Andre Dietisheim Date: Fri, 22 Mar 2024 14:44:00 +0100 Subject: [PATCH] refactor: use IDEAManager in intellij-common (#718) Signed-off-by: Andre Dietisheim --- .../kubernetes/model/client/ClientAdapter.kt | 9 +- .../ssl/CompositeX509ExtendedTrustManager.kt | 129 ------------ .../model/client/ssl/IDEATrustManager.kt | 118 ----------- .../model/client/ssl/IDEATrustManagerTest.kt | 185 ------------------ 4 files changed, 4 insertions(+), 437 deletions(-) delete mode 100644 src/main/kotlin/com/redhat/devtools/intellij/kubernetes/model/client/ssl/CompositeX509ExtendedTrustManager.kt delete mode 100644 src/main/kotlin/com/redhat/devtools/intellij/kubernetes/model/client/ssl/IDEATrustManager.kt delete mode 100644 src/test/kotlin/com/redhat/devtools/intellij/kubernetes/model/client/ssl/IDEATrustManagerTest.kt diff --git a/src/main/kotlin/com/redhat/devtools/intellij/kubernetes/model/client/ClientAdapter.kt b/src/main/kotlin/com/redhat/devtools/intellij/kubernetes/model/client/ClientAdapter.kt index c8d416d4d..a92c41745 100644 --- a/src/main/kotlin/com/redhat/devtools/intellij/kubernetes/model/client/ClientAdapter.kt +++ b/src/main/kotlin/com/redhat/devtools/intellij/kubernetes/model/client/ClientAdapter.kt @@ -11,7 +11,7 @@ package com.redhat.devtools.intellij.kubernetes.model.client import com.redhat.devtools.intellij.common.kubernetes.ClusterHelper -import com.redhat.devtools.intellij.kubernetes.model.client.ssl.IDEATrustManager +import com.redhat.devtools.intellij.common.ssl.IDEATrustManager import io.fabric8.kubernetes.client.Client import io.fabric8.kubernetes.client.Config import io.fabric8.kubernetes.client.KubernetesClient @@ -59,7 +59,7 @@ abstract class ClientAdapter(private val fabric8Client: C) fun create( namespace: String? = null, context: String? = null, - trustManagerProvider: ((toIntegrate: Array) -> X509TrustManager) + trustManagerProvider: ((toIntegrate: List) -> X509TrustManager) = IDEATrustManager()::configure ): ClientAdapter { val config = Config.autoConfigure(context) @@ -69,7 +69,7 @@ abstract class ClientAdapter(private val fabric8Client: C) fun create( namespace: String? = null, config: Config, - externalTrustManagerProvider: (toIntegrate: Array) -> X509TrustManager + externalTrustManagerProvider: (toIntegrate: List) -> X509TrustManager = IDEATrustManager()::configure ): ClientAdapter { setNamespace(namespace, config) @@ -90,11 +90,10 @@ abstract class ClientAdapter(private val fabric8Client: C) private fun setSslContext( builder: HttpClient.Builder, config: Config, - externalTrustManagerProvider: (toIntegrate: Array) -> X509TrustManager + externalTrustManagerProvider: (toIntegrate: List) -> X509TrustManager ) { val clientTrustManagers = SSLUtils.trustManagers(config) .filterIsInstance() - .toTypedArray() val externalTrustManager = externalTrustManagerProvider.invoke(clientTrustManagers) builder.sslContext(SSLUtils.keyManagers(config), arrayOf(externalTrustManager)) } diff --git a/src/main/kotlin/com/redhat/devtools/intellij/kubernetes/model/client/ssl/CompositeX509ExtendedTrustManager.kt b/src/main/kotlin/com/redhat/devtools/intellij/kubernetes/model/client/ssl/CompositeX509ExtendedTrustManager.kt deleted file mode 100644 index d15bbb817..000000000 --- a/src/main/kotlin/com/redhat/devtools/intellij/kubernetes/model/client/ssl/CompositeX509ExtendedTrustManager.kt +++ /dev/null @@ -1,129 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2023 Red Hat, Inc. - * Distributed under license by Red Hat, Inc. All rights reserved. - * This program is made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v20.html - * - * Contributors: - * Based on nl.altindag.ssl.trustmanager.CompositeX509ExtendedTrustManager at https://github.com/Hakky54/sslcontext-kickstart - * Red Hat, Inc. - initial API and implementation - ******************************************************************************/ - -package com.redhat.devtools.intellij.kubernetes.model.client.ssl - -import java.net.Socket -import java.security.cert.CertificateException -import java.security.cert.X509Certificate -import java.util.* -import java.util.function.Consumer -import javax.net.ssl.SSLEngine -import javax.net.ssl.X509ExtendedTrustManager - -class CompositeX509ExtendedTrustManager(trustManagers: List): X509ExtendedTrustManager() { - - companion object { - private const val CERTIFICATE_EXCEPTION_MESSAGE = "None of the TrustManagers trust this certificate chain" - } - - val innerTrustManagers: List - private val acceptedIssuers: Array - - init { - innerTrustManagers = Collections.unmodifiableList(trustManagers) - acceptedIssuers = trustManagers - .map { manager: X509ExtendedTrustManager -> manager.acceptedIssuers } - .flatMap { acceptedIssuers: Array? -> - acceptedIssuers?.asList() ?: emptyList() - } - .toTypedArray() - } - - override fun getAcceptedIssuers(): Array { - return Arrays.copyOf(acceptedIssuers, acceptedIssuers.size) - } - - @Throws(CertificateException::class) - override fun checkClientTrusted(chain: Array, authType: String) { - checkTrusted { trustManager: X509ExtendedTrustManager -> - trustManager.checkClientTrusted( - chain, - authType - ) - } - } - - @Throws(CertificateException::class) - override fun checkClientTrusted(chain: Array, authType: String, socket: Socket) { - checkTrusted { trustManager: X509ExtendedTrustManager -> - trustManager.checkClientTrusted( - chain, - authType, - socket - ) - } - } - - @Throws(CertificateException::class) - override fun checkClientTrusted(chain: Array, authType: String, sslEngine: SSLEngine) { - checkTrusted { trustManager: X509ExtendedTrustManager -> - trustManager.checkClientTrusted( - chain, - authType, - sslEngine - ) - } - } - - @Throws(CertificateException::class) - override fun checkServerTrusted(chain: Array, authType: String) { - checkTrusted{ trustManager: X509ExtendedTrustManager -> - trustManager.checkServerTrusted( - chain, - authType - ) - } - } - - @Throws(CertificateException::class) - override fun checkServerTrusted(chain: Array, authType: String, socket: Socket) { - checkTrusted{ trustManager: X509ExtendedTrustManager -> - trustManager.checkServerTrusted( - chain, - authType, - socket - ) - } - } - - @Throws(CertificateException::class) - override fun checkServerTrusted(chain: Array, authType: String, sslEngine: SSLEngine) { - checkTrusted { trustManager: X509ExtendedTrustManager -> - trustManager.checkServerTrusted( - chain, - authType, - sslEngine - ) - } - } - - @Throws(CertificateException::class) - private fun checkTrusted(consumer: (trustManager: X509ExtendedTrustManager) -> Unit) { - val certificateExceptions: MutableList = ArrayList() - for (trustManager in innerTrustManagers) { - try { - consumer.invoke(trustManager) - return - } catch (e: CertificateException) { - certificateExceptions.add(e) - } - } - val certificateException = CertificateException(CERTIFICATE_EXCEPTION_MESSAGE) - certificateExceptions.forEach(Consumer { exception: CertificateException? -> - certificateException.addSuppressed( - exception - ) - }) - throw certificateException - } -} diff --git a/src/main/kotlin/com/redhat/devtools/intellij/kubernetes/model/client/ssl/IDEATrustManager.kt b/src/main/kotlin/com/redhat/devtools/intellij/kubernetes/model/client/ssl/IDEATrustManager.kt deleted file mode 100644 index 8bfd5c774..000000000 --- a/src/main/kotlin/com/redhat/devtools/intellij/kubernetes/model/client/ssl/IDEATrustManager.kt +++ /dev/null @@ -1,118 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2023 Red Hat, Inc. - * Distributed under license by Red Hat, Inc. All rights reserved. - * This program is made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v20.html - * - * Contributors: - * Red Hat, Inc. - initial API and implementation - ******************************************************************************/ -package com.redhat.devtools.intellij.kubernetes.model.client.ssl - -import com.intellij.openapi.diagnostic.logger -import com.intellij.util.net.ssl.CertificateManager -import com.intellij.util.net.ssl.ConfirmingTrustManager -import java.lang.reflect.Field -import javax.net.ssl.X509ExtendedTrustManager -import javax.net.ssl.X509TrustManager -import org.apache.commons.lang3.reflect.FieldUtils - -class IDEATrustManager(private val trustManager: X509TrustManager = CertificateManager.getInstance().trustManager) { - - fun configure(toAdd: Array): X509TrustManager { - try { - if (hasSystemManagerField()) { - // < IC-2022.2 - setCompositeManager(toAdd, trustManager) - } else { - // >= IC-2022.2 - addCompositeManager(toAdd, trustManager) - } - } catch (e: RuntimeException) { - logger().warn("Could not configure IDEA trust manager.", e) - } - return trustManager - } - - /** - * Returns `true` if [ConfirmingTrustManager] has a private field `mySystemManager`. - * Returns `false` otherwise. - * IDEA < IC-2022.2 manages a single [X509TrustManager] in a private field called `mySystemManager`. - * IDEA >= IC-2022.2 manages a list of [X509TrustManager]s in a private list called `mySystemManagers`. - * - * @return true if com.intellij.util.net.ssl.ConfirmingTrustManager has a field mySystemManager. False otherwise. - */ - private fun hasSystemManagerField(): Boolean { - return getSystemManagerField() != null - } - - private fun getSystemManagerField(): Field? { - return FieldUtils.getDeclaredField( - trustManager::class.java, - "mySystemManager", - true - ) - } - - /** - * Sets a [CompositeX509ExtendedTrustManager] with the given [X509TrustManager]s - * to the given destination [X509TrustManager]. - * If a [CompositeX509ExtendedTrustManager] already exists, his first entry is taken and set to a new - * [CompositeX509ExtendedTrustManager] that replaces the existing one. - * - * @param trustManagers the trust managers that should be set to the destination trust manager - * @param destination the destination trust manager that should receive the trust managers - * @return true if the operation worked - */ - private fun setCompositeManager( - trustManagers: Array, - destination: X509TrustManager - ): Boolean { - val systemManagerField = getSystemManagerField() ?: return false - val systemManager = systemManagerField.get(destination) as? X509ExtendedTrustManager ?: return false - val compositeTrustManager = createCompositeTrustManager(systemManager, trustManagers) - systemManagerField.set(destination, compositeTrustManager) - return true - } - - private fun createCompositeTrustManager( - systemManager: X509ExtendedTrustManager, - clientTrustManagers: Array - ): X509ExtendedTrustManager { - val trustManagers = if (systemManager is CompositeX509ExtendedTrustManager) { - // already patched CertificateManager, take 1st entry in existing system manager - mutableListOf(systemManager.innerTrustManagers[0]) - } else { - // unpatched CertificateManager, take system manager - mutableListOf(systemManager) - } - trustManagers.addAll(clientTrustManagers) - return CompositeX509ExtendedTrustManager(trustManagers) - } - - /** - * Adds a [CompositeX509ExtendedTrustManager] to the given destination [X509TrustManager]. - * If a [CompositeX509ExtendedTrustManager] already exists, it is replaced by a new [CompositeX509ExtendedTrustManager]. - * - * @param trustManagers the trust managers that should be added to destination trust manager - * @param destination the trust manager that should receive the given trust managers - */ - private fun addCompositeManager( - trustManagers: Array, - destination: X509TrustManager - ): Boolean { - val systemManagersField = FieldUtils.getDeclaredField( - destination::class.java, - "mySystemManagers", - true - ) ?: return false - val managers = systemManagersField.get(destination) as? MutableList ?: return false - val nonCompositeManagers = managers.filterNot { it is CompositeX509ExtendedTrustManager } - val clientTrustManager = CompositeX509ExtendedTrustManager(trustManagers.asList()) - managers.clear() - managers.addAll(nonCompositeManagers) - managers.add(clientTrustManager) - return true - } -} \ No newline at end of file diff --git a/src/test/kotlin/com/redhat/devtools/intellij/kubernetes/model/client/ssl/IDEATrustManagerTest.kt b/src/test/kotlin/com/redhat/devtools/intellij/kubernetes/model/client/ssl/IDEATrustManagerTest.kt deleted file mode 100644 index 9b77bf6c7..000000000 --- a/src/test/kotlin/com/redhat/devtools/intellij/kubernetes/model/client/ssl/IDEATrustManagerTest.kt +++ /dev/null @@ -1,185 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2023 Red Hat, Inc. - * Distributed under license by Red Hat, Inc. All rights reserved. - * This program is made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v20.html - * - * Contributors: - * Red Hat, Inc. - initial API and implementation - ******************************************************************************/ -package com.redhat.devtools.intellij.kubernetes.model.client.ssl - -import com.nhaarman.mockitokotlin2.mock -import java.security.cert.X509Certificate -import javax.net.ssl.X509ExtendedTrustManager -import javax.net.ssl.X509TrustManager -import org.assertj.core.api.Assertions.assertThat -import org.junit.Test - -class IDEATrustManagerTest { - - @Test - fun `single system manager field - should replace existing trust manager with new composite trust manager`() { - // given - val trustManager = TrustManagerWithMySystemManagerField(mock()) - val operator = IDEATrustManager(trustManager) - assertThat(trustManager.mySystemManager) - .isNotInstanceOf(CompositeX509ExtendedTrustManager::class.java) - // when - operator.configure(emptyArray()) - // then - assertThat(trustManager.mySystemManager) - .isInstanceOf(CompositeX509ExtendedTrustManager::class.java) - } - - @Test - fun `single system manager field - should replace existing trust manager with new composite trust manager that contains given trust managers`() { - // given - val trustManager = TrustManagerWithMySystemManagerField(mock()) - val operator = IDEATrustManager(trustManager) - val newTrustManagers = arrayOf( - mock(), - mock() - ) - // when - operator.configure(newTrustManagers) - // then - assertThat(trustManager.mySystemManager) - .isInstanceOf(CompositeX509ExtendedTrustManager::class.java) - val afterConfigure = (trustManager.mySystemManager as CompositeX509ExtendedTrustManager).innerTrustManagers - assertThat(afterConfigure) - .contains(*newTrustManagers) // new instance contains list given to configure() - } - - @Test - fun `single system manager field - should replace existing trust manager with new composite trust manager that has replaced trust manager as 1st entry`() { - // given - val beforeReplace = mock() - val trustManager = TrustManagerWithMySystemManagerField(beforeReplace) - val operator = IDEATrustManager(trustManager) - // when - operator.configure( - arrayOf( - mock(), - mock() - ) - ) - // then - assertThat(trustManager.mySystemManager) - .isInstanceOf(CompositeX509ExtendedTrustManager::class.java) - val afterConfigure = (trustManager.mySystemManager as CompositeX509ExtendedTrustManager).innerTrustManagers - assertThat(afterConfigure[0]) // new instance contains 1st entry of replaced instance - .isEqualTo(beforeReplace) - } - - @Test - fun `single system manager field - should replace composite trust manager with new instance that has 1st entry of replaced composite manager`() { - // given - val toInclude = mock() - val toExclude = mock() - val compositeTrustManager = CompositeX509ExtendedTrustManager(listOf(toInclude, toExclude)) - val trustManager = TrustManagerWithMySystemManagerField(compositeTrustManager) - val manager = IDEATrustManager(trustManager) - // when - manager.configure( - arrayOf( - mock(), - mock() - ) - ) - // then - assertThat(trustManager.mySystemManager) - .isNotSameAs(compositeTrustManager) // a new instance was created - assertThat(trustManager.mySystemManager) - .isInstanceOf(CompositeX509ExtendedTrustManager::class.java) - val afterConfigure = (trustManager.mySystemManager as CompositeX509ExtendedTrustManager).innerTrustManagers - assertThat(afterConfigure[0]) // new instance contains 1st entry of replaced instance - .isEqualTo(toInclude) - } - - @Test - fun `multi system managers field - should still contain existing trust managers`() { - // given - val existing = mock() - val managers = mutableListOf(existing) - val trustManager = TrustManagerWithMySystemManagersField(managers) - val operator = IDEATrustManager(trustManager) - // when - operator.configure(emptyArray()) - // then - assertThat(trustManager.mySystemManagers) - .contains(existing) - } - - @Test - fun `multi system managers field - should add composite manager that contains new trust managers`() { - // given - val managers = mutableListOf(mock()) - val trustManager = TrustManagerWithMySystemManagersField(managers) - val operator = IDEATrustManager(trustManager) - val new = arrayOf( - mock(), - mock() - ) - // when - operator.configure(new) - // then - val composite = trustManager.mySystemManagers.find { - it is CompositeX509ExtendedTrustManager - } as CompositeX509ExtendedTrustManager - assertThat(composite.innerTrustManagers).containsExactly(*new) - } - - @Test - fun `multi system managers field - should replace existing composite manager that contains new trust managers`() { - // given - val existingTrustManager = mock() - val existingCompositeManager = CompositeX509ExtendedTrustManager(listOf(mock())) - val managers = mutableListOf(existingTrustManager, existingCompositeManager) - val trustManager = TrustManagerWithMySystemManagersField(managers) - val operator = IDEATrustManager(trustManager) - val new = arrayOf( - mock(), - mock() - ) - // when - operator.configure(new) - // then - assertThat(trustManager.mySystemManagers).doesNotContain(existingCompositeManager) - val composite = trustManager.mySystemManagers.find { - it is CompositeX509ExtendedTrustManager - } as CompositeX509ExtendedTrustManager - assertThat(composite.innerTrustManagers).containsExactly(*new) - } - - /** [com.intellij.util.net.ssl.ConfirmingTrustManager] in < IC-2022.2 */ - private class TrustManagerWithMySystemManagerField(var mySystemManager: X509TrustManager): X509TrustManager { - - override fun checkClientTrusted(chain: Array?, authType: String?) { - } - - override fun checkServerTrusted(chain: Array?, authType: String?) { - } - - override fun getAcceptedIssuers(): Array { - return emptyArray() - } - - } - - /** [com.intellij.util.net.ssl.ConfirmingTrustManager] in >= IC-2022.2 */ - private class TrustManagerWithMySystemManagersField(var mySystemManagers: MutableList = mutableListOf()): X509TrustManager { - - override fun checkClientTrusted(chain: Array?, authType: String?) { - } - - override fun checkServerTrusted(chain: Array?, authType: String?) { - } - - override fun getAcceptedIssuers(): Array { - return emptyArray() - } - - } -} \ No newline at end of file