Skip to content

Commit b58b828

Browse files
author
Emile Joubert
committed
Report authentication failures as returned by the broker
1 parent a96d6c0 commit b58b828

24 files changed

+147
-49
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// The contents of this file are subject to the Mozilla Public License
2+
// Version 1.1 (the "License"); you may not use this file except in
3+
// compliance with the License. You may obtain a copy of the License
4+
// at http://www.mozilla.org/MPL/
5+
//
6+
// Software distributed under the License is distributed on an "AS IS"
7+
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
8+
// the License for the specific language governing rights and
9+
// limitations under the License.
10+
//
11+
// The Original Code is RabbitMQ.
12+
//
13+
// The Initial Developer of the Original Code is GoPivotal, Inc.
14+
// Copyright (c) 2013-2013 GoPivotal, Inc. All rights reserved.
15+
//
16+
17+
package com.rabbitmq.client;
18+
19+
/**
20+
* Thrown when the broker refuses access due to an authentication failure.
21+
*/
22+
23+
public class AuthenticationFailureException extends Exception
24+
{
25+
public AuthenticationFailureException(String reason) {
26+
super(reason);
27+
}
28+
}

src/com/rabbitmq/client/ConnectionFactory.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,7 @@ protected void configureSocket(Socket socket) throws IOException{
484484
* @return an interface to the connection
485485
* @throws IOException if it encounters a problem
486486
*/
487-
public Connection newConnection(Address[] addrs) throws IOException {
487+
public Connection newConnection(Address[] addrs) throws IOException, AuthenticationFailureException {
488488
return newConnection(null, addrs);
489489
}
490490

@@ -494,10 +494,10 @@ public Connection newConnection(Address[] addrs) throws IOException {
494494
* @param addrs an array of known broker addresses (hostname/port pairs) to try in order
495495
* @return an interface to the connection
496496
* @throws IOException if it encounters a problem
497+
* @throws AuthenticationFailureException if the login fails
497498
*/
498499
public Connection newConnection(ExecutorService executor, Address[] addrs)
499-
throws IOException
500-
{
500+
throws IOException, AuthenticationFailureException {
501501
IOException lastException = null;
502502
for (Address addr : addrs) {
503503
try {
@@ -528,8 +528,9 @@ public Connection newConnection(ExecutorService executor, Address[] addrs)
528528
* Create a new broker connection
529529
* @return an interface to the connection
530530
* @throws IOException if it encounters a problem
531+
* @throws AuthenticationFailureException if the login fails
531532
*/
532-
public Connection newConnection() throws IOException {
533+
public Connection newConnection() throws IOException, AuthenticationFailureException {
533534
return newConnection(null,
534535
new Address[] {new Address(getHost(), getPort())}
535536
);
@@ -540,8 +541,9 @@ public Connection newConnection() throws IOException {
540541
* @param executor thread execution service for consumers on the connection
541542
* @return an interface to the connection
542543
* @throws IOException if it encounters a problem
544+
* @throws AuthenticationFailureException if the login fails
543545
*/
544-
public Connection newConnection(ExecutorService executor) throws IOException {
546+
public Connection newConnection(ExecutorService executor) throws IOException, AuthenticationFailureException {
545547
return newConnection(executor,
546548
new Address[] {new Address(getHost(), getPort())}
547549
);

src/com/rabbitmq/client/PossibleAuthenticationFailureException.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
import java.io.IOException;
2020

2121
/**
22-
* Thrown when the likely cause is an authentication failure.
22+
* Thrown when the likely cause is an authentication failure and the broker
23+
* communicates this by closing the connection.
2324
*/
2425
public class PossibleAuthenticationFailureException extends IOException
2526
{

src/com/rabbitmq/client/impl/AMQConnection.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.util.concurrent.TimeoutException;
3131

3232
import com.rabbitmq.client.AMQP;
33+
import com.rabbitmq.client.AuthenticationFailureException;
3334
import com.rabbitmq.client.BlockedListener;
3435
import com.rabbitmq.client.Method;
3536
import com.rabbitmq.client.AlreadyClosedException;
@@ -85,6 +86,7 @@ public static final Map<String, Object> defaultClientProperties() {
8586
capabilities.put("basic.nack", true);
8687
capabilities.put("consumer_cancel_notify", true);
8788
capabilities.put("connection.blocked", true);
89+
capabilities.put("authentication_failure_close", true);
8890

8991
props.put("capabilities", capabilities);
9092

@@ -277,15 +279,16 @@ public AMQConnection(String username,
277279
* and frame max values after tuning has taken place.
278280
* @throws IOException if an error is encountered
279281
* either before, or during, protocol negotiation;
282+
* @throws AuthenticationFailureException if the broker closes the
283+
* connection with ACCESS_REFUSED.
280284
* sub-classes {@link ProtocolVersionMismatchException} and
281285
* {@link PossibleAuthenticationFailureException} will be thrown in the
282286
* corresponding circumstances. If an exception is thrown, connection
283287
* resources allocated can all be garbage collected when the connection
284288
* object is no longer referenced.
285289
*/
286290
public void start()
287-
throws IOException
288-
{
291+
throws IOException, AuthenticationFailureException {
289292
this._running = true;
290293
// Make sure that the first thing we do is to send the header,
291294
// which should cause any socket errors to show up for us, rather
@@ -357,6 +360,16 @@ public void start()
357360
response = sm.handleChallenge(challenge, this.username, this.password);
358361
}
359362
} catch (ShutdownSignalException e) {
363+
Object shutdownReason = e.getReason();
364+
if (shutdownReason instanceof AMQCommand) {
365+
Method shutdownMethod = ((AMQCommand) shutdownReason).getMethod();
366+
if (shutdownMethod instanceof AMQP.Connection.Close) {
367+
AMQP.Connection.Close shutdownClose = (AMQP.Connection.Close) shutdownMethod;
368+
if (shutdownClose.getReplyCode() == AMQP.ACCESS_REFUSED) {
369+
throw new AuthenticationFailureException(shutdownClose.getReplyText());
370+
}
371+
}
372+
}
360373
throw new PossibleAuthenticationFailureException(e);
361374
}
362375
} while (connTune == null);
@@ -645,7 +658,7 @@ public boolean processControlCommand(Command c) throws IOException
645658
}
646659

647660
public void handleConnectionClose(Command closeCommand) {
648-
ShutdownSignalException sse = shutdown(closeCommand, false, null, false);
661+
ShutdownSignalException sse = shutdown(closeCommand, false, null, _inConnectionNegotiation);
649662
try {
650663
_channel0.quiescingTransmit(new AMQP.Connection.CloseOk.Builder().build());
651664
} catch (IOException _) { } // ignore

test/src/com/rabbitmq/client/test/AMQConnectionTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import junit.framework.TestCase;
2929
import junit.framework.TestSuite;
3030

31+
import com.rabbitmq.client.AuthenticationFailureException;
3132
import com.rabbitmq.client.Channel;
3233
import com.rabbitmq.client.Connection;
3334
import com.rabbitmq.client.ConnectionFactory;
@@ -81,7 +82,7 @@ public static TestSuite suite() {
8182
/** Check the AMQConnection does send exactly 1 initial header, and deal correctly with
8283
* the frame handler throwing an exception when we try to read data
8384
*/
84-
public void testConnectionSendsSingleHeaderAndTimesOut() {
85+
public void testConnectionSendsSingleHeaderAndTimesOut() throws AuthenticationFailureException {
8586
IOException exception = new SocketTimeoutException();
8687
_mockFrameHandler.setExceptionOnReadingFrames(exception);
8788
MyExceptionHandler handler = new MyExceptionHandler();
@@ -124,7 +125,7 @@ public void testConnectionSendsSingleHeaderAndTimesOut() {
124125
/**
125126
* Test that we catch timeout between connect and negotiation of the connection being finished.
126127
*/
127-
public void testConnectionHangInNegotiation() {
128+
public void testConnectionHangInNegotiation() throws AuthenticationFailureException {
128129
this._mockFrameHandler.setTimeoutCount(10); // to limit hang
129130
MyExceptionHandler handler = new MyExceptionHandler();
130131
assertEquals(0, this._mockFrameHandler.countHeadersSent());

test/src/com/rabbitmq/client/test/BrokerTestCase.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import com.rabbitmq.client.Method;
3131
import com.rabbitmq.client.ShutdownSignalException;
3232
import com.rabbitmq.client.AlreadyClosedException;
33+
import com.rabbitmq.client.AuthenticationFailureException;
3334
import com.rabbitmq.client.impl.ShutdownNotifierComponent;
3435
import com.rabbitmq.client.AMQP;
3536
import com.rabbitmq.tools.Host;
@@ -81,7 +82,7 @@ protected void releaseResources()
8182
}
8283

8384
protected void restart()
84-
throws IOException {
85+
throws IOException, AuthenticationFailureException {
8586
tearDown();
8687
bareRestart();
8788
setUp();
@@ -95,7 +96,11 @@ protected void bareRestart()
9596
public void openConnection()
9697
throws IOException {
9798
if (connection == null) {
98-
connection = connectionFactory.newConnection();
99+
try {
100+
connection = connectionFactory.newConnection();
101+
} catch (AuthenticationFailureException afe) {
102+
fail("Unexpected authentication failure");
103+
}
99104
}
100105
}
101106

test/src/com/rabbitmq/client/test/functional/ClusteredTestBase.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.rabbitmq.client.test.functional;
1818

1919
import com.rabbitmq.client.AMQP;
20+
import com.rabbitmq.client.AuthenticationFailureException;
2021
import com.rabbitmq.client.Channel;
2122
import com.rabbitmq.client.Connection;
2223
import com.rabbitmq.client.ConnectionFactory;
@@ -63,6 +64,9 @@ public void openConnection() throws IOException {
6364
catch (IOException e) {
6465
// Must be no secondary node
6566
}
67+
catch (AuthenticationFailureException afe) {
68+
fail("Unexpected authentication failure");
69+
}
6670
}
6771

6872
if (clusteredConnection != null &&

test/src/com/rabbitmq/client/test/functional/ConnectionOpen.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.net.Socket;
2323

2424
import com.rabbitmq.client.AMQP;
25+
import com.rabbitmq.client.AuthenticationFailureException;
2526
import com.rabbitmq.client.MalformedFrameException;
2627
import com.rabbitmq.client.Method;
2728
import com.rabbitmq.client.impl.SocketFrameHandler;
@@ -85,7 +86,7 @@ public void testCrazyProtocolHeader() throws IOException {
8586
}
8687
}
8788

88-
public void testFrameMaxLessThanFrameMinSize() throws IOException {
89+
public void testFrameMaxLessThanFrameMinSize() throws IOException, AuthenticationFailureException {
8990
ConnectionFactory factory = new ConnectionFactory();
9091
factory.setRequestedFrameMax(100);
9192
try {

test/src/com/rabbitmq/client/test/functional/FrameMax.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.concurrent.ExecutorService;
2525

2626
import com.rabbitmq.client.Address;
27+
import com.rabbitmq.client.AuthenticationFailureException;
2728
import com.rabbitmq.client.AMQP;
2829
import com.rabbitmq.client.Connection;
2930
import com.rabbitmq.client.ConnectionFactory;
@@ -71,7 +72,7 @@ public void testFrameSizes()
7172
/* server should reject frames larger than AMQP.FRAME_MIN_SIZE
7273
* during connection negotiation */
7374
public void testRejectLargeFramesDuringConnectionNegotiation()
74-
throws IOException
75+
throws IOException, AuthenticationFailureException
7576
{
7677
ConnectionFactory cf = new ConnectionFactory();
7778
cf.getClientProperties().put("too_long", LongStringHelper.asLongString(new byte[AMQP.FRAME_MIN_SIZE]));
@@ -85,7 +86,7 @@ public void testRejectLargeFramesDuringConnectionNegotiation()
8586
/* server should reject frames larger than the negotiated frame
8687
* size */
8788
public void testRejectExceedingFrameMax()
88-
throws IOException
89+
throws IOException, AuthenticationFailureException
8990
{
9091
closeChannel();
9192
closeConnection();
@@ -161,7 +162,7 @@ public GenerousAMQConnection(ConnectionFactory factory,
161162
private static class GenerousConnectionFactory extends ConnectionFactory {
162163

163164
@Override public Connection newConnection(ExecutorService executor, Address[] addrs)
164-
throws IOException
165+
throws IOException, AuthenticationFailureException
165166
{
166167
IOException lastException = null;
167168
for (Address addr : addrs) {

test/src/com/rabbitmq/client/test/functional/QueueExclusivity.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.HashMap;
2222

2323
import com.rabbitmq.client.AMQP;
24+
import com.rabbitmq.client.AuthenticationFailureException;
2425
import com.rabbitmq.client.Channel;
2526
import com.rabbitmq.client.Connection;
2627
import com.rabbitmq.client.QueueingConsumer;
@@ -36,7 +37,11 @@ public class QueueExclusivity extends BrokerTestCase {
3637
String q = "exclusiveQ";
3738

3839
protected void createResources() throws IOException {
39-
altConnection = connectionFactory.newConnection();
40+
try {
41+
altConnection = connectionFactory.newConnection();
42+
} catch (AuthenticationFailureException afe) {
43+
fail("Unexpected authentication failure");
44+
}
4045
altChannel = altConnection.createChannel();
4146
altChannel.queueDeclare(q,
4247
// not durable, exclusive, not auto-delete

0 commit comments

Comments
 (0)