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