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