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