mirror of https://github.com/apache/activemq.git
Allow for early SASL authentication and failure if credentials not valid.
This commit is contained in:
parent
7fdfdeba79
commit
67ccfcad88
|
@ -19,6 +19,8 @@ package org.apache.activemq.transport.amqp;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -27,6 +29,7 @@ import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import javax.jms.Destination;
|
import javax.jms.Destination;
|
||||||
|
@ -65,6 +68,8 @@ import org.apache.activemq.command.SessionInfo;
|
||||||
import org.apache.activemq.command.ShutdownInfo;
|
import org.apache.activemq.command.ShutdownInfo;
|
||||||
import org.apache.activemq.command.SubscriptionInfo;
|
import org.apache.activemq.command.SubscriptionInfo;
|
||||||
import org.apache.activemq.command.TransactionInfo;
|
import org.apache.activemq.command.TransactionInfo;
|
||||||
|
import org.apache.activemq.security.AuthenticationBroker;
|
||||||
|
import org.apache.activemq.security.SecurityContext;
|
||||||
import org.apache.activemq.selector.SelectorParser;
|
import org.apache.activemq.selector.SelectorParser;
|
||||||
import org.apache.activemq.store.PersistenceAdapterSupport;
|
import org.apache.activemq.store.PersistenceAdapterSupport;
|
||||||
import org.apache.activemq.transport.amqp.message.AMQPNativeInboundTransformer;
|
import org.apache.activemq.transport.amqp.message.AMQPNativeInboundTransformer;
|
||||||
|
@ -139,6 +144,7 @@ class AmqpProtocolConverter implements IAmqpProtocolConverter {
|
||||||
private final AmqpTransport amqpTransport;
|
private final AmqpTransport amqpTransport;
|
||||||
private final AmqpWireFormat amqpWireFormat;
|
private final AmqpWireFormat amqpWireFormat;
|
||||||
private final BrokerService brokerService;
|
private final BrokerService brokerService;
|
||||||
|
private AuthenticationBroker authenticator;
|
||||||
|
|
||||||
protected int prefetch;
|
protected int prefetch;
|
||||||
protected int producerCredit;
|
protected int producerCredit;
|
||||||
|
@ -310,14 +316,22 @@ class AmqpProtocolConverter implements IAmqpProtocolConverter {
|
||||||
if (parts.length > 1) {
|
if (parts.length > 1) {
|
||||||
connectionInfo.setPassword(parts[1].utf8().toString());
|
connectionInfo.setPassword(parts[1].utf8().toString());
|
||||||
}
|
}
|
||||||
// We can't really auth at this point since we don't
|
|
||||||
// know the client id yet.. :(
|
if (tryAuthenticate(connectionInfo, amqpTransport.getPeerCertificates())) {
|
||||||
sasl.done(Sasl.SaslOutcome.PN_SASL_OK);
|
sasl.done(Sasl.SaslOutcome.PN_SASL_OK);
|
||||||
|
} else {
|
||||||
|
sasl.done(Sasl.SaslOutcome.PN_SASL_AUTH);
|
||||||
|
}
|
||||||
|
|
||||||
amqpTransport.getWireFormat().resetMagicRead();
|
amqpTransport.getWireFormat().resetMagicRead();
|
||||||
sasl = null;
|
sasl = null;
|
||||||
LOG.debug("SASL [PLAIN] Handshake complete.");
|
LOG.debug("SASL [PLAIN] Handshake complete.");
|
||||||
} else if ("ANONYMOUS".equals(sasl.getRemoteMechanisms()[0])) {
|
} else if ("ANONYMOUS".equals(sasl.getRemoteMechanisms()[0])) {
|
||||||
|
if (tryAuthenticate(connectionInfo, amqpTransport.getPeerCertificates())) {
|
||||||
sasl.done(Sasl.SaslOutcome.PN_SASL_OK);
|
sasl.done(Sasl.SaslOutcome.PN_SASL_OK);
|
||||||
|
} else {
|
||||||
|
sasl.done(Sasl.SaslOutcome.PN_SASL_AUTH);
|
||||||
|
}
|
||||||
amqpTransport.getWireFormat().resetMagicRead();
|
amqpTransport.getWireFormat().resetMagicRead();
|
||||||
sasl = null;
|
sasl = null;
|
||||||
LOG.debug("SASL [ANONYMOUS] Handshake complete.");
|
LOG.debug("SASL [ANONYMOUS] Handshake complete.");
|
||||||
|
@ -1690,4 +1704,46 @@ class AmqpProtocolConverter implements IAmqpProtocolConverter {
|
||||||
|
|
||||||
return subscriptions;
|
return subscriptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean tryAuthenticate(ConnectionInfo info, X509Certificate[] peerCertificates) {
|
||||||
|
try {
|
||||||
|
if (getAuthenticator().authenticate(info.getUserName(), info.getPassword(), peerCertificates) != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} catch (Throwable error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AuthenticationBroker getAuthenticator() {
|
||||||
|
if (authenticator == null) {
|
||||||
|
try {
|
||||||
|
authenticator = (AuthenticationBroker) brokerService.getBroker().getAdaptor(AuthenticationBroker.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.debug("Failed to lookup AuthenticationBroker from Broker, will use a default Noop version.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authenticator == null) {
|
||||||
|
authenticator = new DefaultAuthenticationBroker();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return authenticator;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DefaultAuthenticationBroker implements AuthenticationBroker {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityContext authenticate(String username, String password, X509Certificate[] peerCertificates) throws SecurityException {
|
||||||
|
return new SecurityContext(username) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Principal> getPrincipals() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,8 +69,6 @@ public class JMSClientSimpleAuthTest {
|
||||||
try {
|
try {
|
||||||
Connection connection = JMSClientContext.INSTANCE.createConnection(amqpURI, "", "");
|
Connection connection = JMSClientContext.INSTANCE.createConnection(amqpURI, "", "");
|
||||||
connection.start();
|
connection.start();
|
||||||
Thread.sleep(500);
|
|
||||||
connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
|
||||||
fail("Expected JMSException");
|
fail("Expected JMSException");
|
||||||
} catch (JMSSecurityException ex) {
|
} catch (JMSSecurityException ex) {
|
||||||
LOG.debug("Failed to authenticate connection with no user / password.");
|
LOG.debug("Failed to authenticate connection with no user / password.");
|
||||||
|
@ -91,8 +89,6 @@ public class JMSClientSimpleAuthTest {
|
||||||
try {
|
try {
|
||||||
Connection connection = JMSClientContext.INSTANCE.createConnection(amqpURI, "nosuchuser", "blah");
|
Connection connection = JMSClientContext.INSTANCE.createConnection(amqpURI, "nosuchuser", "blah");
|
||||||
connection.start();
|
connection.start();
|
||||||
Thread.sleep(500);
|
|
||||||
connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
|
||||||
fail("Expected JMSException");
|
fail("Expected JMSException");
|
||||||
} catch (JMSSecurityException ex) {
|
} catch (JMSSecurityException ex) {
|
||||||
LOG.debug("Failed to authenticate connection with no user / password.");
|
LOG.debug("Failed to authenticate connection with no user / password.");
|
||||||
|
@ -113,8 +109,6 @@ public class JMSClientSimpleAuthTest {
|
||||||
try {
|
try {
|
||||||
Connection connection = JMSClientContext.INSTANCE.createConnection(amqpURI, "user", "wrongPassword");
|
Connection connection = JMSClientContext.INSTANCE.createConnection(amqpURI, "user", "wrongPassword");
|
||||||
connection.start();
|
connection.start();
|
||||||
Thread.sleep(500);
|
|
||||||
connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
|
||||||
fail("Expected JMSException");
|
fail("Expected JMSException");
|
||||||
} catch (JMSSecurityException ex) {
|
} catch (JMSSecurityException ex) {
|
||||||
LOG.debug("Failed to authenticate connection with no user / password.");
|
LOG.debug("Failed to authenticate connection with no user / password.");
|
||||||
|
@ -130,6 +124,33 @@ public class JMSClientSimpleAuthTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 30000)
|
||||||
|
public void testRepeatedWrongPasswordAttempts() throws Exception {
|
||||||
|
for (int i = 0; i < 25; ++i) {
|
||||||
|
Connection connection = null;
|
||||||
|
try {
|
||||||
|
connection = JMSClientContext.INSTANCE.createConnection(amqpURI, "user", "wrongPassword");
|
||||||
|
connection.start();
|
||||||
|
fail("Expected JMSException");
|
||||||
|
} catch (JMSSecurityException ex) {
|
||||||
|
LOG.debug("Failed to authenticate connection with no user / password.");
|
||||||
|
} catch (JMSException e) {
|
||||||
|
Exception linkedException = e.getLinkedException();
|
||||||
|
if (linkedException != null && linkedException instanceof ConnectionClosedException) {
|
||||||
|
ConnectionClosedException cce = (ConnectionClosedException) linkedException;
|
||||||
|
assertEquals("Error{condition=unauthorized-access,description=User name [user] or password is invalid.}", cce.getRemoteError().toString());
|
||||||
|
} else {
|
||||||
|
LOG.error("Unexpected Exception", e);
|
||||||
|
fail("Unexpected exception: " + e.getMessage());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (connection != null) {
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test(timeout = 30000)
|
@Test(timeout = 30000)
|
||||||
public void testSendReceive() throws Exception {
|
public void testSendReceive() throws Exception {
|
||||||
Connection connection = JMSClientContext.INSTANCE.createConnection(amqpURI, "user", "userPassword");
|
Connection connection = JMSClientContext.INSTANCE.createConnection(amqpURI, "user", "userPassword");
|
||||||
|
@ -205,4 +226,3 @@ public class JMSClientSimpleAuthTest {
|
||||||
brokerService.waitUntilStarted();
|
brokerService.waitUntilStarted();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ import org.apache.activemq.broker.ConnectionContext;
|
||||||
import org.apache.activemq.command.ActiveMQDestination;
|
import org.apache.activemq.command.ActiveMQDestination;
|
||||||
import org.apache.activemq.command.ConnectionInfo;
|
import org.apache.activemq.command.ConnectionInfo;
|
||||||
|
|
||||||
public class AbstractAuthenticationBroker extends BrokerFilter {
|
public abstract class AbstractAuthenticationBroker extends BrokerFilter implements AuthenticationBroker {
|
||||||
|
|
||||||
protected final CopyOnWriteArrayList<SecurityContext> securityContexts =
|
protected final CopyOnWriteArrayList<SecurityContext> securityContexts =
|
||||||
new CopyOnWriteArrayList<SecurityContext>();
|
new CopyOnWriteArrayList<SecurityContext>();
|
||||||
|
@ -51,10 +51,6 @@ public class AbstractAuthenticationBroker extends BrokerFilter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Previously logged in users may no longer have the same access anymore.
|
|
||||||
* Refresh all the logged into users.
|
|
||||||
*/
|
|
||||||
public void refresh() {
|
public void refresh() {
|
||||||
for (SecurityContext sc : securityContexts) {
|
for (SecurityContext sc : securityContexts) {
|
||||||
sc.getAuthorizedReadDests().clear();
|
sc.getAuthorizedReadDests().clear();
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.activemq.security;
|
||||||
|
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base for all broker plugins that wish to provide connection authentication services
|
||||||
|
*/
|
||||||
|
public interface AuthenticationBroker {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authenticate the given user using the mechanism provided by this service.
|
||||||
|
*
|
||||||
|
* @param username
|
||||||
|
* the given user name to authenticate, null indicates an anonymous user.
|
||||||
|
* @param password
|
||||||
|
* the given password for the user to authenticate.
|
||||||
|
* @param peerCertificates
|
||||||
|
* for an SSL channel the certificates from remote peer.
|
||||||
|
*
|
||||||
|
* @return a new SecurityContext for the authenticated user.
|
||||||
|
*
|
||||||
|
* @throws SecurityException if the user cannot be authenticated.
|
||||||
|
*/
|
||||||
|
SecurityContext authenticate(String username, String password, X509Certificate[] peerCertificates) throws SecurityException;
|
||||||
|
|
||||||
|
}
|
|
@ -16,10 +16,6 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.activemq.security;
|
package org.apache.activemq.security;
|
||||||
|
|
||||||
import org.apache.activemq.command.ActiveMQDestination;
|
|
||||||
import org.apache.activemq.filter.DestinationMap;
|
|
||||||
import org.apache.activemq.filter.DestinationMapEntry;
|
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
|
@ -29,6 +25,10 @@ import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.activemq.command.ActiveMQDestination;
|
||||||
|
import org.apache.activemq.filter.DestinationMap;
|
||||||
|
import org.apache.activemq.filter.DestinationMapEntry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a destination based configuration of policies so that individual
|
* Represents a destination based configuration of policies so that individual
|
||||||
* destinations or wildcard hierarchies of destinations can be configured using
|
* destinations or wildcard hierarchies of destinations can be configured using
|
||||||
|
@ -64,6 +64,7 @@ public class DefaultAuthorizationMap extends DestinationMap implements Authoriza
|
||||||
return this.tempDestinationAuthorizationEntry;
|
return this.tempDestinationAuthorizationEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Object> getTempDestinationAdminACLs() {
|
public Set<Object> getTempDestinationAdminACLs() {
|
||||||
if (tempDestinationAuthorizationEntry != null) {
|
if (tempDestinationAuthorizationEntry != null) {
|
||||||
Set<Object> answer = new WildcardAwareSet<Object>();
|
Set<Object> answer = new WildcardAwareSet<Object>();
|
||||||
|
@ -74,6 +75,7 @@ public class DefaultAuthorizationMap extends DestinationMap implements Authoriza
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Object> getTempDestinationReadACLs() {
|
public Set<Object> getTempDestinationReadACLs() {
|
||||||
if (tempDestinationAuthorizationEntry != null) {
|
if (tempDestinationAuthorizationEntry != null) {
|
||||||
Set<Object> answer = new WildcardAwareSet<Object>();
|
Set<Object> answer = new WildcardAwareSet<Object>();
|
||||||
|
@ -84,6 +86,7 @@ public class DefaultAuthorizationMap extends DestinationMap implements Authoriza
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Object> getTempDestinationWriteACLs() {
|
public Set<Object> getTempDestinationWriteACLs() {
|
||||||
if (tempDestinationAuthorizationEntry != null) {
|
if (tempDestinationAuthorizationEntry != null) {
|
||||||
Set<Object> answer = new WildcardAwareSet<Object>();
|
Set<Object> answer = new WildcardAwareSet<Object>();
|
||||||
|
@ -94,6 +97,7 @@ public class DefaultAuthorizationMap extends DestinationMap implements Authoriza
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Object> getAdminACLs(ActiveMQDestination destination) {
|
public Set<Object> getAdminACLs(ActiveMQDestination destination) {
|
||||||
Set<AuthorizationEntry> entries = getAllEntries(destination);
|
Set<AuthorizationEntry> entries = getAllEntries(destination);
|
||||||
Set<Object> answer = new WildcardAwareSet<Object>();
|
Set<Object> answer = new WildcardAwareSet<Object>();
|
||||||
|
@ -106,6 +110,7 @@ public class DefaultAuthorizationMap extends DestinationMap implements Authoriza
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Object> getReadACLs(ActiveMQDestination destination) {
|
public Set<Object> getReadACLs(ActiveMQDestination destination) {
|
||||||
Set<AuthorizationEntry> entries = getAllEntries(destination);
|
Set<AuthorizationEntry> entries = getAllEntries(destination);
|
||||||
Set<Object> answer = new WildcardAwareSet<Object>();
|
Set<Object> answer = new WildcardAwareSet<Object>();
|
||||||
|
@ -118,6 +123,7 @@ public class DefaultAuthorizationMap extends DestinationMap implements Authoriza
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Object> getWriteACLs(ActiveMQDestination destination) {
|
public Set<Object> getWriteACLs(ActiveMQDestination destination) {
|
||||||
Set<AuthorizationEntry> entries = getAllEntries(destination);
|
Set<AuthorizationEntry> entries = getAllEntries(destination);
|
||||||
Set<Object> answer = new WildcardAwareSet<Object>();
|
Set<Object> answer = new WildcardAwareSet<Object>();
|
||||||
|
@ -150,7 +156,7 @@ public class DefaultAuthorizationMap extends DestinationMap implements Authoriza
|
||||||
* matching values.
|
* matching values.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
@SuppressWarnings("rawtypes")
|
||||||
public synchronized Set get(ActiveMQDestination key) {
|
public synchronized Set get(ActiveMQDestination key) {
|
||||||
if (key.isComposite()) {
|
if (key.isComposite()) {
|
||||||
ActiveMQDestination[] destinations = key.getCompositeDestinations();
|
ActiveMQDestination[] destinations = key.getCompositeDestinations();
|
||||||
|
@ -184,6 +190,7 @@ public class DefaultAuthorizationMap extends DestinationMap implements Authoriza
|
||||||
this.defaultEntry = defaultEntry;
|
this.defaultEntry = defaultEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
protected Class<? extends DestinationMapEntry> getEntryClass() {
|
protected Class<? extends DestinationMapEntry> getEntryClass() {
|
||||||
return AuthorizationEntry.class;
|
return AuthorizationEntry.class;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.apache.activemq.security;
|
package org.apache.activemq.security;
|
||||||
|
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.security.auth.Subject;
|
import javax.security.auth.Subject;
|
||||||
|
@ -58,32 +59,36 @@ public class JaasAuthenticationBroker extends AbstractAuthenticationBroker {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
|
public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
|
||||||
|
|
||||||
if (context.getSecurityContext() == null) {
|
if (context.getSecurityContext() == null) {
|
||||||
// Set the TCCL since it seems JAAS needs it to find the login
|
// Set the TCCL since it seems JAAS needs it to find the login module classes.
|
||||||
// module classes.
|
|
||||||
ClassLoader original = Thread.currentThread().getContextClassLoader();
|
ClassLoader original = Thread.currentThread().getContextClassLoader();
|
||||||
Thread.currentThread().setContextClassLoader(JaasAuthenticationBroker.class.getClassLoader());
|
Thread.currentThread().setContextClassLoader(JaasAuthenticationBroker.class.getClassLoader());
|
||||||
try {
|
|
||||||
// Do the login.
|
|
||||||
try {
|
|
||||||
JassCredentialCallbackHandler callback = new JassCredentialCallbackHandler(info
|
|
||||||
.getUserName(), info.getPassword());
|
|
||||||
LoginContext lc = new LoginContext(jassConfiguration, callback);
|
|
||||||
lc.login();
|
|
||||||
Subject subject = lc.getSubject();
|
|
||||||
|
|
||||||
SecurityContext s = new JaasSecurityContext(info.getUserName(), subject);
|
try {
|
||||||
|
SecurityContext s = authenticate(info.getUserName(), info.getPassword(), null);
|
||||||
context.setSecurityContext(s);
|
context.setSecurityContext(s);
|
||||||
securityContexts.add(s);
|
securityContexts.add(s);
|
||||||
} catch (Exception e) {
|
|
||||||
throw (SecurityException)new SecurityException("User name [" + info.getUserName() + "] or password is invalid.")
|
|
||||||
.initCause(e);
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
Thread.currentThread().setContextClassLoader(original);
|
Thread.currentThread().setContextClassLoader(original);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.addConnection(context, info);
|
super.addConnection(context, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityContext authenticate(String username, String password, X509Certificate[] certificates) throws SecurityException {
|
||||||
|
SecurityContext result = null;
|
||||||
|
JassCredentialCallbackHandler callback = new JassCredentialCallbackHandler(username, password);
|
||||||
|
try {
|
||||||
|
LoginContext lc = new LoginContext(jassConfiguration, callback);
|
||||||
|
lc.login();
|
||||||
|
Subject subject = lc.getSubject();
|
||||||
|
|
||||||
|
result = new JaasSecurityContext(username, subject);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new SecurityException("User name [" + username + "] or password is invalid.", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,8 @@ import org.apache.activemq.jaas.UserPrincipal;
|
||||||
* grant JAAS access to incoming connections' SSL certificate chains. NOTE:
|
* grant JAAS access to incoming connections' SSL certificate chains. NOTE:
|
||||||
* There is a chance that the incoming connection does not have a valid
|
* There is a chance that the incoming connection does not have a valid
|
||||||
* certificate (has null).
|
* certificate (has null).
|
||||||
*
|
|
||||||
* @author sepandm@gmail.com (Sepand)
|
|
||||||
*/
|
*/
|
||||||
public class JaasCertificateAuthenticationBroker extends BrokerFilter {
|
public class JaasCertificateAuthenticationBroker extends BrokerFilter implements AuthenticationBroker {
|
||||||
private final String jaasConfiguration;
|
private final String jaasConfiguration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,6 +65,7 @@ public class JaasCertificateAuthenticationBroker extends BrokerFilter {
|
||||||
* @param info The ConnectionInfo Command representing the incoming
|
* @param info The ConnectionInfo Command representing the incoming
|
||||||
* connection.
|
* connection.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
|
public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
|
||||||
|
|
||||||
if (context.getSecurityContext() == null) {
|
if (context.getSecurityContext() == null) {
|
||||||
|
@ -79,9 +78,29 @@ public class JaasCertificateAuthenticationBroker extends BrokerFilter {
|
||||||
ClassLoader original = Thread.currentThread().getContextClassLoader();
|
ClassLoader original = Thread.currentThread().getContextClassLoader();
|
||||||
Thread.currentThread().setContextClassLoader(JaasAuthenticationBroker.class.getClassLoader());
|
Thread.currentThread().setContextClassLoader(JaasAuthenticationBroker.class.getClassLoader());
|
||||||
try {
|
try {
|
||||||
// Do the login.
|
SecurityContext s = authenticate(info.getUserName(), info.getPassword(), (X509Certificate[]) info.getTransportContext());
|
||||||
|
context.setSecurityContext(s);
|
||||||
|
} finally {
|
||||||
|
Thread.currentThread().setContextClassLoader(original);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.addConnection(context, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overriding removeConnection to make sure the security context is cleaned.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Exception {
|
||||||
|
super.removeConnection(context, info, error);
|
||||||
|
|
||||||
|
context.setSecurityContext(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityContext authenticate(String username, String password, X509Certificate[] peerCertificates) throws SecurityException {
|
||||||
try {
|
try {
|
||||||
CallbackHandler callback = new JaasCertificateCallbackHandler((X509Certificate[])info.getTransportContext());
|
CallbackHandler callback = new JaasCertificateCallbackHandler(peerCertificates);
|
||||||
LoginContext lc = new LoginContext(jaasConfiguration, callback);
|
LoginContext lc = new LoginContext(jaasConfiguration, callback);
|
||||||
lc.login();
|
lc.login();
|
||||||
Subject subject = lc.getSubject();
|
Subject subject = lc.getSubject();
|
||||||
|
@ -94,24 +113,10 @@ public class JaasCertificateAuthenticationBroker extends BrokerFilter {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SecurityContext s = new JaasCertificateSecurityContext(dnName, subject, (X509Certificate[])info.getTransportContext());
|
|
||||||
context.setSecurityContext(s);
|
return new JaasCertificateSecurityContext(dnName, subject, peerCertificates);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new SecurityException("User name [" + info.getUserName() + "] or password is invalid. " + e.getMessage(), e);
|
throw new SecurityException("User name [" + username + "] or password is invalid. " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
Thread.currentThread().setContextClassLoader(original);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.addConnection(context, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overriding removeConnection to make sure the security context is cleaned.
|
|
||||||
*/
|
|
||||||
public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Exception {
|
|
||||||
super.removeConnection(context, info, error);
|
|
||||||
|
|
||||||
context.setSecurityContext(null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
package org.apache.activemq.security;
|
package org.apache.activemq.security;
|
||||||
|
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
import org.apache.activemq.broker.Broker;
|
import org.apache.activemq.broker.Broker;
|
||||||
import org.apache.activemq.broker.BrokerFilter;
|
import org.apache.activemq.broker.BrokerFilter;
|
||||||
import org.apache.activemq.broker.ConnectionContext;
|
import org.apache.activemq.broker.ConnectionContext;
|
||||||
|
@ -56,7 +58,7 @@ import org.apache.activemq.transport.tcp.SslTransportServer;
|
||||||
* };
|
* };
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
public class JaasDualAuthenticationBroker extends BrokerFilter {
|
public class JaasDualAuthenticationBroker extends BrokerFilter implements AuthenticationBroker {
|
||||||
private final JaasCertificateAuthenticationBroker sslBroker;
|
private final JaasCertificateAuthenticationBroker sslBroker;
|
||||||
private final JaasAuthenticationBroker nonSslBroker;
|
private final JaasAuthenticationBroker nonSslBroker;
|
||||||
|
|
||||||
|
@ -87,13 +89,11 @@ public class JaasDualAuthenticationBroker extends BrokerFilter {
|
||||||
@Override
|
@Override
|
||||||
public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
|
public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
|
||||||
if (context.getSecurityContext() == null) {
|
if (context.getSecurityContext() == null) {
|
||||||
boolean isSSL;
|
boolean isSSL = false;
|
||||||
Connector connector = context.getConnector();
|
Connector connector = context.getConnector();
|
||||||
if (connector instanceof TransportConnector) {
|
if (connector instanceof TransportConnector) {
|
||||||
TransportConnector transportConnector = (TransportConnector) connector;
|
TransportConnector transportConnector = (TransportConnector) connector;
|
||||||
isSSL = transportConnector.getServer().isSslServer();
|
isSSL = transportConnector.getServer().isSslServer();
|
||||||
} else {
|
|
||||||
isSSL = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSSL) {
|
if (isSSL) {
|
||||||
|
@ -134,4 +134,13 @@ public class JaasDualAuthenticationBroker extends BrokerFilter {
|
||||||
|
|
||||||
super.removeDestination(context, destination, timeout);
|
super.removeDestination(context, destination, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityContext authenticate(String username, String password, X509Certificate[] peerCertificates) throws SecurityException {
|
||||||
|
if (peerCertificates != null) {
|
||||||
|
return this.sslBroker.authenticate(username, password, peerCertificates);
|
||||||
|
} else {
|
||||||
|
return this.nonSslBroker.authenticate(username, password, peerCertificates);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.apache.activemq.security;
|
package org.apache.activemq.security;
|
||||||
|
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -65,13 +66,30 @@ public class SimpleAuthenticationBroker extends AbstractAuthenticationBroker {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
|
public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
|
||||||
|
SecurityContext securityContext = context.getSecurityContext();
|
||||||
|
if (securityContext == null) {
|
||||||
|
securityContext = authenticate(info.getUserName(), info.getPassword(), null);
|
||||||
|
context.setSecurityContext(securityContext);
|
||||||
|
securityContexts.add(securityContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
super.addConnection(context, info);
|
||||||
|
} catch (Exception e) {
|
||||||
|
securityContexts.remove(securityContext);
|
||||||
|
context.setSecurityContext(null);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityContext authenticate(String username, String password, X509Certificate[] certificates) throws SecurityException {
|
||||||
|
SecurityContext securityContext = null;
|
||||||
|
|
||||||
SecurityContext s = context.getSecurityContext();
|
|
||||||
if (s == null) {
|
|
||||||
// Check the username and password.
|
// Check the username and password.
|
||||||
if (anonymousAccessAllowed && info.getUserName() == null && info.getPassword() == null) {
|
if (anonymousAccessAllowed && username == null && password == null) {
|
||||||
info.setUserName(anonymousUser);
|
username = anonymousUser;
|
||||||
s = new SecurityContext(info.getUserName()) {
|
securityContext = new SecurityContext(username) {
|
||||||
@Override
|
@Override
|
||||||
public Set<Principal> getPrincipals() {
|
public Set<Principal> getPrincipals() {
|
||||||
Set<Principal> groups = new HashSet<Principal>();
|
Set<Principal> groups = new HashSet<Principal>();
|
||||||
|
@ -80,14 +98,13 @@ public class SimpleAuthenticationBroker extends AbstractAuthenticationBroker {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
String pw = userPasswords.get(info.getUserName());
|
String pw = userPasswords.get(username);
|
||||||
if (pw == null || !pw.equals(info.getPassword())) {
|
if (pw == null || !pw.equals(password)) {
|
||||||
throw new SecurityException(
|
throw new SecurityException("User name [" + username + "] or password is invalid.");
|
||||||
"User name [" + info.getUserName() + "] or password is invalid.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final Set<Principal> groups = userGroups.get(info.getUserName());
|
final Set<Principal> groups = userGroups.get(username);
|
||||||
s = new SecurityContext(info.getUserName()) {
|
securityContext = new SecurityContext(username) {
|
||||||
@Override
|
@Override
|
||||||
public Set<Principal> getPrincipals() {
|
public Set<Principal> getPrincipals() {
|
||||||
return groups;
|
return groups;
|
||||||
|
@ -95,16 +112,6 @@ public class SimpleAuthenticationBroker extends AbstractAuthenticationBroker {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
context.setSecurityContext(s);
|
return securityContext;
|
||||||
securityContexts.add(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
super.addConnection(context, info);
|
|
||||||
} catch (Exception e) {
|
|
||||||
securityContexts.remove(s);
|
|
||||||
context.setSecurityContext(null);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue