From bcc8a015ae552726742b4f437b2cb9e809270f96 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Thu, 10 Apr 2014 18:58:09 -0400
Subject: [PATCH] Handle ssh keys as objects, not strings, and improve the ls and rm key commands

---
 src/main/java/com/gitblit/transport/ssh/gitblit/KeysDispatcher.java |   88 ++++++++++++++++++++++++++++++-------------
 1 files changed, 61 insertions(+), 27 deletions(-)

diff --git a/src/main/java/com/gitblit/transport/ssh/gitblit/KeysDispatcher.java b/src/main/java/com/gitblit/transport/ssh/gitblit/KeysDispatcher.java
index 8c1bfd2..52fa875 100644
--- a/src/main/java/com/gitblit/transport/ssh/gitblit/KeysDispatcher.java
+++ b/src/main/java/com/gitblit/transport/ssh/gitblit/KeysDispatcher.java
@@ -16,18 +16,17 @@
 package com.gitblit.transport.ssh.gitblit;
 
 import java.io.IOException;
-import java.security.PublicKey;
 import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.commons.codec.binary.Base64;
-import org.apache.sshd.common.util.Buffer;
 import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.Option;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.gitblit.models.UserModel;
 import com.gitblit.transport.ssh.IPublicKeyManager;
+import com.gitblit.transport.ssh.SshKey;
 import com.gitblit.transport.ssh.commands.CommandMetaData;
 import com.gitblit.transport.ssh.commands.DispatchCommand;
 import com.gitblit.transport.ssh.commands.SshCommand;
@@ -61,7 +60,8 @@
 			String username = getContext().getClient().getUsername();
 			List<String> keys = readKeys(addKeys);
 			for (String key : keys) {
-				getKeyManager().addKey(username, key);
+				SshKey sshKey = parseKey(key);
+				getKeyManager().addKey(username, sshKey);
 				log.info("added SSH public key for {}", username);
 			}
 		}
@@ -74,51 +74,85 @@
 
 		private final String ALL = "ALL";
 
-		@Argument(metaVar = "<stdin>|<KEY>|ALL", usage = "the key to remove")
+		@Argument(metaVar = "-|<INDEX>|<KEY>|ALL", usage = "the key to remove", required = true)
 		private List<String> removeKeys = new ArrayList<String>();
 
 		@Override
 		public void run() throws IOException, UnloggedFailure {
 			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()) {
+				throw new UnloggedFailure(1, "There are no registered keys!");
+			}
+
 			List<String> keys = readKeys(removeKeys);
 			if (keys.contains(ALL)) {
-				getKeyManager().removeAllKeys(username);
-				log.info("removed all SSH public keys from {}", username);
+				if (getKeyManager().removeAllKeys(username)) {
+					stdout.println("Removed all keys.");
+					log.info("removed all SSH public keys from {}", username);
+				} else {
+					log.warn("failed to remove all SSH public keys from {}", username);
+				}
 			} else {
 				for (String key : keys) {
-					getKeyManager().removeKey(username, key);
-					log.info("removed SSH public key from {}", username);
+					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.");
+							}
+							throw new UnloggedFailure(1, String.format("Invalid index specified. There are %d registered keys.", keys.size()));
+						}
+						SshKey sshKey = currentKeys.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()));
+						}
+					} 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()));
+						}
+					}
 				}
 			}
 		}
 	}
 
-	@CommandMetaData(name = "list", aliases = { "ls" }, description = "List your public keys")
+	@CommandMetaData(name = "list", aliases = { "ls" }, description = "List your registered public keys")
 	public static class ListKeys extends SshCommand {
+
+		@Option(name = "-L", usage = "list complete public key parameters")
+		private boolean showRaw;
 
 		@Override
 		public void run() {
 			IPublicKeyManager keyManager = getContext().getGitblit().getPublicKeyManager();
 			String username = getContext().getClient().getUsername();
-			List<PublicKey> keys = keyManager.getKeys(username);
-			if (keys == null) {
-				stdout.println(String.format("%s has not added any public keys for ssh authentication", username));
+			List<SshKey> keys = keyManager.getKeys(username);
+			if (keys == null || keys.isEmpty()) {
+				stdout.println("You have not registered any public keys for ssh authentication.");
 				return;
 			}
-			for (PublicKey key : keys) {
-				// two-steps - perhaps this could be improved
-				Buffer buf = new Buffer();
-
-				// 1: identify the algorithm
-				buf.putRawPublicKey(key);
-				String alg = buf.getString();
-
-				// 2: encode the key
-				buf.clear();
-				buf.putPublicKey(key);
-				String b64 = Base64.encodeBase64String(buf.getBytes());
-
-				stdout.println(alg + " " + b64);
+			for (int i = 0; i < keys.size(); i++) {
+				if (showRaw) {
+					// output in the same format as authorized_keys
+					stdout.println(keys.get(i).getRawData());
+				} else {
+					// show 1-based index numbers with the fingerprint
+					// this is useful for comparing with "ssh-add -l"
+					stdout.println("#" + (i + 1) + ": " + keys.get(i).getFingerprint());
+				}
 			}
 		}
 	}

--
Gitblit v1.9.1