Skip to content
This repository was archived by the owner on May 28, 2018. It is now read-only.

Commit 5a501d1

Browse files
author
alessandro.gherardi
committed
Fixed leak in basic and digest authenticators if 401 response has content
1 parent 1f46147 commit 5a501d1

3 files changed

Lines changed: 131 additions & 1 deletion

File tree

connectors/apache-connector/src/test/java/org/glassfish/jersey/apache/connector/AuthTest.java

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,20 @@
5858

5959
import org.glassfish.jersey.client.ClientConfig;
6060
import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
61+
import org.glassfish.jersey.client.authentication.ResponseAuthenticationException;
6162
import org.glassfish.jersey.server.ResourceConfig;
6263
import org.glassfish.jersey.test.JerseyTest;
6364

6465
import org.apache.http.auth.AuthScope;
6566
import org.apache.http.auth.UsernamePasswordCredentials;
6667
import org.apache.http.client.CredentialsProvider;
68+
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
6769
import org.junit.Ignore;
6870
import org.junit.Test;
6971
import static org.junit.Assert.assertEquals;
7072
import static org.junit.Assert.assertNotNull;
7173
import static org.junit.Assert.assertTrue;
74+
import static org.junit.Assert.fail;
7275

7376
/**
7477
* @author Paul Sandoz
@@ -229,6 +232,40 @@ public String deleteFilterWithEntity(@Context HttpHeaders h, String e) {
229232

230233
return e;
231234
}
235+
236+
@GET
237+
@Path("content")
238+
public String getWithContent(@Context HttpHeaders h) {
239+
requestCount++;
240+
String value = h.getRequestHeaders().getFirst("Authorization");
241+
if (value == null) {
242+
assertEquals(1, requestCount);
243+
throw new WebApplicationException(
244+
Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"")
245+
.entity("Forbidden").build());
246+
} else {
247+
assertTrue(requestCount > 1);
248+
}
249+
250+
return "GET";
251+
}
252+
253+
@GET
254+
@Path("contentDigestAuth")
255+
public String getWithContentDigestAuth(@Context HttpHeaders h) {
256+
requestCount++;
257+
String value = h.getRequestHeaders().getFirst("Authorization");
258+
if (value == null) {
259+
assertEquals(1, requestCount);
260+
throw new WebApplicationException(
261+
Response.status(401).header("WWW-Authenticate", "Digest nonce=\"1234\"")
262+
.entity("Forbidden").build());
263+
} else {
264+
assertTrue(requestCount > 1);
265+
}
266+
267+
return "GET";
268+
}
232269
}
233270

234271
@Test
@@ -353,4 +390,50 @@ public void testAuthInteractivePost() {
353390

354391
assertEquals("POST", r.request().post(Entity.text("POST"), String.class));
355392
}
393+
394+
@Test
395+
public void testAuthGetWithBasicFilter() {
396+
ClientConfig cc = new ClientConfig();
397+
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
398+
cc.connectorProvider(new ApacheConnectorProvider());
399+
cc.property(ApacheClientProperties.CONNECTION_MANAGER, cm);
400+
Client client = ClientBuilder.newClient(cc);
401+
client.register(HttpAuthenticationFeature.universalBuilder().build());
402+
WebTarget r = client.target(getBaseUri()).path("test/content");
403+
404+
try {
405+
assertEquals("GET", r.request().get(String.class));
406+
fail();
407+
} catch (ResponseAuthenticationException ex) {
408+
// expected
409+
}
410+
411+
// Verify the connection that was used for the request is available for reuse
412+
// and no connections are leased
413+
assertEquals(cm.getTotalStats().getAvailable(), 1);
414+
assertEquals(cm.getTotalStats().getLeased(), 0);
415+
}
416+
417+
@Test
418+
public void testAuthGetWithDigestFilter() {
419+
ClientConfig cc = new ClientConfig();
420+
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
421+
cc.connectorProvider(new ApacheConnectorProvider());
422+
cc.property(ApacheClientProperties.CONNECTION_MANAGER, cm);
423+
Client client = ClientBuilder.newClient(cc);
424+
client.register(HttpAuthenticationFeature.universalBuilder().build());
425+
WebTarget r = client.target(getBaseUri()).path("test/contentDigestAuth");
426+
427+
try {
428+
assertEquals("GET", r.request().get(String.class));
429+
fail();
430+
} catch (ResponseAuthenticationException ex) {
431+
// expected
432+
}
433+
434+
// Verify the connection that was used for the request is available for reuse
435+
// and no connections are leased
436+
assertEquals(cm.getTotalStats().getAvailable(), 1);
437+
assertEquals(cm.getTotalStats().getLeased(), 0);
438+
}
356439
}

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040

4141
package org.glassfish.jersey.client.authentication;
4242

43+
import java.io.IOException;
44+
import java.io.InputStream;
45+
4346
import javax.ws.rs.client.ClientRequestContext;
4447
import javax.ws.rs.client.ClientResponseContext;
4548
import javax.ws.rs.core.HttpHeaders;
@@ -120,11 +123,33 @@ public boolean filterResponseAndAuthenticate(ClientRequestContext request, Clien
120123
.getCredentials(request, defaultCredentials, HttpAuthenticationFilter.Type.BASIC);
121124

122125
if (credentials == null) {
126+
if (response.hasEntity()) {
127+
discardInputAndClose(response.getEntityStream());
128+
}
123129
throw new ResponseAuthenticationException(null, LocalizationMessages.AUTHENTICATION_CREDENTIALS_MISSING_BASIC());
124130
}
125131

126132
return HttpAuthenticationFilter.repeatRequest(request, response, calculateAuthentication(credentials));
127133
}
128134
return false;
129135
}
136+
137+
private static void discardInputAndClose(InputStream is) {
138+
byte[] buf = new byte[4096];
139+
try {
140+
while (true) {
141+
if (is.read(buf) <= 0) {
142+
break;
143+
}
144+
}
145+
} catch (IOException ex) {
146+
// ignore
147+
} finally {
148+
try {
149+
is.close();
150+
} catch (IOException ex) {
151+
// ignore
152+
}
153+
}
154+
}
130155
}

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
package org.glassfish.jersey.client.authentication;
4242

4343
import java.io.IOException;
44+
import java.io.InputStream;
4445
import java.net.URI;
4546
import java.security.MessageDigest;
4647
import java.security.NoSuchAlgorithmException;
@@ -149,7 +150,9 @@ public boolean filterResponse(final ClientRequestContext request, final ClientRe
149150
final HttpAuthenticationFilter.Credentials cred = HttpAuthenticationFilter.getCredentials(request,
150151
this.credentials, HttpAuthenticationFilter.Type.DIGEST);
151152
if (cred == null) {
152-
153+
if (response.hasEntity()) {
154+
discardInputAndClose(response.getEntityStream());
155+
}
153156
throw new ResponseAuthenticationException(null, LocalizationMessages.AUTHENTICATION_CREDENTIALS_MISSING_DIGEST());
154157
}
155158

@@ -373,6 +376,25 @@ private String randomBytes(final int nbBytes) {
373376
return bytesToHex(bytes);
374377
}
375378

379+
private static void discardInputAndClose(InputStream is) {
380+
byte[] buf = new byte[4096];
381+
try {
382+
while (true) {
383+
if (is.read(buf) <= 0) {
384+
break;
385+
}
386+
}
387+
} catch (IOException ex) {
388+
// ignore
389+
} finally {
390+
try {
391+
is.close();
392+
} catch (IOException ex) {
393+
// ignore
394+
}
395+
}
396+
}
397+
376398
private enum QOP {
377399

378400
UNSPECIFIED(null),

0 commit comments

Comments
 (0)