Merge "Changed zeromq-routingtable module to remoterpc-routingtable based on coderevi...
authorGiovanni Meo <gmeo@cisco.com>
Tue, 17 Dec 2013 18:24:32 +0000 (18:24 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Tue, 17 Dec 2013 18:24:32 +0000 (18:24 +0000)
21 files changed:
opendaylight/forwardingrulesmanager/implementation/src/main/java/org/opendaylight/controller/forwardingrulesmanager/internal/ForwardingRulesManager.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ReadConfAndOperDataTest.java [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImplTest.java
opendaylight/netconf/netconf-it/pom.xml
opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java
opendaylight/netconf/netconf-ssh/pom.xml
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/osgi/NetconfSSHActivator.java
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/AuthProvider.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/AuthProviderInterface.java [moved from opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/KeyStoreHandler.java with 52% similarity]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/RSAKey.java [deleted file]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java
opendaylight/netconf/netconf-ssh/src/main/resources/RSA.pk [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/SSHServerTest.java [moved from opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/ssh/SSHServerTest.java with 53% similarity]
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/StubUserManager.java [new file with mode: 0644]
opendaylight/netconf/pom.xml
opendaylight/switchmanager/implementation/src/main/java/org/opendaylight/controller/switchmanager/internal/SwitchManager.java
opendaylight/usermanager/api/src/main/java/org/opendaylight/controller/usermanager/UserConfig.java
opendaylight/usermanager/api/src/test/java/org/opendaylight/controller/usermanager/AuthorizationUserConfigTest.java
opendaylight/usermanager/implementation/pom.xml
opendaylight/web/flows/src/main/resources/js/page.js

index 45fb11a83eda110b96b1588dd43b0c8fd6c7b164..3d6a0292ef67225079b11759a3560bdfddf85377 100644 (file)
@@ -2930,8 +2930,16 @@ public class ForwardingRulesManager implements
         }
         if (target != null) {
             // Update Configuration database
-            target.toggleInstallation();
-            target.setStatus(StatusCode.SUCCESS.toString());
+            if (target.getHardTimeout() != null || target.getIdleTimeout() != null) {
+                /*
+                 * No need for checking if actual values: these strings were
+                 * validated at configuration creation. Also, after a switch
+                 * down scenario, no use to reinstall a timed flow. Mark it as
+                 * "do not install". User can manually toggle it.
+                 */
+                target.toggleInstallation();
+            }
+            target.setStatus(StatusCode.GONE.toString());
             staticFlows.put(key, target);
         }
 
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ReadConfAndOperDataTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ReadConfAndOperDataTest.java
new file mode 100644 (file)
index 0000000..bc77430
--- /dev/null
@@ -0,0 +1,125 @@
+package org.opendaylight.controller.sal.restconf.impl.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URLEncoder;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider;
+import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
+import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
+import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Charsets;
+
+public class ReadConfAndOperDataTest extends JerseyTest {
+
+    private static ControllerContext controllerContext;
+    private static BrokerFacade brokerFacade;
+    private static RestconfImpl restconfImpl;
+    private static final MediaType MEDIA_TYPE_DRAFT02 = new MediaType("application", "yang.data+xml");
+
+    @BeforeClass
+    public static void init() throws FileNotFoundException {
+        Set<Module> allModules = TestUtils.loadModules(RestconfImplTest.class.getResource("/full-versions/yangs")
+                .getPath());
+        SchemaContext schemaContext = TestUtils.loadSchemaContext(allModules);
+        controllerContext = ControllerContext.getInstance();
+        controllerContext.setSchemas(schemaContext);
+        brokerFacade = mock(BrokerFacade.class);
+        restconfImpl = RestconfImpl.getInstance();
+        restconfImpl.setBroker(brokerFacade);
+        restconfImpl.setControllerContext(controllerContext);
+    }
+
+    @Before
+    public void logs() {
+        List<LogRecord> loggedRecords = getLoggedRecords();
+        for (LogRecord l : loggedRecords) {
+            System.out.println(l.getMessage());
+        }
+    }
+
+    @Test
+    public void testReadConfigurationData() throws UnsupportedEncodingException, FileNotFoundException {
+
+        String uri = createUri("/config/", "ietf-interfaces:interfaces/interface/eth0");
+
+        InputStream xmlStream = RestconfImplTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces.xml");
+        CompositeNode loadedCompositeNode = TestUtils.loadCompositeNode(xmlStream);
+        when(brokerFacade.readConfigurationData(any(InstanceIdentifier.class))).thenReturn(loadedCompositeNode);
+
+        Response response = target(uri).request(MEDIA_TYPE_DRAFT02).get();
+        assertEquals(200, response.getStatus());
+        
+        uri = createUri("/config/", "ietf-interfaces:interfaces/interface/example");
+        when(brokerFacade.readConfigurationData(any(InstanceIdentifier.class))).thenReturn(null);
+        
+        response = target(uri).request(MEDIA_TYPE_DRAFT02).get();
+        assertEquals(404, response.getStatus());
+    }
+
+    @Test
+    public void testReadOperationalData() throws UnsupportedEncodingException, FileNotFoundException {
+        String uri = createUri("/operational/", "ietf-interfaces:interfaces/interface/eth0");
+
+        InputStream xmlStream = RestconfImplTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces.xml");
+        CompositeNode loadedCompositeNode = TestUtils.loadCompositeNode(xmlStream);
+        when(brokerFacade.readOperationalData(any(InstanceIdentifier.class))).thenReturn(loadedCompositeNode);
+
+        Response response = target(uri).request(MEDIA_TYPE_DRAFT02).get();
+        assertEquals(200, response.getStatus());
+        
+        uri = createUri("/config/", "ietf-interfaces:interfaces/interface/example");
+        when(brokerFacade.readConfigurationData(any(InstanceIdentifier.class))).thenReturn(null);
+        
+        response = target(uri).request(MEDIA_TYPE_DRAFT02).get();
+        assertEquals(404, response.getStatus());
+    }
+
+    private String createUri(String prefix, String encodedPart) throws UnsupportedEncodingException {
+        return URI.create(prefix + URLEncoder.encode(encodedPart, Charsets.US_ASCII.name()).toString()).toASCIIString();
+    }
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.RECORD_LOG_LEVEL);
+        set(TestProperties.RECORD_LOG_LEVEL, Level.ALL.intValue());
+
+        ResourceConfig resourceConfig = new ResourceConfig();
+        resourceConfig = resourceConfig.registerInstances(restconfImpl, StructuredDataToXmlProvider.INSTANCE,
+                XmlToCompositeNodeProvider.INSTANCE);
+        return resourceConfig;
+    }
+}
index 0d459cfb865904fee47d19f0831aa62b8f795fa0..53e14ba5fbe8e1d0d945ac957b98c9e45ba65ad8 100644 (file)
@@ -19,16 +19,27 @@ import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
 import org.opendaylight.yangtools.yang.common.QName;
 
 import java.net.URI;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Map;
 import java.util.Set;
 
+import static org.junit.Assert.fail;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
 public class NetconfOperationServiceImplTest {
 
-    private Date date = new Date(0);
+    private static final Date date1970_01_01;
+
+    static {
+        try {
+            date1970_01_01 = new SimpleDateFormat("yyyy-MM-dd").parse("1970-01-01");
+        } catch (ParseException e) {
+            throw new IllegalStateException(e);
+        }
+    }
 
     @Test
     public void testCheckConsistencyBetweenYangStoreAndConfig_ok() throws Exception {
@@ -51,22 +62,22 @@ public class NetconfOperationServiceImplTest {
                 mockYangStoreSnapshot());
     }
 
-    @Test(expected = IllegalStateException.class)
+    @Test
     public void testCheckConsistencyBetweenYangStoreAndConfig_yangStoreMore() throws Exception {
         try {
             NetconfOperationServiceImpl.checkConsistencyBetweenYangStoreAndConfig(mockJmxClient("qname1"),
                     mockYangStoreSnapshot("qname2", "qname1"));
+            fail("An exception of type " + IllegalArgumentException.class + " was expected");
         } catch (IllegalStateException e) {
             String message = e.getMessage();
             Assert.assertThat(
                     message,
                     JUnitMatchers
-                            .containsString(" missing from config subsystem but present in yangstore: [(namespace?revision=1970-01-01)qname2]"));
+                            .containsString("missing from config subsystem but present in yangstore: [(namespace?revision=1970-01-01)qname2]"));
             Assert.assertThat(
                     message,
                     JUnitMatchers
                             .containsString("All modules present in config: [(namespace?revision=1970-01-01)qname1]"));
-            throw e;
         }
     }
 
@@ -97,7 +108,7 @@ public class NetconfOperationServiceImplTest {
     }
 
     private QName getQName(String qname) {
-        return new QName(URI.create("namespace"), date, qname);
+        return new QName(URI.create("namespace"), date1970_01_01, qname);
     }
 
     private LookupRegistry mockJmxClient(String... visibleQNames) {
index f2de91ef46d2be313b7626262076463fa8cc687d..31248137e8fef8f741d9473b71322b777b00f3e5 100644 (file)
             <artifactId>netconf-ssh</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-ssh</artifactId>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>netconf-util</artifactId>
index c61dab7f643ebd119544cb12d76c0b420435d6af..4818b5f0a3c7868be0a02dafd19e8c1657e0d777 100644 (file)
@@ -8,18 +8,14 @@
 
 package org.opendaylight.controller.netconf.it;
 
-import static java.util.Collections.emptyList;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertTrue;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
+import ch.ethz.ssh2.Connection;
+import ch.ethz.ssh2.Session;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.EventLoopGroup;
 import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.util.HashedWheelTimer;
-
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.management.ManagementFactory;
@@ -31,12 +27,9 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
-
 import javax.management.ObjectName;
 import javax.xml.parsers.ParserConfigurationException;
-
 import junit.framework.Assert;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
@@ -51,6 +44,7 @@ import org.opendaylight.controller.config.yang.test.impl.DepTestImplModuleFactor
 import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleFactory;
 import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleMXBean;
 import org.opendaylight.controller.config.yang.test.impl.TestImplModuleFactory;
+import org.opendaylight.controller.netconf.StubUserManager;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.client.NetconfClient;
 import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
@@ -68,6 +62,7 @@ import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFact
 import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshot;
 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
 import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
+import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
 import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
 import org.opendaylight.controller.netconf.util.xml.ExiParameters;
 import org.opendaylight.controller.netconf.util.xml.XmlElement;
@@ -79,12 +74,13 @@ import org.w3c.dom.Element;
 import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
 import org.xml.sax.SAXException;
-
-import ch.ethz.ssh2.Connection;
-import ch.ethz.ssh2.Session;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
+import static java.util.Collections.emptyList;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 
 public class NetconfITTest extends AbstractConfigTest {
 
@@ -435,7 +431,9 @@ public class NetconfITTest extends AbstractConfigTest {
 
     private void startSSHServer() throws Exception{
         logger.info("Creating SSH server");
-        Thread sshServerThread = new Thread(NetconfSSHServer.start(10830,tcpAddress));
+        StubUserManager um = new StubUserManager(USERNAME,PASSWORD);
+        AuthProvider ap = new AuthProvider(um);
+        Thread sshServerThread = new Thread(NetconfSSHServer.start(10830,tcpAddress,ap));
         sshServerThread.setDaemon(true);
         sshServerThread.start();
         logger.info("SSH server on");
index f60b4b02f5edb571c15313139ce32895fb4843a8..5dde0448bd03ac6b16b49aee77f5589969ea77d1 100644 (file)
             <groupId>commons-io</groupId>
             <artifactId>commons-io</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>usermanager</artifactId>
+            <version>0.4.1-SNAPSHOT</version>
+        </dependency>
     </dependencies>
 
     <build>
         <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>2.4</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
             <plugin>
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
                             org.apache.commons.io,
                             org.opendaylight.controller.netconf.util,
                             org.opendaylight.controller.netconf.util.osgi,
+                            org.opendaylight.controller.usermanager,
+                            org.opendaylight.controller.sal.authorization,
+                            org.opendaylight.controller.sal.utils,
                             org.opendaylight.protocol.framework,
                             org.osgi.framework,
-                            org.slf4j
+                            org.osgi.util.tracker,
+                            org.slf4j,
                         </Import-Package>
                     </instructions>
                 </configuration>
index b91824866a107ffef75290f3a52af53318dca7a5..3b513790bd128806a7ef1bec2561bd90fee6420a 100644 (file)
@@ -10,9 +10,14 @@ package org.opendaylight.controller.netconf.osgi;
 import com.google.common.base.Optional;
 import java.net.InetSocketAddress;
 import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
+import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
 import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
+import org.opendaylight.controller.usermanager.IUserManager;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -31,18 +36,57 @@ public class NetconfSSHActivator implements BundleActivator{
     private NetconfSSHServer server;
     private static final Logger logger =  LoggerFactory.getLogger(NetconfSSHActivator.class);
     private static final String EXCEPTION_MESSAGE = "Netconf ssh bridge is not available.";
+    private IUserManager iUserManager;
+    private BundleContext context = null;
+
+    ServiceTrackerCustomizer<IUserManager, IUserManager> customizer = new ServiceTrackerCustomizer<IUserManager, IUserManager>(){
+        @Override
+        public IUserManager addingService(ServiceReference<IUserManager> reference) {
+            logger.info("Service IUserManager added, let there be SSH bridge.");
+            iUserManager =  context.getService(reference);
+            try {
+                onUserManagerFound(iUserManager);
+            } catch (Exception e) {
+                logger.trace("Can't start SSH server due to {}",e);
+            }
+            return iUserManager;
+        }
+        @Override
+        public void modifiedService(ServiceReference<IUserManager> reference, IUserManager service) {
+            logger.info("Replacing modified service IUserManager in netconf SSH.");
+            server.addUserManagerService(service);
+        }
+        @Override
+        public void removedService(ServiceReference<IUserManager> reference, IUserManager service) {
+            logger.info("Removing service IUserManager from netconf SSH. " +
+                    "SSH won't authenticate users until IUserManeger service will be started.");
+            removeUserManagerService();
+        }
+    };
+
 
     @Override
     public void start(BundleContext context) throws Exception {
+        this.context = context;
+        listenForManagerService();
+    }
 
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        if (server != null){
+            server.stop();
+            logger.trace("Netconf SSH bridge is down ...");
+        }
+    }
+    private void startSSHServer() throws Exception {
         logger.trace("Starting netconf SSH  bridge.");
-
-        Optional<InetSocketAddress> sshSocketAddressOptional = NetconfConfigUtil.extractSSHNetconfAddress(context,EXCEPTION_MESSAGE);
+        Optional<InetSocketAddress> sshSocketAddressOptional = NetconfConfigUtil.extractSSHNetconfAddress(context, EXCEPTION_MESSAGE);
         InetSocketAddress tcpSocketAddress = NetconfConfigUtil.extractTCPNetconfAddress(context,
                 EXCEPTION_MESSAGE, true);
 
         if (sshSocketAddressOptional.isPresent()){
-            server = NetconfSSHServer.start(sshSocketAddressOptional.get().getPort(),tcpSocketAddress);
+            AuthProvider authProvider = new AuthProvider(iUserManager);
+            this.server = NetconfSSHServer.start(sshSocketAddressOptional.get().getPort(),tcpSocketAddress,authProvider);
             Thread serverThread = new  Thread(server,"netconf SSH server thread");
             serverThread.setDaemon(true);
             serverThread.start();
@@ -52,13 +96,18 @@ public class NetconfSSHActivator implements BundleActivator{
             throw new Exception("No valid connection configuration for SSH bridge found.");
         }
     }
-
-    @Override
-    public void stop(BundleContext context) throws Exception {
-        if (server != null){
-            logger.trace("Netconf SSH bridge going down ...");
-            server.stop();
-            logger.trace("Netconf SSH bridge is down ...");
+    private void onUserManagerFound(IUserManager userManager) throws Exception{
+        if (server!=null && server.isUp()){
+           server.addUserManagerService(userManager);
+        } else {
+           startSSHServer();
         }
     }
+    private void removeUserManagerService(){
+        this.server.removeUserManagerService();
+    }
+    private void listenForManagerService(){
+        ServiceTracker<IUserManager, IUserManager> listenerTracker = new ServiceTracker<>(context, IUserManager.class,customizer);
+        listenerTracker.open();
+    }
 }
index 72135cc7dcdd39acddfd8d69d826f2683ee9b921..45807a83334f8e655b268c00bcb9c9e163ccf88f 100644 (file)
@@ -12,20 +12,23 @@ import java.net.InetSocketAddress;
 import java.net.ServerSocket;
 import java.util.concurrent.atomic.AtomicLong;
 import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
 import org.opendaylight.controller.netconf.ssh.threads.SocketThread;
+import org.opendaylight.controller.usermanager.IUserManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @ThreadSafe
 public class NetconfSSHServer implements Runnable {
 
-    private static boolean acceptMore = true;
     private ServerSocket ss = null;
     private static final Logger logger =  LoggerFactory.getLogger(NetconfSSHServer.class);
     private static final AtomicLong sesssionId = new AtomicLong();
     private final InetSocketAddress clientAddress;
+    private final AuthProvider authProvider;
+    private boolean up = false;
 
-    private NetconfSSHServer(int serverPort,InetSocketAddress clientAddress) throws Exception{
+    private NetconfSSHServer(int serverPort,InetSocketAddress clientAddress, AuthProvider authProvider) throws Exception{
 
         logger.trace("Creating SSH server socket on port {}",serverPort);
         this.ss = new ServerSocket(serverPort);
@@ -34,27 +37,37 @@ public class NetconfSSHServer implements Runnable {
         }
         logger.trace("Server socket created.");
         this.clientAddress = clientAddress;
-
+        this.authProvider = authProvider;
+        this.up = true;
     }
 
-
-    public static NetconfSSHServer start(int serverPort, InetSocketAddress clientAddress) throws Exception {
-        return new NetconfSSHServer(serverPort, clientAddress);
+    public static NetconfSSHServer start(int serverPort, InetSocketAddress clientAddress,AuthProvider authProvider) throws Exception {
+        return new NetconfSSHServer(serverPort, clientAddress,authProvider);
     }
 
     public void stop() throws Exception {
-        acceptMore = false;
+        up = false;
         logger.trace("Closing SSH server socket.");
         ss.close();
         logger.trace("SSH server socket closed.");
     }
 
+    public void removeUserManagerService(){
+        this.authProvider.removeUserManagerService();
+    }
+
+    public void addUserManagerService(IUserManager userManagerService){
+        this.authProvider.addUserManagerService(userManagerService);
+    }
+    public boolean isUp(){
+        return this.up;
+    }
     @Override
     public void run() {
-        while (acceptMore) {
+        while (up) {
             logger.trace("Starting new socket thread.");
             try {
-               SocketThread.start(ss.accept(), clientAddress, sesssionId.incrementAndGet());
+               SocketThread.start(ss.accept(), clientAddress, sesssionId.incrementAndGet(),authProvider);
             } catch (IOException e) {
                 e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
             }
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/AuthProvider.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/AuthProvider.java
new file mode 100644 (file)
index 0000000..a73dfdf
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.ssh.authentication;
+
+import ch.ethz.ssh2.signature.RSAPrivateKey;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.io.IOUtils;
+import org.opendaylight.controller.sal.authorization.AuthResultEnum;
+import org.opendaylight.controller.sal.authorization.UserLevel;
+import org.opendaylight.controller.usermanager.IUserManager;
+import org.opendaylight.controller.usermanager.UserConfig;
+
+public class AuthProvider implements AuthProviderInterface {
+
+    private static RSAPrivateKey hostkey = null;
+    private static IUserManager um;
+    private static final String DEAFULT_USER = "netconf";
+    private static final String DEAFULT_PASSWORD = "netconf";
+
+
+    public AuthProvider(IUserManager ium) throws Exception {
+
+        this.um = ium;
+
+        if (this.um  == null){
+            throw new Exception("No usermanager service available.");
+        }
+
+        List<String> roles = new ArrayList<String>(1);
+        roles.add(UserLevel.SYSTEMADMIN.toString());
+        this.um.addLocalUser(new UserConfig(DEAFULT_USER, DEAFULT_PASSWORD, roles));
+    }
+    @Override
+    public boolean authenticated(String username, String password)  throws Exception {
+        if (this.um  == null){
+            throw new Exception("No usermanager service available.");
+        }
+        AuthResultEnum authResult = this.um.authenticate(username,password);
+        if (authResult.equals(AuthResultEnum.AUTH_ACCEPT) || authResult.equals(AuthResultEnum.AUTH_ACCEPT_LOC)){
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public char[] getPEMAsCharArray() {
+
+        InputStream is = getClass().getResourceAsStream("/RSA.pk");
+        try {
+            return IOUtils.toCharArray(is);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    @Override
+    public void removeUserManagerService() {
+        this.um = null;
+    }
+
+    @Override
+    public void addUserManagerService(IUserManager userManagerService) {
+        this.um = userManagerService;
+    }
+
+
+}
@@ -1,3 +1,4 @@
+
 /*
  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
  *
@@ -7,8 +8,12 @@
  */
 package org.opendaylight.controller.netconf.ssh.authentication;
 
-import ch.ethz.ssh2.signature.RSAPrivateKey;
+import org.opendaylight.controller.usermanager.IUserManager;
+
+public interface AuthProviderInterface {
 
-public interface KeyStoreHandler {
-    public RSAPrivateKey getPrivateKey();
+    public boolean authenticated(String username, String password) throws Exception;
+    public char[] getPEMAsCharArray();
+    public void removeUserManagerService();
+    public void addUserManagerService(IUserManager userManagerService);
 }
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/RSAKey.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/RSAKey.java
deleted file mode 100644 (file)
index b420b33..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.netconf.ssh.authentication;
-
-import ch.ethz.ssh2.signature.RSAPrivateKey;
-
-import java.math.BigInteger;
-
-public class RSAKey implements KeyStoreHandler {
-
-    private static RSAPrivateKey hostkey = null;
-    private static String user = "netconf";
-    private static String password = "netconf";
-    static {
-
-        BigInteger p = new BigInteger("2967886344240998436887630478678331145236162666668503940430852241825039192450179076148979094256007292741704260675085192441025058193581327559331546948442042987131728039318861235625879376246169858586459472691398815098207618446039");    //.BigInteger.probablePrime(N / 2, rnd);
-        BigInteger q = new BigInteger("4311534819291430017572425052029278681302539382618633848168923130451247487970187151403375389974616614405320169278870943605377518341666894603659873284783174749122655429409273983428000534304828056597676444751611433784228298909767"); //BigInteger.probablePrime(N / 2, rnd);
-        BigInteger phi = (p.subtract(BigInteger.ONE)).multiply(q.subtract(BigInteger.ONE));
-
-        BigInteger n = p.multiply(q);
-        BigInteger e = new BigInteger("65537");
-        BigInteger d = e.modInverse(phi);
-
-        hostkey = new RSAPrivateKey(d, e, n);
-    }
-
-    @Override
-    public RSAPrivateKey getPrivateKey() {
-        return hostkey;
-    }
-}
index 15d99a44ee4db4a7b31bd0ee2fd6abd25476079c..e5da03b4cf4c9d97765ce098a8c2761b0eda8e39 100644 (file)
@@ -13,7 +13,7 @@ import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.net.Socket;
 import javax.annotation.concurrent.ThreadSafe;
-import org.opendaylight.controller.netconf.ssh.authentication.RSAKey;
+import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -30,27 +30,38 @@ public class SocketThread implements Runnable, ServerAuthenticationCallback, Ser
     private long sessionId;
     private String currentUser;
     private final String remoteAddressWithPort;
+    private final AuthProvider authProvider;
 
 
-    public static void start(Socket socket, InetSocketAddress clientAddress, long sessionId) throws IOException{
-        Thread netconf_ssh_socket_thread = new Thread(new SocketThread(socket,clientAddress,sessionId));
+    public static void start(Socket socket,
+                             InetSocketAddress clientAddress,
+                             long sessionId,
+                             AuthProvider authProvider) throws IOException{
+        Thread netconf_ssh_socket_thread = new Thread(new SocketThread(socket,clientAddress,sessionId,authProvider));
         netconf_ssh_socket_thread.setDaemon(true);
         netconf_ssh_socket_thread.start();
     }
-    private SocketThread(Socket socket, InetSocketAddress clientAddress, long sessionId) throws IOException {
+    private SocketThread(Socket socket,
+                         InetSocketAddress clientAddress,
+                         long sessionId,
+                         AuthProvider authProvider) throws IOException {
 
         this.socket = socket;
         this.clientAddress = clientAddress;
         this.sessionId = sessionId;
         this.remoteAddressWithPort = socket.getRemoteSocketAddress().toString().replaceFirst("/","");
+        this.authProvider = authProvider;
 
     }
 
     @Override
     public void run() {
         conn = new ServerConnection(socket);
-        RSAKey keyStore = new RSAKey();
-        conn.setRsaHostKey(keyStore.getPrivateKey());
+        try {
+            conn.setPEMHostKey(authProvider.getPEMAsCharArray(),"netconf");
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
         conn.setAuthenticationCallback(this);
         conn.setServerConnectionCallback(this);
         try {
@@ -90,7 +101,7 @@ public class SocketThread implements Runnable, ServerAuthenticationCallback, Ser
                                 netconf_ssh_output.start();
 
                             } catch (Throwable t){
-                                logger.error(t.getMessage(),t);
+                                logger.error("SSH bridge couldn't create echo socket",t.getMessage(),t);
 
                                 try {
                                     if (netconf_ssh_input!=null){
@@ -166,13 +177,16 @@ public class SocketThread implements Runnable, ServerAuthenticationCallback, Ser
 
     public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password)
     {
-        if (USER.equals(username) && PASSWORD.equals(password)){
-            currentUser = username;
-            logger.trace("user {}@{} authenticated",currentUser,remoteAddressWithPort);
-            return AuthenticationResult.SUCCESS;
-        }
-
 
+        try {
+            if (authProvider.authenticated(username,password)){
+                currentUser = username;
+                logger.trace("user {}@{} authenticated",currentUser,remoteAddressWithPort);
+                return AuthenticationResult.SUCCESS;
+            }
+        } catch (Exception e){
+            logger.info("Authentication failed due to :" + e.getLocalizedMessage());
+        }
         return AuthenticationResult.FAILURE;
     }
 
diff --git a/opendaylight/netconf/netconf-ssh/src/main/resources/RSA.pk b/opendaylight/netconf/netconf-ssh/src/main/resources/RSA.pk
new file mode 100644 (file)
index 0000000..c0266c7
--- /dev/null
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAuC9hbEacpewvylI0mwFwjy3Wou2hpr/ncN9BBiFDSaG5yW2k
+3Oy+SCAcFCL+ZKWb6cc6Ch4gUeCwyEHRojZguuhliKtak9YQf6qbvpPLe00842Lx
+iqNAGurMpzizCDsGFq8ChaAkBZQI3TvcHuPoSUWSMJ+K8xHpRyUdVr6g2yEjezKJ
+sTXBtWaeCCh6YUafFujuDJk7fvYcPW7Je5KRBBStIKvxcMW0zB+7eq04deTHwGbJ
+gGjKWilQ72hsDDP3Hbp5CJMAYg1r4GlCmFx3KyHRGztgWgNgaD7nNpKCkTLjtmA6
+b4x7TA+jrzZ6Af2z5TMrI4dv5w1SrxHaZ+ziLQIDAQABAoIBAHTndeGgq/rQf8De
+Do+4CTaHtK0zQSAyu/azbXUzlZ7drKuCEVs8VMY4wzmwwGEnkF+A2YDkgEUX5X0l
+8aYQ97KKoS9u+43MGCrAIhyDeGrpqlT1TzRcy+qJz53v6gq2U/X/3QztiQ+VV078
+mIluxNgE9XYxPaNsYfGLSCTv1+9c8y/hjGVX2kwFK+u4ut0ZZETggNa8UxfaHVDS
+fIJQX9Gm3J3GSUV30fDGMBIUW6ESLc2L8b7u8Mp9TRP39ZeQSuEUjBe8MYKv0Rel
+oEpjZvcnniMTpFbLpndBYn7/AoIiEBvtCN8faVTuRRcvvLcsRm09IctzKQYnMh6M
+6PLKV+ECgYEA8HFRYaKHUzxpzE/fyon82GQbzqFFY0/bbWrfWICMfNbIgshJUie6
+FmH5iUFMfeqaT7v557HFM0GB9FeIeSbvd88YmiBAcRopZ3DfMkDH+DT73yJ+/TKG
+2nrQtdhyuTIs4bwHqeS2BBJYs7PK9R2rratF3l34Tf7mjlvyOgygHdUCgYEAxBo2
+8hEBlAVNcNb1hTYUxe1w1B6675/mFlmw98Xmj9dRYfICXNhahs8tX3/lsBEd+vBu
+fI0oyHaff8m5bPgGzD1ZMybfeROujNrgxaKVk7Ef0FDRRCop4bm18OroFlFAt9l8
+wMp++ToACbdvQvL/mjWMPYlIxhB/YxHswICZZvkCgYAexxKYwdo6sGAGlC7cWT9x
+X5cjowcjyEQZRHXkeUgCbufpvcOM7aLnXJE5nY8yCwbHsBM0MlBA2GDPKylAANjk
+aDEJAZneIHAuWodngl1Wi0m2bU7+ECqs6s2uiU9eH2sZVh1RBQK7kLGkBx6ys6KX
+L3ZZGYRAT6GplWFzRsx0JQKBgCeVlxPD5QqpC1nEumi6YvUVGdpnnZpzL3HBhxxs
+wT612wKnZFyze4qM1X7ahVXGDsQxtkvD/sCAWW/lG13orw6ZL6FIroF1PJ3ILOkY
+CZN3hJF7TtKwpCWhZB2OfWzL2AGEkE8mUP0j/Q/5DCd6f6f0OSvOw3bfq6cm3iB5
+lP2ZAoGAXsRN5TZTX4AQ2xTlrDQ8A5XgcvyWQpJOmEXMTyHV7VaJVzmNWFVAvndK
+5UIq8ALDwB2t7vjmMUW6euvIwqtXiop7G79UOb3e3NhzeyWFGQyBLqCRznGaXQTT
+dlFy73xhukZMhFnj006bjKCYvOPnwuGl3+0fuWil5Rq3jOuY5c8=
+-----END RSA PRIVATE KEY-----
similarity index 53%
rename from opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/ssh/SSHServerTest.java
rename to opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/SSHServerTest.java
index 54bc7bc4b686771279e5542bb7c29e4dabc0d8d2..62396ed87ac1f4db3dcaa7942f676012ab35ade8 100644 (file)
@@ -5,15 +5,14 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-package org.opendaylight.controller.netconf.ssh;
+package org.opendaylight.controller.netconf;
 
 import ch.ethz.ssh2.Connection;
-import ch.ethz.ssh2.Session;
-import java.io.IOException;
 import java.net.InetSocketAddress;
 import junit.framework.Assert;
-import org.apache.commons.io.IOUtils;
 import org.junit.Test;
+import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
+import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -26,36 +25,37 @@ public class SSHServerTest {
     private static final int PORT = 1830;
     private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 8383);
     private static final Logger logger =  LoggerFactory.getLogger(SSHServerTest.class);
+    private Thread sshServerThread;
+
+
+
 
-//    @Before
     public void startSSHServer() throws Exception{
-            logger.info("Creating SSH server");
-            NetconfSSHServer server = NetconfSSHServer.start(PORT,tcpAddress);
-            Thread sshServerThread = new Thread(server);
-            sshServerThread.setDaemon(true);
-            sshServerThread.start();
-            logger.info("SSH server on");
+        logger.info("Creating SSH server");
+        StubUserManager um = new StubUserManager(USER,PASSWORD);
+        AuthProvider ap = new AuthProvider(um);
+        NetconfSSHServer server = NetconfSSHServer.start(PORT,tcpAddress,ap);
+        sshServerThread = new Thread(server);
+        sshServerThread.setDaemon(true);
+        sshServerThread.start();
+        logger.info("SSH server on");
     }
 
     @Test
     public void connect(){
-        Connection conn = new Connection(HOST,PORT);
-        Assert.assertNotNull(conn);
         try {
+            this.startSSHServer();
+            Connection conn = new Connection(HOST,PORT);
+            Assert.assertNotNull(conn);
             logger.info("connecting to SSH server");
             conn.connect();
             logger.info("authenticating ...");
             boolean isAuthenticated = conn.authenticateWithPassword(USER,PASSWORD);
             Assert.assertTrue(isAuthenticated);
-            logger.info("opening session");
-            Session sess = conn.openSession();
-            logger.info("subsystem netconf");
-            sess.startSubSystem("netconf");
-            sess.getStdin().write("<?xml version=\"1.0\" encoding=\"UTF-8\"?><hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><capabilities><capability>urn:ietf:params:netconf:base:1.1</capability></capabilities></hello>]]>]]>".getBytes());
-            IOUtils.copy(sess.getStdout(), System.out);
-        } catch (IOException e) {
-            e.printStackTrace();
+        } catch (Exception e) {
+            logger.error("Error while starting SSH server.", e);
         }
+
     }
 
 }
diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/StubUserManager.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/StubUserManager.java
new file mode 100644 (file)
index 0000000..4a3a650
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.controller.sal.authorization.AuthResultEnum;
+import org.opendaylight.controller.sal.authorization.UserLevel;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.opendaylight.controller.usermanager.AuthorizationConfig;
+import org.opendaylight.controller.usermanager.ISessionManager;
+import org.opendaylight.controller.usermanager.IUserManager;
+import org.opendaylight.controller.usermanager.ServerConfig;
+import org.opendaylight.controller.usermanager.UserConfig;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.web.context.SecurityContextRepository;
+
+public class StubUserManager implements IUserManager{
+
+
+    private static String user;
+    private static String password;
+
+    public StubUserManager(String user, String password){
+        this.user = user;
+        this.password = password;
+    }
+    @Override
+    public List<String> getUserRoles(String userName) {
+        return null;
+    }
+
+    @Override
+    public AuthResultEnum authenticate(String username, String password) {
+        if (this.user.equals(username) && this.password.equals(password)){
+            return AuthResultEnum.AUTH_ACCEPT_LOC;
+        }
+        return AuthResultEnum.AUTH_REJECT_LOC;
+    }
+
+    @Override
+    public Status addAAAServer(ServerConfig configObject) {
+        return null;
+    }
+
+    @Override
+    public Status removeAAAServer(ServerConfig configObject) {
+        return null;
+    }
+
+    @Override
+    public Status addLocalUser(UserConfig configObject) {
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    @Override
+    public Status modifyLocalUser(UserConfig configObject) {
+        return null;
+    }
+
+    @Override
+    public Status removeLocalUser(UserConfig configObject) {
+        return null;
+    }
+
+    @Override
+    public Status removeLocalUser(String userName) {
+        return null;
+    }
+
+    @Override
+    public Status addAuthInfo(AuthorizationConfig AAAconf) {
+        return null;
+    }
+
+    @Override
+    public Status removeAuthInfo(AuthorizationConfig AAAconf) {
+        return null;
+    }
+
+    @Override
+    public List<AuthorizationConfig> getAuthorizationList() {
+        return null;
+    }
+
+    @Override
+    public Set<String> getAAAProviderNames() {
+        return null;
+    }
+
+    @Override
+    public Status changeLocalUserPassword(String user, String curPassword, String newPassword) {
+        return null;
+    }
+
+    @Override
+    public List<ServerConfig> getAAAServerList() {
+        return null;
+    }
+
+    @Override
+    public List<UserConfig> getLocalUserList() {
+        return null;
+    }
+
+    @Override
+    public Status saveLocalUserList() {
+        return null;
+    }
+
+    @Override
+    public Status saveAAAServerList() {
+        return null;
+    }
+
+    @Override
+    public Status saveAuthorizationList() {
+        return null;
+    }
+
+    @Override
+    public void userLogout(String username) {
+
+    }
+
+    @Override
+    public void userTimedOut(String username) {
+
+    }
+
+    @Override
+    public Map<String, List<String>> getUserLoggedIn() {
+        return null;
+    }
+
+    @Override
+    public String getAccessDate(String user) {
+        return null;
+    }
+
+    @Override
+    public UserLevel getUserLevel(String userName) {
+        return null;
+    }
+
+    @Override
+    public List<UserLevel> getUserLevels(String userName) {
+        return null;
+    }
+
+    @Override
+    public SecurityContextRepository getSecurityContextRepo() {
+        return null;
+    }
+
+    @Override
+    public ISessionManager getSessionManager() {
+        return null;
+    }
+
+    @Override
+    public boolean isRoleInUse(String role) {
+        return false;
+    }
+
+    @Override
+    public String getPassword(String username) {
+        return null;
+    }
+
+    @Override
+    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
+        return null;
+    }
+
+}
index e70df1b9493634806c701b5ebd861bac36ab4b6d..c4b146722004d6792303b0f0a3a423d2d89f91e6 100644 (file)
                 <artifactId>netconf-ssh</artifactId>
                 <version>${netconf.version}</version>
             </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-ssh</artifactId>
+                <version>${netconf.version}</version>
+                <type>test-jar</type>
+            </dependency>
             <dependency>
                 <groupId>${project.groupId}</groupId>
                 <artifactId>netconf-mapping-api</artifactId>
index f895ca0c85bd4583405453d65fcd4195dcb8b5ce..b5d0a48c28709e575f13646971e00ab1ddbc3ea0 100644 (file)
@@ -319,7 +319,7 @@ public class SwitchManager implements ISwitchManager, IConfigurationContainerAwa
     @Override
     public SubnetConfig getSubnetConfig(String subnet) {
         // if there are no subnets, return the default subnet
-        if(subnetsConfigList.size() == 0 && subnet == DEFAULT_SUBNET_NAME){
+        if(subnetsConfigList.isEmpty() && subnet.equalsIgnoreCase(DEFAULT_SUBNET_NAME)){
             return DEFAULT_SUBNETCONFIG;
         }else{
             return subnetsConfigList.get(subnet);
@@ -428,11 +428,11 @@ public class SwitchManager implements ISwitchManager, IConfigurationContainerAwa
     }
 
     private Status semanticCheck(SubnetConfig conf) {
-        Subnet newSubnet = new Subnet(conf);
         Set<InetAddress> IPs = subnets.keySet();
         if (IPs == null) {
             return new Status(StatusCode.SUCCESS);
         }
+        Subnet newSubnet = new Subnet(conf);
         for (InetAddress i : IPs) {
             Subnet existingSubnet = subnets.get(i);
             if ((existingSubnet != null) && !existingSubnet.isMutualExclusive(newSubnet)) {
@@ -462,7 +462,7 @@ public class SwitchManager implements ISwitchManager, IConfigurationContainerAwa
                 return status;
             }
         } else {
-            if (conf.getName().equals(DEFAULT_SUBNET_NAME)) {
+            if (conf.getName().equalsIgnoreCase(DEFAULT_SUBNET_NAME)) {
                 return new Status(StatusCode.NOTALLOWED, "The specified subnet gateway cannot be removed");
             }
         }
@@ -506,7 +506,7 @@ public class SwitchManager implements ISwitchManager, IConfigurationContainerAwa
 
     @Override
     public Status removeSubnet(String name) {
-        if (name.equals(DEFAULT_SUBNET_NAME)) {
+        if (name.equalsIgnoreCase(DEFAULT_SUBNET_NAME)) {
             return new Status(StatusCode.NOTALLOWED, "The specified subnet gateway cannot be removed");
         }
         SubnetConfig conf = subnetsConfigList.get(name);
index 0c14dea38a4a9ff69993d1e6a88503ceb5db0792..83532a1012f6a9431cd4f75a3f1b201e653ec395 100644 (file)
@@ -9,10 +9,11 @@
 package org.opendaylight.controller.usermanager;
 
 import java.io.Serializable;
-import java.nio.charset.Charset;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.regex.Matcher;
@@ -24,6 +25,7 @@ import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
 
 import org.opendaylight.controller.sal.authorization.AuthResultEnum;
+import org.opendaylight.controller.sal.packet.BitBufferHelper;
 import org.opendaylight.controller.sal.utils.HexEncode;
 import org.opendaylight.controller.sal.utils.Status;
 import org.opendaylight.controller.sal.utils.StatusCode;
@@ -46,6 +48,7 @@ public class UserConfig implements Serializable {
     protected static final String PASSWORD_REGEX = "(?=.*[^a-zA-Z0-9])(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{8,256}$";
     private static final Pattern INVALID_USERNAME_CHARACTERS = Pattern.compile("([/\\s\\.\\?#%;\\\\]+)");
     private static MessageDigest oneWayFunction;
+    private static SecureRandom randomGenerator;
 
     static {
         try {
@@ -54,6 +57,7 @@ public class UserConfig implements Serializable {
             log.error(String.format("Implementation of %s digest algorithm not found: %s", DIGEST_ALGORITHM,
                     e.getMessage()));
         }
+        UserConfig.randomGenerator = new SecureRandom(BitBufferHelper.toByteArray(System.currentTimeMillis()));
     }
 
     /**
@@ -81,6 +85,8 @@ public class UserConfig implements Serializable {
     @XmlElement
     private String password;
 
+    private byte[] salt;
+
 
 
     public UserConfig() {
@@ -102,11 +108,19 @@ public class UserConfig implements Serializable {
         /*
          * Password validation to be done on clear text password. If fails, mark
          * the password with a well known label, so that object validation can
-         * report the proper error. Only if password is a valid one, hash it.
+         * report the proper error. Only if password is a valid one, generate
+         * salt, concatenate it with clear text password and hash the
+         * resulting string. Hash result is going to be our stored password.
          */
-        this.password = (validatePassword(password).isSuccess()) ? hash(password) : BAD_PASSWORD;
+        if (validateClearTextPassword(password).isSuccess()) {
+            this.salt = BitBufferHelper.toByteArray(randomGenerator.nextLong());
+            this.password = hash(salt, password);
+        } else {
+            this.salt = null;
+            this.password = BAD_PASSWORD;
+        }
 
-        this.roles = (roles == null) ? new ArrayList<String>() : new ArrayList<String>(roles);
+        this.roles = (roles == null) ? Collections.<String>emptyList() : new ArrayList<String>(roles);
     }
 
     public String getUser() {
@@ -176,6 +190,7 @@ public class UserConfig implements Serializable {
     public Status validate() {
         Status validCheck = validateUsername();
         if (validCheck.isSuccess()) {
+            // Password validation was run at object construction time
             validCheck = (!password.equals(BAD_PASSWORD)) ? new Status(StatusCode.SUCCESS) : new Status(
                     StatusCode.BADREQUEST,
                     "Password should be 8 to 256 characters long, contain both upper and lower case letters, "
@@ -203,7 +218,7 @@ public class UserConfig implements Serializable {
         return new Status(StatusCode.SUCCESS);
     }
 
-    private Status validatePassword(String password) {
+    private Status validateClearTextPassword(String password) {
         if (password == null || password.isEmpty()) {
             return new Status(StatusCode.BADREQUEST, "Password cannot be empty");
         }
@@ -227,14 +242,14 @@ public class UserConfig implements Serializable {
 
         // To make any changes to a user configured profile, current password
         // must always be provided
-        if (!this.password.equals(hash(currentPassword))) {
+        if (!this.password.equals(hash(this.salt, currentPassword))) {
             return new Status(StatusCode.BADREQUEST, "Current password is incorrect");
         }
 
         // Create a new object with the proposed modifications
         UserConfig proposed = new UserConfig();
         proposed.user = this.user;
-        proposed.password = (newPassword == null)? this.password : hash(newPassword);
+        proposed.password = (newPassword == null)? this.password : hash(this.salt, newPassword);
         proposed.roles = (newRoles == null)? this.roles : newRoles;
 
         // Validate it
@@ -251,9 +266,9 @@ public class UserConfig implements Serializable {
         return status;
     }
 
-    public AuthResponse authenticate(String clearTextPass) {
+    public AuthResponse authenticate(String clearTextPassword) {
         AuthResponse locResponse = new AuthResponse();
-        if (password.equals(hash(clearTextPass))) {
+        if (password.equals(hash(this.salt, clearTextPassword))) {
             locResponse.setStatus(AuthResultEnum.AUTH_ACCEPT_LOC);
             locResponse.addData(getRolesString());
         } else {
@@ -275,12 +290,32 @@ public class UserConfig implements Serializable {
         return buffer.toString();
     }
 
-    public static String hash(String message) {
+    private static byte[] concatenate(byte[] salt, String password) {
+        byte[] messageArray = password.getBytes();
+        byte[] concatenation = new byte[salt.length + password.length()];
+        System.arraycopy(salt, 0, concatenation, 0, salt.length);
+        System.arraycopy(messageArray, 0, concatenation, salt.length, messageArray.length);
+        return concatenation;
+    }
+
+    private static String hash(byte[] salt, String message) {
         if (message == null) {
+            log.warn("Password hash requested but empty or no password provided");
             return message;
         }
+        if (salt == null || salt.length == 0) {
+            log.warn("Password hash requested but empty or no salt provided");
+            return message;
+        }
+
+        // Concatenate salt and password
+        byte[] messageArray = message.getBytes();
+        byte[] concatenation = new byte[salt.length + message.length()];
+        System.arraycopy(salt, 0, concatenation, 0, salt.length);
+        System.arraycopy(messageArray, 0, concatenation, salt.length, messageArray.length);
+
         UserConfig.oneWayFunction.reset();
-        return HexEncode.bytesToHexString(UserConfig.oneWayFunction.digest(message.getBytes(Charset.defaultCharset())));
+        return HexEncode.bytesToHexString(UserConfig.oneWayFunction.digest(concatenate(salt, message)));
     }
 
     /**
@@ -299,7 +334,8 @@ public class UserConfig implements Serializable {
     public static UserConfig getUncheckedUserConfig(String userName, String password, List<String> roles) {
         UserConfig config = new UserConfig();
         config.user = userName;
-        config.password = hash(password);
+        config.salt = BitBufferHelper.toByteArray(randomGenerator.nextLong());
+        config.password = hash(config.salt, password);
         config.roles = roles;
         return config;
     }
index d225e5c76014bfc2e647ceeb0fbff756de4065ba..8fc7f07df1db2fc3283057c1f55caed85799d604 100644 (file)
@@ -7,7 +7,6 @@
  */
 package org.opendaylight.controller.usermanager;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
@@ -71,12 +70,12 @@ public class AuthorizationUserConfigTest {
                 .isSuccess());
 
         // New Password = null, No change in password
-        assertTrue(userConfig.getPassword().equals(UserConfig.hash("ciscocisco")));
+        assertTrue(userConfig.authenticate("ciscocisco").getStatus().equals(AuthResultEnum.AUTH_ACCEPT_LOC));
 
         // Password changed successfully, no change in user role
         assertTrue(userConfig.update("ciscocisco", "cisco123", roles)
                 .isSuccess());
-        assertTrue(userConfig.getPassword().equals(UserConfig.hash("cisco123")));
+        assertTrue(userConfig.authenticate("cisco123").getStatus().equals(AuthResultEnum.AUTH_ACCEPT_LOC));
         assertTrue(userConfig.getRoles().get(0).equals(
                 UserLevel.NETWORKOPERATOR.toString()));
 
@@ -85,14 +84,14 @@ public class AuthorizationUserConfigTest {
         roles.add(UserLevel.SYSTEMADMIN.toString());
         assertTrue(userConfig.update("cisco123", "cisco123", roles)
                 .isSuccess());
-        assertTrue(userConfig.getPassword().equals(UserConfig.hash("cisco123")));
+        assertTrue(userConfig.authenticate("cisco123").getStatus().equals(AuthResultEnum.AUTH_ACCEPT_LOC));
         assertTrue(userConfig.getRoles().get(0)
                 .equals(UserLevel.SYSTEMADMIN.toString()));
 
         // Password and role changed successfully
         assertTrue(userConfig.update("cisco123", "ciscocisco", roles)
                 .isSuccess());
-        assertTrue(userConfig.getPassword().equals(UserConfig.hash("ciscocisco")));
+        assertTrue(userConfig.authenticate("ciscocisco").getStatus().equals(AuthResultEnum.AUTH_ACCEPT_LOC));
         assertTrue(userConfig.getRoles().get(0)
                 .equals(UserLevel.SYSTEMADMIN.toString()));
 
@@ -104,14 +103,6 @@ public class AuthorizationUserConfigTest {
         assertTrue(authresp.getStatus().equals(AuthResultEnum.AUTH_ACCEPT_LOC));
         authresp = userConfig.authenticate("wrongPassword");
         assertTrue(authresp.getStatus().equals(AuthResultEnum.AUTH_REJECT_LOC));
-
-        // test equals()
-        roles.clear();
-        roles.add(UserLevel.NETWORKOPERATOR.toString());
-        userConfig = new UserConfig("uname", "ciscocisco", roles);
-        assertEquals(userConfig, userConfig);
-        UserConfig userConfig2 = new UserConfig("uname", "ciscocisco", roles);
-        assertEquals(userConfig, userConfig2);
     }
 
     @Test
index 4eedf7eda99f42bb70d011dd7daba2d81d2d4ae0..dcdc2f6d50447e942fe868c22bc69bc732bfe248 100644 (file)
               org.opendaylight.controller.usermanager
             </Import-Package>
             <Export-Package>
-<!--
+               <!--
               org.opendaylight.controller.usermanager,
               org.opendaylight.controller.usermanager.internal
- -->
             -->
              </Export-Package>
             <Bundle-Activator>
               org.opendaylight.controller.usermanager.internal.Activator
index 094562fac07252b182e601774e79209d0a473e20..6b4cfa00253ef4f36026a51f639fba6af02ed4b6 100644 (file)
@@ -390,8 +390,8 @@ one.f.flows = {
                     $tr = $(tr);
                     $span = $("td span", $tr);
                     var flowstatus = $span.data("flowstatus");
-                    if($span.data("installinhw") != null) {
-                        var installInHw = $span.data("installinhw").toString();
+                    if($span.data("installInHw") != null) {
+                        var installInHw = $span.data("installInHw").toString();
                         if(installInHw == "true" && flowstatus == "Success") {
                             $tr.addClass("success");
                         } else {