• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hazendaz / httpunit / 656

06 Dec 2025 09:11PM UTC coverage: 80.452% (+0.02%) from 80.435%
656

push

github

hazendaz
[maven-release-plugin] prepare for next development iteration

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

8245 of 10137 relevant lines covered (81.34%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.61
/src/main/java/com/meterware/httpunit/AuthenticationChallenge.java
1
/*
2
 * MIT License
3
 *
4
 * Copyright 2011-2025 Russell Gold
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
7
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
8
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
9
 * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions
12
 * of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
15
 * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
17
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
18
 * DEALINGS IN THE SOFTWARE.
19
 */
20
package com.meterware.httpunit;
21

22
import java.net.MalformedURLException;
23
import java.net.PasswordAuthentication;
24
import java.nio.charset.StandardCharsets;
25
import java.security.MessageDigest;
26
import java.security.NoSuchAlgorithmException;
27
import java.util.Base64;
28

29
/**
30
 * A challenge for authentication from the server to a client.
31
 **/
32
class AuthenticationChallenge extends HttpHeader {
33

34
    /** The client. */
35
    private WebClient _client;
36

37
    /** The request. */
38
    private WebRequest _request;
39

40
    /** The Constant BASIC_AUTHENTICATION. */
41
    private static final AuthenticationStrategy BASIC_AUTHENTICATION = new BasicAuthenticationStrategy();
1✔
42

43
    /** The Constant DIGEST_AUTHENTICATION. */
44
    private static final AuthenticationStrategy DIGEST_AUTHENTICATION = new DigestAuthenticationStrategy();
1✔
45

46
    /**
47
     * Creates the exception.
48
     *
49
     * @param wwwAuthenticateHeader
50
     *            the www authenticate header
51
     *
52
     * @return the authorization required exception
53
     */
54
    static AuthorizationRequiredException createException(String wwwAuthenticateHeader) {
55
        AuthenticationChallenge challenge = new AuthenticationChallenge(null, null, wwwAuthenticateHeader);
×
56
        return challenge.createAuthorizationRequiredException();
×
57
    }
58

59
    /**
60
     * Instantiates a new authentication challenge.
61
     *
62
     * @param client
63
     *            the client
64
     * @param request
65
     *            the request
66
     * @param headerString
67
     *            the header string
68
     */
69
    AuthenticationChallenge(WebClient client, WebRequest request, String headerString) {
70
        super(headerString, "Basic");
1✔
71
        _client = client;
1✔
72
        _request = request;
1✔
73
    }
1✔
74

75
    /**
76
     * check whether authentication is needed.
77
     *
78
     * @return true, if successful
79
     */
80
    boolean needToAuthenticate() {
81
        if (getAuthenticationType() == null) {
1✔
82
            return false;
1✔
83
        }
84
        if (getCredentialsForRealm() != null) {
1✔
85
            return true;
1✔
86
        }
87
        if (!_client.getExceptionsThrownOnErrorStatus()) {
1!
88
            return false;
×
89
        }
90

91
        throw createAuthorizationRequiredException();
1✔
92
    }
93

94
    /**
95
     * Gets the authentication type.
96
     *
97
     * @return the authentication type
98
     */
99
    private String getAuthenticationType() {
100
        String result = getLabel();
1✔
101
        if (_headerString != null && _headerString.equals("Negotiate")) {
1✔
102
            result = null;
1✔
103
        }
104
        return result;
1✔
105
    }
106

107
    /**
108
     * Creates the authentication header.
109
     *
110
     * @return the string
111
     */
112
    String createAuthenticationHeader() {
113
        PasswordAuthentication credentials = getCredentialsForRealm();
1✔
114
        return getAuthenticationStrategy().createAuthenticationHeader(this, credentials.getUserName(),
1✔
115
                new String(credentials.getPassword()));
1✔
116
    }
117

118
    /**
119
     * Gets the authentication strategy.
120
     *
121
     * @return the authentication strategy
122
     */
123
    private AuthenticationStrategy getAuthenticationStrategy() {
124
        if (getAuthenticationType().equalsIgnoreCase("basic")) {
1✔
125
            return BASIC_AUTHENTICATION;
1✔
126
        }
127
        if (getAuthenticationType().equalsIgnoreCase("digest")) {
1!
128
            return DIGEST_AUTHENTICATION;
1✔
129
        }
130
        throw new RuntimeException("Unsupported authentication type '" + getAuthenticationType() + "'");
×
131
    }
132

133
    /**
134
     * Creates the authorization required exception.
135
     *
136
     * @return the authorization required exception
137
     */
138
    private AuthorizationRequiredException createAuthorizationRequiredException() {
139
        return AuthorizationRequiredException.createException(getAuthenticationType(), getProperties());
1✔
140
    }
141

142
    /**
143
     * get the credentials for the realm property.
144
     *
145
     * @return the credentials for realm
146
     */
147
    private PasswordAuthentication getCredentialsForRealm() {
148
        String realm = getProperty("realm");
1✔
149
        PasswordAuthentication result = null;
1✔
150
        if (realm != null) {
1!
151
            result = _client.getCredentialsForRealm(realm);
1✔
152
        }
153
        return result;
1✔
154
    }
155

156
    /**
157
     * Gets the method.
158
     *
159
     * @return the method
160
     */
161
    private String getMethod() {
162
        return null == _request ? null : _request.getMethod();
1!
163
    }
164

165
    /**
166
     * Gets the request uri.
167
     *
168
     * @return the request uri
169
     */
170
    private String getRequestUri() {
171
        try {
172
            return null == _request ? null : _request.getURL().getFile();
1!
173
        } catch (MalformedURLException e) {
×
174
            return null;
×
175
        }
176
    }
177

178
    /**
179
     * The Interface AuthenticationStrategy.
180
     */
181
    private interface AuthenticationStrategy {
182

183
        /**
184
         * Creates the authentication header.
185
         *
186
         * @param challenge
187
         *            the challenge
188
         * @param username
189
         *            the username
190
         * @param password
191
         *            the password
192
         *
193
         * @return the string
194
         */
195
        String createAuthenticationHeader(AuthenticationChallenge challenge, String username, String password);
196
    }
197

198
    /**
199
     * The Class BasicAuthenticationStrategy.
200
     */
201
    private static class BasicAuthenticationStrategy implements AuthenticationStrategy {
202

203
        @Override
204
        public String createAuthenticationHeader(AuthenticationChallenge challenge, String userName, String password) {
205
            return "Basic "
1✔
206
                    + Base64.getEncoder().encodeToString((userName + ':' + password).getBytes(StandardCharsets.UTF_8));
1✔
207
        }
208

209
    }
210

211
    /**
212
     * The Class DigestAuthenticationStrategy.
213
     */
214
    private static class DigestAuthenticationStrategy implements AuthenticationStrategy {
215

216
        /**
217
         * The Class Algorithm.
218
         */
219
        private static class Algorithm {
220

221
            /**
222
             * Append params.
223
             *
224
             * @param sb
225
             *            the sb
226
             * @param challenge
227
             *            the challenge
228
             * @param userName
229
             *            the user name
230
             * @param password
231
             *            the password
232
             */
233
            public void appendParams(StringBuilder sb, AuthenticationChallenge challenge, String userName,
234
                    String password) {
235
                appendDigestParams(sb, challenge.getProperty("realm"), challenge.getProperty("nonce"),
1✔
236
                        challenge.getRequestUri(), userName, password, challenge.getMethod(),
1✔
237
                        challenge.getProperty("opaque"));
1✔
238
            }
1✔
239

240
            /**
241
             * Append digest params.
242
             *
243
             * @param sb
244
             *            the sb
245
             * @param realm
246
             *            the realm
247
             * @param nonce
248
             *            the nonce
249
             * @param uri
250
             *            the uri
251
             * @param userName
252
             *            the user name
253
             * @param password
254
             *            the password
255
             * @param method
256
             *            the method
257
             * @param opaque
258
             *            the opaque
259
             */
260
            protected void appendDigestParams(StringBuilder sb, String realm, String nonce, String uri, String userName,
261
                    String password, String method, String opaque) {
262
                sb.append("username=").append(quote(userName));
1✔
263
                append(sb, "realm", realm);
1✔
264
                append(sb, "nonce", nonce);
1✔
265
                append(sb, "uri", uri);
1✔
266
                append(sb, "response", getResponse(userName, realm, password, nonce, uri, method));
1✔
267
                if (opaque != null) {
1✔
268
                    append(sb, "opaque", opaque);
1✔
269
                }
270
            }
1✔
271

272
            /**
273
             * Gets the response.
274
             *
275
             * @param userName
276
             *            the user name
277
             * @param realm
278
             *            the realm
279
             * @param password
280
             *            the password
281
             * @param nonce
282
             *            the nonce
283
             * @param uri
284
             *            the uri
285
             * @param method
286
             *            the method
287
             *
288
             * @return the response
289
             */
290
            protected String getResponse(String userName, String realm, String password, String nonce, String uri,
291
                    String method) {
292
                try {
293
                    String a1 = A1(userName, password, realm, nonce);
1✔
294
                    String a2 = A2(uri, method);
1✔
295
                    String ha1 = H(a1);
1✔
296
                    String ha2 = H(a2);
1✔
297
                    return KD(ha1, nonce + ':' + ha2);
1✔
298
                } catch (NoSuchAlgorithmException e) {
×
299
                    return "";
×
300
                }
301
            }
302

303
            /**
304
             * A1.
305
             *
306
             * @param userName
307
             *            the user name
308
             * @param password
309
             *            the password
310
             * @param realm
311
             *            the realm
312
             * @param nonce
313
             *            the nonce
314
             *
315
             * @return the string
316
             */
317
            protected String A1(String userName, String password, String realm, String nonce) {
318
                return userName + ':' + realm + ':' + password;
1✔
319
            }
320

321
            /**
322
             * A2.
323
             *
324
             * @param uri
325
             *            the uri
326
             * @param method
327
             *            the method
328
             *
329
             * @return the string
330
             */
331
            protected String A2(String uri, String method) {
332
                return method + ':' + uri;
1✔
333
            }
334

335
            /**
336
             * Kd.
337
             *
338
             * @param secret
339
             *            the secret
340
             * @param data
341
             *            the data
342
             *
343
             * @return the string
344
             *
345
             * @throws NoSuchAlgorithmException
346
             *             the no such algorithm exception
347
             */
348
            protected final String KD(String secret, String data) throws NoSuchAlgorithmException {
349
                return H(secret + ":" + data);
1✔
350
            }
351

352
            /**
353
             * H.
354
             *
355
             * @param data
356
             *            the data
357
             *
358
             * @return the string
359
             *
360
             * @throws NoSuchAlgorithmException
361
             *             the no such algorithm exception
362
             */
363
            protected final String H(String data) throws NoSuchAlgorithmException {
364
                MessageDigest digest = MessageDigest.getInstance("SHA-256");
1✔
365
                digest.update(data.getBytes(StandardCharsets.UTF_8));
1✔
366
                byte[] bytes = digest.digest();
1✔
367
                StringBuilder sb = new StringBuilder();
1✔
368
                for (byte element : bytes) {
1✔
369
                    int aByte = element;
1✔
370
                    if (aByte < 0) {
1✔
371
                        aByte += 256;
1✔
372
                    }
373
                    if (aByte < 16) {
1✔
374
                        sb.append('0');
1✔
375
                    }
376
                    sb.append(Integer.toHexString(aByte));
1✔
377
                }
378

379
                return sb.toString();
1✔
380
            }
381

382
            /**
383
             * Append.
384
             *
385
             * @param sb
386
             *            the sb
387
             * @param name
388
             *            the name
389
             * @param value
390
             *            the value
391
             */
392
            private void append(StringBuilder sb, String name, String value) {
393
                sb.append(",").append(name).append("=").append(quote(value));
1✔
394
            }
1✔
395

396
            /**
397
             * Quote.
398
             *
399
             * @param value
400
             *            the value
401
             *
402
             * @return the string
403
             */
404
            private String quote(String value) {
405
                if (value.startsWith("\"")) {
1!
406
                    return value;
×
407
                }
408
                return "\"" + value + "\"";
1✔
409
            }
410

411
        }
412

413
        @Override
414
        public String createAuthenticationHeader(AuthenticationChallenge challenge, String userName, String password) {
415
            StringBuilder sb = new StringBuilder("Digest ");
1✔
416
            Algorithm algorithm = new Algorithm();
1✔
417
            algorithm.appendParams(sb, challenge, userName, password);
1✔
418
            return sb.toString();
1✔
419
        }
420

421
    }
422

423
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc