View Javadoc
1   /*
2    * Copyright (c) 2011-2022, jcabi.com
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met: 1) Redistributions of source code must retain the above
8    * copyright notice, this list of conditions and the following
9    * disclaimer. 2) Redistributions in binary form must reproduce the above
10   * copyright notice, this list of conditions and the following
11   * disclaimer in the documentation and/or other materials provided
12   * with the distribution. 3) Neither the name of the jcabi.com nor
13   * the names of its contributors may be used to endorse or promote
14   * products derived from this software without specific prior written
15   * permission.
16   *
17   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
19   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28   * OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package com.jcabi.http.wire;
31  
32  import com.jcabi.http.mock.MkAnswer;
33  import com.jcabi.http.mock.MkContainer;
34  import com.jcabi.http.mock.MkGrizzlyContainer;
35  import com.jcabi.http.mock.MkQueryMatchers;
36  import com.jcabi.http.request.JdkRequest;
37  import com.jcabi.http.response.RestResponse;
38  import jakarta.ws.rs.core.HttpHeaders;
39  import jakarta.ws.rs.core.UriBuilder;
40  import jakarta.xml.bind.DatatypeConverter;
41  import java.net.HttpURLConnection;
42  import java.net.URI;
43  import java.net.URLEncoder;
44  import java.nio.charset.StandardCharsets;
45  import javax.net.ssl.HttpsURLConnection;
46  import org.hamcrest.MatcherAssert;
47  import org.hamcrest.Matchers;
48  import org.junit.jupiter.api.Assertions;
49  import org.junit.jupiter.api.Test;
50  import org.junit.jupiter.api.function.Executable;
51  import org.junit.jupiter.params.ParameterizedTest;
52  import org.junit.jupiter.params.provider.CsvSource;
53  
54  /**
55   * Test case for {@link BasicAuthWire}.
56   *
57   * @since 1.17.1
58   */
59  final class BasicAuthWireTest {
60  
61      /**
62       * The format of the credentials as <code>username:password</code>.
63       */
64      private static final String CRED_FORMAT = "%s:%s";
65  
66      /**
67       * Tests if the wire generates the authorization header correctly.
68       *
69       * @param username The username to user for authentication
70       * @param password The password to user for authentication
71       * @throws Exception If something goes wrong
72       */
73      @ParameterizedTest
74      @CsvSource({
75          "Alice,  secret",
76          "Bob,    s&e+c`ret",
77          "user,  \u20ac\u20ac"
78      })
79      void testHeader(
80          final String username, final String password
81      ) throws Exception {
82          final MkContainer container = new MkGrizzlyContainer().next(
83              new MkAnswer.Simple("")
84          ).start();
85          final URI uri = UriBuilder.fromUri(container.home()).userInfo(
86              String.format(
87                  BasicAuthWireTest.CRED_FORMAT,
88                  URLEncoder.encode(username, StandardCharsets.UTF_8.displayName()),
89                  URLEncoder.encode(password, StandardCharsets.UTF_8.displayName())
90              )
91          ).build();
92          final String expected = BasicAuthWireTest.expectHeader(
93              username,
94              password
95          );
96          new JdkRequest(uri)
97              .through(BasicAuthWire.class)
98              .fetch()
99              .as(RestResponse.class)
100             .assertStatus(HttpURLConnection.HTTP_OK);
101         container.stop();
102         MatcherAssert.assertThat(
103             container.take().headers().get(HttpHeaders.AUTHORIZATION).get(0),
104             Matchers.equalTo(expected)
105         );
106     }
107 
108     /**
109      * Tests if the wire strips user info from URI, after the header was added.
110      *
111      * @throws Exception If something goes wrong
112      */
113     @Test
114     void shouldStripUserInfo() throws Exception {
115         final MkContainer container = new MkGrizzlyContainer().next(
116             new MkAnswer.Simple(HttpsURLConnection.HTTP_NOT_FOUND),
117             MkQueryMatchers.hasHeader(
118                 "Authorization", Matchers.contains(
119                     BasicAuthWireTest.expectHeader("foo", "bar")
120                 )
121             )
122         ).start();
123         final String userinfo = "foo:bar";
124         final URI uri = UriBuilder.fromUri(container.home()).userInfo(
125             userinfo
126         ).build();
127         MatcherAssert.assertThat(
128             Assertions.assertThrows(
129                 AssertionError.class,
130                 new Executable() {
131                     @Override
132                     public void execute() throws Throwable {
133                         new JdkRequest(uri)
134                             .through(BasicAuthWire.class)
135                             .fetch()
136                             .as(RestResponse.class)
137                             .assertStatus(HttpURLConnection.HTTP_OK);
138                     }
139                 }
140             ),
141             Matchers.<AssertionError>hasToString(
142                 Matchers.not(
143                     Matchers.containsString(userinfo)
144                 )
145             )
146         );
147         container.stop();
148     }
149 
150     /**
151      * Creates the expected authorization header value for the
152      * given username.
153      *
154      * @param username The username to create the header for
155      * @param password The password to create the header for
156      * @return The header value in the form
157      *  <code>Basic &lt;base64 of username:password&gt;</code>
158      */
159     private static String expectHeader(
160         final String username,
161         final String password
162     ) {
163         final String credentials = DatatypeConverter.printBase64Binary(
164             String.format(
165                 BasicAuthWireTest.CRED_FORMAT,
166                 username,
167                 password
168             ).getBytes(StandardCharsets.UTF_8)
169         );
170         return String.format("Basic %s", credentials);
171     }
172 }