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

hazendaz / httpunit / 389

12 Aug 2025 11:17PM UTC coverage: 80.48% (-0.02%) from 80.503%
389

push

github

hazendaz
Merge branch 'master' into javax

3216 of 4105 branches covered (78.34%)

Branch coverage included in aggregate %.

238 of 258 new or added lines in 68 files covered. (92.25%)

2 existing lines in 2 files now uncovered.

8254 of 10147 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
    private WebClient _client;
35
    private WebRequest _request;
36

37
    private static final AuthenticationStrategy BASIC_AUTHENTICATION = new BasicAuthenticationStrategy();
1✔
38
    private static final AuthenticationStrategy DIGEST_AUTHENTICATION = new DigestAuthenticationStrategy();
1✔
39

40
    static AuthorizationRequiredException createException(String wwwAuthenticateHeader) {
41
        AuthenticationChallenge challenge = new AuthenticationChallenge(null, null, wwwAuthenticateHeader);
×
42
        return challenge.createAuthorizationRequiredException();
×
43
    }
44

45
    AuthenticationChallenge(WebClient client, WebRequest request, String headerString) {
46
        super(headerString, "Basic");
1✔
47
        _client = client;
1✔
48
        _request = request;
1✔
49
    }
1✔
50

51
    /**
52
     * check whether authentication is needed
53
     *
54
     * @return
55
     */
56
    boolean needToAuthenticate() {
57
        if (getAuthenticationType() == null) {
1✔
58
            return false;
1✔
59
        }
60
        if (getCredentialsForRealm() != null) {
1✔
61
            return true;
1✔
62
        }
63
        if (!_client.getExceptionsThrownOnErrorStatus()) {
1!
64
            return false;
×
65
        }
66

67
        throw createAuthorizationRequiredException();
1✔
68
    }
69

70
    private String getAuthenticationType() {
71
        String result = getLabel();
1✔
72
        if (_headerString != null && _headerString.equals("Negotiate")) {
1✔
73
            result = null;
1✔
74
        }
75
        return result;
1✔
76
    }
77

78
    String createAuthenticationHeader() {
79
        PasswordAuthentication credentials = getCredentialsForRealm();
1✔
80
        return getAuthenticationStrategy().createAuthenticationHeader(this, credentials.getUserName(),
1✔
81
                new String(credentials.getPassword()));
1✔
82
    }
83

84
    private AuthenticationStrategy getAuthenticationStrategy() {
85
        if (getAuthenticationType().equalsIgnoreCase("basic")) {
1✔
86
            return BASIC_AUTHENTICATION;
1✔
87
        }
88
        if (getAuthenticationType().equalsIgnoreCase("digest")) {
1!
89
            return DIGEST_AUTHENTICATION;
1✔
90
        }
91
        throw new RuntimeException("Unsupported authentication type '" + getAuthenticationType() + "'");
×
92
    }
93

94
    private AuthorizationRequiredException createAuthorizationRequiredException() {
95
        return AuthorizationRequiredException.createException(getAuthenticationType(), getProperties());
1✔
96
    }
97

98
    /**
99
     * get the credentials for the realm property
100
     *
101
     * @return
102
     */
103
    private PasswordAuthentication getCredentialsForRealm() {
104
        String realm = getProperty("realm");
1✔
105
        PasswordAuthentication result = null;
1✔
106
        if (realm != null) {
1!
107
            result = _client.getCredentialsForRealm(realm);
1✔
108
        }
109
        return result;
1✔
110
    }
111

112
    private String getMethod() {
113
        return null == _request ? null : _request.getMethod();
1!
114
    }
115

116
    private String getRequestUri() {
117
        try {
118
            return null == _request ? null : _request.getURL().getFile();
1!
119
        } catch (MalformedURLException e) {
×
120
            return null;
×
121
        }
122
    }
123

124
    private interface AuthenticationStrategy {
125
        String createAuthenticationHeader(AuthenticationChallenge challenge, String username, String password);
126
    }
127

