View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit;
16  
17  import static org.junit.jupiter.api.Assertions.fail;
18  
19  import java.io.StringWriter;
20  import java.net.URL;
21  
22  import org.apache.commons.lang3.StringUtils;
23  import org.apache.logging.log4j.Level;
24  import org.apache.logging.log4j.LogManager;
25  import org.apache.logging.log4j.core.Logger;
26  import org.apache.logging.log4j.core.appender.WriterAppender;
27  import org.apache.logging.log4j.core.config.Configurator;
28  import org.apache.logging.log4j.core.layout.PatternLayout;
29  import org.htmlunit.html.HtmlPage;
30  import org.htmlunit.junit.annotation.Alerts;
31  import org.junit.jupiter.api.Test;
32  
33  /**
34   * Tests for {@link DefaultCredentialsProvider}.
35   *
36   * @author Ahmed Ashour
37   * @author Ronald Brill
38   */
39  public class DefaultCredentialsProvider2Test extends WebServerTestCase {
40  
41      /**
42       * {@inheritDoc}
43       */
44      @Override
45      protected boolean isBasicAuthentication() {
46          return true;
47      }
48  
49      /**
50       * @throws Exception if an error occurs
51       */
52      @Test
53      public void basicAuthenticationWrongUserName() throws Exception {
54          getMockWebConnection().setResponse(URL_SECOND, "Hello World");
55  
56          // wrong user name
57          getWebClient().getCredentialsProvider().clear();
58          ((DefaultCredentialsProvider) getWebClient().getCredentialsProvider())
59                                          .addCredentials("joe", "jetty".toCharArray());
60  
61          try {
62              loadPage("Hi There");
63              fail("Should not be authorized");
64          }
65          catch (final FailingHttpStatusCodeException e) {
66              //success
67          }
68      }
69  
70      /**
71       * @throws Exception if an error occurs
72       */
73      @Test
74      public void basicAuthenticationWrongPassword() throws Exception {
75          getMockWebConnection().setResponse(URL_SECOND, "Hello World");
76  
77          // wrong user name
78          getWebClient().getCredentialsProvider().clear();
79          ((DefaultCredentialsProvider) getWebClient().getCredentialsProvider())
80                                          .addCredentials("jetty", "secret".toCharArray());
81  
82          try {
83              loadPage("Hi There");
84              fail("Should not be authorized");
85          }
86          catch (final FailingHttpStatusCodeException e) {
87              //success
88          }
89      }
90  
91      /**
92       * Tests that on calling the website twice, only the first time unauthorized response is returned.
93       *
94       * @throws Exception if an error occurs
95       */
96      @Test
97      public void basicAuthentication_singleAuthenticaiton() throws Exception {
98          final Logger logger = (Logger) LogManager.getLogger("org.apache.http.headers");
99          final Level oldLevel = logger.getLevel();
100         Configurator.setLevel(logger.getName(), Level.DEBUG);
101 
102         final StringWriter stringWriter = new StringWriter();
103         final PatternLayout layout = PatternLayout.newBuilder().withPattern("%msg%n").build();
104 
105         final WriterAppender writerAppender = WriterAppender.newBuilder().setName("writeLogger").setTarget(stringWriter)
106                 .setLayout(layout).build();
107         writerAppender.start();
108 
109         logger.addAppender(writerAppender);
110         try {
111             ((DefaultCredentialsProvider) getWebClient().getCredentialsProvider())
112                                             .addCredentials("jetty", "jetty".toCharArray());
113 
114             loadPage("Hi There");
115             int unauthorizedCount = StringUtils.countMatches(stringWriter.toString(), "HTTP/1.1 401");
116             assertEquals(1, unauthorizedCount);
117 
118             // and again
119             loadPage("Hi There");
120             unauthorizedCount = StringUtils.countMatches(stringWriter.toString(), "HTTP/1.1 401");
121             assertEquals(1, unauthorizedCount);
122         }
123         finally {
124             logger.removeAppender(writerAppender);
125             Configurator.setLevel(logger.getName(), oldLevel);
126         }
127     }
128 
129     /**
130      * @throws Exception if an error occurs
131      */
132     @Test
133     @Alerts("SecRet")
134     public void basicAuthenticationUserFromUrl() throws Exception {
135         final String html = DOCTYPE_HTML + "<html><body onload='alert(\"SecRet\")'></body></html>";
136         getMockWebConnection().setDefaultResponse(html);
137 
138         getWebClient().getCredentialsProvider().clear();
139 
140         try {
141             loadPage(html, URL_FIRST);
142             fail("Should not be authorized");
143         }
144         catch (final FailingHttpStatusCodeException e) {
145             //success
146         }
147 
148         final boolean urlWithCredentials = true;
149 
150         try {
151             //  now a url with credentials
152             final URL url = new URL("http://jetty:jetty@localhost:" + PORT + "/");
153             loadPageWithAlerts(html, url);
154             if (!urlWithCredentials) {
155                 fail("Should not be authorized");
156             }
157         }
158         catch (final FailingHttpStatusCodeException e) {
159             if (urlWithCredentials) {
160                 throw e;
161             }
162         }
163 
164         try {
165             // next step without credentials but the credentials are still known
166             loadPageWithAlerts(html, URL_FIRST);
167             if (!urlWithCredentials) {
168                 fail("Should not be authorized");
169             }
170         }
171         catch (final FailingHttpStatusCodeException e) {
172             if (urlWithCredentials) {
173                 throw e;
174             }
175         }
176     }
177 
178     /**
179      * @throws Exception if an error occurs
180      */
181     @Test
182     @Alerts("SecRet")
183     public void basicAuthenticationUserFromUrlUsedForNextSteps() throws Exception {
184         final String html = DOCTYPE_HTML + "<html><body onload='alert(\"SecRet\")'></body></html>";
185         getMockWebConnection().setDefaultResponse(html);
186 
187         getWebClient().getCredentialsProvider().clear();
188 
189         try {
190             // no credentials
191             loadPage(html, URL_FIRST);
192             fail("Should not be authorized");
193         }
194         catch (final FailingHttpStatusCodeException e) {
195             //success
196         }
197 
198         final boolean urlWithCredentials = true;
199 
200         try {
201             // now a url with credentials
202             final URL url = new URL("http://jetty:jetty@localhost:" + PORT + "/");
203             loadPageWithAlerts(url);
204             if (!urlWithCredentials) {
205                 fail("Should not be authorized");
206             }
207         }
208         catch (final FailingHttpStatusCodeException e) {
209             if (urlWithCredentials) {
210                 throw e;
211             }
212         }
213 
214         try {
215             // next step without credentials but the credentials are still known
216             loadPageWithAlerts(URL_FIRST);
217             if (!urlWithCredentials) {
218                 fail("Should not be authorized");
219             }
220         }
221         catch (final FailingHttpStatusCodeException e) {
222             if (urlWithCredentials) {
223                 throw e;
224             }
225         }
226 
227         try {
228             // different path
229             final URL url = new URL(URL_FIRST, "somewhere");
230             loadPageWithAlerts(url);
231             if (!urlWithCredentials) {
232                 fail("Should not be authorized");
233             }
234         }
235         catch (final FailingHttpStatusCodeException e) {
236             if (urlWithCredentials) {
237                 throw e;
238             }
239         }
240     }
241 
242     /**
243      * @throws Exception if an error occurs
244      */
245     @Test
246     @Alerts("SecRet")
247     public void basicAuthenticationUserFromUrlOverwrite() throws Exception {
248         final String html = DOCTYPE_HTML + "<html><body onload='alert(\"SecRet\")'></body></html>";
249         getMockWebConnection().setDefaultResponse(html);
250 
251         getWebClient().getCredentialsProvider().clear();
252 
253         try {
254             // no credentials
255             loadPage(html, URL_FIRST);
256             fail("Should not be authorized");
257         }
258         catch (final FailingHttpStatusCodeException e) {
259             //success
260         }
261 
262         final boolean urlWithCredentials = true;
263 
264         try {
265             // now a url with credentials
266             final URL url = new URL("http://jetty:jetty@localhost:" + PORT + "/");
267             loadPageWithAlerts(url);
268             if (!urlWithCredentials) {
269                 fail("Should not be authorized");
270             }
271         }
272         catch (final FailingHttpStatusCodeException e) {
273             if (urlWithCredentials) {
274                 throw e;
275             }
276         }
277 
278         try {
279             // next step without credentials but the credentials are still known
280             loadPageWithAlerts(URL_FIRST);
281             if (!urlWithCredentials) {
282                 fail("Should not be authorized");
283             }
284         }
285         catch (final FailingHttpStatusCodeException e) {
286             if (urlWithCredentials) {
287                 throw e;
288             }
289         }
290 
291         try {
292             final URL url = new URL("http://jetty:wrong@localhost:" + PORT + "/");
293             loadPage(html, url);
294             fail("Should not be authorized");
295         }
296         catch (final FailingHttpStatusCodeException e) {
297             //success
298         }
299     }
300 
301     /**
302      * @throws Exception if an error occurs
303      */
304     @Test
305     @Alerts("SecRet")
306     public void basicAuthenticationUserFromUrlOverwriteDefaultCredentials() throws Exception {
307         final String html = DOCTYPE_HTML + "<html><body onload='alert(\"SecRet\")'></body></html>";
308         getMockWebConnection().setDefaultResponse(html);
309 
310         getWebClient().getCredentialsProvider().clear();
311         ((DefaultCredentialsProvider) getWebClient().getCredentialsProvider())
312                                         .addCredentials("jetty", "jetty".toCharArray());
313 
314         // use default credentials
315         loadPageWithAlerts(URL_FIRST);
316 
317         try {
318             final URL url = new URL("http://joe:jetty@localhost:" + PORT + "/");
319             final HtmlPage page = loadPage(html, url);
320             fail("Should not be authorized");
321         }
322         catch (final FailingHttpStatusCodeException e) {
323             //success
324         }
325     }
326 
327     /**
328      * @throws Exception if an error occurs
329      */
330     @Test
331     @Alerts("SecRet")
332     public void basicAuthenticationUserFromUrlOverwriteWrongDefaultCredentials() throws Exception {
333         final String html = DOCTYPE_HTML + "<html><body onload='alert(\"SecRet\")'></body></html>";
334         getMockWebConnection().setDefaultResponse(html);
335 
336         getWebClient().getCredentialsProvider().clear();
337         ((DefaultCredentialsProvider) getWebClient().getCredentialsProvider())
338                                         .addCredentials("joe", "hack".toCharArray());
339 
340         // use default wrong credentials
341         try {
342             loadPage(html, URL_FIRST);
343             fail("Should not be authorized");
344         }
345         catch (final FailingHttpStatusCodeException e) {
346             //success
347         }
348 
349         final boolean urlWithCredentials = true;
350 
351         try {
352             // now a url with correct credentials
353             final URL url = new URL("http://jetty:jetty@localhost:" + PORT + "/");
354             loadPageWithAlerts(url);
355             if (!urlWithCredentials) {
356                 fail("Should not be authorized");
357             }
358         }
359         catch (final FailingHttpStatusCodeException e) {
360             if (urlWithCredentials) {
361                 throw e;
362             }
363         }
364     }
365 
366     /**
367      * @throws Exception if an error occurs
368      */
369     @Test
370     @Alerts("Hello World")
371     public void basicAuthenticationXHR() throws Exception {
372         final String html = DOCTYPE_HTML
373             + "<html><head><script>\n"
374             + "var xhr = new XMLHttpRequest();\n"
375             + "var handler = function() {\n"
376             + "  if (xhr.readyState == 4)\n"
377             + "    alert(xhr.responseText);\n"
378             + "}\n"
379             + "xhr.onreadystatechange = handler;\n"
380             + "xhr.open('GET', '" + URL_SECOND + "', true);\n"
381             + "xhr.send('');\n"
382             + "</script></head><body></body></html>";
383 
384         ((DefaultCredentialsProvider) getWebClient().getCredentialsProvider())
385                                         .addCredentials("jetty", "jetty".toCharArray());
386         getMockWebConnection().setResponse(URL_FIRST, html);
387         getMockWebConnection().setResponse(URL_SECOND, "Hello World");
388         loadPageWithAlerts(html, URL_FIRST, DEFAULT_WAIT_TIME);
389     }
390 
391     /**
392      * @throws Exception if an error occurs
393      */
394     @Test
395     @Alerts("HTTP ERROR 401")
396     public void basicAuthenticationXHRWithUsername() throws Exception {
397         final String html = DOCTYPE_HTML
398             + "<html><head><script>\n"
399             + "var xhr = new XMLHttpRequest();\n"
400             + "var handler = function() {\n"
401             + "  if (xhr.readyState == 4) {\n"
402             + "    var s = xhr.responseText.replace(/[\\r\\n]/g, '')"
403             + ".replace(/.*(HTTP ERROR \\d+).*/g, '$1');\n"
404             + "    alert(s);\n"
405             + "  }\n"
406             + "}\n"
407             + "xhr.onreadystatechange = handler;\n"
408             + "xhr.open('GET', '/foo', true, 'joe');\n"
409             + "xhr.send('');\n"
410             + "</script></head><body></body></html>";
411 
412         ((DefaultCredentialsProvider) getWebClient().getCredentialsProvider())
413                                         .addCredentials("jetty", "jetty".toCharArray());
414         getMockWebConnection().setResponse(URL_FIRST, html);
415         getMockWebConnection().setResponse(URL_SECOND, "Hello World");
416         loadPageWithAlerts(html, URL_FIRST, DEFAULT_WAIT_TIME);
417     }
418 
419     /**
420      * @throws Exception if an error occurs
421      */
422     @Test
423     @Alerts("HTTP ERROR 401")
424     public void basicAuthenticationXHRWithUser() throws Exception {
425         final String html = DOCTYPE_HTML
426             + "<html><head><script>\n"
427             + "var xhr = new XMLHttpRequest();\n"
428             + "var handler = function() {\n"
429             + "  if (xhr.readyState == 4) {\n"
430             + "    var s = xhr.responseText.replace(/[\\r\\n]/g, '')"
431             + ".replace(/.*(HTTP ERROR \\d+).*/g, '$1');\n"
432             + "    alert(s);\n"
433             + "  }\n"
434             + "}\n"
435             + "xhr.onreadystatechange = handler;\n"
436             + "xhr.open('GET', '/foo', true, 'joe', 'secret');\n"
437             + "xhr.send('');\n"
438             + "</script></head><body></body></html>";
439 
440         ((DefaultCredentialsProvider) getWebClient().getCredentialsProvider())
441                                         .addCredentials("jetty", "jetty".toCharArray());
442         getMockWebConnection().setResponse(URL_FIRST, html);
443         getMockWebConnection().setResponse(URL_SECOND, "Hello World");
444         loadPageWithAlerts(html, URL_FIRST, DEFAULT_WAIT_TIME);
445     }
446 }