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

hazendaz / httpunit / 343

07 Jun 2025 08:37PM UTC coverage: 80.528% (-0.06%) from 80.591%
343

push

github

hazendaz
[ci] Remove old jakarta support note from readme as now mainline

3216 of 4105 branches covered (78.34%)

Branch coverage included in aggregate %.

8252 of 10136 relevant lines covered (81.41%)

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.io.UnsupportedEncodingException;
23
import java.net.MalformedURLException;
24
import java.net.PasswordAuthentication;
25
import java.nio.charset.StandardCharsets;
26
import java.security.MessageDigest;
27
import java.security.NoSuchAlgorithmException;
28
import java.util.Base64;
29

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

35
    private WebClient _client;
36
    private WebRequest _request;
37

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

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

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

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

68
        throw createAuthorizationRequiredException();
1✔
69
    }
70

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

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

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

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

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

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

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

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

129
    private static class BasicAuthenticationStrategy implements AuthenticationStrategy {
130

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

137
    }
138

139
    private static class DigestAuthenticationStrategy implements AuthenticationStrategy {
140

141
        private static class Algorithm {
142

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

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

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

175
            protected String A1(String userName, String password, String realm, String nonce)
176
                    throws NoSuchAlgorithmException, UnsupportedEncodingException {
177
                return userName + ':' + realm + ':' + password;
1✔
178
            }
179

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

184
            final protected String KD(String secret, String data)
185
                    throws NoSuchAlgorithmException, UnsupportedEncodingException {
186
                return H(secret + ":" + data);
1✔
187
            }
188

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

205
                return sb.toString();
1✔
206
            }
207

208
            private void append(StringBuilder sb, String name, String value) {
209
                sb.append(",").append(name).append("=").append(quote(value));
1✔
210
            }
1✔
211

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

219
        }
220

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

229
    }
230

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