Skip to content

Commit c9630d8

Browse files
sbouchetadietish
authored andcommitted
feat: add IDEATrustManager classes
Signed-off-by: Stephane Bouchet <[email protected]>
1 parent 59539f2 commit c9630d8

File tree

5 files changed

+360
-6
lines changed

5 files changed

+360
-6
lines changed

build.gradle

+5-4
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,13 @@ java {
5151

5252
dependencies {
5353
implementation (
54-
'io.fabric8:kubernetes-client:6.4.0',
55-
'io.fabric8:openshift-client:6.4.0',
56-
'io.fabric8:kubernetes-httpclient-okhttp:6.4.0',
54+
'io.fabric8:kubernetes-client:6.4.1',
55+
'io.fabric8:openshift-client:6.4.1',
56+
'io.fabric8:kubernetes-httpclient-okhttp:6.4.1',
5757
'org.apache.commons:commons-exec:1.3',
5858
'org.apache.commons:commons-lang3:3.12.0',
59-
'com.twelvemonkeys.common:common-lang:3.9.4'
59+
'com.twelvemonkeys.common:common-lang:3.9.4',
60+
'io.github.hakky54:sslcontext-kickstart:8.3.2'
6061
)
6162
testImplementation(
6263
'org.assertj:assertj-core:3.17.1',

gradle.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
ideaVersion = IC-2021.1
1+
ideaVersion=IC-2021.1
22
projectVersion=1.9.4-SNAPSHOT
33
nexusUser=invalid
44
nexusPassword=invalid

src/main/java/com/redhat/devtools/intellij/common/CommonConstants.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public class CommonConstants {
3030
*
3131
* @deprecated since 1.8.0, use {@link MetadataClutter#properties} instead
3232
*/
33-
@Deprecated
33+
@Deprecated (since = "1.8.0")
3434
public static final List<String> metadataClutter = Arrays.asList(
3535
"clusterName",
3636
"creationTimestamp",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2023 Red Hat, Inc.
3+
* Distributed under license by Red Hat, Inc. All rights reserved.
4+
* This program is made available under the terms of the
5+
* Eclipse Public License v2.0 which accompanies this distribution,
6+
* and is available at http://www.eclipse.org/legal/epl-v20.html
7+
*
8+
* Contributors:
9+
* Red Hat, Inc. - initial API and implementation
10+
******************************************************************************/
11+
package com.redhat.devtools.intellij.common.ssl;
12+
13+
import com.intellij.openapi.diagnostic.Logger;
14+
import com.intellij.util.net.ssl.CertificateManager;
15+
import nl.altindag.ssl.trustmanager.CompositeX509ExtendedTrustManager;
16+
import org.apache.commons.lang3.reflect.FieldUtils;
17+
18+
import javax.net.ssl.X509ExtendedTrustManager;
19+
import javax.net.ssl.X509TrustManager;
20+
import java.lang.reflect.Field;
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
import java.util.stream.Collectors;
24+
25+
public class IDEATrustManager {
26+
27+
private static final Logger LOG = Logger.getInstance(IDEATrustManager.class);
28+
29+
private final X509TrustManager trustManager;
30+
31+
32+
public IDEATrustManager(){
33+
trustManager = CertificateManager.getInstance().getTrustManager();
34+
}
35+
public IDEATrustManager(X509TrustManager trustManager) {
36+
this.trustManager = trustManager;
37+
}
38+
39+
public X509TrustManager configure(List<X509ExtendedTrustManager> toAdd) {
40+
try {
41+
if (hasSystemManagerField()) {
42+
// < IC-2022.2
43+
setCompositeManager(toAdd, trustManager);
44+
} else {
45+
// >= IC-2022.2
46+
addCompositeManager(toAdd, trustManager);
47+
}
48+
} catch (RuntimeException | IllegalAccessException e) {
49+
LOG.warn("Could not configure IDEA trust manager.", e);
50+
}
51+
return trustManager;
52+
}
53+
54+
/**
55+
* Returns `true` if [ConfirmingTrustManager] has a private field `mySystemManager`.
56+
* Returns `false` otherwise.
57+
* IDEA < IC-2022.2 manages a single [X509TrustManager] in a private field called `mySystemManager`.
58+
* IDEA >= IC-2022.2 manages a list of [X509TrustManager]s in a private list called `mySystemManagers`.
59+
*
60+
* @return true if com.intellij.util.net.ssl.ConfirmingTrustManager has a field mySystemManager. False otherwise.
61+
*/
62+
private boolean hasSystemManagerField() {
63+
return getSystemManagerField() != null;
64+
}
65+
66+
private Field getSystemManagerField() {
67+
return FieldUtils.getDeclaredField(
68+
trustManager.getClass(),
69+
"mySystemManager",
70+
true
71+
);
72+
}
73+
74+
/**
75+
* Sets a [CompositeX509ExtendedTrustManager] with the given [X509TrustManager]s
76+
* to the given destination [X509TrustManager].
77+
* If a [CompositeX509ExtendedTrustManager] already exists, his first entry is taken and set to a new
78+
* [CompositeX509ExtendedTrustManager] that replaces the existing one.
79+
*
80+
* @param trustManagers the trust managers that should be set to the destination trust manager
81+
* @param destination the destination trust manager that should receive the trust managers
82+
*/
83+
private void setCompositeManager(
84+
List<X509ExtendedTrustManager> trustManagers,
85+
X509TrustManager destination
86+
) throws IllegalAccessException {
87+
Field systemManagerField = getSystemManagerField();
88+
if (systemManagerField == null)
89+
return;
90+
Object object = systemManagerField.get(destination);
91+
if (!(object instanceof X509ExtendedTrustManager)) {
92+
return;
93+
}
94+
X509ExtendedTrustManager systemManager = (X509ExtendedTrustManager) object;
95+
X509ExtendedTrustManager compositeTrustManager = createCompositeTrustManager(systemManager, trustManagers);
96+
systemManagerField.set(destination, compositeTrustManager);
97+
}
98+
99+
private X509ExtendedTrustManager createCompositeTrustManager(
100+
X509ExtendedTrustManager systemManager,
101+
List<X509ExtendedTrustManager> clientTrustManagers
102+
) {
103+
List<X509ExtendedTrustManager> trustManagers = new ArrayList<>();
104+
if (systemManager instanceof CompositeX509ExtendedTrustManager) {
105+
// already patched CertificateManager, take 1st entry in existing system manager
106+
trustManagers.add(((CompositeX509ExtendedTrustManager) systemManager).getInnerTrustManagers().get(0));
107+
} else {
108+
// unpatched CertificateManager, take system manager
109+
trustManagers.add(systemManager);
110+
}
111+
trustManagers.addAll(clientTrustManagers);
112+
return new CompositeX509ExtendedTrustManager(trustManagers);
113+
}
114+
115+
/**
116+
* Adds a [CompositeX509ExtendedTrustManager] to the given destination [X509TrustManager].
117+
* If a [CompositeX509ExtendedTrustManager] already exists, it is replaced by a new [CompositeX509ExtendedTrustManager].
118+
*
119+
* @param trustManagers the trust managers that should be added to destination trust manager
120+
* @param destination the trust manager that should receive the given trust managers
121+
*/
122+
private void addCompositeManager(
123+
List<X509ExtendedTrustManager> trustManagers,
124+
X509TrustManager destination
125+
) throws IllegalAccessException {
126+
Field systemManagersField = FieldUtils.getDeclaredField(
127+
destination.getClass(),
128+
"mySystemManagers",
129+
true);
130+
if (systemManagersField == null) {
131+
return;
132+
}
133+
Object object = systemManagersField.get(destination);
134+
if (!(object instanceof List))
135+
return;
136+
List<X509TrustManager> managers = (List<X509TrustManager>) object;
137+
List<X509TrustManager> nonCompositeManagers = managers.stream().filter(x509TrustManager -> !(x509TrustManager instanceof CompositeX509ExtendedTrustManager)).collect(Collectors.toList());
138+
CompositeX509ExtendedTrustManager clientTrustManager = new CompositeX509ExtendedTrustManager(new ArrayList<>(trustManagers));
139+
managers.clear();
140+
managers.addAll(nonCompositeManagers);
141+
managers.add(clientTrustManager);
142+
}
143+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2023 Red Hat, Inc.
3+
* Distributed under license by Red Hat, Inc. All rights reserved.
4+
* This program is made available under the terms of the
5+
* Eclipse Public License v2.0 which accompanies this distribution,
6+
* and is available at http://www.eclipse.org/legal/epl-v20.html
7+
*
8+
* Contributors:
9+
* Red Hat, Inc. - initial API and implementation
10+
******************************************************************************/
11+
package com.redhat.devtools.intellij.common.ssl;
12+
13+
import java.security.cert.X509Certificate;
14+
import java.util.ArrayList;
15+
import java.util.Arrays;
16+
import java.util.Collections;
17+
import java.util.List;
18+
import java.util.Optional;
19+
import javax.net.ssl.X509ExtendedTrustManager;
20+
import javax.net.ssl.X509TrustManager;
21+
22+
import nl.altindag.ssl.trustmanager.CompositeX509ExtendedTrustManager;
23+
import org.junit.Test;
24+
25+
import static junit.framework.TestCase.assertTrue;
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
import static org.mockito.Mockito.mock;
28+
29+
public class IDEATrustManagerTest {
30+
31+
@Test
32+
public void single_system_manager_field_should_replace_existing_trust_manager_with_new_composite_trust_manager() {
33+
// given
34+
TrustManagerWithMySystemManagerField trustManager = new TrustManagerWithMySystemManagerField(mock(X509ExtendedTrustManager.class));
35+
IDEATrustManager operator = new IDEATrustManager(trustManager);
36+
assertThat(trustManager.mySystemManager)
37+
.isNotInstanceOf(CompositeX509ExtendedTrustManager.class);
38+
// when
39+
operator.configure(Collections.emptyList());
40+
// then
41+
assertThat(trustManager.mySystemManager)
42+
.isInstanceOf(CompositeX509ExtendedTrustManager.class);
43+
}
44+
45+
@Test
46+
public void single_system_manager_field_should_replace_existing_trust_manager_with_new_composite_trust_manager_that_contains_given_trust_managers() {
47+
// given
48+
TrustManagerWithMySystemManagerField trustManager = new TrustManagerWithMySystemManagerField(mock(X509ExtendedTrustManager.class));
49+
IDEATrustManager operator = new IDEATrustManager(trustManager);
50+
List<X509ExtendedTrustManager> newTrustManagers = Arrays.asList(
51+
mock(X509ExtendedTrustManager.class),
52+
mock(X509ExtendedTrustManager.class)
53+
);
54+
// when
55+
operator.configure(newTrustManagers);
56+
// then
57+
assertThat(trustManager.mySystemManager)
58+
.isInstanceOf(CompositeX509ExtendedTrustManager.class);
59+
List<X509ExtendedTrustManager> afterConfigure = ((CompositeX509ExtendedTrustManager)trustManager.mySystemManager).getInnerTrustManagers();
60+
assertThat(afterConfigure)
61+
.containsAll(newTrustManagers); // new instance contains list given to configure()
62+
}
63+
64+
@Test
65+
public void single_system_manager_field_should_replace_existing_trust_manager_with_new_composite_trust_manager_that_has_replaced_trust_manager_as_1st_entry() {
66+
// given
67+
X509ExtendedTrustManager beforeReplace = mock(X509ExtendedTrustManager.class);
68+
TrustManagerWithMySystemManagerField trustManager = new TrustManagerWithMySystemManagerField(beforeReplace);
69+
IDEATrustManager operator = new IDEATrustManager(trustManager);
70+
// when
71+
operator.configure(
72+
Arrays.asList(
73+
mock(X509ExtendedTrustManager.class),
74+
mock(X509ExtendedTrustManager.class)
75+
)
76+
);
77+
// then
78+
assertThat(trustManager.mySystemManager)
79+
.isInstanceOf(CompositeX509ExtendedTrustManager.class);
80+
List<X509ExtendedTrustManager> afterConfigure = ((CompositeX509ExtendedTrustManager)trustManager.mySystemManager).getInnerTrustManagers();
81+
assertThat(afterConfigure.get(0)) // new instance contains 1st entry of replaced instance
82+
.isEqualTo(beforeReplace);
83+
}
84+
85+
@Test
86+
public void single_system_manager_field_should_replace_composite_trust_manager_with_new_instance_that_has_1st_entry_of_replaced_composite_manager() {
87+
// given
88+
X509ExtendedTrustManager toInclude = mock(X509ExtendedTrustManager.class);
89+
X509ExtendedTrustManager toExclude = mock(X509ExtendedTrustManager.class);
90+
CompositeX509ExtendedTrustManager compositeTrustManager = new CompositeX509ExtendedTrustManager(Arrays.asList(toInclude, toExclude));
91+
TrustManagerWithMySystemManagerField trustManager = new TrustManagerWithMySystemManagerField(compositeTrustManager);
92+
IDEATrustManager manager = new IDEATrustManager(trustManager);
93+
// when
94+
manager.configure(
95+
Arrays.asList(
96+
mock(X509ExtendedTrustManager.class),
97+
mock(X509ExtendedTrustManager.class)
98+
)
99+
);
100+
// then
101+
assertThat(trustManager.mySystemManager)
102+
.isNotSameAs(compositeTrustManager) // a new instance was created
103+
.isInstanceOf(CompositeX509ExtendedTrustManager.class);
104+
List<X509ExtendedTrustManager> afterConfigure = ((CompositeX509ExtendedTrustManager)trustManager.mySystemManager).getInnerTrustManagers();
105+
assertThat(afterConfigure.get(0)) // new instance contains 1st entry of replaced instance
106+
.isEqualTo(toInclude);
107+
}
108+
109+
@Test
110+
public void multi_system_managers_field_should_still_contain_existing_trust_managers() {
111+
// given
112+
X509ExtendedTrustManager existing = mock(X509ExtendedTrustManager.class);
113+
List<X509TrustManager> managers = Collections.singletonList(existing);
114+
TrustManagerWithMySystemManagersField trustManager = new TrustManagerWithMySystemManagersField(managers);
115+
IDEATrustManager operator = new IDEATrustManager(trustManager);
116+
// when
117+
operator.configure(Collections.emptyList());
118+
// then
119+
assertThat(trustManager.mySystemManagers)
120+
.contains(existing);
121+
}
122+
123+
@Test
124+
public void multi_system_managers_field_should_add_composite_manager_that_contains_new_trust_managers() {
125+
// given
126+
List<X509TrustManager> managers = new ArrayList<>();
127+
managers.add(mock(X509ExtendedTrustManager.class));
128+
TrustManagerWithMySystemManagersField trustManager = new TrustManagerWithMySystemManagersField(managers);
129+
IDEATrustManager operator = new IDEATrustManager(trustManager);
130+
List<X509ExtendedTrustManager> newTrustManagers = Arrays.asList(
131+
mock(X509ExtendedTrustManager.class),
132+
mock(X509ExtendedTrustManager.class)
133+
);
134+
// when
135+
operator.configure(newTrustManagers);
136+
// then
137+
Optional<CompositeX509ExtendedTrustManager> composite = trustManager.mySystemManagers.stream().filter(CompositeX509ExtendedTrustManager.class::isInstance).map(CompositeX509ExtendedTrustManager.class::cast ).findFirst();
138+
assertTrue(composite.isPresent());
139+
assertThat(composite.get().getInnerTrustManagers()).containsAll(newTrustManagers);
140+
}
141+
142+
@Test
143+
public void multi_system_managers_field_should_replace_existing_composite_manager_that_contains_new_trust_managers() {
144+
// given
145+
X509ExtendedTrustManager existingTrustManager = mock(X509ExtendedTrustManager.class);
146+
CompositeX509ExtendedTrustManager existingCompositeManager = new CompositeX509ExtendedTrustManager(Collections.singletonList(mock(X509ExtendedTrustManager.class)));
147+
List<X509TrustManager> managers = new ArrayList<>();
148+
managers.add(existingTrustManager);
149+
managers.add(existingCompositeManager);
150+
TrustManagerWithMySystemManagersField trustManager = new TrustManagerWithMySystemManagersField(managers);
151+
IDEATrustManager operator = new IDEATrustManager(trustManager);
152+
List<X509ExtendedTrustManager> newTrustManagers = Arrays.asList(
153+
mock(X509ExtendedTrustManager.class),
154+
mock(X509ExtendedTrustManager.class)
155+
);
156+
// when
157+
operator.configure(newTrustManagers);
158+
// then
159+
assertThat(trustManager.mySystemManagers).doesNotContain(existingCompositeManager);
160+
Optional<CompositeX509ExtendedTrustManager> composite = trustManager.mySystemManagers.stream().filter(CompositeX509ExtendedTrustManager.class::isInstance).map(CompositeX509ExtendedTrustManager.class::cast ).findFirst();
161+
assertTrue(composite.isPresent());
162+
assertThat(composite.get().getInnerTrustManagers()).containsAll(newTrustManagers);
163+
}
164+
165+
/** [com.intellij.util.net.ssl.ConfirmingTrustManager] in < IC-2022.2 */
166+
private static class TrustManagerWithMySystemManagerField implements X509TrustManager {
167+
168+
X509TrustManager mySystemManager;
169+
170+
public TrustManagerWithMySystemManagerField(X509TrustManager mySystemManager) {
171+
this.mySystemManager = mySystemManager;
172+
}
173+
174+
@Override
175+
public void checkClientTrusted(X509Certificate[] chain, String authType) {
176+
}
177+
178+
@Override
179+
public void checkServerTrusted(X509Certificate[] chain, String authType) {
180+
}
181+
182+
@Override
183+
public X509Certificate[] getAcceptedIssuers() {
184+
return new X509Certificate[0];
185+
}
186+
}
187+
188+
/** [com.intellij.util.net.ssl.ConfirmingTrustManager] in >= IC-2022.2 */
189+
private static class TrustManagerWithMySystemManagersField implements X509TrustManager {
190+
191+
List<X509TrustManager> mySystemManagers;
192+
193+
public TrustManagerWithMySystemManagersField(List<X509TrustManager> mySystemManagers){
194+
this.mySystemManagers = mySystemManagers;
195+
}
196+
197+
@Override
198+
public void checkClientTrusted(X509Certificate[] chain, String authType) {
199+
}
200+
201+
@Override
202+
public void checkServerTrusted(X509Certificate[] chain, String authType) {
203+
204+
}
205+
@Override
206+
public X509Certificate[] getAcceptedIssuers() {
207+
return new X509Certificate[0];
208+
}
209+
}
210+
}

0 commit comments

Comments
 (0)