Unit tests for ssh daemon and keys dispatcher
1 files deleted
2 files added
10 files modified
| | |
| | | * @author Eric Myrhe |
| | | * |
| | | */ |
| | | public class CachingPublicKeyAuthenticator implements PublickeyAuthenticator, |
| | | SessionListener { |
| | | public class CachingPublicKeyAuthenticator implements PublickeyAuthenticator, SessionListener { |
| | | |
| | | protected final Logger log = LoggerFactory.getLogger(getClass()); |
| | | |
| | |
| | | |
| | | protected final IAuthenticationManager authManager; |
| | | |
| | | private final Map<ServerSession, Map<PublicKey, Boolean>> cache = |
| | | new ConcurrentHashMap<ServerSession, Map<PublicKey, Boolean>>(); |
| | | private final Map<ServerSession, Map<PublicKey, Boolean>> cache = new ConcurrentHashMap<ServerSession, Map<PublicKey, Boolean>>(); |
| | | |
| | | public CachingPublicKeyAuthenticator(IPublicKeyManager keyManager, |
| | | IAuthenticationManager authManager) { |
| | | public CachingPublicKeyAuthenticator(IPublicKeyManager keyManager, IAuthenticationManager authManager) { |
| | | this.keyManager = keyManager; |
| | | this.authManager = authManager; |
| | | } |
| | | |
| | | @Override |
| | | public boolean authenticate(String username, PublicKey key, |
| | | ServerSession session) { |
| | | public boolean authenticate(String username, PublicKey key, ServerSession session) { |
| | | Map<PublicKey, Boolean> map = cache.get(session); |
| | | if (map == null) { |
| | | map = new HashMap<PublicKey, Boolean>(); |
| | |
| | | return result; |
| | | } |
| | | |
| | | private boolean doAuthenticate(String username, PublicKey suppliedKey, |
| | | ServerSession session) { |
| | | private boolean doAuthenticate(String username, PublicKey suppliedKey, ServerSession session) { |
| | | SshDaemonClient client = session.getAttribute(SshDaemonClient.KEY); |
| | | Preconditions.checkState(client.getUser() == null); |
| | | username = username.toLowerCase(Locale.US); |
| | | List<SshKey> keys = keyManager.getKeys(username); |
| | | if (keys == null || keys.isEmpty()) { |
| | | log.info("{} has not added any public keys for ssh authentication", |
| | | username); |
| | | if (keys.isEmpty()) { |
| | | log.info("{} has not added any public keys for ssh authentication", username); |
| | | return false; |
| | | } |
| | | |
| | | SshKey pk = new SshKey(suppliedKey); |
| | | log.debug("auth supplied {}", pk.getFingerprint()); |
| | | |
| | | for (SshKey key : keys) { |
| | | log.debug("auth compare to {}", key.getFingerprint()); |
| | | if (key.equals(suppliedKey)) { |
| | | UserModel user = authManager.authenticate(username, key); |
| | | if (user != null) { |
| | |
| | | } |
| | | } |
| | | |
| | | log.warn("could not authenticate {} for SSH using the supplied public key", |
| | | username); |
| | | log.warn("could not authenticate {} for SSH using the supplied public key", username); |
| | | return false; |
| | | } |
| | | |
| | |
| | | @Override |
| | | protected List<SshKey> getKeysImpl(String username) { |
| | | try { |
| | | log.info("loading keystore for {}", username); |
| | | log.info("loading ssh keystore for {}", username); |
| | | File keystore = getKeystore(username); |
| | | if (!keystore.exists()) { |
| | | return null; |
| | |
| | | return list; |
| | | } |
| | | } catch (IOException e) { |
| | | throw new RuntimeException("Canot read ssh keys", e); |
| | | throw new RuntimeException("Cannot read ssh keys", e); |
| | | } |
| | | return null; |
| | | } |
| | |
| | | package com.gitblit.transport.ssh; |
| | | |
| | | import java.text.MessageFormat; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.concurrent.ExecutionException; |
| | | import java.util.concurrent.TimeUnit; |
| | |
| | | .build(new CacheLoader<String, List<SshKey>>() { |
| | | @Override |
| | | public List<SshKey> load(String username) { |
| | | return getKeysImpl(username); |
| | | List<SshKey> keys = getKeysImpl(username); |
| | | if (keys == null) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return Collections.unmodifiableList(keys); |
| | | } |
| | | }); |
| | | |
| | |
| | | */ |
| | | public class MemoryKeyManager extends IPublicKeyManager { |
| | | |
| | | Map<String, List<SshKey>> keys; |
| | | final Map<String, List<SshKey>> keys; |
| | | |
| | | public MemoryKeyManager() { |
| | | keys = new HashMap<String, List<SshKey>>(); |
| | |
| | | |
| | | @Override |
| | | protected boolean isStale(String username) { |
| | | return false; |
| | | // always return true so we gets keys from our hashmap |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | |
| | | if (!keys.containsKey(id)) { |
| | | keys.put(id, new ArrayList<SshKey>()); |
| | | } |
| | | log.info("added {} key {}", username, key.getFingerprint()); |
| | | return keys.get(id).add(key); |
| | | } |
| | | |
| | |
| | | public boolean removeKey(String username, SshKey key) { |
| | | String id = username.toLowerCase(); |
| | | if (!keys.containsKey(id)) { |
| | | log.info("can't remove keys for {}", username); |
| | | return false; |
| | | } |
| | | return keys.get(id).remove(key); |
| | | List<SshKey> list = keys.get(id); |
| | | boolean success = list.remove(key); |
| | | if (success) { |
| | | log.info("removed {} key {}", username, key.getFingerprint()); |
| | | } |
| | | |
| | | if (list.isEmpty()) { |
| | | keys.remove(id); |
| | | log.info("no {} keys left, removed {}", username, username); |
| | | } |
| | | return success; |
| | | } |
| | | |
| | | @Override |
| | | public boolean removeAllKeys(String username) { |
| | | String id = username.toLowerCase(); |
| | | keys.remove(id.toLowerCase()); |
| | | log.info("removed all keys for {}", username); |
| | | return true; |
| | | } |
| | | } |
| | |
| | | final byte [] bin = Base64.decodeBase64(Constants.encodeASCII(parts[1])); |
| | | hash = StringUtils.getMD5(bin); |
| | | } else { |
| | | // TODO get hash from publickey |
| | | hash = "todo"; |
| | | // TODO calculate the correct hash from a PublicKey instance |
| | | hash = StringUtils.getMD5(getPublicKey().getEncoded()); |
| | | } |
| | | for (int i = 0; i < hash.length(); i += 2) { |
| | | sb.append(hash.charAt(i)).append(hash.charAt(i + 1)).append(':'); |
| | |
| | | |
| | | @Override |
| | | protected AbstractSession createSession(final IoSession io) throws Exception { |
| | | log.info("connection accepted on " + io); |
| | | log.info("creating ssh session from {}", io.getRemoteAddress()); |
| | | |
| | | if (io instanceof MinaSession) { |
| | | if (((MinaSession) io).getSession().getConfig() instanceof SocketSessionConfig) { |
| | |
| | | session.addCloseSessionListener(new SshFutureListener<CloseFuture>() { |
| | | @Override |
| | | public void operationComplete(CloseFuture future) { |
| | | log.info("connection closed on " + io); |
| | | log.info("closed ssh session from {}", io.getRemoteAddress()); |
| | | } |
| | | }); |
| | | return session; |
| | |
| | | |
| | | protected final Logger log = LoggerFactory.getLogger(getClass()); |
| | | |
| | | @Argument(metaVar = "<KEY>", usage = "the key(s) to add") |
| | | @Argument(metaVar = "<STDIN>", usage = "the key to add") |
| | | private List<String> addKeys = new ArrayList<String>(); |
| | | |
| | | @Option(name = "--permission", aliases = { "-p" }, metaVar = "PERMISSION", usage = "set the key access permission") |
| | |
| | | } |
| | | |
| | | @Override |
| | | public void run() throws IOException, UnloggedFailure { |
| | | public void run() throws IOException, Failure { |
| | | String username = getContext().getClient().getUsername(); |
| | | List<String> keys = readKeys(addKeys); |
| | | for (String key : keys) { |
| | |
| | | try { |
| | | sshKey.setPermission(ap); |
| | | } catch (IllegalArgumentException e) { |
| | | throw new UnloggedFailure(1, e.getMessage()); |
| | | throw new Failure(1, e.getMessage()); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | private final String ALL = "ALL"; |
| | | |
| | | @Argument(metaVar = "<INDEX>|<KEY>|ALL", usage = "the key to remove", required = true) |
| | | private List<String> removeKeys = new ArrayList<String>(); |
| | | @Argument(metaVar = "<INDEX>|ALL", usage = "the key to remove", required = true) |
| | | private List<String> keyParameters = new ArrayList<String>(); |
| | | |
| | | @Override |
| | | public void run() throws IOException, UnloggedFailure { |
| | | public void run() throws IOException, Failure { |
| | | String username = getContext().getClient().getUsername(); |
| | | // remove a key that has been piped to the command |
| | | // or remove all keys |
| | | |
| | | List<SshKey> currentKeys = getKeyManager().getKeys(username); |
| | | if (currentKeys == null || currentKeys.isEmpty()) { |
| | | List<SshKey> registeredKeys = new ArrayList<SshKey>(getKeyManager().getKeys(username)); |
| | | if (registeredKeys.isEmpty()) { |
| | | throw new UnloggedFailure(1, "There are no registered keys!"); |
| | | } |
| | | |
| | | List<String> keys = readKeys(removeKeys); |
| | | if (keys.contains(ALL)) { |
| | | if (keyParameters.contains(ALL)) { |
| | | if (getKeyManager().removeAllKeys(username)) { |
| | | stdout.println("Removed all keys."); |
| | | log.info("removed all SSH public keys from {}", username); |
| | |
| | | log.warn("failed to remove all SSH public keys from {}", username); |
| | | } |
| | | } else { |
| | | for (String key : keys) { |
| | | for (String keyParameter : keyParameters) { |
| | | try { |
| | | // remove a key by it's index (1-based indexing) |
| | | int index = Integer.parseInt(key); |
| | | if (index > keys.size()) { |
| | | if (keys.size() == 1) { |
| | | throw new UnloggedFailure(1, "Invalid index specified. There is only 1 registered key."); |
| | | int index = Integer.parseInt(keyParameter); |
| | | if (index > registeredKeys.size()) { |
| | | if (keyParameters.size() == 1) { |
| | | throw new Failure(1, "Invalid index specified. There is only 1 registered key."); |
| | | } |
| | | throw new UnloggedFailure(1, String.format("Invalid index specified. There are %d registered keys.", keys.size())); |
| | | throw new Failure(1, String.format("Invalid index specified. There are %d registered keys.", registeredKeys.size())); |
| | | } |
| | | SshKey sshKey = currentKeys.get(index - 1); |
| | | SshKey sshKey = registeredKeys.get(index - 1); |
| | | if (getKeyManager().removeKey(username, sshKey)) { |
| | | stdout.println(String.format("Removed %s", sshKey.getFingerprint())); |
| | | } else { |
| | | throw new UnloggedFailure(1, String.format("failed to remove #%s: %s", key, sshKey.getFingerprint())); |
| | | throw new Failure(1, String.format("failed to remove #%s: %s", keyParameter, sshKey.getFingerprint())); |
| | | } |
| | | } catch (Exception e) { |
| | | // remove key by raw key data |
| | | SshKey sshKey = parseKey(key); |
| | | if (getKeyManager().removeKey(username, sshKey)) { |
| | | stdout.println(String.format("Removed %s", sshKey.getFingerprint())); |
| | | log.info("removed SSH public key {} from {}", sshKey.getFingerprint(), username); |
| | | } else { |
| | | log.warn("failed to remove SSH public key {} from {}", sshKey.getFingerprint(), username); |
| | | throw new UnloggedFailure(1, String.format("failed to remove %s", sshKey.getFingerprint())); |
| | | } |
| | | } catch (NumberFormatException e) { |
| | | log.warn("failed to remove SSH public key {} from {}", keyParameter, username); |
| | | throw new Failure(1, String.format("failed to remove key %s", keyParameter)); |
| | | } |
| | | } |
| | | } |
| | |
| | | private List<String> values = new ArrayList<String>(); |
| | | |
| | | @Override |
| | | public void run() throws UnloggedFailure { |
| | | public void run() throws Failure { |
| | | final String username = getContext().getClient().getUsername(); |
| | | IPublicKeyManager keyManager = getContext().getGitblit().getPublicKeyManager(); |
| | | List<SshKey> keys = keyManager.getKeys(username); |
| | |
| | | if (keyManager.addKey(username, key)) { |
| | | stdout.println(String.format("Updated the comment for key #%d.", index)); |
| | | } else { |
| | | throw new UnloggedFailure(1, String.format("Failed to update the comment for key #%d!", index)); |
| | | throw new Failure(1, String.format("Failed to update the comment for key #%d!", index)); |
| | | } |
| | | } |
| | | |
| | |
| | | #log4j.logger.net=INFO |
| | | |
| | | #log4j.logger.com.gitblit=DEBUG |
| | | log4j.logger.org.apache.sshd=ERROR |
| | | log4j.logger.com.gitblit.transport.ssh.SshServerSession=WARN |
| | | log4j.logger.org.apache.sshd=WARN |
| | | log4j.logger.org.apache.mina=WARN |
| | | |
| | | log4j.logger.org.apache.wicket=INFO |
| | | log4j.logger.org.apache.wicket.RequestListenerInterface=WARN |
| | |
| | | SshDaemonTest.class, GroovyScriptTest.class, LuceneExecutorTest.class, RepositoryModelTest.class,
|
| | | FanoutServiceTest.class, Issue0259Test.class, Issue0271Test.class, HtpasswdAuthenticationTest.class,
|
| | | ModelUtilsTest.class, JnaUtilsTest.class, LdapSyncServiceTest.class, FileTicketServiceTest.class, |
| | | BranchTicketServiceTest.class, RedisTicketServiceTest.class, AuthenticationManagerTest.class }) |
| | | BranchTicketServiceTest.class, RedisTicketServiceTest.class, AuthenticationManagerTest.class,
|
| | | SshKeysDispatcherTest.class }) |
| | | public class GitBlitSuite {
|
| | |
|
| | | public static final File BASEFOLDER = new File("data");
|
| | |
| | | Executors.newSingleThreadExecutor().execute(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | GitBlitServer.main("--httpPort", "" + port, "--httpsPort", "0", "--shutdownPort",
|
| | | "" + shutdownPort, "--gitPort", "" + gitPort, "--repositoriesFolder",
|
| | | "\"" + GitBlitSuite.REPOSITORIES.getAbsolutePath() + "\"", "--userService",
|
| | | GitBlitSuite.USERSCONF.getAbsolutePath(), "--settings", GitBlitSuite.SETTINGS.getAbsolutePath(),
|
| | | "--baseFolder", "data", "--sshPort", "" + sshPort);
|
| | | GitBlitServer.main(
|
| | | "--httpPort", "" + port,
|
| | | "--httpsPort", "0",
|
| | | "--shutdownPort", "" + shutdownPort,
|
| | | "--gitPort", "" + gitPort,
|
| | | "--sshPort", "" + sshPort,
|
| | | "--repositoriesFolder", GitBlitSuite.REPOSITORIES.getAbsolutePath(),
|
| | | "--userService", GitBlitSuite.USERSCONF.getAbsolutePath(),
|
| | | "--settings", GitBlitSuite.SETTINGS.getAbsolutePath(),
|
| | | "--baseFolder", "data");
|
| | | }
|
| | | });
|
| | |
|
| | |
| | | */ |
| | | 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 java.io.File; |
| | | import java.text.MessageFormat; |
| | | import java.util.List; |
| | | |
| | | import org.apache.sshd.ClientChannel; |
| | | import org.apache.sshd.ClientSession; |
| | | import org.apache.sshd.SshClient; |
| | | import org.apache.sshd.common.KeyPairProvider; |
| | | import org.junit.After; |
| | | import org.junit.AfterClass; |
| | | import org.junit.Before; |
| | | import org.junit.BeforeClass; |
| | | import org.eclipse.jgit.api.CloneCommand; |
| | | import org.eclipse.jgit.api.Git; |
| | | import org.eclipse.jgit.revwalk.RevCommit; |
| | | import org.eclipse.jgit.transport.SshSessionFactory; |
| | | import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; |
| | | import org.eclipse.jgit.util.FileUtils; |
| | | import org.junit.Test; |
| | | |
| | | import com.gitblit.Constants; |
| | | import com.gitblit.transport.ssh.IPublicKeyManager; |
| | | import com.gitblit.transport.ssh.MemoryKeyManager; |
| | | import com.gitblit.transport.ssh.SshKey; |
| | | import com.gitblit.Constants.AccessRestrictionType; |
| | | import com.gitblit.Constants.AuthorizationControl; |
| | | import com.gitblit.models.RepositoryModel; |
| | | import com.gitblit.utils.JGitUtils; |
| | | |
| | | public class SshDaemonTest extends GitblitUnitTest { |
| | | public class SshDaemonTest extends SshUnitTest { |
| | | |
| | | private static final AtomicBoolean started = new AtomicBoolean(false); |
| | | private static KeyPair pair; |
| | | static File ticgitFolder = new File(GitBlitSuite.REPOSITORIES, "working/ticgit"); |
| | | |
| | | @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(); |
| | | } |
| | | } |
| | | |
| | | protected MemoryKeyManager getKeyManager() { |
| | | IPublicKeyManager mgr = gitblit().getPublicKeyManager(); |
| | | if (mgr instanceof MemoryKeyManager) { |
| | | return (MemoryKeyManager) gitblit().getPublicKeyManager(); |
| | | } else { |
| | | throw new RuntimeException("unexpected key manager type " + mgr.getClass().getName()); |
| | | } |
| | | } |
| | | |
| | | @Before |
| | | public void prepare() { |
| | | MemoryKeyManager keyMgr = getKeyManager(); |
| | | keyMgr.addKey("admin", new SshKey(pair.getPublic())); |
| | | } |
| | | |
| | | @After |
| | | public void tearDown() { |
| | | MemoryKeyManager keyMgr = getKeyManager(); |
| | | keyMgr.removeAllKeys("admin"); |
| | | } |
| | | String url = GitBlitSuite.sshDaemonUrl; |
| | | |
| | | @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()); |
| | | SshClient client = getClient(); |
| | | ClientSession session = client.connect(username, "localhost", GitBlitSuite.sshPort).await().getSession(); |
| | | session.addPublicKeyIdentity(rwKeyPair); |
| | | assertTrue(session.auth().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, "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(); |
| | | |
| | | String result = testSshCommand("version"); |
| | | assertEquals(Constants.getGitBlitVersion(), result); |
| | | } |
| | | |
| | | @Test |
| | | public void testCloneCommand() throws Exception { |
| | | if (ticgitFolder.exists()) { |
| | | GitBlitSuite.close(ticgitFolder); |
| | | FileUtils.delete(ticgitFolder, FileUtils.RECURSIVE); |
| | | } |
| | | |
| | | // set clone restriction |
| | | RepositoryModel model = repositories().getRepositoryModel("ticgit.git"); |
| | | model.accessRestriction = AccessRestrictionType.CLONE; |
| | | model.authorizationControl = AuthorizationControl.NAMED; |
| | | repositories().updateRepositoryModel(model.name, model, false); |
| | | |
| | | JschConfigTestSessionFactory sessionFactory = new JschConfigTestSessionFactory(roKeyPair); |
| | | SshSessionFactory.setInstance(sessionFactory); |
| | | |
| | | CloneCommand clone = Git.cloneRepository(); |
| | | clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(username, password)); |
| | | clone.setURI(MessageFormat.format("{0}/ticgit.git", url)); |
| | | clone.setDirectory(ticgitFolder); |
| | | clone.setBare(false); |
| | | clone.setCloneAllBranches(true); |
| | | Git git = clone.call(); |
| | | List<RevCommit> commits = JGitUtils.getRevLog(git.getRepository(), 10); |
| | | GitBlitSuite.close(git); |
| | | assertEquals(10, commits.size()); |
| | | |
| | | // restore anonymous repository access |
| | | model.accessRestriction = AccessRestrictionType.NONE; |
| | | model.authorizationControl = AuthorizationControl.NAMED; |
| | | repositories().updateRepositoryModel(model.name, model, false); |
| | | } |
| | | } |
New file |
| | |
| | | /* |
| | | * 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.KeyPair; |
| | | import java.util.List; |
| | | |
| | | import org.junit.Test; |
| | | import org.parboiled.common.StringUtils; |
| | | |
| | | import com.gitblit.Constants.AccessPermission; |
| | | import com.gitblit.transport.ssh.SshKey; |
| | | |
| | | /** |
| | | * Tests the Keys Dispatcher and it's commands. |
| | | * |
| | | * @author James Moger |
| | | * |
| | | */ |
| | | public class SshKeysDispatcherTest extends SshUnitTest { |
| | | |
| | | @Test |
| | | public void testKeysListCommand() throws Exception { |
| | | String result = testSshCommand("keys ls -L"); |
| | | List<SshKey> keys = getKeyManager().getKeys(username); |
| | | assertEquals(String.format("There are %d keys!", keys.size()), 2, keys.size()); |
| | | assertEquals(keys.get(0).getRawData() + "\n" + keys.get(1).getRawData(), result); |
| | | } |
| | | |
| | | @Test |
| | | public void testKeysWhichCommand() throws Exception { |
| | | String result = testSshCommand("keys which -L"); |
| | | List<SshKey> keys = getKeyManager().getKeys(username); |
| | | assertEquals(String.format("There are %d keys!", keys.size()), 2, keys.size()); |
| | | assertEquals(keys.get(0).getRawData(), result); |
| | | } |
| | | |
| | | @Test |
| | | public void testKeysRmCommand() throws Exception { |
| | | testSshCommand("keys rm 2"); |
| | | String result = testSshCommand("keys ls -L"); |
| | | List<SshKey> keys = getKeyManager().getKeys(username); |
| | | assertEquals(String.format("There are %d keys!", keys.size()), 1, keys.size()); |
| | | assertEquals(keys.get(0).getRawData(), result); |
| | | } |
| | | |
| | | @Test |
| | | public void testKeysRmAllByIndexCommand() throws Exception { |
| | | testSshCommand("keys rm 1 2"); |
| | | List<SshKey> keys = getKeyManager().getKeys(username); |
| | | assertEquals(String.format("There are %d keys!", keys.size()), 0, keys.size()); |
| | | try { |
| | | testSshCommand("keys ls -L"); |
| | | assertTrue("Authentication worked without a public key?!", false); |
| | | } catch (AssertionError e) { |
| | | assertTrue(true); |
| | | } |
| | | } |
| | | |
| | | @Test |
| | | public void testKeysRmAllCommand() throws Exception { |
| | | testSshCommand("keys rm ALL"); |
| | | List<SshKey> keys = getKeyManager().getKeys(username); |
| | | assertEquals(String.format("There are %d keys!", keys.size()), 0, keys.size()); |
| | | try { |
| | | testSshCommand("keys ls -L"); |
| | | assertTrue("Authentication worked without a public key?!", false); |
| | | } catch (AssertionError e) { |
| | | assertTrue(true); |
| | | } |
| | | } |
| | | |
| | | @Test |
| | | public void testKeysAddCommand() throws Exception { |
| | | KeyPair kp = generator.generateKeyPair(); |
| | | SshKey key = new SshKey(kp.getPublic()); |
| | | testSshCommand("keys add --permission R", key.getRawData()); |
| | | List<SshKey> keys = getKeyManager().getKeys(username); |
| | | assertEquals(String.format("There are %d keys!", keys.size()), 3, keys.size()); |
| | | assertEquals(AccessPermission.CLONE, keys.get(2).getPermission()); |
| | | |
| | | String result = testSshCommand("keys ls -L"); |
| | | StringBuilder sb = new StringBuilder(); |
| | | for (SshKey sk : keys) { |
| | | sb.append(sk.getRawData()); |
| | | sb.append('\n'); |
| | | } |
| | | sb.setLength(sb.length() - 1); |
| | | assertEquals(sb.toString(), result); |
| | | } |
| | | |
| | | @Test |
| | | public void testKeysCommentCommand() throws Exception { |
| | | List<SshKey> keys = getKeyManager().getKeys(username); |
| | | assertTrue(StringUtils.isEmpty(keys.get(0).getComment())); |
| | | String comment = "this is my comment"; |
| | | testSshCommand(String.format("keys comment 1 %s", comment)); |
| | | |
| | | keys = getKeyManager().getKeys(username); |
| | | assertEquals(comment, keys.get(0).getComment()); |
| | | } |
| | | } |
New file |
| | |
| | | /* |
| | | * 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.IOException; |
| | | import java.io.OutputStreamWriter; |
| | | import java.io.Writer; |
| | | import java.net.SocketAddress; |
| | | import java.security.KeyPair; |
| | | import java.security.KeyPairGenerator; |
| | | import java.security.PublicKey; |
| | | 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.client.ServerKeyVerifier; |
| | | import org.apache.sshd.common.util.SecurityUtils; |
| | | import org.junit.After; |
| | | import org.junit.AfterClass; |
| | | import org.junit.Before; |
| | | import org.junit.BeforeClass; |
| | | |
| | | import com.gitblit.Constants.AccessPermission; |
| | | import com.gitblit.transport.ssh.IPublicKeyManager; |
| | | import com.gitblit.transport.ssh.MemoryKeyManager; |
| | | import com.gitblit.transport.ssh.SshKey; |
| | | |
| | | /** |
| | | * Base class for SSH unit tests. |
| | | */ |
| | | public abstract class SshUnitTest extends GitblitUnitTest { |
| | | |
| | | protected static final AtomicBoolean started = new AtomicBoolean(false); |
| | | protected static KeyPairGenerator generator; |
| | | protected KeyPair rwKeyPair; |
| | | protected KeyPair roKeyPair; |
| | | protected String username = "admin"; |
| | | protected String password = "admin"; |
| | | |
| | | @BeforeClass |
| | | public static void startGitblit() throws Exception { |
| | | generator = SecurityUtils.getKeyPairGenerator("RSA"); |
| | | started.set(GitBlitSuite.startGitblit()); |
| | | } |
| | | |
| | | @AfterClass |
| | | public static void stopGitblit() throws Exception { |
| | | if (started.get()) { |
| | | GitBlitSuite.stopGitblit(); |
| | | } |
| | | } |
| | | |
| | | protected MemoryKeyManager getKeyManager() { |
| | | IPublicKeyManager mgr = gitblit().getPublicKeyManager(); |
| | | if (mgr instanceof MemoryKeyManager) { |
| | | return (MemoryKeyManager) gitblit().getPublicKeyManager(); |
| | | } else { |
| | | throw new RuntimeException("unexpected key manager type " + mgr.getClass().getName()); |
| | | } |
| | | } |
| | | |
| | | @Before |
| | | public void prepare() { |
| | | rwKeyPair = generator.generateKeyPair(); |
| | | |
| | | MemoryKeyManager keyMgr = getKeyManager(); |
| | | keyMgr.addKey(username, new SshKey(rwKeyPair.getPublic())); |
| | | |
| | | roKeyPair = generator.generateKeyPair(); |
| | | SshKey sshKey = new SshKey(roKeyPair.getPublic()); |
| | | sshKey.setPermission(AccessPermission.CLONE); |
| | | keyMgr.addKey(username, sshKey); |
| | | } |
| | | |
| | | @After |
| | | public void tearDown() { |
| | | MemoryKeyManager keyMgr = getKeyManager(); |
| | | keyMgr.removeAllKeys(username); |
| | | } |
| | | |
| | | protected SshClient getClient() { |
| | | SshClient client = SshClient.setUpDefaultClient(); |
| | | client.setServerKeyVerifier(new ServerKeyVerifier() { |
| | | @Override |
| | | public boolean verifyServerKey(ClientSession sshClientSession, SocketAddress remoteAddress, PublicKey serverKey) { |
| | | return true; |
| | | } |
| | | }); |
| | | client.start(); |
| | | return client; |
| | | } |
| | | |
| | | protected String testSshCommand(String cmd) throws IOException, InterruptedException { |
| | | return testSshCommand(cmd, null); |
| | | } |
| | | |
| | | protected String testSshCommand(String cmd, String stdin) throws IOException, InterruptedException { |
| | | SshClient client = getClient(); |
| | | ClientSession session = client.connect(username, "localhost", GitBlitSuite.sshPort).await().getSession(); |
| | | session.addPublicKeyIdentity(rwKeyPair); |
| | | assertTrue(session.auth().await().isSuccess()); |
| | | |
| | | ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_EXEC, cmd); |
| | | ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| | | if (stdin != null) { |
| | | Writer w = new OutputStreamWriter(baos); |
| | | w.write(stdin); |
| | | 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(); |
| | | return result; |
| | | } |
| | | } |