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

hazendaz / httpunit / 755

14 Feb 2026 07:14PM UTC coverage: 80.526%. Remained the same
755

push

github

hazendaz
[ci] Fix badge

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

8245 of 10124 relevant lines covered (81.44%)

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
 * SPDX-License-Identifier: MIT
3
 * See LICENSE file for details.
4
 *
5
 * Copyright 2000-2026 Russell Gold
6
 * Copyright 2021-2000 hazendaz
7
 */
8
package com.meterware.httpunit;
9

10
import java.net.MalformedURLException;
11
import java.net.PasswordAuthentication;
12
import java.nio.charset.StandardCharsets;
13
import java.security.MessageDigest;
14
import java.security.NoSuchAlgorithmException;
15
import java.util.Base64;
16

17
/**
18
 * A challenge for authentication from the server to a client.
19
 **/
20
class AuthenticationChallenge extends HttpHeader {
21

22
    /** The client. */
23
    private WebClient _client;
24

25
    /** The request. */
26
    private WebRequest _request;
27

28
    /** The Constant BASIC_AUTHENTICATION. */
29
    private static final AuthenticationStrategy BASIC_AUTHENTICATION = new BasicAuthenticationStrategy();
1✔
30

31
    /** The Constant DIGEST_AUTHENTICATION. */
32
    private static final AuthenticationStrategy DIGEST_AUTHENTICATION = new DigestAuthenticationStrategy();
1✔
33

34
    /**
35
     * Creates the exception.
36
     *
37
     * @param wwwAuthenticateHeader
38
     *            the www authenticate header
39
     *
40
     * @return the authorization required exception
41
     */
42
    static AuthorizationRequiredException createException(String wwwAuthenticateHeader) {
43
        AuthenticationChallenge challenge = new AuthenticationChallenge(null, null, wwwAuthenticateHeader);
×
44
        return challenge.createAuthorizationRequiredException();
×
45
    }
46

47
    /**
48
     * Instantiates a new authentication challenge.
49
     *
50
     * @param client
51
     *            the client
52
     * @param request
53
     *            the request
54
     * @param headerString
55
     *            the header string
56
     */
57
    AuthenticationChallenge(WebClient client, WebRequest request, String headerString) {
58
        super(headerString, "Basic");
1✔
59
        _client = client;
1✔
60
        _request = request;
1✔
61
    }
1✔
62

63
    /**
64
     * check whether authentication is needed.
65
     *
66
     * @return true, if successful
67
     */
68
    boolean needToAuthenticate() {
69
        if (getAuthenticationType() == null) {
1✔
70
            return false;
1✔
71
        }
72
        if (getCredentialsForRealm() != null) {
1✔
73
            return true;
1✔
74
        }
75
        if (!_client.getExceptionsThrownOnErrorStatus()) {
1!
76
            return false;
×
77
        }
78

79
        throw createAuthorizationRequiredException();
1✔
80
    }
81

82
    /**
83
     * Gets the authentication type.
84
     *
85
     * @return the authentication type
86
     */
87
    private String getAuthenticationType() {
88
        String result = getLabel();
1✔
89
        if (_headerString != null && _headerString.equals("Negotiate")) {
1✔
90
            result = null;
1✔
91
        }
92
        return result;
1✔
93
    }
94

95
    /**
96
     * Creates the authentication header.
97
     *
98
     * @return the string
99
     */
100
    String createAuthenticationHeader() {
101
        PasswordAuthentication credentials = getCredentialsForRealm();
1✔
102
        return getAuthenticationStrategy().createAuthenticationHeader(this, credentials.getUserName(),
1✔
103
                new String(credentials.getPassword()));
1✔
104
    }
105

106
    /**
107
     * Gets the authentication strategy.
108
     *
109
     * @return the authentication strategy
110
     */
111
    private AuthenticationStrategy getAuthenticationStrategy() {
112
        if (getAuthenticationType().equalsIgnoreCase("basic")) {
1✔
113
            return BASIC_AUTHENTICATION;
1✔
114
        }
115
        if (getAuthenticationType().equalsIgnoreCase("digest")) {
1!
116
            return DIGEST_AUTHENTICATION;
1✔
117
        }
118
        throw new RuntimeException("Unsupported authentication type '" + getAuthenticationType() + "'");
×
119
    }
120

121
    /**
122
     * Creates the authorization required exception.
123
     *
124
     * @return the authorization required exception
125
     */
126
    private AuthorizationRequiredException createAuthorizationRequiredException() {
127
        return AuthorizationRequiredException.createException(getAuthenticationType(), getProperties());
1✔
128
    }
129

130
    /**
131
     * get the credentials for the realm property.
132
     *
133
     * @return the credentials for realm
134
     */
135
    private PasswordAuthentication getCredentialsForRealm() {
136
        String realm = getProperty("realm");
1✔
137
        PasswordAuthentication result = null;
1✔
138
        if (realm != null) {
1!
139
            result = _client.getCredentialsForRealm(realm);
1✔
140
        }
141
        return result;
1✔
142
    }
143

144
    /**
145
     * Gets the method.
146
     *
147
     * @return the method
148
     */
149
    private String getMethod() {
150
        return null == _request ? null : _request.getMethod();
1!
151
    }
152

153
    /**
154
     * Gets the request uri.
155
     *
156
     * @return the request uri
157
     */
158
    private String getRequestUri() {
159
        try {
160
            return null == _request ? null : _request.getURL().getFile();
1!
161
        } catch (MalformedURLException e) {
×
162
            return null;
×
163
        }
164
    }
165

166
    /**
167
     * The Interface AuthenticationStrategy.
168
     */
169
    private interface AuthenticationStrategy {
170

171
        /**
172
         * Creates the authentication header.
173
         *
174
         * @param challenge
175
         *            the challenge
176
         * @param username
177
         *            the username
178
         * @param password
179
         *            the password
180
         *
181
         * @return the string
182
         */
183
        String createAuthenticationHeader(AuthenticationChallenge challenge, String username, String password);
184
    }
185

186
    /**
187
     * The Class BasicAuthenticationStrategy.
188
     */
189
    private static class BasicAuthenticationStrategy implements AuthenticationStrategy {
190

191
        @Override
192
        public String createAuthenticationHeader(AuthenticationChallenge challenge, String userName, String password) {
193
            return "Basic "
1✔
194
                    + Base64.getEncoder().encodeToString((userName + ':' + password).getBytes(StandardCharsets.UTF_8));
1✔
195
        }
196

197
    }
198

199
    /**
200
     * The Class DigestAuthenticationStrategy.
201
     */
202
    private static class DigestAuthenticationStrategy implements AuthenticationStrategy {
203

204
        /**
205
         * The Class Algorithm.
206
         */
207
        private static class Algorithm {
208

209
            /**
210
             * Append params.
211
             *
212
             * @param sb
213
             *            the sb
214
             * @param challenge
215
             *            the challenge
216
             * @param userName
217
             *            the user name
218
             * @param password
219
             *            the password
220
             */
221
            public void appendParams(StringBuilder sb, AuthenticationChallenge challenge, String userName,
222
                    String password) {
223
                appendDigestParams(sb, challenge.getProperty("realm"), challenge.getProperty("nonce"),
1✔
224
                        challenge.getRequestUri(), userName, password, challenge.getMethod(),
1✔
225
                        challenge.getProperty("opaque"));
1✔
226
            }
1✔
227

228
            /**
229
             * Append digest params.
230
             *
231
             * @param sb
232
             *            the sb
233
             * @param realm
234
             *            the realm
235
             * @param nonce
236
             *            the nonce
237
             * @param uri
238
             *            the uri
239
             * @param userName
240
             *            the user name
241
             * @param password
242
             *            the password
243
             * @param method
244
             *            the method
245
             * @param opaque
246
             *            the opaque
247
             */
248
            protected void appendDigestParams(StringBuilder sb, String realm, String nonce, String uri, String userName,
249
                    String password, String method, String opaque) {
250
                sb.append("username=").append(quote(userName));
1✔
251
                append(sb, "realm", realm);
1✔
252
                append(sb, "nonce", nonce);
1✔
253
                append(sb, "uri", uri);
1✔
254
                append(sb, "response", getResponse(userName, realm, password, nonce, uri, method));
1✔
255
                if (opaque != null) {
1✔
256
                    append(sb, "opaque", opaque);
1✔
257
                }
258
            }
1✔
259

260
            /**
261
             * Gets the response.
262
             *
263
             * @param userName
264
             *            the user name
265
             * @param realm
266
             *            the realm
267
             * @param password
268
             *            the password
269
             * @param nonce
270
             *            the nonce
271
             * @param uri
272
             *            the uri
273
             * @param method
274
             *            the method
275
             *
276
             * @return the response
277
             */
278
            protected String getResponse(String userName, String realm, String password, String nonce, String uri,
279
                    String method) {
280
                try {
281
                    String a1 = A1(userName, password, realm, nonce);
1✔
282
                    String a2 = A2(uri, method);
1✔
283
                    String ha1 = H(a1);
1✔
284
                    String ha2 = H(a2);
1✔
285
                    return KD(ha1, nonce + ':' + ha2);
1✔
286
                } catch (NoSuchAlgorithmException e) {
×
287
                    return "";
×
288
                }
289
            }
290

291
            /**
292
             * A1.
293
             *
294
             * @param userName
295
             *            the user name
296
             * @param password
297
             *            the password
298
             * @param realm
299
             *            the realm
300
             * @param nonce
301
             *            the nonce
302
             *
303
             * @return the string
304
             */
305
            protected String A1(String userName, String password, String realm, String nonce) {
306
                return userName + ':' + realm + ':' + password;
1✔
307
            }
308

309
            /**
310
             * A2.
311
             *
312
             * @param uri
313
             *            the uri
314
             * @param method
315
             *            the method
316
             *
317
             * @return the string
318
             */
319
            protected String A2(String uri, String method) {
320
                return method + ':' + uri;
1✔
321
            }
322

323
            /**
324
             * Kd.
325
             *
326
             * @param secret
327
             *            the secret
328
             * @param data
329
             *            the data
330
             *
331
             * @return the string
332
             *
333
             * @throws NoSuchAlgorithmException
334
             *             the no such algorithm exception
335
             */
336
            protected final String KD(String secret, String data) throws NoSuchAlgorithmException {
337
                return H(secret + ":" + data);
1✔
338
            }
339

340
            /**
341
             * H.
342
             *
343
             * @param data
344
             *            the data
345
             *
346
             * @return the string
347
             *
348
             * @throws NoSuchAlgorithmException
349
             *             the no such algorithm exception
350
             */
351
            protected final String H(String data) throws NoSuchAlgorithmException {
352
                MessageDigest digest = MessageDigest.getInstance("SHA-256");
1✔
353
                digest.update(data.getBytes(StandardCharsets.UTF_8));
1✔
354
                byte[] bytes = digest.digest();
1✔
355
                StringBuilder sb = new StringBuilder();
1✔
356
                for (byte element : bytes) {
1✔
357
                    int aByte = element;
1✔
358
                    if (aByte < 0) {
1✔
359
                        aByte += 256;
1✔
360
                    }
361
                    if (aByte < 16) {
1✔
362
                        sb.append('0');
1✔
363
                    }
364
                    sb.append(Integer.toHexString(aByte));
1✔
365
                }
366

367
                return sb.toString();
1✔
368
            }
369

370
            /**
371
             * Append.
372
             *
373
             * @param sb
374
             *            the sb
375
             * @param name
376
             *            the name
377
             * @param value
378
             *            the value
379
             */
380
            private void append(StringBuilder sb, String name, String value) {
381
                sb.append(",").append(name).append("=").append(quote(value));
1✔
382
            }
1✔
383

384
            /**
385
             * Quote.
386
             *
387
             * @param value
388
             *            the value
389
             *
390
             * @return the string
391
             */
392
            private String quote(String value) {
393
                if (value.startsWith("\"")) {
1!
394
                    return value;
×
395
                }
396
                return "\"" + value + "\"";
1✔
397
            }
398

399
        }
400

401
        @Override
402
        public String createAuthenticationHeader(AuthenticationChallenge challenge, String userName, String password) {
403
            StringBuilder sb = new StringBuilder("Digest ");
1✔
404
            Algorithm algorithm = new Algorithm();
1✔
405
            algorithm.appendParams(sb, challenge, userName, password);
1✔
406
            return sb.toString();
1✔
407
        }
408

409
    }
410

411
}
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