]> source.dussan.org Git - gitblit.git/commitdiff
Add SSH daemon test
authorDavid Ostrovsky <david@ostrovsky.org>
Sun, 16 Mar 2014 21:55:30 +0000 (22:55 +0100)
committerJames Moger <james.moger@gitblit.com>
Thu, 10 Apr 2014 22:58:08 +0000 (18:58 -0400)
src/main/distrib/data/gitblit.properties
src/main/java/com/gitblit/transport/ssh/CachingPublicKeyAuthenticator.java
src/main/java/com/gitblit/transport/ssh/SshDaemon.java
src/main/java/com/gitblit/transport/ssh/commands/DispatchCommand.java
src/test/config/test-gitblit.properties
src/test/java/com/gitblit/tests/BogusPublicKeyAuthenticator.java [new file with mode: 0644]
src/test/java/com/gitblit/tests/GitBlitSuite.java
src/test/java/com/gitblit/tests/SshDaemonTest.java [new file with mode: 0644]
src/test/java/com/gitblit/tests/SshUtils.java [new file with mode: 0644]

index 64a52f5cc548da1898e8438b2be4a0b13e0ecb43..52bb252b15ee6a9016d63473c9d9b5d5c605085b 100644 (file)
@@ -129,6 +129,11 @@ git.sshKeysFolder= ${baseFolder}/ssh
 # SINCE 1.5.0\r
 git.sshBackend = NIO2\r
 \r
+# SSH public key authenticator\r
+#\r
+# SINCE 1.5.0\r
+git.sshPublicKeyAuthenticator = com.gitblit.transport.ssh.CachingPublicKeyAuthenticator\r
+\r
 # Allow push/pull over http/https with JGit servlet.\r
 # If you do NOT want to allow Git clients to clone/push to Gitblit set this\r
 # to false.  You might want to do this if you are only using ssh:// or git://.\r
index ee1de591ba41ed27db5a1a4aa7e5da94596925d1..7d6066c794c97f89df5199312d2a6c43aee97a3d 100644 (file)
@@ -73,7 +73,7 @@ public class CachingPublicKeyAuthenticator implements PublickeyAuthenticator,
                return result;
        }
 
