1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.hc.client5.testing.sync;
28
29 import static org.hamcrest.MatcherAssert.assertThat;
30
31 import java.io.ByteArrayInputStream;
32 import java.io.IOException;
33 import java.nio.charset.StandardCharsets;
34 import java.security.SecureRandom;
35 import java.util.Arrays;
36 import java.util.Collections;
37 import java.util.Queue;
38 import java.util.concurrent.ConcurrentLinkedQueue;
39 import java.util.concurrent.atomic.AtomicLong;
40 import java.util.function.Consumer;
41 import java.util.stream.Collectors;
42
43 import org.apache.hc.client5.http.ClientProtocolException;
44 import org.apache.hc.client5.http.auth.AuthCache;
45 import org.apache.hc.client5.http.auth.AuthScheme;
46 import org.apache.hc.client5.http.auth.AuthSchemeFactory;
47 import org.apache.hc.client5.http.auth.AuthScope;
48 import org.apache.hc.client5.http.auth.BearerToken;
49 import org.apache.hc.client5.http.auth.CredentialsProvider;
50 import org.apache.hc.client5.http.auth.StandardAuthScheme;
51 import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
52 import org.apache.hc.client5.http.classic.methods.HttpGet;
53 import org.apache.hc.client5.http.classic.methods.HttpPost;
54 import org.apache.hc.client5.http.classic.methods.HttpPut;
55 import org.apache.hc.client5.http.config.RequestConfig;
56 import org.apache.hc.client5.http.impl.DefaultAuthenticationStrategy;
57 import org.apache.hc.client5.http.impl.auth.BasicAuthCache;
58 import org.apache.hc.client5.http.impl.auth.BasicScheme;
59 import org.apache.hc.client5.http.impl.auth.BasicSchemeFactory;
60 import org.apache.hc.client5.http.impl.auth.CredentialsProviderBuilder;
61 import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
62 import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
63 import org.apache.hc.client5.http.protocol.HttpClientContext;
64 import org.apache.hc.client5.testing.BasicTestAuthenticator;
65 import org.apache.hc.client5.testing.auth.Authenticator;
66 import org.apache.hc.client5.testing.auth.BearerAuthenticationHandler;
67 import org.apache.hc.client5.testing.classic.AuthenticatingDecorator;
68 import org.apache.hc.client5.testing.classic.EchoHandler;
69 import org.apache.hc.client5.testing.sync.extension.TestClientResources;
70 import org.apache.hc.core5.http.ClassicHttpRequest;
71 import org.apache.hc.core5.http.ClassicHttpResponse;
72 import org.apache.hc.core5.http.HeaderElements;
73 import org.apache.hc.core5.http.HttpEntity;
74 import org.apache.hc.core5.http.HttpException;
75 import org.apache.hc.core5.http.HttpHeaders;
76 import org.apache.hc.core5.http.HttpHost;
77 import org.apache.hc.core5.http.HttpResponse;
78 import org.apache.hc.core5.http.HttpStatus;
79 import org.apache.hc.core5.http.URIScheme;
80 import org.apache.hc.core5.http.config.Http1Config;
81 import org.apache.hc.core5.http.config.Registry;
82 import org.apache.hc.core5.http.config.RegistryBuilder;
83 import org.apache.hc.core5.http.impl.HttpProcessors;
84 import org.apache.hc.core5.http.io.HttpRequestHandler;
85 import org.apache.hc.core5.http.io.entity.EntityUtils;
86 import org.apache.hc.core5.http.io.entity.InputStreamEntity;
87 import org.apache.hc.core5.http.io.entity.StringEntity;
88 import org.apache.hc.core5.http.protocol.HttpContext;
89 import org.apache.hc.core5.http.support.BasicResponseBuilder;
90 import org.apache.hc.core5.net.URIAuthority;
91 import org.apache.hc.core5.testing.classic.ClassicTestServer;
92 import org.apache.hc.core5.util.Timeout;
93 import org.hamcrest.CoreMatchers;
94 import org.junit.jupiter.api.Assertions;
95 import org.junit.jupiter.api.Test;
96 import org.junit.jupiter.api.extension.RegisterExtension;
97 import org.mockito.Mockito;
98
99
100
101
102 public abstract class TestClientAuthentication {
103
104 public static final Timeout TIMEOUT = Timeout.ofMinutes(1);
105
106 @RegisterExtension
107 private TestClientResources testResources;
108
109 protected TestClientAuthentication(final URIScheme scheme) {
110 this.testResources = new TestClientResources(scheme, TIMEOUT);
111 }
112
113 public URIScheme scheme() {
114 return testResources.scheme();
115 }
116
117 public ClassicTestServer startServer(final Authenticator authenticator) throws IOException {
118 return testResources.startServer(
119 null,
120 null,
121 requestHandler -> new AuthenticatingDecorator(requestHandler, authenticator));
122 }
123
124 public ClassicTestServer startServer() throws IOException {
125 return startServer(new BasicTestAuthenticator("test:test", "test realm"));
126 }
127
128 public CloseableHttpClient startClient(final Consumer<HttpClientBuilder> clientCustomizer) throws Exception {
129 return testResources.startClient(clientCustomizer);
130 }
131
132 public CloseableHttpClient startClient() throws Exception {
133 return testResources.startClient(builder -> {});
134 }
135
136 public HttpHost targetHost() {
137 return testResources.targetHost();
138 }
139
140 @Test
141 public void testBasicAuthenticationNoCreds() throws Exception {
142 final ClassicTestServer server = startServer();
143 server.registerHandler("*", new EchoHandler());
144 final HttpHost target = targetHost();
145
146 final CloseableHttpClient client = startClient();
147
148 final HttpClientContext context = HttpClientContext.create();
149 final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
150 context.setCredentialsProvider(credsProvider);
151 final HttpGet httpget = new HttpGet("/");
152
153 client.execute(target, httpget, context, response -> {
154 final HttpEntity entity = response.getEntity();
155 Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
156 Assertions.assertNotNull(entity);
157 EntityUtils.consume(entity);
158 return null;
159 });
160 Mockito.verify(credsProvider).getCredentials(
161 Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any());
162 }
163
164 @Test
165 public void testBasicAuthenticationFailure() throws Exception {
166 final ClassicTestServer server = startServer();
167 server.registerHandler("*", new EchoHandler());
168 final HttpHost target = targetHost();
169
170 final CloseableHttpClient client = startClient();
171
172 final HttpClientContext context = HttpClientContext.create();
173 final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
174 Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any()))
175 .thenReturn(new UsernamePasswordCredentials("test", "all-wrong".toCharArray()));
176 context.setCredentialsProvider(credsProvider);
177 final HttpGet httpget = new HttpGet("/");
178
179 client.execute(target, httpget, context, response -> {
180 final HttpEntity entity = response.getEntity();
181 Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
182 Assertions.assertNotNull(entity);
183 EntityUtils.consume(entity);
184 return null;
185 });
186 Mockito.verify(credsProvider).getCredentials(
187 Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any());
188 }
189
190 @Test
191 public void testBasicAuthenticationSuccess() throws Exception {
192 final ClassicTestServer server = startServer();
193 server.registerHandler("*", new EchoHandler());
194 final HttpHost target = targetHost();
195
196 final CloseableHttpClient client = startClient();
197 final HttpGet httpget = new HttpGet("/");
198 final HttpClientContext context = HttpClientContext.create();
199 final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
200 Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any()))
201 .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray()));
202 context.setCredentialsProvider(credsProvider);
203
204 client.execute(target, httpget, context, response -> {
205 final HttpEntity entity = response.getEntity();
206 Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
207 Assertions.assertNotNull(entity);
208 EntityUtils.consume(entity);
209 return null;
210 });
211 Mockito.verify(credsProvider).getCredentials(
212 Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any());
213 }
214
215 @Test
216 public void testBasicAuthenticationSuccessOnNonRepeatablePutExpectContinue() throws Exception {
217 final ClassicTestServer server = startServer();
218 server.registerHandler("*", new EchoHandler());
219 final HttpHost target = targetHost();
220
221 final CloseableHttpClient client = startClient();
222
223 final RequestConfig config = RequestConfig.custom()
224 .setExpectContinueEnabled(true)
225 .build();
226 final HttpPut httpput = new HttpPut("/");
227 httpput.setConfig(config);
228 httpput.setEntity(new InputStreamEntity(
229 new ByteArrayInputStream(
230 new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 } ),
231 -1, null));
232 final HttpClientContext context = HttpClientContext.create();
233 final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
234 Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any()))
235 .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray()));
236 context.setCredentialsProvider(credsProvider);
237
238 client.execute(target, httpput, context, response -> {
239 final HttpEntity entity = response.getEntity();
240 Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
241 Assertions.assertNotNull(entity);
242 return null;
243 });
244 }
245
246 @Test
247 public void testBasicAuthenticationFailureOnNonRepeatablePutDontExpectContinue() throws Exception {
248 final ClassicTestServer server = startServer();
249 server.registerHandler("*", new EchoHandler());
250 final HttpHost target = targetHost();
251
252 final CloseableHttpClient client = startClient();
253
254 final RequestConfig config = RequestConfig.custom().setExpectContinueEnabled(false).build();
255 final HttpPut httpput = new HttpPut("/");
256 httpput.setConfig(config);
257 httpput.setEntity(new InputStreamEntity(
258 new ByteArrayInputStream(
259 new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 } ),
260 -1, null));
261
262 final HttpClientContext context = HttpClientContext.create();
263 final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
264 Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any()))
265 .thenReturn(new UsernamePasswordCredentials("test", "boom".toCharArray()));
266 context.setCredentialsProvider(credsProvider);
267
268 client.execute(target, httpput, context, response -> {
269 final HttpEntity entity = response.getEntity();
270 Assertions.assertEquals(401, response.getCode());
271 Assertions.assertNotNull(entity);
272 EntityUtils.consume(entity);
273 return null;
274 });
275 }
276
277 @Test
278 public void testBasicAuthenticationSuccessOnRepeatablePost() throws Exception {
279 final ClassicTestServer server = startServer();
280 server.registerHandler("*", new EchoHandler());
281 final HttpHost target = targetHost();
282
283 final CloseableHttpClient client = startClient();
284
285 final HttpPost httppost = new HttpPost("/");
286 httppost.setEntity(new StringEntity("some important stuff", StandardCharsets.US_ASCII));
287
288 final HttpClientContext context = HttpClientContext.create();
289 final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
290 Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any()))
291 .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray()));
292 context.setCredentialsProvider(credsProvider);
293
294 client.execute(target, httppost, context, response -> {
295 final HttpEntity entity = response.getEntity();
296 Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
297 Assertions.assertNotNull(entity);
298 EntityUtils.consume(entity);
299 return null;
300 });
301 Mockito.verify(credsProvider).getCredentials(
302 Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any());
303 }
304
305 @Test
306 public void testBasicAuthenticationFailureOnNonRepeatablePost() throws Exception {
307 final ClassicTestServer server = startServer();
308 server.registerHandler("*", new EchoHandler());
309 final HttpHost target = targetHost();
310
311 final CloseableHttpClient client = startClient();
312
313 final HttpPost httppost = new HttpPost("/");
314 httppost.setEntity(new InputStreamEntity(
315 new ByteArrayInputStream(
316 new byte[] { 0,1,2,3,4,5,6,7,8,9 }), -1, null));
317
318 final HttpClientContext context = HttpClientContext.create();
319 context.setRequestConfig(RequestConfig.custom()
320 .setExpectContinueEnabled(false)
321 .build());
322 final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
323 Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any()))
324 .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray()));
325 context.setCredentialsProvider(credsProvider);
326
327 client.execute(target, httppost, context, response -> {
328 final HttpEntity entity = response.getEntity();
329 Assertions.assertEquals(401, response.getCode());
330 Assertions.assertNotNull(entity);
331 EntityUtils.consume(entity);
332 return null;
333 });
334 }
335
336 @Test
337 public void testBasicAuthenticationCredentialsCaching() throws Exception {
338 final ClassicTestServer server = startServer();
339 server.registerHandler("*", new EchoHandler());
340 final HttpHost target = targetHost();
341
342 final DefaultAuthenticationStrategy authStrategy = Mockito.spy(new DefaultAuthenticationStrategy());
343 final Queue<HttpResponse> responseQueue = new ConcurrentLinkedQueue<>();
344
345 final CloseableHttpClient client = startClient(builder -> builder
346 .setTargetAuthenticationStrategy(authStrategy)
347 .addResponseInterceptorLast((response, entity, context)
348 -> responseQueue.add(BasicResponseBuilder.copy(response).build())));
349
350 final HttpClientContext context = HttpClientContext.create();
351 context.setCredentialsProvider(CredentialsProviderBuilder.create()
352 .add(target, "test", "test".toCharArray())
353 .build());
354
355 for (int i = 0; i < 5; i++) {
356 final HttpGet httpget = new HttpGet("/");
357 client.execute(target, httpget, context, response -> {
358 final HttpEntity entity1 = response.getEntity();
359 Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
360 Assertions.assertNotNull(entity1);
361 EntityUtils.consume(entity1);
362 return null;
363 });
364 }
365
366 Mockito.verify(authStrategy).select(Mockito.any(), Mockito.any(), Mockito.any());
367
368 assertThat(
369 responseQueue.stream().map(HttpResponse::getCode).collect(Collectors.toList()),
370 CoreMatchers.equalTo(Arrays.asList(401, 200, 200, 200, 200, 200)));
371 }
372
373 @Test
374 public void testBasicAuthenticationCredentialsCachingByPathPrefix() throws Exception {
375 final ClassicTestServer server = startServer();
376 server.registerHandler("*", new EchoHandler());
377 final HttpHost target = targetHost();
378
379 final DefaultAuthenticationStrategy authStrategy = Mockito.spy(new DefaultAuthenticationStrategy());
380 final Queue<HttpResponse> responseQueue = new ConcurrentLinkedQueue<>();
381
382 final CloseableHttpClient client = startClient(builder -> builder
383 .setTargetAuthenticationStrategy(authStrategy)
384 .addResponseInterceptorLast((response, entity, context)
385 -> responseQueue.add(BasicResponseBuilder.copy(response).build())));
386
387 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
388 .add(target, "test", "test".toCharArray())
389 .build();
390
391 final AuthCache authCache = new BasicAuthCache();
392 final HttpClientContext context = HttpClientContext.create();
393 context.setAuthCache(authCache);
394 context.setCredentialsProvider(credentialsProvider);
395
396 for (final String requestPath: new String[] {"/blah/a", "/blah/b?huh", "/blah/c", "/bl%61h/%61"}) {
397 final HttpGet httpget = new HttpGet(requestPath);
398 client.execute(target, httpget, context, response -> {
399 final HttpEntity entity1 = response.getEntity();
400 Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
401 Assertions.assertNotNull(entity1);
402 EntityUtils.consume(entity1);
403 return null;
404 });
405 }
406
407
408 Mockito.verify(authStrategy).select(Mockito.any(), Mockito.any(), Mockito.any());
409
410 assertThat(
411 responseQueue.stream().map(HttpResponse::getCode).collect(Collectors.toList()),
412 CoreMatchers.equalTo(Arrays.asList(401, 200, 200, 200, 200)));
413
414 responseQueue.clear();
415 authCache.clear();
416 Mockito.reset(authStrategy);
417
418 for (final String requestPath: new String[] {"/blah/a", "/yada/a", "/blah/blah/", "/buh/a"}) {
419 final HttpGet httpget = new HttpGet(requestPath);
420 client.execute(target, httpget, context, response -> {
421 final HttpEntity entity1 = response.getEntity();
422 Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
423 Assertions.assertNotNull(entity1);
424 EntityUtils.consume(entity1);
425 return null;
426 });
427 }
428
429
430 Mockito.verify(authStrategy, Mockito.times(2)).select(Mockito.any(), Mockito.any(), Mockito.any());
431
432 assertThat(
433 responseQueue.stream().map(HttpResponse::getCode).collect(Collectors.toList()),
434 CoreMatchers.equalTo(Arrays.asList(200, 401, 200, 200, 401, 200)));
435 }
436
437 @Test
438 public void testAuthenticationCredentialsCachingReAuthenticationOnDifferentRealm() throws Exception {
439 final ClassicTestServer server = startServer(new Authenticator() {
440
441 @Override
442 public boolean authenticate(final URIAuthority authority, final String requestUri, final String credentials) {
443 if (requestUri.equals("/this")) {
444 return "test:this".equals(credentials);
445 } else if (requestUri.equals("/that")) {
446 return "test:that".equals(credentials);
447 } else {
448 return "test:test".equals(credentials);
449 }
450 }
451
452 @Override
453 public String getRealm(final URIAuthority authority, final String requestUri) {
454 if (requestUri.equals("/this")) {
455 return "this realm";
456 } else if (requestUri.equals("/that")) {
457 return "that realm";
458 } else {
459 return "test realm";
460 }
461 }
462
463 });
464 server.registerHandler("*", new EchoHandler());
465 final HttpHost target = targetHost();
466
467 final DefaultAuthenticationStrategy authStrategy = Mockito.spy(new DefaultAuthenticationStrategy());
468
469 final CloseableHttpClient client = startClient(builder -> builder
470 .setTargetAuthenticationStrategy(authStrategy)
471 );
472
473 final CredentialsProvider credsProvider = CredentialsProviderBuilder.create()
474 .add(new AuthScope(target, "this realm", null), "test", "this".toCharArray())
475 .add(new AuthScope(target, "that realm", null), "test", "that".toCharArray())
476 .build();
477
478 final HttpClientContext context = HttpClientContext.create();
479 context.setCredentialsProvider(credsProvider);
480
481 final HttpGet httpget1 = new HttpGet("/this");
482
483 client.execute(target, httpget1, context, response -> {
484 final HttpEntity entity1 = response.getEntity();
485 Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
486 Assertions.assertNotNull(entity1);
487 EntityUtils.consume(entity1);
488 return null;
489 });
490
491 final HttpGet httpget2 = new HttpGet("/this");
492
493 client.execute(target, httpget2, context, response -> {
494 final HttpEntity entity2 = response.getEntity();
495 Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
496 Assertions.assertNotNull(entity2);
497 EntityUtils.consume(entity2);
498 return null;
499 });
500
501 final HttpGet httpget3 = new HttpGet("/that");
502
503 client.execute(target, httpget3, context, response -> {
504 final HttpEntity entity3 = response.getEntity();
505 Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
506 Assertions.assertNotNull(entity3);
507 EntityUtils.consume(entity3);
508 return null;
509 });
510
511 Mockito.verify(authStrategy, Mockito.times(2)).select(Mockito.any(), Mockito.any(), Mockito.any());
512 }
513
514 @Test
515 public void testAuthenticationUserinfoInRequest() throws Exception {
516 final ClassicTestServer server = startServer();
517 server.registerHandler("*", new EchoHandler());
518 final HttpHost target = targetHost();
519
520 final CloseableHttpClient client = startClient();
521 final HttpGet httpget = new HttpGet("http://test:test@" + target.toHostString() + "/");
522
523 final HttpClientContext context = HttpClientContext.create();
524 Assertions.assertThrows(ClientProtocolException.class, () -> client.execute(target, httpget, context, response -> null));
525 }
526
527 @Test
528 public void testPreemptiveAuthentication() throws Exception {
529 final Authenticator authenticator = Mockito.spy(new BasicTestAuthenticator("test:test", "test realm"));
530 final ClassicTestServer server = startServer(authenticator);
531 server.registerHandler("*", new EchoHandler());
532 final HttpHost target = targetHost();
533
534 final CloseableHttpClient client = startClient();
535
536 final BasicScheme basicScheme = new BasicScheme();
537 basicScheme.initPreemptive(new UsernamePasswordCredentials("test", "test".toCharArray()));
538 final HttpClientContext context = HttpClientContext.create();
539 final AuthCache authCache = new BasicAuthCache();
540 authCache.put(target, basicScheme);
541 context.setAuthCache(authCache);
542
543 final HttpGet httpget = new HttpGet("/");
544 client.execute(target, httpget, context, response -> {
545 final HttpEntity entity1 = response.getEntity();
546 Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
547 Assertions.assertNotNull(entity1);
548 EntityUtils.consume(entity1);
549 return null;
550 });
551
552 Mockito.verify(authenticator).authenticate(Mockito.any(), Mockito.any(), Mockito.any());
553 }
554
555 @Test
556 public void testPreemptiveAuthenticationFailure() throws Exception {
557 final Authenticator authenticator = Mockito.spy(new BasicTestAuthenticator("test:test", "test realm"));
558 final ClassicTestServer server = startServer(authenticator);
559 server.registerHandler("*", new EchoHandler());
560 final HttpHost target = targetHost();
561
562 final CloseableHttpClient client = startClient();
563
564 final HttpClientContext context = HttpClientContext.create();
565 final AuthCache authCache = new BasicAuthCache();
566 authCache.put(target, new BasicScheme());
567 context.setAuthCache(authCache);
568 context.setCredentialsProvider(CredentialsProviderBuilder.create()
569 .add(target, "test", "stuff".toCharArray())
570 .build());
571
572 final HttpGet httpget = new HttpGet("/");
573 client.execute(target, httpget, context, response -> {
574 final HttpEntity entity1 = response.getEntity();
575 Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
576 Assertions.assertNotNull(entity1);
577 EntityUtils.consume(entity1);
578 return null;
579 });
580
581 Mockito.verify(authenticator).authenticate(Mockito.any(), Mockito.any(), Mockito.any());
582 }
583
584 static class ProxyAuthHandler implements HttpRequestHandler {
585
586 @Override
587 public void handle(
588 final ClassicHttpRequest request,
589 final ClassicHttpResponse response,
590 final HttpContext context) throws HttpException, IOException {
591 final String creds = (String) context.getAttribute("creds");
592 if (creds == null || !creds.equals("test:test")) {
593 response.setCode(HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED);
594 } else {
595 response.setCode(HttpStatus.SC_OK);
596 final StringEntity entity = new StringEntity("success", StandardCharsets.US_ASCII);
597 response.setEntity(entity);
598 }
599 }
600
601 }
602
603 @Test
604 public void testAuthenticationTargetAsProxy() throws Exception {
605 final ClassicTestServer server = testResources.startServer(null, null, null);
606 server.registerHandler("*", new ProxyAuthHandler());
607 final HttpHost target = testResources.targetHost();
608
609 final CloseableHttpClient client = testResources.startClient(builder -> {});
610
611 final HttpClientContext context = HttpClientContext.create();
612 final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
613 context.setCredentialsProvider(credsProvider);
614
615 final HttpGet httpget = new HttpGet("/");
616 client.execute(target, httpget, context, response -> {
617 final HttpEntity entity = response.getEntity();
618 Assertions.assertEquals(HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED, response.getCode());
619 EntityUtils.consume(entity);
620 return null;
621 });
622 }
623
624 @Test
625 public void testConnectionCloseAfterAuthenticationSuccess() throws Exception {
626 final ClassicTestServer server = testResources.startServer(
627 Http1Config.DEFAULT,
628 HttpProcessors.server(),
629 requestHandler -> new AuthenticatingDecorator(requestHandler, new BasicTestAuthenticator("test:test", "test realm")) {
630
631 @Override
632 protected void customizeUnauthorizedResponse(final ClassicHttpResponse unauthorized) {
633 unauthorized.addHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE);
634 }
635
636 }
637 );
638 server.registerHandler("*", new EchoHandler());
639 final HttpHost target = targetHost();
640
641 final CloseableHttpClient client = startClient();
642
643 final HttpClientContext context = HttpClientContext.create();
644 final CredentialsProvider credsProvider = CredentialsProviderBuilder.create()
645 .add(target, "test", "test".toCharArray())
646 .build();
647 context.setCredentialsProvider(credsProvider);
648
649 for (int i = 0; i < 2; i++) {
650 final HttpGet httpget = new HttpGet("/");
651
652 client.execute(target, httpget, context, response -> {
653 EntityUtils.consume(response.getEntity());
654 Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
655 return null;
656 });
657 }
658 }
659
660 @Test
661 public void testReauthentication() throws Exception {
662 final BasicSchemeFactory myBasicAuthSchemeFactory = new BasicSchemeFactory() {
663
664 @Override
665 public AuthScheme create(final HttpContext context) {
666 return new BasicScheme() {
667 private static final long serialVersionUID = 1L;
668
669 @Override
670 public String getName() {
671 return "MyBasic";
672 }
673
674 };
675 }
676
677 };
678
679 final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
680 Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any()))
681 .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray()));
682
683 final RequestConfig config = RequestConfig.custom()
684 .setTargetPreferredAuthSchemes(Collections.singletonList("MyBasic"))
685 .build();
686 final Registry<AuthSchemeFactory> authSchemeRegistry = RegistryBuilder.<AuthSchemeFactory>create()
687 .register("MyBasic", myBasicAuthSchemeFactory)
688 .build();
689
690 final Authenticator authenticator = new BasicTestAuthenticator("test:test", "test realm") {
691
692 private final AtomicLong count = new AtomicLong(0);
693
694 @Override
695 public boolean authenticate(final URIAuthority authority, final String requestUri, final String credentials) {
696 final boolean authenticated = super.authenticate(authority, requestUri, credentials);
697 if (authenticated) {
698 return this.count.incrementAndGet() % 4 != 0;
699 }
700 return false;
701 }
702 };
703
704 final ClassicTestServer server = testResources.startServer(
705 Http1Config.DEFAULT,
706 HttpProcessors.server(),
707 requestHandler -> new AuthenticatingDecorator(requestHandler, authenticator) {
708
709 @Override
710 protected void customizeUnauthorizedResponse(final ClassicHttpResponse unauthorized) {
711 unauthorized.removeHeaders(HttpHeaders.WWW_AUTHENTICATE);
712 unauthorized.addHeader(HttpHeaders.WWW_AUTHENTICATE, "MyBasic realm=\"test realm\"");
713 }
714
715 }
716 );
717 server.registerHandler("*", new EchoHandler());
718 final HttpHost target = targetHost();
719
720 final CloseableHttpClient client = startClient(builder -> builder
721 .setDefaultAuthSchemeRegistry(authSchemeRegistry)
722 .setDefaultCredentialsProvider(credsProvider)
723 );
724
725 final HttpClientContext context = HttpClientContext.create();
726 for (int i = 0; i < 10; i++) {
727 final HttpGet httpget = new HttpGet("/");
728 httpget.setConfig(config);
729 client.execute(target, httpget, context, response -> {
730 final HttpEntity entity = response.getEntity();
731 Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
732 Assertions.assertNotNull(entity);
733 EntityUtils.consume(entity);
734 return null;
735 });
736 }
737 }
738
739 @Test
740 public void testAuthenticationFallback() throws Exception {
741 final ClassicTestServer server = testResources.startServer(
742 Http1Config.DEFAULT,
743 HttpProcessors.server(),
744 requestHandler -> new AuthenticatingDecorator(requestHandler, new BasicTestAuthenticator("test:test", "test realm")) {
745
746 @Override
747 protected void customizeUnauthorizedResponse(final ClassicHttpResponse unauthorized) {
748 unauthorized.addHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.DIGEST + " realm=\"test realm\" invalid");
749 }
750
751 }
752 );
753 server.registerHandler("*", new EchoHandler());
754 final HttpHost target = targetHost();
755
756 final CloseableHttpClient client = startClient();
757
758 final HttpClientContext context = HttpClientContext.create();
759 final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
760 Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any()))
761 .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray()));
762 context.setCredentialsProvider(credsProvider);
763 final HttpGet httpget = new HttpGet("/");
764
765 client.execute(target, httpget, context, response -> {
766 final HttpEntity entity = response.getEntity();
767 Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
768 Assertions.assertNotNull(entity);
769 EntityUtils.consume(entity);
770 return null;
771 });
772 Mockito.verify(credsProvider).getCredentials(
773 Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any());
774 }
775
776 private final static String CHARS = "0123456789abcdef";
777
778 @Test
779 public void testBearerTokenAuthentication() throws Exception {
780 final SecureRandom secureRandom = SecureRandom.getInstanceStrong();
781 secureRandom.setSeed(System.currentTimeMillis());
782 final StringBuilder buf = new StringBuilder();
783 for (int i = 0; i < 16; i++) {
784 buf.append(CHARS.charAt(secureRandom.nextInt(CHARS.length() - 1)));
785 }
786 final String token = buf.toString();
787 final ClassicTestServer server = testResources.startServer(
788 Http1Config.DEFAULT,
789 HttpProcessors.server(),
790 requestHandler -> new AuthenticatingDecorator(
791 requestHandler,
792 new BearerAuthenticationHandler(),
793 new BasicTestAuthenticator(token, "test realm")));
794 server.registerHandler("*", new EchoHandler());
795 final HttpHost target = targetHost();
796
797 final CloseableHttpClient client = startClient();
798
799 final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class);
800
801 final HttpClientContext context1 = HttpClientContext.create();
802 context1.setCredentialsProvider(credsProvider);
803 final HttpGet httpget1 = new HttpGet("/");
804 client.execute(target, httpget1, context1, response -> {
805 final HttpEntity entity = response.getEntity();
806 Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
807 Assertions.assertNotNull(entity);
808 EntityUtils.consume(entity);
809 return null;
810 });
811 Mockito.verify(credsProvider).getCredentials(
812 Mockito.eq(new AuthScope(target, "test realm", "bearer")), Mockito.any());
813
814 final HttpClientContext context2 = HttpClientContext.create();
815 Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any()))
816 .thenReturn(new BearerToken(token));
817 context2.setCredentialsProvider(credsProvider);
818 final HttpGet httpget2 = new HttpGet("/");
819 client.execute(target, httpget2, context2, response -> {
820 final HttpEntity entity = response.getEntity();
821 Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
822 Assertions.assertNotNull(entity);
823 EntityUtils.consume(entity);
824 return null;
825 });
826
827 final HttpClientContext context3 = HttpClientContext.create();
828 Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any()))
829 .thenReturn(new BearerToken(token + "-expired"));
830 context3.setCredentialsProvider(credsProvider);
831 final HttpGet httpget3 = new HttpGet("/");
832 client.execute(target, httpget3, context3, response -> {
833 final HttpEntity entity = response.getEntity();
834 Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
835 Assertions.assertNotNull(entity);
836 EntityUtils.consume(entity);
837 return null;
838 });
839 }
840
841 }