View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.hc.client5.testing.async;
28  
29  import java.util.Collections;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.concurrent.Future;
33  import java.util.concurrent.atomic.AtomicLong;
34  
35  import org.apache.hc.client5.http.AuthenticationStrategy;
36  import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
37  import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
38  import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
39  import org.apache.hc.client5.http.auth.AuthChallenge;
40  import org.apache.hc.client5.http.auth.AuthScheme;
41  import org.apache.hc.client5.http.auth.AuthSchemeFactory;
42  import org.apache.hc.client5.http.auth.AuthScope;
43  import org.apache.hc.client5.http.auth.ChallengeType;
44  import org.apache.hc.client5.http.auth.Credentials;
45  import org.apache.hc.client5.http.auth.CredentialsStore;
46  import org.apache.hc.client5.http.auth.StandardAuthScheme;
47  import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
48  import org.apache.hc.client5.http.config.RequestConfig;
49  import org.apache.hc.client5.http.impl.DefaultAuthenticationStrategy;
50  import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
51  import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
52  import org.apache.hc.client5.http.impl.auth.BasicScheme;
53  import org.apache.hc.client5.http.protocol.HttpClientContext;
54  import org.apache.hc.client5.testing.BasicTestAuthenticator;
55  import org.apache.hc.client5.testing.auth.Authenticator;
56  import org.apache.hc.core5.function.Decorator;
57  import org.apache.hc.core5.function.Supplier;
58  import org.apache.hc.core5.http.ContentType;
59  import org.apache.hc.core5.http.HttpException;
60  import org.apache.hc.core5.http.HttpHeaders;
61  import org.apache.hc.core5.http.HttpHost;
62  import org.apache.hc.core5.http.HttpResponse;
63  import org.apache.hc.core5.http.HttpStatus;
64  import org.apache.hc.core5.http.HttpVersion;
65  import org.apache.hc.core5.http.URIScheme;
66  import org.apache.hc.core5.http.config.Http1Config;
67  import org.apache.hc.core5.http.config.Lookup;
68  import org.apache.hc.core5.http.config.Registry;
69  import org.apache.hc.core5.http.config.RegistryBuilder;
70  import org.apache.hc.core5.http.impl.HttpProcessors;
71  import org.apache.hc.core5.http.message.BasicHeader;
72  import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
73  import org.apache.hc.core5.http.protocol.HttpContext;
74  import org.apache.hc.core5.http.protocol.HttpCoreContext;
75  import org.apache.hc.core5.http2.config.H2Config;
76  import org.apache.hc.core5.http2.impl.H2Processors;
77  import org.apache.hc.core5.net.URIAuthority;
78  import org.junit.Assert;
79  import org.junit.Test;
80  
81  public abstract class AbstractHttpAsyncClientAuthentication<T extends CloseableHttpAsyncClient> extends AbstractIntegrationTestBase<T> {
82  
83      protected final HttpVersion protocolVersion;
84  
85      public AbstractHttpAsyncClientAuthentication(final URIScheme scheme, final HttpVersion protocolVersion) {
86          super(scheme);
87          this.protocolVersion = protocolVersion;
88      }
89  
90      @Override
91      public final HttpHost start() throws Exception {
92          return start(new Decorator<AsyncServerExchangeHandler>() {
93  
94              @Override
95              public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler requestHandler) {
96                  return new AuthenticatingAsyncDecorator(requestHandler, new BasicTestAuthenticator("test:test", "test realm"));
97              }
98  
99          });
100     }
101 
102     public final HttpHost start(
103             final Decorator<AsyncServerExchangeHandler> exchangeHandlerDecorator) throws Exception {
104         if (protocolVersion.greaterEquals(HttpVersion.HTTP_2_0)) {
105             return super.start(
106                     H2Processors.server(),
107                     exchangeHandlerDecorator,
108                     H2Config.DEFAULT);
109         } else {
110             return super.start(
111                     HttpProcessors.server(),
112                     exchangeHandlerDecorator,
113                     Http1Config.DEFAULT);
114         }
115     }
116 
117     abstract void setDefaultAuthSchemeRegistry(Lookup<AuthSchemeFactory> authSchemeRegistry);
118 
119     abstract void setTargetAuthenticationStrategy(AuthenticationStrategy targetAuthStrategy);
120 
121     static class TestCredentialsProvider implements CredentialsStore {
122 
123         private final Credentials creds;
124         private AuthScope authscope;
125 
126         TestCredentialsProvider(final Credentials creds) {
127             super();
128             this.creds = creds;
129         }
130 
131         @Override
132         public void clear() {
133         }
134 
135         @Override
136         public Credentials getCredentials(final AuthScope authscope, final HttpContext context) {
137             this.authscope = authscope;
138             return this.creds;
139         }
140 
141         @Override
142         public void setCredentials(final AuthScope authscope, final Credentials credentials) {
143         }
144 
145         public AuthScope getAuthScope() {
146             return this.authscope;
147         }
148 
149     }
150 
151     @Test
152     public void testBasicAuthenticationNoCreds() throws Exception {
153         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
154 
155             @Override
156             public AsyncServerExchangeHandler get() {
157                 return new AsyncEchoHandler();
158             }
159 
160         });
161         final HttpHost target = start();
162 
163         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(null);
164         final HttpClientContext context = HttpClientContext.create();
165         context.setCredentialsProvider(credsProvider);
166 
167         final Future<SimpleHttpResponse> future = httpclient.execute(SimpleRequestBuilder.get()
168                         .setHttpHost(target)
169                         .setPath("/")
170                         .build(), context, null);
171         final HttpResponse response = future.get();
172 
173         Assert.assertNotNull(response);
174         Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
175         final AuthScope authscope = credsProvider.getAuthScope();
176         Assert.assertNotNull(authscope);
177         Assert.assertEquals("test realm", authscope.getRealm());
178     }
179 
180     @Test
181     public void testBasicAuthenticationFailure() throws Exception {
182         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
183 
184             @Override
185             public AsyncServerExchangeHandler get() {
186                 return new AsyncEchoHandler();
187             }
188 
189         });
190         final HttpHost target = start();
191 
192         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
193                 new UsernamePasswordCredentials("test", "all-wrong".toCharArray()));
194         final HttpClientContext context = HttpClientContext.create();
195         context.setCredentialsProvider(credsProvider);
196 
197         final Future<SimpleHttpResponse> future = httpclient.execute(SimpleRequestBuilder.get()
198                         .setHttpHost(target)
199                         .setPath("/")
200                         .build(), context, null);
201         final HttpResponse response = future.get();
202 
203         Assert.assertNotNull(response);
204         Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
205         final AuthScope authscope = credsProvider.getAuthScope();
206         Assert.assertNotNull(authscope);
207         Assert.assertEquals("test realm", authscope.getRealm());
208     }
209 
210     @Test
211     public void testBasicAuthenticationSuccess() throws Exception {
212         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
213 
214             @Override
215             public AsyncServerExchangeHandler get() {
216                 return new AsyncEchoHandler();
217             }
218 
219         });
220         final HttpHost target = start();
221 
222         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
223                 new UsernamePasswordCredentials("test", "test".toCharArray()));
224         final HttpClientContext context = HttpClientContext.create();
225         context.setCredentialsProvider(credsProvider);
226 
227         final Future<SimpleHttpResponse> future = httpclient.execute(
228                 SimpleRequestBuilder.get()
229                         .setHttpHost(target)
230                         .setPath("/")
231                         .build(), context, null);
232         final HttpResponse response = future.get();
233 
234         Assert.assertNotNull(response);
235         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
236         final AuthScope authscope = credsProvider.getAuthScope();
237         Assert.assertNotNull(authscope);
238         Assert.assertEquals("test realm", authscope.getRealm());
239     }
240 
241     @Test
242     public void testBasicAuthenticationWithEntitySuccess() throws Exception {
243         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
244 
245             @Override
246             public AsyncServerExchangeHandler get() {
247                 return new AsyncEchoHandler();
248             }
249 
250         });
251         final HttpHost target = start();
252 
253         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
254                 new UsernamePasswordCredentials("test", "test".toCharArray()));
255         final HttpClientContext context = HttpClientContext.create();
256         context.setCredentialsProvider(credsProvider);
257         final Future<SimpleHttpResponse> future = httpclient.execute(
258                 SimpleRequestBuilder.put()
259                         .setHttpHost(target)
260                         .setPath("/")
261                         .setBody("Some important stuff", ContentType.TEXT_PLAIN)
262                         .build(), context, null);
263         final HttpResponse response = future.get();
264 
265         Assert.assertNotNull(response);
266         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
267         final AuthScope authscope = credsProvider.getAuthScope();
268         Assert.assertNotNull(authscope);
269         Assert.assertEquals("test realm", authscope.getRealm());
270     }
271 
272     @Test
273     public void testBasicAuthenticationExpectationFailure() throws Exception {
274         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
275 
276             @Override
277             public AsyncServerExchangeHandler get() {
278                 return new AsyncEchoHandler();
279             }
280 
281         });
282         final HttpHost target = start();
283 
284         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
285                 new UsernamePasswordCredentials("test", "all-wrong".toCharArray()));
286         final HttpClientContext context = HttpClientContext.create();
287         context.setCredentialsProvider(credsProvider);
288         context.setRequestConfig(RequestConfig.custom().setExpectContinueEnabled(true).build());
289         final Future<SimpleHttpResponse> future = httpclient.execute(
290                 SimpleRequestBuilder.put()
291                         .setHttpHost(target)
292                         .setPath("/")
293                         .setBody("Some important stuff", ContentType.TEXT_PLAIN)
294                         .build(), context, null);
295         final HttpResponse response = future.get();
296 
297         Assert.assertNotNull(response);
298         Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
299     }
300 
301     @Test
302     public void testBasicAuthenticationExpectationSuccess() throws Exception {
303         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
304 
305             @Override
306             public AsyncServerExchangeHandler get() {
307                 return new AsyncEchoHandler();
308             }
309 
310         });
311         final HttpHost target = start();
312 
313         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
314                 new UsernamePasswordCredentials("test", "test".toCharArray()));
315         final HttpClientContext context = HttpClientContext.create();
316         context.setCredentialsProvider(credsProvider);
317         context.setRequestConfig(RequestConfig.custom().setExpectContinueEnabled(true).build());
318         final Future<SimpleHttpResponse> future = httpclient.execute(
319                 SimpleRequestBuilder.put()
320                         .setHttpHost(target)
321                         .setPath("/")
322                         .setBody("Some important stuff", ContentType.TEXT_PLAIN)
323                         .build(), context, null);
324         final HttpResponse response = future.get();
325 
326         Assert.assertNotNull(response);
327         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
328         final AuthScope authscope = credsProvider.getAuthScope();
329         Assert.assertNotNull(authscope);
330         Assert.assertEquals("test realm", authscope.getRealm());
331     }
332 
333     @Test
334     public void testBasicAuthenticationCredentialsCaching() throws Exception {
335         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
336 
337             @Override
338             public AsyncServerExchangeHandler get() {
339                 return new AsyncEchoHandler();
340             }
341 
342         });
343 
344         final AtomicLong count = new AtomicLong(0);
345         setTargetAuthenticationStrategy(new DefaultAuthenticationStrategy() {
346 
347             @Override
348             public List<AuthScheme> select(
349                     final ChallengeType challengeType,
350                     final Map<String, AuthChallenge> challenges,
351                     final HttpContext context) {
352                 count.incrementAndGet();
353                 return super.select(challengeType, challenges, context);
354             }
355         });
356         final HttpHost target = start();
357 
358         final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
359         credsProvider.setCredentials(new AuthScope(null, null, -1, null ,null),
360                 new UsernamePasswordCredentials("test", "test".toCharArray()));
361         final HttpClientContext context = HttpClientContext.create();
362         context.setCredentialsProvider(credsProvider);
363 
364         final Future<SimpleHttpResponse> future1 = httpclient.execute(SimpleRequestBuilder.get()
365                         .setHttpHost(target)
366                         .setPath("/")
367                         .build(), context, null);
368         final HttpResponse response1 = future1.get();
369         Assert.assertNotNull(response1);
370         Assert.assertEquals(HttpStatus.SC_OK, response1.getCode());
371 
372         final Future<SimpleHttpResponse> future2 = httpclient.execute(SimpleRequestBuilder.get()
373                         .setHttpHost(target)
374                         .setPath("/")
375                         .build(), context, null);
376         final HttpResponse response2 = future2.get();
377         Assert.assertNotNull(response2);
378         Assert.assertEquals(HttpStatus.SC_OK, response2.getCode());
379 
380         Assert.assertEquals(1, count.get());
381     }
382 
383     @Test
384     public void testAuthenticationUserinfoInRequestSuccess() throws Exception {
385         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
386 
387             @Override
388             public AsyncServerExchangeHandler get() {
389                 return new AsyncEchoHandler();
390             }
391 
392         });
393         final HttpHost target = start();
394 
395         final HttpClientContext context = HttpClientContext.create();
396         final Future<SimpleHttpResponse> future = httpclient.execute(
397                 SimpleRequestBuilder.get()
398                         .setScheme(target.getSchemeName())
399                         .setAuthority(new URIAuthority("test:test", target.getHostName(), target.getPort()))
400                         .setPath("/")
401                         .build(), context, null);
402         final SimpleHttpResponse response = future.get();
403 
404         Assert.assertNotNull(response);
405         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
406     }
407 
408     @Test
409     public void testAuthenticationUserinfoInRequestFailure() throws Exception {
410         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
411 
412             @Override
413             public AsyncServerExchangeHandler get() {
414                 return new AsyncEchoHandler();
415             }
416 
417         });
418         final HttpHost target = start();
419 
420         final HttpClientContext context = HttpClientContext.create();
421         final Future<SimpleHttpResponse> future = httpclient.execute(SimpleRequestBuilder.get()
422                         .setScheme(target.getSchemeName())
423                         .setAuthority(new URIAuthority("test:all-worng", target.getHostName(), target.getPort()))
424                         .setPath("/")
425                         .build(), context, null);
426         final SimpleHttpResponse response = future.get();
427 
428         Assert.assertNotNull(response);
429         Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
430     }
431 
432     @Test
433     public void testAuthenticationUserinfoInRedirectSuccess() throws Exception {
434         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
435 
436             @Override
437             public AsyncServerExchangeHandler get() {
438                 return new AsyncEchoHandler();
439             }
440 
441         });
442         final HttpHost target = start();
443         server.register("/thatway", new Supplier<AsyncServerExchangeHandler>() {
444 
445             @Override
446             public AsyncServerExchangeHandler get() {
447                 return new AbstractSimpleServerExchangeHandler() {
448 
449                     @Override
450                     protected SimpleHttpResponse handle(
451                             final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
452                         final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_MOVED_PERMANENTLY);
453                         response.addHeader(new BasicHeader("Location", target.getSchemeName() + "://test:test@" + target.toHostString() + "/"));
454                         return response;
455                     }
456                 };
457             }
458 
459         });
460 
461         final HttpClientContext context = HttpClientContext.create();
462         final Future<SimpleHttpResponse> future = httpclient.execute(
463                 SimpleRequestBuilder.get()
464                         .setScheme(target.getSchemeName())
465                         .setAuthority(new URIAuthority("test:test", target.getHostName(), target.getPort()))
466                         .setPath("/thatway")
467                         .build(), context, null);
468         final SimpleHttpResponse response = future.get();
469 
470         Assert.assertNotNull(response);
471         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
472     }
473 
474     @Test
475     public void testReauthentication() throws Exception {
476         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
477 
478             @Override
479             public AsyncServerExchangeHandler get() {
480                 return new AsyncEchoHandler();
481             }
482 
483         });
484         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
485                 new UsernamePasswordCredentials("test", "test".toCharArray()));
486 
487         final Registry<AuthSchemeFactory> authSchemeRegistry = RegistryBuilder.<AuthSchemeFactory>create()
488                 .register("MyBasic", new AuthSchemeFactory() {
489 
490                     @Override
491                     public AuthScheme create(final HttpContext context) {
492                         return new BasicScheme() {
493 
494                             private static final long serialVersionUID = 1L;
495 
496                             @Override
497                             public String getName() {
498                                 return "MyBasic";
499                             }
500 
501                         };
502                     }
503 
504                 })
505                 .build();
506         setDefaultAuthSchemeRegistry(authSchemeRegistry);
507 
508         final Authenticator authenticator = new BasicTestAuthenticator("test:test", "test realm") {
509 
510             private final AtomicLong count = new AtomicLong(0);
511 
512             @Override
513             public boolean authenticate(final URIAuthority authority, final String requestUri, final String credentials) {
514                 final boolean authenticated = super.authenticate(authority, requestUri, credentials);
515                 if (authenticated) {
516                     return this.count.incrementAndGet() % 4 != 0;
517                 }
518                 return false;
519             }
520         };
521 
522         final HttpHost target = start(
523                 new Decorator<AsyncServerExchangeHandler>() {
524 
525                     @Override
526                     public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
527                         return new AuthenticatingAsyncDecorator(exchangeHandler, authenticator) {
528 
529                             @Override
530                             protected void customizeUnauthorizedResponse(final HttpResponse unauthorized) {
531                                 unauthorized.removeHeaders(HttpHeaders.WWW_AUTHENTICATE);
532                                 unauthorized.addHeader(HttpHeaders.WWW_AUTHENTICATE, "MyBasic realm=\"test realm\"");
533                             }
534 
535                         };
536                     }
537 
538                 });
539 
540         final RequestConfig config = RequestConfig.custom()
541                 .setTargetPreferredAuthSchemes(Collections.singletonList("MyBasic"))
542                 .build();
543         final HttpClientContext context = HttpClientContext.create();
544         context.setCredentialsProvider(credsProvider);
545 
546         for (int i = 0; i < 10; i++) {
547             final SimpleHttpRequest request = SimpleRequestBuilder.get()
548                         .setHttpHost(target)
549                         .setPath("/")
550                         .build();
551             request.setConfig(config);
552             final Future<SimpleHttpResponse> future = httpclient.execute(request, context, null);
553             final SimpleHttpResponse response = future.get();
554             Assert.assertNotNull(response);
555             Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
556         }
557     }
558 
559     @Test
560     public void testAuthenticationFallback() throws Exception {
561         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
562 
563             @Override
564             public AsyncServerExchangeHandler get() {
565                 return new AsyncEchoHandler();
566             }
567 
568         });
569         final HttpHost target = start(
570                 new Decorator<AsyncServerExchangeHandler>() {
571 
572                     @Override
573                     public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
574                         return new AuthenticatingAsyncDecorator(exchangeHandler, new BasicTestAuthenticator("test:test", "test realm")) {
575 
576                             @Override
577                             protected void customizeUnauthorizedResponse(final HttpResponse unauthorized) {
578                                 unauthorized.addHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.DIGEST + " realm=\"test realm\" invalid");
579                             }
580 
581                         };
582                     }
583 
584                 });
585 
586         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
587                 new UsernamePasswordCredentials("test", "test".toCharArray()));
588         final HttpClientContext context = HttpClientContext.create();
589         context.setCredentialsProvider(credsProvider);
590 
591         final Future<SimpleHttpResponse> future = httpclient.execute(SimpleRequestBuilder.get()
592                         .setHttpHost(target)
593                         .setPath("/")
594                         .build(), context, null);
595         final SimpleHttpResponse response = future.get();
596         Assert.assertNotNull(response);
597         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
598         final AuthScope authscope = credsProvider.getAuthScope();
599         Assert.assertNotNull(authscope);
600         Assert.assertEquals("test realm", authscope.getRealm());
601     }
602 
603 }