Skip to content

Commit 25373ee

Browse files
committed
allow the BASIC auth to be not on the 1st place for the WWW_AUTHENTICATE header
Signed-off-by: Maxim Nesen <[email protected]>
1 parent de3e7fe commit 25373ee

File tree

3 files changed

+139
-16
lines changed

3 files changed

+139
-16
lines changed

core-client/pom.xml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!--
33
4-
Copyright (c) 2011, 2024 Oracle and/or its affiliates. All rights reserved.
4+
Copyright (c) 2011, 2025 Oracle and/or its affiliates. All rights reserved.
55
66
This program and the accompanying materials are made available under the
77
terms of the Eclipse Public License v. 2.0, which is available at
@@ -153,6 +153,19 @@
153153
</dependencies>
154154

155155
<profiles>
156+
<profile>
157+
<id>mockito_jdk_11</id>
158+
<activation>
159+
<jdk>11</jdk>
160+
</activation>
161+
<dependencies>
162+
<dependency>
163+
<groupId>jakarta.xml.bind</groupId>
164+
<artifactId>jakarta.xml.bind-api</artifactId>
165+
<scope>test</scope>
166+
</dependency>
167+
</dependencies>
168+
</profile>
156169
<profile>
157170
<id>sonar</id>
158171
<build>

core-client/src/main/java/org/glassfish/jersey/client/authentication/BasicAuthenticator.java

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2013, 2025 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -17,8 +17,8 @@
1717
package org.glassfish.jersey.client.authentication;
1818

1919
import java.util.Base64;
20+
import java.util.List;
2021
import java.util.Locale;
21-
import java.util.logging.Level;
2222
import java.util.logging.Logger;
2323