128
    private static class BasicAuthenticationStrategy implements AuthenticationStrategy {
129

130
        @Override
131
        public String createAuthenticationHeader(AuthenticationChallenge challenge, String userName, String password) {
132
            return "Basic "
1✔
133
                    + Base64.getEncoder().encodeToString((userName + ':' + password).getBytes(StandardCharsets.UTF_8));
1✔
134
        }
135

136
    }
137

138
    private static class DigestAuthenticationStrategy implements AuthenticationStrategy {
139

140
        private static class Algorithm {
141

142
            public void appendParams(StringBuilder sb, AuthenticationChallenge challenge, String userName,
143
                    String password) {
144
                appendDigestParams(sb, challenge.getProperty("realm"), challenge.getProperty("nonce"),
1✔
145
                        challenge.getRequestUri(), userName, password, challenge.getMethod(),
1✔
146
                        challenge.getProperty("opaque"));
1✔
147
            }
1✔
148

149
            protected void appendDigestParams(StringBuilder sb, String realm, String nonce, String uri, String userName,
150
                    String password, String method, String opaque) {
151
                sb.append("username=").append(quote(userName));
1✔
152
                append(sb, "realm", realm);
1✔
153
                append(sb, "nonce", nonce);
1✔
154
                append(sb, "uri", uri);
1✔
155
                append(sb, "response", getResponse(userName, realm, password, nonce, uri, method));
1✔
156
                if (opaque != null) {
1✔
157
                    append(sb, "opaque", opaque);
1✔
158
                }
159
            }
1✔
160

161
            protected String getResponse(String userName, String realm, String password, String nonce, String uri,
162
                    String method) {
163
                try {
164
                    String a1 = A1(userName, password, realm, nonce);
1✔
165
                    String a2 = A2(uri, method);
1✔
166
                    String ha1 = H(a1);
1✔
167
                    String ha2 = H(a2);
1✔
168
                    return KD(ha1, nonce + ':' + ha2);
1✔
NEW
169
                } catch (NoSuchAlgorithmException e) {
×
170
                    return "";
×
171
                }
172
            }
173

174
            protected String A1(String userName, String password, String realm, String nonce) {
175
                return userName + ':' + realm + ':' + password;
1✔
176
            }
177

178
            protected String A2(String uri, String method) {
179
                return method + ':' + uri;
1✔
180
            }
181

182
            protected final String KD(String secret, String data) throws NoSuchAlgorithmException {
183
                return H(secret + ":" + data);
1✔
184
            }
185

186
            protected final String H(String data) throws NoSuchAlgorithmException {
187
                MessageDigest digest = MessageDigest.getInstance("SHA-256");
1✔
188
                digest.update(data.getBytes(StandardCharsets.UTF_8));
1✔
189
                byte[] bytes = digest.digest();
1✔
190
                StringBuilder sb = new StringBuilder();
1✔
191
                for (byte element : bytes) {
1✔
192
                    int aByte = element;
1✔
193
                    if (aByte < 0) {
1✔
194
                        aByte += 256;
1✔
195
                    }
196
                    if (aByte < 16) {
1✔
197
                        sb.append('0');
1✔
198
                    }
199
                    sb.append(Integer.toHexString(aByte));
1✔
200
                }
201

202
                return sb.toString();
1✔
203
            }
204

205
            private void append(StringBuilder sb, String name, String value) {
206
                sb.append(",").append(name).append("=").append(quote(value));
1✔
207
            }
1✔
208

209
            private String quote(String value) {
210
                if (value.startsWith("\"")) {
1!
211
                    return value;
×
212
                }
213
                return "\"" + value + "\"";
1✔
214
            }
215

216
        }
217

218
        @Override
219
        public String createAuthenticationHeader(AuthenticationChallenge challenge, String userName, String password) {
220
            StringBuilder sb = new StringBuilder("Digest ");
1✔
221
            Algorithm algorithm = new Algorithm();
1✔
222
            algorithm.appendParams(sb, challenge, userName, password);
1✔
223
            return sb.toString();
1✔
224
        }
225

226
    }
227

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