Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion core-client/pom.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

Copyright (c) 2011, 2024 Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2011, 2025 Oracle and/or its affiliates. All rights reserved.

This program and the accompanying materials are made available under the
terms of the Eclipse Public License v. 2.0, which is available at
Expand Down Expand Up @@ -153,6 +153,19 @@
</dependencies>

<profiles>
<profile>
<id>mockito_jdk_11</id>
<activation>
<jdk>11</jdk>
</activation>
<dependencies>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
<profile>
<id>sonar</id>
<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2025 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
Expand All @@ -17,8 +17,8 @@
package org.glassfish.jersey.client.authentication;

import java.util.Base64;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.ws.rs.client.ClientRequestContext;
Expand Down Expand Up @@ -96,20 +96,22 @@ public void filterRequest(ClientRequestContext request) {
* @throws ResponseAuthenticationException in case that basic credentials missing or are in invalid format
*/
public boolean filterResponseAndAuthenticate(ClientRequestContext request, ClientResponseContext response) {
final String authenticate = response.getHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE);
if (authenticate != null && authenticate.trim().toUpperCase(Locale.ROOT).startsWith("BASIC")) {
HttpAuthenticationFilter.Credentials credentials = HttpAuthenticationFilter
.getCredentials(request, defaultCredentials, HttpAuthenticationFilter.Type.BASIC);

if (credentials == null) {
if (response.hasEntity()) {
AuthenticationUtil.discardInputAndClose(response.getEntityStream());
}
throw new ResponseAuthenticationException(null, LocalizationMessages.AUTHENTICATION_CREDENTIALS_MISSING_BASIC());
}
final List<String> authHeaders = response.getHeaders().get(HttpHeaders.WWW_AUTHENTICATE);
if (authHeaders == null || authHeaders.size() == 0 || authHeaders.stream()
.noneMatch(h -> h != null && h.toUpperCase(Locale.ROOT).startsWith("BASIC"))) {
return false;
}

HttpAuthenticationFilter.Credentials credentials = HttpAuthenticationFilter
.getCredentials(request, defaultCredentials, HttpAuthenticationFilter.Type.BASIC);

return HttpAuthenticationFilter.repeatRequest(request, response, calculateAuthentication(credentials));
if (credentials == null) {
if (response.hasEntity()) {
AuthenticationUtil.discardInputAndClose(response.getEntityStream());
}
throw new ResponseAuthenticationException(null, LocalizationMessages.AUTHENTICATION_CREDENTIALS_MISSING_BASIC());
}
return false;

return HttpAuthenticationFilter.repeatRequest(request, response, calculateAuthentication(credentials));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.client.authentication;

import org.junit.jupiter.api.Test;

import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap;
import java.util.Arrays;
import java.util.Collections;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class BasicAuthenticatorTest {

@Test
void filterResponseAndAuthenticateNoAuthHeadersTest() {
final BasicAuthenticator authenticator
= new BasicAuthenticator(new HttpAuthenticationFilter.Credentials("foo", "bar"));
final ClientRequestContext request = mock(ClientRequestContext.class);
final ClientResponseContext response = mock(ClientResponseContext.class);

when(response.getHeaders()).thenReturn(mock(MultivaluedMap.class));

assertFalse(authenticator.filterResponseAndAuthenticate(request, response));
}

@Test
void filterResponseAndAuthenticateAuthHeaderNotBasicTest() {
final BasicAuthenticator authenticator
= new BasicAuthenticator(new HttpAuthenticationFilter.Credentials("foo", "bar"));
final ClientRequestContext request = mock(ClientRequestContext.class);
final ClientResponseContext response = mock(ClientResponseContext.class);

final MultivaluedMap<String, String> headers = mock(MultivaluedMap.class);
when(response.getHeaders()).thenReturn(headers);
when(headers.get(HttpHeaders.WWW_AUTHENTICATE)).thenReturn(Collections.singletonList("Digest realm=\"test\""));

assertFalse(authenticator.filterResponseAndAuthenticate(request, response));
}

@Test
void filterResponseAndAuthenticateEmptyListTest() {
final BasicAuthenticator authenticator
= new BasicAuthenticator(new HttpAuthenticationFilter.Credentials("foo", "bar"));
final ClientRequestContext request = mock(ClientRequestContext.class);
final ClientResponseContext response = mock(ClientResponseContext.class);

final MultivaluedMap<String, String> headers = mock(MultivaluedMap.class);
when(response.getHeaders()).thenReturn(headers);
when(headers.get(HttpHeaders.WWW_AUTHENTICATE)).thenReturn(Collections.emptyList());

assertFalse(authenticator.filterResponseAndAuthenticate(request, response));
}

@Test
void filterResponseAndAuthenticateNullListTest() {
final BasicAuthenticator authenticator
= new BasicAuthenticator(new HttpAuthenticationFilter.Credentials("foo", "bar"));
final ClientRequestContext request = mock(ClientRequestContext.class);
final ClientResponseContext response = mock(ClientResponseContext.class);

final MultivaluedMap<String, String> headers = mock(MultivaluedMap.class);
when(response.getHeaders()).thenReturn(headers);
when(headers.get(HttpHeaders.WWW_AUTHENTICATE)).thenReturn(null);

assertFalse(authenticator.filterResponseAndAuthenticate(request, response));
}

@Test
void filterResponseAndAuthenticateMissingCredentialsMultipleAuthRealmsTest() {
final String[] authHeaders = new String[] {
"Digest realm=\"test\"",
"Basic realm=\"test\""
};
final BasicAuthenticator authenticator = new BasicAuthenticator(null);
final ClientRequestContext request = mock(ClientRequestContext.class);
final ClientResponseContext response = mock(ClientResponseContext.class);

final MultivaluedMap<String, String> headers = mock(MultivaluedMap.class);
when(response.getHeaders()).thenReturn(headers);
when(headers.get(HttpHeaders.WWW_AUTHENTICATE)).thenReturn(Arrays.asList(authHeaders));
when(response.hasEntity()).thenReturn(false);

assertThrows(ResponseAuthenticationException.class,
() -> authenticator.filterResponseAndAuthenticate(request, response));
}

}