Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Move IDEATrustManager from Kubernetes to Common #206

Merged
merged 1 commit into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,13 @@ java {

dependencies {
implementation (
'io.fabric8:kubernetes-client:6.4.0',
'io.fabric8:openshift-client:6.4.0',
'io.fabric8:kubernetes-httpclient-okhttp:6.4.0',
'io.fabric8:kubernetes-client:6.4.1',
'io.fabric8:openshift-client:6.4.1',
'io.fabric8:kubernetes-httpclient-okhttp:6.4.1',
'org.apache.commons:commons-exec:1.3',
'org.apache.commons:commons-lang3:3.12.0',
'com.twelvemonkeys.common:common-lang:3.9.4'
'com.twelvemonkeys.common:common-lang:3.9.4',
'io.github.hakky54:sslcontext-kickstart:8.3.2'
)
testImplementation(
'org.assertj:assertj-core:3.17.1',
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ideaVersion = IC-2021.1
ideaVersion=IC-2021.1
projectVersion=1.9.4-SNAPSHOT
nexusUser=invalid
nexusPassword=invalid
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class CommonConstants {
*
* @deprecated since 1.8.0, use {@link MetadataClutter#properties} instead
*/
@Deprecated
@Deprecated (since = "1.8.0")
public static final List<String> metadataClutter = Arrays.asList(
"clusterName",
"creationTimestamp",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*******************************************************************************
* 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.common.ssl;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.net.ssl.CertificateManager;
import nl.altindag.ssl.trustmanager.CompositeX509ExtendedTrustManager;
import org.apache.commons.lang3.reflect.FieldUtils;

import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class IDEATrustManager {

private static final Logger LOG = Logger.getInstance(IDEATrustManager.class);

private final X509TrustManager trustManager;


public IDEATrustManager(){
trustManager = CertificateManager.getInstance().getTrustManager();
}
public IDEATrustManager(X509TrustManager trustManager) {
this.trustManager = trustManager;
}

public X509TrustManager configure(List<X509ExtendedTrustManager> toAdd) {
try {
if (hasSystemManagerField()) {
// < IC-2022.2
setCompositeManager(toAdd, trustManager);
} else {
// >= IC-2022.2
addCompositeManager(toAdd, trustManager);
}
} catch (RuntimeException | IllegalAccessException e) {
LOG.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 boolean hasSystemManagerField() {
return getSystemManagerField() != null;
}

private Field getSystemManagerField() {
return FieldUtils.getDeclaredField(
trustManager.getClass(),
"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
*/
private void setCompositeManager(
List<X509ExtendedTrustManager> trustManagers,
X509TrustManager destination
) throws IllegalAccessException {
Field systemManagerField = getSystemManagerField();
if (systemManagerField == null)
return;
Object object = systemManagerField.get(destination);
if (!(object instanceof X509ExtendedTrustManager)) {
return;
}
X509ExtendedTrustManager systemManager = (X509ExtendedTrustManager) object;
X509ExtendedTrustManager compositeTrustManager = createCompositeTrustManager(systemManager, trustManagers);
systemManagerField.set(destination, compositeTrustManager);
}

private X509ExtendedTrustManager createCompositeTrustManager(
X509ExtendedTrustManager systemManager,
List<X509ExtendedTrustManager> clientTrustManagers
) {
List<X509ExtendedTrustManager> trustManagers = new ArrayList<>();
if (systemManager instanceof CompositeX509ExtendedTrustManager) {
// already patched CertificateManager, take 1st entry in existing system manager
trustManagers.add(((CompositeX509ExtendedTrustManager) systemManager).getInnerTrustManagers().get(0));
} else {
// unpatched CertificateManager, take system manager
trustManagers.add(systemManager);
}
trustManagers.addAll(clientTrustManagers);
return new 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 void addCompositeManager(
List<X509ExtendedTrustManager> trustManagers,
X509TrustManager destination
) throws IllegalAccessException {
Field systemManagersField = FieldUtils.getDeclaredField(
destination.getClass(),
"mySystemManagers",
true);
if (systemManagersField == null) {
return;
}
Object object = systemManagersField.get(destination);
if (!(object instanceof List))
return;
List<X509TrustManager> managers = (List<X509TrustManager>) object;
List<X509TrustManager> nonCompositeManagers = managers.stream().filter(x509TrustManager -> !(x509TrustManager instanceof CompositeX509ExtendedTrustManager)).collect(Collectors.toList());
CompositeX509ExtendedTrustManager clientTrustManager = new CompositeX509ExtendedTrustManager(new ArrayList<>(trustManagers));
managers.clear();
managers.addAll(nonCompositeManagers);
managers.add(clientTrustManager);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
/*******************************************************************************
* 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.common.ssl;

import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;

import nl.altindag.ssl.trustmanager.CompositeX509ExtendedTrustManager;
import org.junit.Test;

import static junit.framework.TestCase.assertTrue;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

public class IDEATrustManagerTest {

@Test
public void single_system_manager_field_should_replace_existing_trust_manager_with_new_composite_trust_manager() {
// given
TrustManagerWithMySystemManagerField trustManager = new TrustManagerWithMySystemManagerField(mock(X509ExtendedTrustManager.class));
IDEATrustManager operator = new IDEATrustManager(trustManager);
assertThat(trustManager.mySystemManager)
.isNotInstanceOf(CompositeX509ExtendedTrustManager.class);
// when
operator.configure(Collections.emptyList());
// then
assertThat(trustManager.mySystemManager)
.isInstanceOf(CompositeX509ExtendedTrustManager.class);
}

@Test
public void single_system_manager_field_should_replace_existing_trust_manager_with_new_composite_trust_manager_that_contains_given_trust_managers() {
// given
TrustManagerWithMySystemManagerField trustManager = new TrustManagerWithMySystemManagerField(mock(X509ExtendedTrustManager.class));
IDEATrustManager operator = new IDEATrustManager(trustManager);
List<X509ExtendedTrustManager> newTrustManagers = Arrays.asList(
mock(X509ExtendedTrustManager.class),
mock(X509ExtendedTrustManager.class)
);
// when
operator.configure(newTrustManagers);
// then
assertThat(trustManager.mySystemManager)
.isInstanceOf(CompositeX509ExtendedTrustManager.class);
List<X509ExtendedTrustManager> afterConfigure = ((CompositeX509ExtendedTrustManager)trustManager.mySystemManager).getInnerTrustManagers();
assertThat(afterConfigure)
.containsAll(newTrustManagers); // new instance contains list given to configure()
}

@Test
public void single_system_manager_field_should_replace_existing_trust_manager_with_new_composite_trust_manager_that_has_replaced_trust_manager_as_1st_entry() {
// given
X509ExtendedTrustManager beforeReplace = mock(X509ExtendedTrustManager.class);
TrustManagerWithMySystemManagerField trustManager = new TrustManagerWithMySystemManagerField(beforeReplace);
IDEATrustManager operator = new IDEATrustManager(trustManager);
// when
operator.configure(
Arrays.asList(
mock(X509ExtendedTrustManager.class),
mock(X509ExtendedTrustManager.class)
)
);
// then
assertThat(trustManager.mySystemManager)
.isInstanceOf(CompositeX509ExtendedTrustManager.class);
List<X509ExtendedTrustManager> afterConfigure = ((CompositeX509ExtendedTrustManager)trustManager.mySystemManager).getInnerTrustManagers();
assertThat(afterConfigure.get(0)) // new instance contains 1st entry of replaced instance
.isEqualTo(beforeReplace);
}

@Test
public void single_system_manager_field_should_replace_composite_trust_manager_with_new_instance_that_has_1st_entry_of_replaced_composite_manager() {
// given
X509ExtendedTrustManager toInclude = mock(X509ExtendedTrustManager.class);
X509ExtendedTrustManager toExclude = mock(X509ExtendedTrustManager.class);
CompositeX509ExtendedTrustManager compositeTrustManager = new CompositeX509ExtendedTrustManager(Arrays.asList(toInclude, toExclude));
TrustManagerWithMySystemManagerField trustManager = new TrustManagerWithMySystemManagerField(compositeTrustManager);
IDEATrustManager manager = new IDEATrustManager(trustManager);
// when
manager.configure(
Arrays.asList(
mock(X509ExtendedTrustManager.class),
mock(X509ExtendedTrustManager.class)
)
);
// then
assertThat(trustManager.mySystemManager)
.isNotSameAs(compositeTrustManager) // a new instance was created
.isInstanceOf(CompositeX509ExtendedTrustManager.class);
List<X509ExtendedTrustManager> afterConfigure = ((CompositeX509ExtendedTrustManager)trustManager.mySystemManager).getInnerTrustManagers();
assertThat(afterConfigure.get(0)) // new instance contains 1st entry of replaced instance
.isEqualTo(toInclude);
}

@Test
public void multi_system_managers_field_should_still_contain_existing_trust_managers() {
// given
X509ExtendedTrustManager existing = mock(X509ExtendedTrustManager.class);
List<X509TrustManager> managers = Collections.singletonList(existing);
TrustManagerWithMySystemManagersField trustManager = new TrustManagerWithMySystemManagersField(managers);
IDEATrustManager operator = new IDEATrustManager(trustManager);
// when
operator.configure(Collections.emptyList());
// then
assertThat(trustManager.mySystemManagers)
.contains(existing);
}

@Test
public void multi_system_managers_field_should_add_composite_manager_that_contains_new_trust_managers() {
// given
List<X509TrustManager> managers = new ArrayList<>();
managers.add(mock(X509ExtendedTrustManager.class));
TrustManagerWithMySystemManagersField trustManager = new TrustManagerWithMySystemManagersField(managers);
IDEATrustManager operator = new IDEATrustManager(trustManager);
List<X509ExtendedTrustManager> newTrustManagers = Arrays.asList(
mock(X509ExtendedTrustManager.class),
mock(X509ExtendedTrustManager.class)
);
// when
operator.configure(newTrustManagers);
// then
Optional<CompositeX509ExtendedTrustManager> composite = trustManager.mySystemManagers.stream().filter(CompositeX509ExtendedTrustManager.class::isInstance).map(CompositeX509ExtendedTrustManager.class::cast ).findFirst();
assertTrue(composite.isPresent());
assertThat(composite.get().getInnerTrustManagers()).containsAll(newTrustManagers);
}

@Test
public void multi_system_managers_field_should_replace_existing_composite_manager_that_contains_new_trust_managers() {
// given
X509ExtendedTrustManager existingTrustManager = mock(X509ExtendedTrustManager.class);
CompositeX509ExtendedTrustManager existingCompositeManager = new CompositeX509ExtendedTrustManager(Collections.singletonList(mock(X509ExtendedTrustManager.class)));
List<X509TrustManager> managers = new ArrayList<>();
managers.add(existingTrustManager);
managers.add(existingCompositeManager);
TrustManagerWithMySystemManagersField trustManager = new TrustManagerWithMySystemManagersField(managers);
IDEATrustManager operator = new IDEATrustManager(trustManager);
List<X509ExtendedTrustManager> newTrustManagers = Arrays.asList(
mock(X509ExtendedTrustManager.class),
mock(X509ExtendedTrustManager.class)
);
// when
operator.configure(newTrustManagers);
// then
assertThat(trustManager.mySystemManagers).doesNotContain(existingCompositeManager);
Optional<CompositeX509ExtendedTrustManager> composite = trustManager.mySystemManagers.stream().filter(CompositeX509ExtendedTrustManager.class::isInstance).map(CompositeX509ExtendedTrustManager.class::cast ).findFirst();
assertTrue(composite.isPresent());
assertThat(composite.get().getInnerTrustManagers()).containsAll(newTrustManagers);
}

/** [com.intellij.util.net.ssl.ConfirmingTrustManager] in < IC-2022.2 */
private static class TrustManagerWithMySystemManagerField implements X509TrustManager {

X509TrustManager mySystemManager;

public TrustManagerWithMySystemManagerField(X509TrustManager mySystemManager) {
this.mySystemManager = mySystemManager;
}

@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}

@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}

@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}

/** [com.intellij.util.net.ssl.ConfirmingTrustManager] in >= IC-2022.2 */
private static class TrustManagerWithMySystemManagersField implements X509TrustManager {

List<X509TrustManager> mySystemManagers;

public TrustManagerWithMySystemManagersField(List<X509TrustManager> mySystemManagers){
this.mySystemManagers = mySystemManagers;
}

@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}

@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {

}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
}
Loading