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.Arrays;
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.SimpleHttpRequests;
38  import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
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.StandardAuthScheme;
43  import org.apache.hc.client5.http.auth.AuthScope;
44  import org.apache.hc.client5.http.auth.ChallengeType;
45  import org.apache.hc.client5.http.auth.Credentials;
46  import org.apache.hc.client5.http.auth.CredentialsStore;
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(SimpleHttpRequests.get(target, "/"), context, null);
168         final HttpResponse response = future.get();
169 
170         Assert.assertNotNull(response);
171         Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
172         final AuthScope authscope = credsProvider.getAuthScope();
173         Assert.assertNotNull(authscope);
174         Assert.assertEquals("test realm", authscope.getRealm());
175     }
176 
177     @Test
178     public void testBasicAuthenticationFailure() throws Exception {
179         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
180 
181             @Override
182             public AsyncServerExchangeHandler get() {
183                 return new AsyncEchoHandler();
184             }
185 
186         });
187         final HttpHost target = start();
188 
189         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
190                 new UsernamePasswordCredentials("test", "all-wrong".toCharArray()));
191         final HttpClientContext context = HttpClientContext.create();
192         context.setCredentialsProvider(credsProvider);
193 
194         final Future<SimpleHttpResponse> future = httpclient.execute(SimpleHttpRequests.get(target, "/"), context, null);
195         final HttpResponse response = future.get();
196 
197         Assert.assertNotNull(response);
198         Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
199         final AuthScope authscope = credsProvider.getAuthScope();
200         Assert.assertNotNull(authscope);
201         Assert.assertEquals("test realm", authscope.getRealm());
202     }
203 
204     @Test
205     public void testBasicAuthenticationSuccess() throws Exception {
206         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
207 
208             @Override
209             public AsyncServerExchangeHandler get() {
210                 return new AsyncEchoHandler();
211             }
212 
213         });
214         final HttpHost target = start();
215 
216         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
217                 new UsernamePasswordCredentials("test", "test".toCharArray()));
218         final HttpClientContext context = HttpClientContext.create();
219         context.setCredentialsProvider(credsProvider);
220 
221         final Future<SimpleHttpResponse> future = httpclient.execute(SimpleHttpRequests.get(target, "/"), context, null);
222         final HttpResponse response = future.get();
223 
224         Assert.assertNotNull(response);
225         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
226         final AuthScope authscope = credsProvider.getAuthScope();
227         Assert.assertNotNull(authscope);
228         Assert.assertEquals("test realm", authscope.getRealm());
229     }
230 
231     @Test
232     public void testBasicAuthenticationWithEntitySuccess() throws Exception {
233         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
234 
235             @Override
236             public AsyncServerExchangeHandler get() {
237                 return new AsyncEchoHandler();
238             }
239 
240         });
241         final HttpHost target = start();
242 
243         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
244                 new UsernamePasswordCredentials("test", "test".toCharArray()));
245         final HttpClientContext context = HttpClientContext.create();
246         context.setCredentialsProvider(credsProvider);
247 
248         final SimpleHttpRequest put = SimpleHttpRequests.put(target, "/");
249         put.setBody("Some important stuff", ContentType.TEXT_PLAIN);
250         final Future<SimpleHttpResponse> future = httpclient.execute(put, context, null);
251         final HttpResponse response = future.get();
252 
253         Assert.assertNotNull(response);
254         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
255         final AuthScope authscope = credsProvider.getAuthScope();
256         Assert.assertNotNull(authscope);
257         Assert.assertEquals("test realm", authscope.getRealm());
258     }
259 
260     @Test
261     public void testBasicAuthenticationExpectationFailure() throws Exception {
262         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
263 
264             @Override
265             public AsyncServerExchangeHandler get() {
266                 return new AsyncEchoHandler();
267             }
268 
269         });
270         final HttpHost target = start();
271 
272         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
273                 new UsernamePasswordCredentials("test", "all-wrong".toCharArray()));
274         final HttpClientContext context = HttpClientContext.create();
275         context.setCredentialsProvider(credsProvider);
276         context.setRequestConfig(RequestConfig.custom().setExpectContinueEnabled(true).build());
277 
278         final SimpleHttpRequest put = SimpleHttpRequests.put(target, "/");
279         put.setBody("Some important stuff", ContentType.TEXT_PLAIN);
280         final Future<SimpleHttpResponse> future = httpclient.execute(put, context, null);
281         final HttpResponse response = future.get();
282 
283         Assert.assertNotNull(response);
284         Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
285     }
286 
287     @Test
288     public void testBasicAuthenticationExpectationSuccess() throws Exception {
289         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
290 
291             @Override
292             public AsyncServerExchangeHandler get() {
293                 return new AsyncEchoHandler();
294             }
295 
296         });
297         final HttpHost target = start();
298 
299         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
300                 new UsernamePasswordCredentials("test", "test".toCharArray()));
301         final HttpClientContext context = HttpClientContext.create();
302         context.setCredentialsProvider(credsProvider);
303         context.setRequestConfig(RequestConfig.custom().setExpectContinueEnabled(true).build());
304 
305         final SimpleHttpRequest put = SimpleHttpRequests.put(target, "/");
306         put.setBody("Some important stuff", ContentType.TEXT_PLAIN);
307         final Future<SimpleHttpResponse> future = httpclient.execute(put, context, null);
308         final HttpResponse response = future.get();
309 
310         Assert.assertNotNull(response);
311         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
312         final AuthScope authscope = credsProvider.getAuthScope();
313         Assert.assertNotNull(authscope);
314         Assert.assertEquals("test realm", authscope.getRealm());
315     }
316 
317     @Test
318     public void testBasicAuthenticationCredentialsCaching() throws Exception {
319         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
320 
321             @Override
322             public AsyncServerExchangeHandler get() {
323                 return new AsyncEchoHandler();
324             }
325 
326         });
327 
328         final AtomicLong count = new AtomicLong(0);
329         setTargetAuthenticationStrategy(new DefaultAuthenticationStrategy() {
330 
331             @Override
332             public List<AuthScheme> select(
333                     final ChallengeType challengeType,
334                     final Map<String, AuthChallenge> challenges,
335                     final HttpContext context) {
336                 count.incrementAndGet();
337                 return super.select(challengeType, challenges, context);
338             }
339         });
340         final HttpHost target = start();
341 
342         final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
343         credsProvider.setCredentials(new AuthScope(null, null, -1, null ,null),
344                 new UsernamePasswordCredentials("test", "test".toCharArray()));
345         final HttpClientContext context = HttpClientContext.create();
346         context.setCredentialsProvider(credsProvider);
347 
348         final Future<SimpleHttpResponse> future1 = httpclient.execute(SimpleHttpRequests.get(target, "/"), context, null);
349         final HttpResponse response1 = future1.get();
350         Assert.assertNotNull(response1);
351         Assert.assertEquals(HttpStatus.SC_OK, response1.getCode());
352 
353         final Future<SimpleHttpResponse> future2 = httpclient.execute(SimpleHttpRequests.get(target, "/"), context, null);
354         final HttpResponse response2 = future2.get();
355         Assert.assertNotNull(response2);
356         Assert.assertEquals(HttpStatus.SC_OK, response2.getCode());
357 
358         Assert.assertEquals(1, count.get());
359     }
360 
361     @Test
362     public void testAuthenticationUserinfoInRequestSuccess() throws Exception {
363         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
364 
365             @Override
366             public AsyncServerExchangeHandler get() {
367                 return new AsyncEchoHandler();
368             }
369 
370         });
371         final HttpHost target = start();
372 
373         final HttpClientContext context = HttpClientContext.create();
374         final Future<SimpleHttpResponse> future = httpclient.execute(
375                 SimpleHttpRequests.get(target.getSchemeName() + "://test:test@" +  target.toHostString() + "/"), context, null);
376         final SimpleHttpResponse response = future.get();
377 
378         Assert.assertNotNull(response);
379         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
380     }
381 
382     @Test
383     public void testAuthenticationUserinfoInRequestFailure() throws Exception {
384         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
385 
386             @Override
387             public AsyncServerExchangeHandler get() {
388                 return new AsyncEchoHandler();
389             }
390 
391         });
392         final HttpHost target = start();
393 
394         final HttpClientContext context = HttpClientContext.create();
395         final Future<SimpleHttpResponse> future = httpclient.execute(
396                 SimpleHttpRequests.get(target.getSchemeName() + "://test:all-worng@" +  target.toHostString() + "/"), context, null);
397         final SimpleHttpResponse response = future.get();
398 
399         Assert.assertNotNull(response);
400         Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
401     }
402 
403     @Test
404     public void testAuthenticationUserinfoInRedirectSuccess() throws Exception {
405         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
406 
407             @Override
408             public AsyncServerExchangeHandler get() {
409                 return new AsyncEchoHandler();
410             }
411 
412         });
413         final HttpHost target = start();
414         server.register("/thatway", new Supplier<AsyncServerExchangeHandler>() {
415 
416             @Override
417             public AsyncServerExchangeHandler get() {
418                 return new AbstractSimpleServerExchangeHandler() {
419 
420                     @Override
421                     protected SimpleHttpResponse handle(
422                             final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
423                         final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_MOVED_PERMANENTLY);
424                         response.addHeader(new BasicHeader("Location", target.getSchemeName() + "://test:test@" + target.toHostString() + "/"));
425                         return response;
426                     }
427                 };
428             }
429 
430         });
431 
432         final HttpClientContext context = HttpClientContext.create();
433         final Future<SimpleHttpResponse> future = httpclient.execute(
434                 SimpleHttpRequests.get(target.getSchemeName() + "://test:test@" +  target.toHostString() + "/thatway"), context, null);
435         final SimpleHttpResponse response = future.get();
436 
437         Assert.assertNotNull(response);
438         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
439     }
440 
441     @Test
442     public void testReauthentication() throws Exception {
443         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
444 
445             @Override
446             public AsyncServerExchangeHandler get() {
447                 return new AsyncEchoHandler();
448             }
449 
450         });
451         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
452                 new UsernamePasswordCredentials("test", "test".toCharArray()));
453 
454         final Registry<AuthSchemeFactory> authSchemeRegistry = RegistryBuilder.<AuthSchemeFactory>create()
455                 .register("MyBasic", new AuthSchemeFactory() {
456 
457                     @Override
458                     public AuthScheme create(final HttpContext context) {
459                         return new BasicScheme() {
460 
461                             private static final long serialVersionUID = 1L;
462 
463                             @Override
464                             public String getName() {
465                                 return "MyBasic";
466                             }
467 
468                         };
469                     }
470 
471                 })
472                 .build();
473         setDefaultAuthSchemeRegistry(authSchemeRegistry);
474 
475         final Authenticator authenticator = new BasicTestAuthenticator("test:test", "test realm") {
476 
477             private final AtomicLong count = new AtomicLong(0);
478 
479             @Override
480             public boolean authenticate(final URIAuthority authority, final String requestUri, final String credentials) {
481                 final boolean authenticated = super.authenticate(authority, requestUri, credentials);
482                 if (authenticated) {
483                     return this.count.incrementAndGet() % 4 != 0;
484                 }
485                 return false;
486             }
487         };
488 
489         final HttpHost target = start(
490                 new Decorator<AsyncServerExchangeHandler>() {
491 
492                     @Override
493                     public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
494                         return new AuthenticatingAsyncDecorator(exchangeHandler, authenticator) {
495 
496                             @Override
497                             protected void customizeUnauthorizedResponse(final HttpResponse unauthorized) {
498                                 unauthorized.removeHeaders(HttpHeaders.WWW_AUTHENTICATE);
499                                 unauthorized.addHeader(HttpHeaders.WWW_AUTHENTICATE, "MyBasic realm=\"test realm\"");
500                             }
501 
502                         };
503                     }
504 
505                 });
506 
507         final RequestConfig config = RequestConfig.custom()
508                 .setTargetPreferredAuthSchemes(Arrays.asList("MyBasic"))
509                 .build();
510         final HttpClientContext context = HttpClientContext.create();
511         context.setCredentialsProvider(credsProvider);
512 
513         for (int i = 0; i < 10; i++) {
514             final SimpleHttpRequest request = SimpleHttpRequests.get(target, "/");
515             request.setConfig(config);
516             final Future<SimpleHttpResponse> future = httpclient.execute(request, context, null);
517             final SimpleHttpResponse response = future.get();
518             Assert.assertNotNull(response);
519             Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
520         }
521     }
522 
523     @Test
524     public void testAuthenticationFallback() throws Exception {
525         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
526 
527             @Override
528             public AsyncServerExchangeHandler get() {
529                 return new AsyncEchoHandler();
530             }
531 
532         });
533         final HttpHost target = start(
534                 new Decorator<AsyncServerExchangeHandler>() {
535 
536                     @Override
537                     public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
538                         return new AuthenticatingAsyncDecorator(exchangeHandler, new BasicTestAuthenticator("test:test", "test realm")) {
539 
540                             @Override
541                             protected void customizeUnauthorizedResponse(final HttpResponse unauthorized) {
542                                 unauthorized.addHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.DIGEST + " realm=\"test realm\" invalid");
543                             }
544 
545                         };
546                     }
547 
548                 });
549 
550         final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
551                 new UsernamePasswordCredentials("test", "test".toCharArray()));
552         final HttpClientContext context = HttpClientContext.create();
553         context.setCredentialsProvider(credsProvider);
554 
555         final Future<SimpleHttpResponse> future = httpclient.execute(SimpleHttpRequests.get(target, "/"), context, null);
556         final SimpleHttpResponse response = future.get();
557         Assert.assertNotNull(response);
558         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
559         final AuthScope authscope = credsProvider.getAuthScope();
560         Assert.assertNotNull(authscope);
561         Assert.assertEquals("test realm", authscope.getRealm());
562     }
563 
564 }