-       private boolean doAuthenticate(String username, PublicKey suppliedKey,
+       protected boolean doAuthenticate(String username, PublicKey suppliedKey,
                        ServerSession session) {
                SshDaemonClient client = session.getAttribute(SshDaemonClient.KEY);
                Preconditions.checkState(client.getUser() == null);
index c954b347c9430231d7b7c3322b048dea9675347b..40a310e79b510af6cff702a00280097874df13a5 100644 (file)
@@ -34,6 +34,7 @@ import org.slf4j.LoggerFactory;
 
 import com.gitblit.IStoredSettings;
 import com.gitblit.Keys;
+import com.gitblit.manager.IAuthenticationManager;
 import com.gitblit.manager.IGitblit;
 import com.gitblit.utils.IdGenerator;
 import com.gitblit.utils.StringUtils;
@@ -104,8 +105,8 @@ public class SshDaemon {
                        addr = new InetSocketAddress(bindInterface, port);
                }
 
-               CachingPublicKeyAuthenticator keyAuthenticator = 
-                               new CachingPublicKeyAuthenticator(keyManager, gitblit);
+               CachingPublicKeyAuthenticator keyAuthenticator =
+                               getPublicKeyAuthenticator(keyManager, gitblit);
 
                sshd = SshServer.setUpDefaultServer();
                sshd.setPort(addr.getPort());
@@ -122,6 +123,27 @@ public class SshDaemon {
                run = new AtomicBoolean(false);
        }
 
+       private CachingPublicKeyAuthenticator getPublicKeyAuthenticator(
+                       IKeyManager keyManager, IGitblit gitblit) {
+               IStoredSettings settings = gitblit.getSettings();
+               String clazz = settings.getString(Keys.git.sshPublicKeyAuthenticator,
+                               CachingPublicKeyAuthenticator.class.getName());
+               if (StringUtils.isEmpty(clazz)) {
+                       clazz = CachingPublicKeyAuthenticator.class.getName();
+               }
+               try {
+                       Class<CachingPublicKeyAuthenticator> authClass =
+                                       (Class<CachingPublicKeyAuthenticator>) Class.forName(clazz);
+                       return authClass.getConstructor(
+                                       new Class[] { IKeyManager.class,
+                                                       IAuthenticationManager.class }).newInstance(
+                                       keyManager, gitblit);
+               } catch (Exception e) {
+                       log.error("failed to create ssh auth manager " + clazz, e);
+               }
+               return null;
+       }
+
        public String formatUrl(String gituser, String servername, String repository) {
                if (sshd.getPort() == DEFAULT_PORT) {
                        // standard port
@@ -200,6 +222,29 @@ public class SshDaemon {
                return keyManager;
        }
 
+       @SuppressWarnings("unchecked")
+       protected IKeyManager getKeyAuthenticator() {
+               IKeyManager keyManager = null;
+               IStoredSettings settings = gitblit.getSettings();
+               String clazz = settings.getString(Keys.git.sshKeysManager, FileKeyManager.class.getName());
+               if (StringUtils.isEmpty(clazz)) {
+                       clazz = FileKeyManager.class.getName();
+               }
+               try {
+                       Class<? extends IKeyManager> managerClass = (Class<? extends IKeyManager>) Class.forName(clazz);
+                       keyManager = injector.get(managerClass).start();
+                       if (keyManager.isReady()) {
+                               log.info("{} is ready.", keyManager);
+                       } else {
+                               log.warn("{} is disabled.", keyManager);
+                       }
+               } catch (Exception e) {
+                       log.error("failed to create ssh key manager " + clazz, e);
+                       keyManager = injector.get(NullKeyManager.class).start();
+               }
+               return keyManager;
+       }
+
        /**
         * A nested Dagger graph is used for constructor dependency injection of
         * complex classes.
index 3c041af6bfa0740c804eb19e8ced34eaa10fe2d5..8e13be035dd16bbc99c6d5d60a4de8a174925d83 100644 (file)
@@ -79,7 +79,7 @@ public class DispatchCommand extends BaseCommand {
                                        CommandMetaData.class.getName()));
                }
                CommandMetaData meta = cmd.getAnnotation(CommandMetaData.class);
-               if (meta.admin() && user.canAdmin()) {
+               if (meta.admin() && user != null && user.canAdmin()) {
                        log.debug(MessageFormat.format("excluding admin command {0} for {1}", meta.name(), user.username));
                        return;
                }
index e636469ef23f084093672e1a6113a13f88a16cc6..7d8e9a79a81d44c7df7a6f93daaa234aa66e3616 100644 (file)
@@ -7,6 +7,8 @@ git.repositoriesFolder = ${baseFolder}/git
 git.searchRepositoriesSubfolders = true
 git.enableGitServlet = true
 git.daemonPort = 8300
+git.sshPort = 29418
+git.sshPublicKeyAuthenticator = com.gitblit.tests.BogusPublicKeyAuthenticator
 groovy.scriptsFolder = src/main/distrib/data/groovy
 groovy.preReceiveScripts = blockpush
 groovy.postReceiveScripts = sendmail
diff --git a/src/test/java/com/gitblit/tests/BogusPublicKeyAuthenticator.java b/src/test/java/com/gitblit/tests/BogusPublicKeyAuthenticator.java
new file mode 100644 (file)
index 0000000..80be1a0
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 gitblit.com.
+ *
+ * Licensed 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 com.gitblit.tests;
+
+import java.security.PublicKey;
+
+import org.apache.sshd.server.session.ServerSession;
+
+import com.gitblit.manager.IAuthenticationManager;
+import com.gitblit.transport.ssh.CachingPublicKeyAuthenticator;
+import com.gitblit.transport.ssh.IKeyManager;
+
+public class BogusPublicKeyAuthenticator extends CachingPublicKeyAuthenticator {
+
+       public BogusPublicKeyAuthenticator(IKeyManager keyManager,
+                       IAuthenticationManager authManager) {
+               super(keyManager, authManager);
+       }
+
+       @Override
+       protected boolean doAuthenticate(String username, PublicKey suppliedKey,
+                       ServerSession session) {
+               // TODO(davido): put authenticated user in session
+               return true;
+       }
+}
index c015c847e1b7009f5a846c7c784b1e0d0b0496c4..17d609e75aceafa5528cf52841c916cd382446e9 100644 (file)
@@ -61,7 +61,7 @@ import com.gitblit.utils.JGitUtils;
                MarkdownUtilsTest.class, JGitUtilsTest.class, SyndicationUtilsTest.class,\r
                DiffUtilsTest.class, MetricUtilsTest.class, X509UtilsTest.class,\r
                GitBlitTest.class, FederationTests.class, RpcTests.class, GitServletTest.class, GitDaemonTest.class,\r
-               GroovyScriptTest.class, LuceneExecutorTest.class, RepositoryModelTest.class,\r
+               GroovyScriptTest.class, LuceneExecutorTest.class, RepositoryModelTest.class, SshDaemonTest.class,\r
                FanoutServiceTest.class, Issue0259Test.class, Issue0271Test.class, HtpasswdAuthenticationTest.class,\r
                ModelUtilsTest.class, JnaUtilsTest.class, LdapSyncServiceTest.class, FileTicketServiceTest.class,
                BranchTicketServiceTest.class, RedisTicketServiceTest.class, AuthenticationManagerTest.class })
@@ -78,6 +78,16 @@ public class GitBlitSuite {
        static int port = 8280;\r
        static int gitPort = 8300;\r
        static int shutdownPort = 8281;\r
+       static int sshPort = 29418;\r
+\r
+// Overriding of keys doesn't seem to work\r
+//     static {\r
+//             try {\r
+//                     sshPort = SshUtils.getFreePort();\r
+//             } catch (Exception e) {\r
+//                     e.printStackTrace();\r
+//             }\r
+//     }\r
 \r
        public static String url = "http://localhost:" + port;\r
        public static String gitServletUrl = "http://localhost:" + port + "/git";\r
@@ -140,6 +150,8 @@ public class GitBlitSuite {
                                                "\"" + GitBlitSuite.REPOSITORIES.getAbsolutePath() + "\"", "--userService",\r
                                                GitBlitSuite.USERSCONF.getAbsolutePath(), "--settings", GitBlitSuite.SETTINGS.getAbsolutePath(),\r
                                                "--baseFolder", "data");\r
+                               // doesn't work\r
+                               //, "--sshPort", "" + sshPort);\r
                        }\r
                });\r
 \r
diff --git a/src/test/java/com/gitblit/tests/SshDaemonTest.java b/src/test/java/com/gitblit/tests/SshDaemonTest.java
new file mode 100644 (file)
index 0000000..5294f69
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2014 gitblit.com.
+ *
+ * Licensed 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 com.gitblit.tests;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.security.KeyPair;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.sshd.ClientChannel;
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.SshClient;
+import org.apache.sshd.common.KeyPairProvider;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.gitblit.Constants;
+
+public class SshDaemonTest extends GitblitUnitTest {
+
+       private static final AtomicBoolean started = new AtomicBoolean(false);
+       private static KeyPair pair;
+
+       @BeforeClass
+       public static void startGitblit() throws Exception {
+               started.set(GitBlitSuite.startGitblit());
+               pair = SshUtils.createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA);
+       }
+
+       @AfterClass
+       public static void stopGitblit() throws Exception {
+               if (started.get()) {
+                       GitBlitSuite.stopGitblit();
+               }
+       }
+
+       @Test
+       public void testPublicKeyAuthentication() throws Exception {
+               SshClient client = SshClient.setUpDefaultClient();
+        client.start();
+        ClientSession session = client.connect("localhost", GitBlitSuite.sshPort).await().getSession();
+        pair.getPublic().getEncoded();
+        assertTrue(session.authPublicKey("admin", pair).await().isSuccess());
+       }
+
+       @Test
+       public void testVersionCommand() throws Exception {
+               SshClient client = SshClient.setUpDefaultClient();
+        client.start();
+        ClientSession session = client.connect("localhost", GitBlitSuite.sshPort).await().getSession();
+        pair.getPublic().getEncoded();
+        assertTrue(session.authPublicKey("admin", pair).await().isSuccess());
+
+        ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_EXEC, "gitblit version");
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        Writer w = new OutputStreamWriter(baos);
+        w.close();
+        channel.setIn(new ByteArrayInputStream(baos.toByteArray()));
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ByteArrayOutputStream err = new ByteArrayOutputStream();
+        channel.setOut(out);
+        channel.setErr(err);
+        channel.open();
+
+        channel.waitFor(ClientChannel.CLOSED, 0);
+
+        String result = out.toString().trim();
+        channel.close(false);
+        client.stop();
+
+        assertEquals(Constants.getGitBlitVersion(), result);
+     }
+}
diff --git a/src/test/java/com/gitblit/tests/SshUtils.java b/src/test/java/com/gitblit/tests/SshUtils.java
new file mode 100644 (file)
index 0000000..9760f75
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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 com.gitblit.tests;
+
+import java.io.File;
+import java.net.ServerSocket;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+
+public class SshUtils {
+
+    public static KeyPairProvider createTestHostKeyProvider() {
+        return new SimpleGeneratorHostKeyProvider("target/hostkey.rsa", "RSA");
+    }
+
+    public static FileKeyPairProvider createTestKeyPairProvider(String resource) {
+        return new FileKeyPairProvider(new String[] { getFile(resource) });
+    }
+
+    public static int getFreePort() throws Exception {
+        ServerSocket s = new ServerSocket(0);
+        try {
+            return s.getLocalPort();
+        } finally {
+            s.close();
+        }
+    }
+
+    private static String getFile(String resource) {
+        URL url = SshUtils.class.getClassLoader().getResource(resource);
+        File f;
+        try {
+            f = new File(url.toURI());
+        } catch(URISyntaxException e) {
+            f = new File(url.getPath());
+        }
+        return f.toString();
+    }
+
+    public static void deleteRecursive(File file) {
+        if (file != null) {
+            if (file.isDirectory()) {
+                File[] children = file.listFiles();
+                if (children != null) {
+                    for (File child : children) {
+                        deleteRecursive(child);
+                    }
+                }
+            }
+            file.delete();
+        }
+    }
+
+}