Skip to content

Commit 08e703e

Browse files
authored
Exposed authSchemeProviderRegistry in apache http client builder (#5874)
* authSchemeProviderRegistry field added * auth scheme registry added * unit test added * default registry deleted * minor fix * added changelog
1 parent 7ffe6bc commit 08e703e

File tree

3 files changed

+187
-0
lines changed

3 files changed

+187
-0
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "feature",
3+
"category": "Apache Http Client",
4+
"contributor": "",
5+
"description": "Allow users to configure authSchemeProviderRegistry for ApacheHttpClient"
6+
}

http-clients/apache-client/src/main/java/software/amazon/awssdk/http/apache/ApacheHttpClient.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.apache.http.Header;
4141
import org.apache.http.HeaderIterator;
4242
import org.apache.http.HttpResponse;
43+
import org.apache.http.auth.AuthSchemeProvider;
4344
import org.apache.http.client.CredentialsProvider;
4445
import org.apache.http.client.methods.HttpRequestBase;
4546
import org.apache.http.client.protocol.HttpClientContext;
@@ -149,6 +150,12 @@ private ConnectionManagerAwareHttpClient createClient(ApacheHttpClient.DefaultBu
149150
// from the reaper. See https://github.com/aws/aws-sdk-java/issues/722.
150151
HttpClientConnectionManager cm = cmFactory.create(configuration, standardOptions);
151152

153+
Registry<AuthSchemeProvider> authSchemeProviderRegistry = configuration.authSchemeProviderRegistry;
154+
if (authSchemeProviderRegistry != null) {
155+
builder.setDefaultAuthSchemeRegistry(authSchemeProviderRegistry);
156+
}
157+
158+
152159
builder.setRequestExecutor(new HttpRequestExecutor())
153160
// SDK handles decompression
154161
.disableContentCompression()
@@ -449,10 +456,17 @@ public interface Builder extends SdkHttpClient.Builder<ApacheHttpClient.Builder>
449456
* when constructing the SSL context.
450457
*/
451458
Builder tlsTrustManagersProvider(TlsTrustManagersProvider tlsTrustManagersProvider);
459+
460+
/**
461+
* Configure the authentication scheme registry that can be used to obtain the corresponding authentication scheme
462+
* implementation for a given type of authorization challenge.
463+
*/
464+
Builder authSchemeProviderRegistry(Registry<AuthSchemeProvider> authSchemeProviderRegistry);
452465
}
453466

454467
private static final class DefaultBuilder implements Builder {
455468
private final AttributeMap.Builder standardOptions = AttributeMap.builder();
469+
private Registry<AuthSchemeProvider> authSchemeProviderRegistry;
456470
private ProxyConfiguration proxyConfiguration = ProxyConfiguration.builder().build();
457471
private InetAddress localAddress;
458472
private Boolean expectContinueEnabled;
@@ -640,6 +654,16 @@ public void setTlsTrustManagersProvider(TlsTrustManagersProvider tlsTrustManager
640654
tlsTrustManagersProvider(tlsTrustManagersProvider);
641655
}
642656

657+
@Override
658+
public Builder authSchemeProviderRegistry(Registry<AuthSchemeProvider> authSchemeProviderRegistry) {
659+
this.authSchemeProviderRegistry = authSchemeProviderRegistry;
660+
return this;
661+
}
662+
663+
public void setAuthSchemeProviderRegistry(Registry<AuthSchemeProvider> authSchemeProviderRegistry) {
664+
authSchemeProviderRegistry(authSchemeProviderRegistry);
665+
}
666+
643667
@Override
644668
public SdkHttpClient buildWithDefaults(AttributeMap serviceDefaults) {
645669
AttributeMap resolvedOptions = standardOptions.build().merge(serviceDefaults).merge(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.http.apache;
17+
18+
19+
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
20+
21+
import java.net.URI;
22+
import org.apache.http.auth.AuthSchemeProvider;
23+
import org.apache.http.auth.AuthScope;
24+
import org.apache.http.auth.UsernamePasswordCredentials;
25+
import org.apache.http.client.CredentialsProvider;
26+
import org.apache.http.client.config.AuthSchemes;
27+
import org.apache.http.config.Registry;
28+
import org.apache.http.config.RegistryBuilder;
29+
import org.apache.http.impl.auth.BasicSchemeFactory;
30+
import org.apache.http.impl.auth.KerberosSchemeFactory;
31+
import org.apache.http.impl.client.BasicCredentialsProvider;
32+
33+
import org.junit.jupiter.api.Test;
34+
import org.junit.jupiter.api.extension.RegisterExtension;
35+
import software.amazon.awssdk.http.HttpExecuteRequest;
36+
import software.amazon.awssdk.http.SdkHttpMethod;
37+
import software.amazon.awssdk.http.SdkHttpRequest;
38+
import software.amazon.awssdk.http.ExecutableHttpRequest;
39+
import software.amazon.awssdk.http.HttpExecuteResponse;
40+
41+
import static com.github.tomakehurst.wiremock.client.WireMock.*;
42+
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
43+
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
44+
45+
46+
class ApacheHttpClientAuthRegistryTest {
47+
48+
@RegisterExtension
49+
static WireMockExtension proxyWireMock = WireMockExtension.newInstance()
50+
.options(wireMockConfig().dynamicPort())
51+
.build();
52+
@RegisterExtension
53+
static WireMockExtension serverWireMock = WireMockExtension.newInstance()
54+
.options(wireMockConfig().dynamicPort())
55+
.build();
56+
57+
private ApacheHttpClient httpClient;
58+
private static final String PROXY_AUTH_SCENARIO = "Proxy Auth";
59+
private static final String SERVER_AUTH_SCENARIO = "Server Auth";
60+
private static final String CHALLENGED_STATE = "Challenged";
61+
62+
63+
private Registry<AuthSchemeProvider> createAuthSchemeRegistry(String scheme, AuthSchemeProvider provider) {
64+
return RegistryBuilder.<AuthSchemeProvider>create()
65+
.register(scheme, provider)
66+
.build();
67+
}
68+
69+
private ApacheHttpClient createHttpClient(Registry<AuthSchemeProvider> authSchemeRegistry) {
70+
CredentialsProvider credsProvider = new BasicCredentialsProvider();
71+
credsProvider.setCredentials(
72+
new AuthScope("localhost", AuthScope.ANY_PORT),
73+
new UsernamePasswordCredentials("u1", "p1"));
74+
75+
return (ApacheHttpClient) ApacheHttpClient.builder()
76+
.proxyConfiguration(ProxyConfiguration.builder().endpoint(URI.create("http://localhost:" + proxyWireMock.getPort()))
77+
.build())
78+
.authSchemeProviderRegistry(authSchemeRegistry)
79+
.credentialsProvider(credsProvider)
80+
.build();
81+
}
82+
83+
private SdkHttpRequest createHttpRequest() {
84+
return SdkHttpRequest.builder()
85+
.uri(URI.create("http://localhost:" + serverWireMock.getPort()))
86+
.method(SdkHttpMethod.GET)
87+
.build();
88+
}
89+
private void setupProxyWireMockStub() {
90+
proxyWireMock.stubFor(get(urlMatching(".*"))
91+
.inScenario(PROXY_AUTH_SCENARIO)
92+
.whenScenarioStateIs(STARTED)
93+
.willReturn(aResponse()
94+
.withStatus(401)
95+
.withHeader("WWW-Authenticate", "Basic"))
96+
.willSetStateTo(CHALLENGED_STATE));
97+
98+
proxyWireMock.stubFor(get(urlMatching(".*"))
99+
.inScenario(PROXY_AUTH_SCENARIO)
100+
.whenScenarioStateIs(CHALLENGED_STATE)
101+
//.withHeader("WWW-Authenticate", matching(".*"))
102+
.willReturn(aResponse()
103+
.withStatus(200))
104+
.willSetStateTo("success"));
105+
}
106+
107+
private void setupWireMockStub() {
108+
serverWireMock.stubFor(get(urlMatching(".*"))
109+
.inScenario(SERVER_AUTH_SCENARIO)
110+
.whenScenarioStateIs(STARTED)
111+
.withHeader("Authorization", matching(".*"))
112+
.willReturn(aResponse().withStatus(200)));
113+
}
114+
115+
private HttpExecuteResponse executeRequest(SdkHttpRequest request) throws Exception {
116+
HttpExecuteRequest executeRequest = HttpExecuteRequest.builder()
117+
.request(request)
118+
.build();
119+
ExecutableHttpRequest executableRequest = httpClient.prepareRequest(executeRequest);
120+
return executableRequest.call();
121+
}
122+
123+
@Test
124+
void authSchemeRegistryConfigured_registeredAuthShouldPass() throws Exception {
125+
Registry<AuthSchemeProvider> authSchemeRegistry = createAuthSchemeRegistry(
126+
AuthSchemes.BASIC,
127+
new BasicSchemeFactory()
128+
);
129+
130+
httpClient = createHttpClient(authSchemeRegistry);
131+
setupProxyWireMockStub();
132+
setupWireMockStub();
133+
134+
HttpExecuteResponse response = executeRequest(createHttpRequest());
135+
136+
proxyWireMock.verify(1, getRequestedFor(urlMatching(".*"))
137+
.withHeader("Authorization", matching(".*"))
138+
);
139+
}
140+
141+
@Test
142+
void authSchemeRegistryConfigured_unRegisteredAuthShouldWarn() throws Exception {
143+
Registry<AuthSchemeProvider> authSchemeRegistry = createAuthSchemeRegistry(
144+
AuthSchemes.KERBEROS,
145+
new KerberosSchemeFactory()
146+
);
147+
148+
httpClient = createHttpClient(authSchemeRegistry);
149+
setupProxyWireMockStub();
150+
setupWireMockStub();
151+
152+
HttpExecuteResponse response = executeRequest(createHttpRequest());
153+
proxyWireMock.verify(0, getRequestedFor(urlMatching(".*"))
154+
.withHeader("Authorization", matching(".*"))
155+
);
156+
}
157+
}

0 commit comments

Comments
 (0)