2424
import javax.ws.rs.client.ClientRequestContext;
@@ -96,20 +96,22 @@ public void filterRequest(ClientRequestContext request) {
9696
* @throws ResponseAuthenticationException in case that basic credentials missing or are in invalid format
9797
*/
9898
public boolean filterResponseAndAuthenticate(ClientRequestContext request, ClientResponseContext response) {
99-
final String authenticate = response.getHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE);
100-
if (authenticate != null && authenticate.trim().toUpperCase(Locale.ROOT).startsWith("BASIC")) {
101-
HttpAuthenticationFilter.Credentials credentials = HttpAuthenticationFilter
102-
.getCredentials(request, defaultCredentials, HttpAuthenticationFilter.Type.BASIC);
103-
104-
if (credentials == null) {
105-
if (response.hasEntity()) {
106-
AuthenticationUtil.discardInputAndClose(response.getEntityStream());
107-
}
108-
throw new ResponseAuthenticationException(null, LocalizationMessages.AUTHENTICATION_CREDENTIALS_MISSING_BASIC());
109-
}
99+
final List<String> authHeaders = response.getHeaders().get(HttpHeaders.WWW_AUTHENTICATE);
100+
if (authHeaders == null || authHeaders.size() == 0 || authHeaders.stream()
101+
.noneMatch(h -> h != null && h.toUpperCase(Locale.ROOT).startsWith("BASIC"))) {
102+
return false;
103+
}
104+
105+
HttpAuthenticationFilter.Credentials credentials = HttpAuthenticationFilter
106+
.getCredentials(request, defaultCredentials, HttpAuthenticationFilter.Type.BASIC);
110107

111-
return HttpAuthenticationFilter.repeatRequest(request, response, calculateAuthentication(credentials));
108+
if (credentials == null) {
109+
if (response.hasEntity()) {
110+
AuthenticationUtil.discardInputAndClose(response.getEntityStream());
111+
}
112+
throw new ResponseAuthenticationException(null, LocalizationMessages.AUTHENTICATION_CREDENTIALS_MISSING_BASIC());
112113
}
113-
return false;
114+
115+
return HttpAuthenticationFilter.repeatRequest(request, response, calculateAuthentication(credentials));
114116
}
115117
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package org.glassfish.jersey.client.authentication;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import javax.ws.rs.client.ClientRequestContext;
22+
import javax.ws.rs.client.ClientResponseContext;
23+
import javax.ws.rs.core.HttpHeaders;
24+
import javax.ws.rs.core.MultivaluedMap;
25+
import java.util.Arrays;
26+
import java.util.Collections;
27+
28+
import static org.junit.jupiter.api.Assertions.assertFalse;
29+
import static org.junit.jupiter.api.Assertions.assertThrows;
30+
import static org.mockito.Mockito.mock;
31+
import static org.mockito.Mockito.when;
32+
33+
class BasicAuthenticatorTest {
34+
35+
@Test
36+
void filterResponseAndAuthenticateNoAuthHeadersTest() {
37+
final BasicAuthenticator authenticator
38+
= new BasicAuthenticator(new HttpAuthenticationFilter.Credentials("foo", "bar"));
39+
final ClientRequestContext request = mock(ClientRequestContext.class);
40+
final ClientResponseContext response = mock(ClientResponseContext.class);
41+
42+
when(response.getHeaders()).thenReturn(mock(MultivaluedMap.class));
43+
44+
assertFalse(authenticator.filterResponseAndAuthenticate(request, response));
45+
}
46+
47+
@Test
48+
void filterResponseAndAuthenticateAuthHeaderNotBasicTest() {
49+
final BasicAuthenticator authenticator
50+
= new BasicAuthenticator(new HttpAuthenticationFilter.Credentials("foo", "bar"));
51+
final ClientRequestContext request = mock(ClientRequestContext.class);
52+
final ClientResponseContext response = mock(ClientResponseContext.class);
53+
54+
final MultivaluedMap<String, String> headers = mock(MultivaluedMap.class);
55+
when(response.getHeaders()).thenReturn(headers);
56+
when(headers.get(HttpHeaders.WWW_AUTHENTICATE)).thenReturn(Collections.singletonList("Digest realm=\"test\""));
57+
58+
assertFalse(authenticator.filterResponseAndAuthenticate(request, response));
59+
}
60+
61+
@Test
62+
void filterResponseAndAuthenticateEmptyListTest() {
63+
final BasicAuthenticator authenticator
64+
= new BasicAuthenticator(new HttpAuthenticationFilter.Credentials("foo", "bar"));
65+
final ClientRequestContext request = mock(ClientRequestContext.class);
66+
final ClientResponseContext response = mock(ClientResponseContext.class);
67+
68+
final MultivaluedMap<String, String> headers = mock(MultivaluedMap.class);
69+
when(response.getHeaders()).thenReturn(headers);
70+
when(headers.get(HttpHeaders.WWW_AUTHENTICATE)).thenReturn(Collections.emptyList());
71+
72+
assertFalse(authenticator.filterResponseAndAuthenticate(request, response));
73+
}
74+
75+
@Test
76+
void filterResponseAndAuthenticateNullListTest() {
77+
final BasicAuthenticator authenticator
78+
= new BasicAuthenticator(new HttpAuthenticationFilter.Credentials("foo", "bar"));
79+
final ClientRequestContext request = mock(ClientRequestContext.class);
80+
final ClientResponseContext response = mock(ClientResponseContext.class);
81+
82+
final MultivaluedMap<String, String> headers = mock(MultivaluedMap.class);
83+
when(response.getHeaders()).thenReturn(headers);
84+
when(headers.get(HttpHeaders.WWW_AUTHENTICATE)).thenReturn(null);
85+
86+
assertFalse(authenticator.filterResponseAndAuthenticate(request, response));
87+
}
88+
89+
@Test
90+
void filterResponseAndAuthenticateMissingCredentialsMultipleAuthRealmsTest() {
91+
final String[] authHeaders = new String[] {
92+
"Digest realm=\"test\"",
93+
"Basic realm=\"test\""
94+
};
95+
final BasicAuthenticator authenticator = new BasicAuthenticator(null);
96+
final ClientRequestContext request = mock(ClientRequestContext.class);
97+
final ClientResponseContext response = mock(ClientResponseContext.class);
98+
99+
final MultivaluedMap<String, String> headers = mock(MultivaluedMap.class);
100+
when(response.getHeaders()).thenReturn(headers);
101+
when(headers.get(HttpHeaders.WWW_AUTHENTICATE)).thenReturn(Arrays.asList(authHeaders));
102+
when(response.hasEntity()).thenReturn(false);
103+
104+
assertThrows(ResponseAuthenticationException.class,
105+
() -> authenticator.filterResponseAndAuthenticate(request, response));
106+
}
107+
108+
}

0 commit comments

Comments
 (0)