Author: ron.sigal(a)jboss.com
Date: 2010-10-20 00:17:43 -0400 (Wed, 20 Oct 2010)
New Revision: 6120
Added:
remoting3/trunk/jboss-remoting/src/test/java/org/jboss/remoting3/test/security/
remoting3/trunk/jboss-remoting/src/test/java/org/jboss/remoting3/test/security/AuthenticationTestCase.java
Log:
JBREM-1228: New unit test.
Added:
remoting3/trunk/jboss-remoting/src/test/java/org/jboss/remoting3/test/security/AuthenticationTestCase.java
===================================================================
---
remoting3/trunk/jboss-remoting/src/test/java/org/jboss/remoting3/test/security/AuthenticationTestCase.java
(rev 0)
+++
remoting3/trunk/jboss-remoting/src/test/java/org/jboss/remoting3/test/security/AuthenticationTestCase.java 2010-10-20
04:17:43 UTC (rev 6120)
@@ -0,0 +1,475 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+
+package org.jboss.remoting3.test.security;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServerFactory;
+
+import org.jboss.marshalling.river.RiverProviderDescriptor;
+import org.jboss.remoting3.Client;
+import org.jboss.remoting3.ClientContext;
+import org.jboss.remoting3.ClientListener;
+import org.jboss.remoting3.CloseHandler;
+import org.jboss.remoting3.Connection;
+import org.jboss.remoting3.Endpoint;
+import org.jboss.remoting3.Registration;
+import org.jboss.remoting3.RemoteExecutionException;
+import org.jboss.remoting3.Remoting;
+import org.jboss.remoting3.RequestContext;
+import org.jboss.remoting3.RequestListener;
+import org.jboss.remoting3.Endpoint.ServiceBuilder;
+import org.jboss.remoting3.remote.RemoteProtocolDescriptor;
+import org.jboss.remoting3.security.ServerAuthenticationProvider;
+import org.jboss.remoting3.security.SimpleClientCallbackHandler;
+import org.jboss.remoting3.security.SimpleServerAuthenticationProvider;
+import org.jboss.remoting3.spi.ConnectionProviderFactory;
+import org.jboss.remoting3.spi.NetworkServerProvider;
+import org.jboss.remoting3.spi.ProtocolServiceType;
+import org.jboss.remoting3.spi.RemotingServiceDescriptor;
+import org.jboss.remoting3.test.RemotingTestBase;
+import org.jboss.xnio.ChannelListener;
+import org.jboss.xnio.IoFuture;
+import org.jboss.xnio.OptionMap;
+import org.jboss.xnio.Options;
+import org.jboss.xnio.TcpServer;
+import org.jboss.xnio.Xnio;
+import org.jboss.xnio.channels.BoundChannel;
+import org.jboss.xnio.channels.ConnectedStreamChannel;
+import org.jboss.xnio.log.Logger;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author <a href="ron.sigal(a)jboss.com">Ron Sigal</a>
+ * @version $Revision: 1.1 $
+ * <p>
+ * Copyright Jun 22, 2010
+ */
+@Test(suiteName = "Authentication")
+public class AuthenticationTestCase extends RemotingTestBase {
+
+ private static final Logger log = Logger.getLogger(AuthenticationTestCase.class);
+ private static int counter;
+
+ protected static final String SERVICE = "service";
+ protected static final String INSTANCE = "instance";
+
+ protected HashSet<String> mechanismSet = new HashSet<String>();
+ protected Endpoint serverEndpoint;
+ protected TestRequestListener requestListener;
+ protected int port;
+
+ @BeforeTest
+ public void getSASLMechanisms() {
+ Enumeration<SaslServerFactory> e = Sasl.getSaslServerFactories();
+ while (e.hasMoreElements()) {
+ SaslServerFactory ssf = e.nextElement();
+ log.debug(ssf.toString());
+ String[] mechanisms = ssf.getMechanismNames(null);
+ for (int i = 0; i < mechanisms.length; i++) {
+ log.debug("mechanism: " + mechanisms[i]);
+ mechanismSet.add(mechanisms[i]);
+ }
+ }
+ log.debug("number of mechanims: " + mechanismSet.size());
+ }
+
+ @BeforeMethod
+ public void setUp() {
+ }
+
+ @AfterMethod
+ public void tearDown() throws IOException {
+ }
+
+ @Test
+ public void testDIGEST_MD5() throws Exception {
+ enter();
+
+ try {
+ if (mechanismSet.contains("DIGEST-MD5")) {
+ log.debug("running DIGEST-MD5 test");
+ OptionMap clientOptionMap = OptionMap.EMPTY;
+ OptionMap serverOptionMap =
OptionMap.builder().setSequence(Options.SASL_MECHANISMS, new
String[]{"DIGEST-MD5"}).getMap();
+ doTest(clientOptionMap, serverOptionMap);
+ } else {
+ log.info("DIGEST-MD5 mechanism is not supported. Unable to run
test");
+ }
+ log.info(getName() + " PASSES");
+ } finally {
+ if (serverEndpoint != null) {
+ serverEndpoint.close();
+ }
+ exit();
+ }
+ }
+
+ @Test
+ public void testCRAM_MD5() throws Exception {
+ enter();
+
+ try {
+ if (mechanismSet.contains("CRAM-MD5")) {
+ log.debug("running CRAM-MD5 test");
+ OptionMap clientOptionMap = OptionMap.EMPTY;
+ OptionMap serverOptionMap =
OptionMap.builder().setSequence(Options.SASL_MECHANISMS, new
String[]{"CRAM-MD5"}).getMap();
+ doTest(clientOptionMap, serverOptionMap);
+ } else {
+ log.info("CRAM-MD5 mechanism is not supported. Unable to run
test");
+ }
+ log.info(getName() + " PASSES");
+ } finally {
+ if (serverEndpoint != null) {
+ serverEndpoint.close();
+ }
+ exit();
+ }
+ }
+
+ @Test
+ public void testAnonymous() throws Exception {
+ enter();
+
+ try {
+ if (mechanismSet.contains("ANONYMOUS")) {
+ log.debug("running ANONYMOUS test");
+ OptionMap serverOptionMap =
OptionMap.builder().setSequence(Options.SASL_MECHANISMS, new
String[]{"ANONYMOUS"}).getMap();
+
+ ServerAuthenticationProvider authenticationProvider = new
ServerAuthenticationProvider() {
+ public CallbackHandler getCallbackHandler() {
+ return null;
+ }
+ };
+
+ int id = counter++;
+ setupServer(id, serverOptionMap, authenticationProvider);
+ Endpoint clientEndpoint = Remoting.getConfiguredEndpoint();
+ URI uri = null;
+ Connection connection = null;
+ Client<Object, Object> client = null;
+
+ try {
+ uri = new URI(getRemotingScheme() + "://localhost:" + port);
+ connection = getFutureResult(clientEndpoint.connect(uri), "cannot
connect to " + uri);
+ log.debug("connected to " + uri);
+ client = getFutureResult(connection.openClient(SERVICE, INSTANCE,
Object.class, Object.class), "cannot create client to " + SERVICE +
":" + INSTANCE);
+ assertNotNull(client.invoke("hello"));
+ } finally {
+ if (client != null) {
+ client.close();
+ }
+ if (connection != null) {
+ connection.close();
+ }
+ }
+
+ } else {
+ log.info("ANONYMOUS mechanism is not supported. Unable to run
test");
+ }
+ log.info(getName() + " PASSES");
+ } finally {
+ if (serverEndpoint != null) {
+ serverEndpoint.close();
+ }
+ exit();
+ }
+ }
+
+ protected void doTest(OptionMap clientOptionMap, OptionMap serverOptionMap) throws
Exception {
+ try {
+ int id = counter++;
+ String realm = "endpoint" + id;
+ SimpleServerAuthenticationProvider authenticationProvider = new
SimpleServerAuthenticationProvider();
+ authenticationProvider.addUser("trustin", realm,
"lee".toCharArray());
+ authenticationProvider.addUser("david", realm,
"lloyd".toCharArray());
+ authenticationProvider.addUser("*", realm,
"*".toCharArray());
+ setupServer(id, serverOptionMap, authenticationProvider);
+ Endpoint clientEndpoint = Remoting.getConfiguredEndpoint();
+ URI uri = null;
+ Connection connection = null;
+ Client<Object, Object> client = null;
+
+ try {
+ uri = new URI(getRemotingScheme() + "://localhost:" + port);
+ connection = getFutureResult(clientEndpoint.connect(uri, clientOptionMap,
"trustin", realm, "lee".toCharArray()), "cannot connect to "
+ uri);
+ log.debug("connected to " + uri);
+ client = getFutureResult(connection.openClient(SERVICE, INSTANCE,
Object.class, Object.class), "cannot create client to " + SERVICE +
":" + INSTANCE);
+ assertNotNull(client.invoke("hello"));
+ } finally {
+ if (client != null) {
+ client.close();
+ }
+ if (connection != null) {
+ connection.close();
+ }
+ }
+
+ try {
+ uri = new URI(getRemotingScheme() + "://localhost:" + port);
+ connection = getFutureResult(clientEndpoint.connect(uri, clientOptionMap,
"trustin", realm, "notlee".toCharArray()), "cannot connect to
" + uri);
+ } catch (SaslException e) {
+ assertEquals("Authentication failed", e.getMessage());
+ } catch (Exception e) {
+ fail("expected SaslException");
+ } finally {
+ if (connection != null) {
+ connection.close();
+ }
+ }
+
+ try {
+ uri = new URI(getRemotingScheme() + "://localhost:" + port);
+ connection = getFutureResult(clientEndpoint.connect(uri, clientOptionMap,
"nottrustin", realm, "lee".toCharArray()), "cannot connect to
" + uri);
+ } catch (SaslException e) {
+ assertEquals("Authentication failed", e.getMessage());
+ } catch (Exception e) {
+ fail("expected SaslException");
+ } finally {
+ if (connection != null) {
+ connection.close();
+ }
+ }
+
+ try {
+ uri = new URI(getRemotingScheme() + "://localhost:" + port);
+ CallbackHandler callbackHandler = new
SimpleClientCallbackHandler("trustin", realm, "lee".toCharArray());
+ connection = getFutureResult(clientEndpoint.connect(uri, clientOptionMap,
callbackHandler), "cannot connect to " + uri);
+ log.debug("connected to " + uri);
+ client = getFutureResult(connection.openClient(SERVICE, INSTANCE,
Object.class, Object.class), "cannot create client to " + SERVICE +
":" + INSTANCE);
+ assertNotNull(client.invoke("hello"));
+ } finally {
+ if (client != null) {
+ client.close();
+ }
+ if (connection != null) {
+ connection.close();
+ }
+ }
+
+ try {
+ uri = new URI(getRemotingScheme() + "://localhost:" + port);
+ CallbackHandler callbackHandler = new
SimpleClientCallbackHandler("trustin", realm,
"notlee".toCharArray());
+ connection = getFutureResult(clientEndpoint.connect(uri, clientOptionMap,
callbackHandler), "cannot connect to " + uri);
+ } catch (SaslException e) {
+ assertEquals("Authentication failed", e.getMessage());
+ } catch (Exception e) {
+ fail("expected SaslException");
+ } finally {
+ if (connection != null) {
+ connection.close();
+ }
+ }
+
+ try {
+ uri = new URI(getRemotingScheme() + "://localhost:" + port);
+ CallbackHandler callbackHandler = new
SimpleClientCallbackHandler("nottrustin", realm,
"lee".toCharArray());
+ connection = getFutureResult(clientEndpoint.connect(uri, clientOptionMap,
callbackHandler), "cannot connect to " + uri);
+ } catch (SaslException e) {
+ assertEquals("Authentication failed", e.getMessage());
+ } catch (Exception e) {
+ fail("expected SaslException");
+ } finally {
+ if (connection != null) {
+ connection.close();
+ }
+ }
+
+ // The following test fails because it doesn't specify a password.
+// uri = new URI(getRemotingScheme() + "://trustin;" + realm +
"@localhost:" + port);
+// connection = getFutureResult(clientEndpoint.connect(uri), "cannot connect
to " + uri);
+// log.debug("connected to " + uri);
+// client = getFutureResult(connection.openClient(SERVICE, INSTANCE,
Object.class, Object.class), "cannot create client to " + SERVICE +
":" + INSTANCE);
+// assertNotNull(client.invoke("hello"));
+
+ try {
+ uri = new URI(getRemotingScheme() + "://localhost:" + port);
+ connection = getFutureResult(clientEndpoint.connect(uri, clientOptionMap,
"*", realm, "*".toCharArray()), "cannot connect to " +
uri);
+ log.debug("connected to " + uri);
+ client = getFutureResult(connection.openClient(SERVICE, INSTANCE,
Object.class, Object.class), "cannot create client to " + SERVICE +
":" + INSTANCE);
+ assertNotNull(client.invoke("hello"));
+ } finally {
+ if (client != null) {
+ client.close();
+ }
+ if (connection != null) {
+ connection.close();
+ }
+ }
+ } finally {
+ if (serverEndpoint != null) {
+ serverEndpoint.close();
+ }
+ }
+ }
+
+ protected String getRemotingScheme() {
+ return "remote";
+ }
+
+ protected RemotingServiceDescriptor<ConnectionProviderFactory>
getProtocolDescriptor() {
+ return new RemoteProtocolDescriptor();
+ }
+
+ protected void setupServer(int id, OptionMap optionMap, ServerAuthenticationProvider
authenticationProvider) throws IOException {
+
+ // Create and configure endpoint.
+ final ThreadPoolExecutor executor = new ThreadPoolExecutor(8, 64, 30,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(64));
+ serverEndpoint = Remoting.createEndpoint("endpoint" + id, executor,
optionMap);
+ final Registration marshallerRegistration =
serverEndpoint.addProtocolService(ProtocolServiceType.MARSHALLER_PROVIDER_DESCRIPTOR,
"river", new RiverProviderDescriptor());
+ String xnioProviderId = "authentication-" + id;
+ Properties props = new Properties();
+ props.put("remote.xnio.provider", xnioProviderId);
+ final Registration transportRegistration =
serverEndpoint.addConnectionProvider(getRemotingScheme(),
getProtocolDescriptor().getService(props));
+
+ // Register service with endpoint.
+ requestListener = new TestRequestListener(id);
+ ServiceBuilder<Object, Object> sb =
serverEndpoint.serviceBuilder(Object.class, Object.class);
+ sb.setInstanceName("instance").setServiceType("service");
+ sb.setClientListener(new TestClientListener(requestListener));
+ final Registration serviceRegistration = sb.register();
+
+ // Set up XNIO layer.
+ NetworkServerProvider provider =
serverEndpoint.getConnectionProviderInterface(getRemotingScheme(),
NetworkServerProvider.class);
+ ChannelListener<ConnectedStreamChannel<InetSocketAddress>> listener =
provider.getServerListener(optionMap, authenticationProvider);
+ final Xnio xnio = Xnio.getInstance(xnioProviderId);
+ final TcpServer tcpServer = createTcpServer(xnio, optionMap, listener);
+ port = tcpServer.getChannels().iterator().next().getLocalAddress().getPort();
+
+ // Prepare to close everything.
+ serverEndpoint.addCloseHandler(new TestCloseHandler(serviceRegistration,
transportRegistration, marshallerRegistration, tcpServer, xnio, executor));
+ }
+
+ protected TcpServer createTcpServer(Xnio xnio, OptionMap optionMap,
ChannelListener<ConnectedStreamChannel<InetSocketAddress>> listener) throws
IOException {
+ log.debug(this + " creating TcpServer");
+ TcpServer tcpServer = xnio.createTcpServer(listener, optionMap);
+ IoFuture<? extends BoundChannel<InetSocketAddress>> future =
tcpServer.bind(new InetSocketAddress("localhost", 0));
+ getFutureResult(future, "unable to bind " + tcpServer);
+ return tcpServer;
+ }
+
+ static class TestRequestListener implements RequestListener<Object, Object> {
+ public int counter = 0;
+ private int answer;
+
+ public TestRequestListener(int answer) {
+ this.answer = answer;
+ }
+
+ public void handleRequest(RequestContext<Object> context, Object request)
throws RemoteExecutionException {
+ try {
+ counter++;
+ context.sendReply(answer);
+ } catch (IOException e) {
+ try {
+ context.sendFailure("error returning response", e);
+ } catch (IOException e1) {
+ e.printStackTrace();
+ throw new RemoteExecutionException("error returning exception",
e1);
+ }
+ }
+ }
+ }
+
+ static class TestClientListener implements ClientListener<Object, Object> {
+ private RequestListener<Object, Object> requestListener;
+
+ TestClientListener(RequestListener<Object, Object> requestListener) {
+ this.requestListener = requestListener;
+ }
+
+ public RequestListener<Object, Object> handleClientOpen(final ClientContext
clientContext, final OptionMap optionMap) {
+ clientContext.addCloseHandler(new CloseHandler<ClientContext>() {
+ public void handleClose(final ClientContext closed) {
+ log.debug("Client closed");
+ }
+ });
+ return requestListener;
+ }
+ }
+
+ static class TestCloseHandler implements CloseHandler<Endpoint> {
+ private Registration serviceRegistration;
+ private Registration transportRegistration;
+ private Registration marshallerRegistration;
+ private TcpServer tcpServer;
+ private Xnio xnio;
+ private ThreadPoolExecutor executor;
+
+ public TestCloseHandler(Registration serviceRegistration, Registration
transportRegistration, Registration marshallerRegistration, TcpServer tcpServer, Xnio
xnio, ThreadPoolExecutor executor) {
+ this.marshallerRegistration = marshallerRegistration;
+ this.transportRegistration = transportRegistration;
+ this.serviceRegistration = serviceRegistration;
+ this.tcpServer = tcpServer;
+ this.xnio = xnio;
+ this.executor = executor;
+ }
+
+ public void handleClose(Endpoint closed) {
+ if (serviceRegistration != null) {
+ serviceRegistration.close();
+ }
+ if (transportRegistration != null) {
+ transportRegistration.close();
+ }
+ if (marshallerRegistration != null) {
+ marshallerRegistration.close();
+ }
+ if (tcpServer != null) {
+ try {
+ tcpServer.close();
+ } catch (IOException e) {
+ log.error("unable to close " + tcpServer);
+ }
+ }
+ if (xnio != null) {
+ try {
+ xnio.close();
+ } catch (IOException e) {
+ log.error("unable to close " + xnio);
+ }
+ }
+ if (executor != null) {
+ executor.shutdown();
+ }
+ }
+ }
+}