Skip to content

Commit 80b8f35

Browse files
authored
Merge pull request #575 from splitio/FME-4285-proxy-auth-factory
Update factory
2 parents 74a4533 + b42fddc commit 80b8f35

File tree

3 files changed

+191
-5
lines changed

3 files changed

+191
-5
lines changed

client/src/main/java/io/split/client/SplitFactoryImpl.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
import io.split.telemetry.synchronizer.TelemetrySynchronizer;
9393

9494
import org.apache.hc.client5.http.auth.AuthScope;
95+
import org.apache.hc.client5.http.auth.BearerToken;
9596
import org.apache.hc.client5.http.auth.Credentials;
9697
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
9798
import org.apache.hc.client5.http.config.RequestConfig;
@@ -113,11 +114,14 @@
113114
import org.slf4j.LoggerFactory;
114115
import pluggable.CustomStorageWrapper;
115116

117+
import javax.net.ssl.SSLContext;
116118
import java.io.IOException;
117119
import java.io.InputStream;
118120
import java.net.InetAddress;
119121
import java.net.URI;
120122
import java.net.URISyntaxException;
123+
import java.nio.file.Paths;
124+
import java.security.KeyStore;
121125
import java.util.concurrent.ExecutorService;
122126
import java.util.stream.Collectors;
123127
import java.util.HashSet;
@@ -518,8 +522,28 @@ public boolean isDestroyed() {
518522
protected static SplitHttpClient buildSplitHttpClient(String apiToken, SplitClientConfig config,
519523
SDKMetadata sdkMetadata, RequestDecorator requestDecorator)
520524
throws URISyntaxException {
525+
526+
SSLContext sslContext;
527+
if (config.proxyMTLSAuth() != null) {
528+
_log.debug("Proxy setup using mTLS");
529+
try {
530+
KeyStore keyStore = KeyStore.getInstance("PKCS12");
531+
InputStream keystoreStream = java.nio.file.Files.newInputStream(Paths.get(config.proxyMTLSAuth().getP12File()));
532+
keyStore.load(keystoreStream, config.proxyMTLSAuth().getP12FilePassKey().toCharArray());
533+
sslContext = SSLContexts.custom()
534+
.loadKeyMaterial(keyStore, config.proxyMTLSAuth().getP12FilePassKey().toCharArray())
535+
.build();
536+
} catch (Exception e) {
537+
_log.error("Exception caught while processing p12 file for Proxy mTLS auth: ", e);
538+
_log.warn("Ignoring p12 mTLS config and switching to default context");
539+
sslContext = SSLContexts.createSystemDefault();
540+
}
541+
} else {
542+
sslContext = SSLContexts.createSystemDefault();
543+
}
544+
521545
SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder.create()
522-
.setSslContext(SSLContexts.createSystemDefault())
546+
.setSslContext(sslContext)
523547
.setTlsVersions(TLS.V_1_1, TLS.V_1_2)
524548
.build();
525549

@@ -604,6 +628,15 @@ private static HttpClientBuilder setupProxy(HttpClientBuilder httpClientbuilder,
604628
httpClientbuilder.setDefaultCredentialsProvider(credsProvider);
605629
}
606630

631+
if (config.proxyToken() != null) {
632+
_log.debug("Proxy setup using token");
633+
BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
634+
AuthScope siteScope = new AuthScope(config.proxy().getHostName(), config.proxy().getPort());
635+
Credentials siteCreds = new BearerToken(config.proxyToken());
636+
credsProvider.setCredentials(siteScope, siteCreds);
637+
httpClientbuilder.setDefaultCredentialsProvider(credsProvider);
638+
}
639+
607640
return httpClientbuilder;
608641
}
609642

client/src/test/java/io/split/client/SplitFactoryImplTest.java

Lines changed: 157 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
11
package io.split.client;
22

3+
import io.split.client.dtos.ProxyMTLSAuth;
34
import io.split.client.impressions.ImpressionsManager;
45
import io.split.client.utils.FileTypeEnum;
56
import io.split.integrations.IntegrationsConfig;
7+
import io.split.service.SplitHttpClientImpl;
68
import io.split.storages.enums.OperationMode;
79
import io.split.storages.pluggable.domain.UserStorageWrapper;
810
import io.split.telemetry.storage.TelemetryStorage;
911
import io.split.telemetry.synchronizer.TelemetrySynchronizer;
1012
import junit.framework.TestCase;
13+
import org.apache.hc.client5.http.auth.AuthScope;
14+
import org.apache.hc.client5.http.auth.BearerToken;
15+
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
16+
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
17+
import org.apache.hc.client5.http.impl.io.DefaultHttpClientConnectionOperator;
18+
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
19+
import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
20+
import org.apache.hc.core5.http.HttpHost;
21+
import org.apache.hc.core5.http.config.Registry;
1122
import org.awaitility.Awaitility;
1223
import org.junit.Assert;
13-
import org.junit.Ignore;
1424
import org.junit.Test;
1525
import org.mockito.Mockito;
1626
import static org.mockito.Mockito.when;
@@ -24,6 +34,8 @@
2434
import java.lang.reflect.Method;
2535
import java.lang.reflect.Modifier;
2636
import java.net.URISyntaxException;
37+
import java.util.HashMap;
38+
import java.util.concurrent.ConcurrentHashMap;
2739
import java.util.concurrent.TimeUnit;
2840

2941

@@ -87,12 +99,12 @@ public void testFactoryInstantiationIntegrationsConfig() throws Exception {
8799
}
88100

89101
@Test
90-
public void testFactoryInstantiationWithProxy() throws Exception {
102+
public void testFactoryInstantiationWithProxyCredentials() throws Exception {
91103
SplitClientConfig splitClientConfig = SplitClientConfig.builder()
92104
.enableDebug()
93105
.impressionsMode(ImpressionsManager.Mode.DEBUG)
94106
.impressionsRefreshRate(1)
95-
.endpoint(ENDPOINT,EVENTS_ENDPOINT)
107+
.endpoint(ENDPOINT, EVENTS_ENDPOINT)
96108
.telemetryURL(SplitClientConfig.TELEMETRY_ENDPOINT)
97109
.authServiceURL(AUTH_SERVICE)
98110
.setBlockUntilReadyTimeout(1000)
@@ -102,9 +114,150 @@ public void testFactoryInstantiationWithProxy() throws Exception {
102114
.proxyHost(ENDPOINT)
103115
.build();
104116
SplitFactoryImpl splitFactory = new SplitFactoryImpl(API_KEY, splitClientConfig);
105-
106117
assertNotNull(splitFactory.client());
107118
assertNotNull(splitFactory.manager());
119+
120+
Field splitHttpClientField = SplitFactoryImpl.class.getDeclaredField("_splitHttpClient");
121+
splitHttpClientField.setAccessible(true);
122+
SplitHttpClientImpl client = (SplitHttpClientImpl) splitHttpClientField.get(splitFactory);
123+
124+
Field httpClientField = SplitHttpClientImpl.class.getDeclaredField("_client");
125+
httpClientField.setAccessible(true);
126+
Class<?> InternalHttp = Class.forName("org.apache.hc.client5.http.impl.classic.InternalHttpClient");
127+
128+
Field routePlannerField = InternalHttp.getDeclaredField("routePlanner");
129+
routePlannerField.setAccessible(true);
130+
DefaultProxyRoutePlanner routePlanner = (DefaultProxyRoutePlanner) routePlannerField.get(InternalHttp.cast(httpClientField.get(client)));
131+
132+
Field proxyField = DefaultProxyRoutePlanner.class.getDeclaredField("proxy");
133+
proxyField.setAccessible(true);
134+
HttpHost proxy = (HttpHost) proxyField.get(routePlanner);
135+
136+
Assert.assertEquals("http", proxy.getSchemeName());
137+
Assert.assertEquals(ENDPOINT, proxy.getHostName());
138+
Assert.assertEquals(6060, proxy.getPort());
139+
140+
Field credentialsProviderField = InternalHttp.getDeclaredField("credentialsProvider");
141+
credentialsProviderField.setAccessible(true);
142+
BasicCredentialsProvider credentialsProvider = (BasicCredentialsProvider) credentialsProviderField.get(InternalHttp.cast(httpClientField.get(client)));
143+
144+
Field credMapField = BasicCredentialsProvider.class.getDeclaredField("credMap");
145+
credMapField.setAccessible(true);
146+
ConcurrentHashMap<AuthScope, UsernamePasswordCredentials> credMap = (ConcurrentHashMap) credMapField.get(credentialsProvider);
147+
148+
Assert.assertEquals("test", credMap.entrySet().stream().iterator().next().getValue().getUserName());
149+
assertNotNull(credMap.entrySet().stream().iterator().next().getValue().getUserPassword());
150+
151+
splitFactory.destroy();
152+
}
153+
154+
@Test
155+
public void testFactoryInstantiationWithProxyToken() throws Exception {
156+
SplitClientConfig splitClientConfig = SplitClientConfig.builder()
157+
.enableDebug()
158+
.impressionsMode(ImpressionsManager.Mode.DEBUG)
159+
.impressionsRefreshRate(1)
160+
.endpoint(ENDPOINT, EVENTS_ENDPOINT)
161+
.telemetryURL(SplitClientConfig.TELEMETRY_ENDPOINT)
162+
.authServiceURL(AUTH_SERVICE)
163+
.setBlockUntilReadyTimeout(1000)
164+
.proxyPort(6060)
165+
.proxyToken("123456789")
166+
.proxyHost(ENDPOINT)
167+
.build();
168+
SplitFactoryImpl splitFactory2 = new SplitFactoryImpl(API_KEY, splitClientConfig);
169+
assertNotNull(splitFactory2.client());
170+
assertNotNull(splitFactory2.manager());
171+
172+
Field splitHttpClientField2 = SplitFactoryImpl.class.getDeclaredField("_splitHttpClient");
173+
splitHttpClientField2.setAccessible(true);
174+
SplitHttpClientImpl client2 = (SplitHttpClientImpl) splitHttpClientField2.get(splitFactory2);
175+
176+
Field httpClientField2 = SplitHttpClientImpl.class.getDeclaredField("_client");
177+
httpClientField2.setAccessible(true);
178+
Class<?> InternalHttp2 = Class.forName("org.apache.hc.client5.http.impl.classic.InternalHttpClient");
179+
180+
Field credentialsProviderField2 = InternalHttp2.getDeclaredField("credentialsProvider");
181+
credentialsProviderField2.setAccessible(true);
182+
BasicCredentialsProvider credentialsProvider2 = (BasicCredentialsProvider) credentialsProviderField2.get(InternalHttp2.cast(httpClientField2.get(client2)));
183+
184+
Field credMapField2 = BasicCredentialsProvider.class.getDeclaredField("credMap");
185+
credMapField2.setAccessible(true);
186+
ConcurrentHashMap<AuthScope, BearerToken> credMap2 = (ConcurrentHashMap) credMapField2.get(credentialsProvider2);
187+
188+
Assert.assertEquals("123456789", credMap2.entrySet().stream().iterator().next().getValue().getToken());
189+
190+
splitFactory2.destroy();
191+
}
192+
193+
@Test
194+
public void testFactoryInstantiationWithProxyMtls() throws Exception {
195+
SplitClientConfig splitClientConfig = SplitClientConfig.builder()
196+
.enableDebug()
197+
.impressionsMode(ImpressionsManager.Mode.DEBUG)
198+
.impressionsRefreshRate(1)
199+
.endpoint(ENDPOINT,EVENTS_ENDPOINT)
200+
.telemetryURL(SplitClientConfig.TELEMETRY_ENDPOINT)
201+
.authServiceURL(AUTH_SERVICE)
202+
.setBlockUntilReadyTimeout(1000)
203+
.proxyPort(6060)
204+
.proxyScheme("https")
205+
.proxyMtlsAuth(new ProxyMTLSAuth.Builder().proxyP12File("src/test/resources/keyStore.p12").proxyP12FilePassKey("split").build())
206+
.proxyHost(ENDPOINT)
207+
.build();
208+
SplitFactoryImpl splitFactory3 = new SplitFactoryImpl(API_KEY, splitClientConfig);
209+
assertNotNull(splitFactory3.client());
210+
assertNotNull(splitFactory3.manager());
211+
212+
Field splitHttpClientField3 = SplitFactoryImpl.class.getDeclaredField("_splitHttpClient");
213+
splitHttpClientField3.setAccessible(true);
214+
SplitHttpClientImpl client3 = (SplitHttpClientImpl) splitHttpClientField3.get(splitFactory3);
215+
216+
Field httpClientField3 = SplitHttpClientImpl.class.getDeclaredField("_client");
217+
httpClientField3.setAccessible(true);
218+
Class<?> InternalHttp3 = Class.forName("org.apache.hc.client5.http.impl.classic.InternalHttpClient");
219+
220+
Field connManagerField = InternalHttp3.getDeclaredField("connManager");
221+
connManagerField.setAccessible(true);
222+
PoolingHttpClientConnectionManager connManager = (PoolingHttpClientConnectionManager) connManagerField.get(InternalHttp3.cast(httpClientField3.get(client3)));
223+
224+
Field connectionOperatorField = PoolingHttpClientConnectionManager.class.getDeclaredField("connectionOperator");
225+
connectionOperatorField.setAccessible(true);
226+
DefaultHttpClientConnectionOperator connectionOperator = (DefaultHttpClientConnectionOperator) connectionOperatorField.get(connManager);
227+
228+
Field tlsSocketStrategyLookupField = DefaultHttpClientConnectionOperator.class.getDeclaredField("tlsSocketStrategyLookup");
229+
tlsSocketStrategyLookupField.setAccessible(true);
230+
Registry tlsSocketStrategyLookup = (Registry) tlsSocketStrategyLookupField.get(connectionOperator);
231+
232+
Field mapField = Registry.class.getDeclaredField("map");
233+
mapField.setAccessible(true);
234+
Class<?> map = mapField.get(tlsSocketStrategyLookup).getClass();
235+
236+
Class<?> value = ((ConcurrentHashMap) map.cast(mapField.get(tlsSocketStrategyLookup))).get("https").getClass();
237+
238+
Field arg1Field = value.getDeclaredField("arg$1");
239+
arg1Field.setAccessible(true);
240+
Class<?> sslConnectionSocketFactory = arg1Field.get(((ConcurrentHashMap) map.cast(mapField.get(tlsSocketStrategyLookup))).get("https")).getClass();
241+
242+
Field socketFactoryField = sslConnectionSocketFactory.getDeclaredField("socketFactory");
243+
socketFactoryField.setAccessible(true);
244+
Class<?> socketFactory = socketFactoryField.get(arg1Field.get(((ConcurrentHashMap) map.cast(mapField.get(tlsSocketStrategyLookup))).get("https"))).getClass();
245+
246+
Field contextField = socketFactory.getDeclaredField("context");
247+
contextField.setAccessible(true);
248+
Class<?> context = Class.forName("sun.security.ssl.SSLContextImpl");
249+
250+
Field keyManagerField = context.getDeclaredField("keyManager");
251+
keyManagerField.setAccessible(true);
252+
Class<?> keyManager = keyManagerField.get(contextField.get(socketFactoryField.get(arg1Field.get(((ConcurrentHashMap) map.cast(mapField.get(tlsSocketStrategyLookup))).get("https"))))).getClass();
253+
254+
Field credentialsMapField = keyManager.getDeclaredField("credentialsMap");
255+
credentialsMapField.setAccessible(true);
256+
HashMap<String,Object> credentialsMap = (HashMap) credentialsMapField.get(keyManagerField.get(contextField.get(socketFactoryField.get(arg1Field.get(((ConcurrentHashMap) map.cast(mapField.get(tlsSocketStrategyLookup))).get("https"))))));
257+
258+
assertNotNull(credentialsMap.get("1"));
259+
260+
splitFactory3.destroy();
108261
}
109262

110263
@Test
2.94 KB
Binary file not shown.

0 commit comments

Comments
 (0)