The public key manager can disable writing keys, which hides commands

Some public key mangers may be read-only, i.e. not allow to add or
delete keys, or to change the key comment or assigned permissions.
In such a case the respective commands should not be available on the
SSH shell and the SSH Keys panel should also not offer the possibility.

The `IPublicKeyManager` gets three new methods, modelled after the
`AuthenticationManager`:
`supportsWritingKeys`, `supportsCommentChanges` and
`supportsPermissionChanges`. They return true if a key manager allows for
keys to be written or updated.
For example the existing `FileKeyManager` will return true for all three
since it allows to store and update keys in a file.
The new `LdapKeyManager` returns false since it only accesses LDAP and
can not add or update any keys in the directory.
A future key manager might get keys from an LDAP directory but still
keep comments and permissions for it in a local copy.

If writing of keys is not supported:
* the welcome shell does not suggest adding a key,
* the `SshKeysDispatcher` does not offer the "add", "remove", "comment" and
  "permission" commands, and
* the SSH keys panel hides the "delete" button in the key list, and the
  "Add Key" form.

The hiding of the "Add key" form is not perfect since the surrounding
div is still shown, but I don't know how to hide it and it didn't look
too bad, either.
diff --git a/src/main/java/com/gitblit/transport/ssh/IPublicKeyManager.java b/src/main/java/com/gitblit/transport/ssh/IPublicKeyManager.java
index 1e74b2f..ffe64f5 100644
--- a/src/main/java/com/gitblit/transport/ssh/IPublicKeyManager.java
+++ b/src/main/java/com/gitblit/transport/ssh/IPublicKeyManager.java
@@ -25,6 +25,7 @@
 import org.slf4j.LoggerFactory;
 
 import com.gitblit.manager.IManager;
+import com.gitblit.models.UserModel;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.CacheLoader.InvalidCacheLoadException;
@@ -99,4 +100,16 @@
 	public abstract boolean removeKey(String username, SshKey key);
 
 	public abstract boolean removeAllKeys(String username);
+
+	public boolean supportsWritingKeys(UserModel user) {
+		return (user != null);
+	}
+
+	public boolean supportsCommentChanges(UserModel user) {
+		return (user != null);
+	}
+
+	public boolean supportsPermissionChanges(UserModel user) {
+		return (user != null);
+	}
 }
diff --git a/src/main/java/com/gitblit/transport/ssh/LdapKeyManager.java b/src/main/java/com/gitblit/transport/ssh/LdapKeyManager.java
index 9612a96..6b8f1e4 100644
--- a/src/main/java/com/gitblit/transport/ssh/LdapKeyManager.java
+++ b/src/main/java/com/gitblit/transport/ssh/LdapKeyManager.java
@@ -34,6 +34,7 @@
 import com.gitblit.Keys;
 import com.gitblit.Constants.AccessPermission;
 import com.gitblit.ldap.LdapConnection;
+import com.gitblit.models.UserModel;
 import com.gitblit.utils.StringUtils;
 import com.google.common.base.Joiner;
 import com.google.inject.Inject;
@@ -219,6 +220,18 @@
 	}
 
 
+	public boolean supportsWritingKeys(UserModel user) {
+		return false;
+	}
+
+	public boolean supportsCommentChanges(UserModel user) {
+		return false;
+	}
+
+	public boolean supportsPermissionChanges(UserModel user) {
+		return false;
+	}
+
 
 	private void setKeyPermissions(SshKey key, GbAuthorizedKeyEntry keyEntry) {
 		List<String> env = keyEntry.getLoginOptionValues("environment");
diff --git a/src/main/java/com/gitblit/transport/ssh/SshDaemon.java b/src/main/java/com/gitblit/transport/ssh/SshDaemon.java
index 5a94c9a..4fb05f7 100644
--- a/src/main/java/com/gitblit/transport/ssh/SshDaemon.java
+++ b/src/main/java/com/gitblit/transport/ssh/SshDaemon.java
@@ -134,7 +134,7 @@
 		sshd.setFileSystemFactory(new DisabledFilesystemFactory());
 		sshd.setTcpipForwardingFilter(new NonForwardingFilter());
 		sshd.setCommandFactory(new SshCommandFactory(gitblit, workQueue));
-		sshd.setShellFactory(new WelcomeShell(settings));
+		sshd.setShellFactory(new WelcomeShell(gitblit));
 
 		// Set the server id.  This can be queried with:
 		//   ssh-keyscan -t rsa,dsa -p 29418 localhost
diff --git a/src/main/java/com/gitblit/transport/ssh/WelcomeShell.java b/src/main/java/com/gitblit/transport/ssh/WelcomeShell.java
index ec6f729..7c407d3 100644
--- a/src/main/java/com/gitblit/transport/ssh/WelcomeShell.java
+++ b/src/main/java/com/gitblit/transport/ssh/WelcomeShell.java
@@ -34,6 +34,7 @@
 
 import com.gitblit.IStoredSettings;
 import com.gitblit.Keys;
+import com.gitblit.manager.IGitblit;
 import com.gitblit.models.UserModel;
 import com.gitblit.transport.ssh.commands.DispatchCommand;
 import com.gitblit.transport.ssh.commands.SshCommandFactory;
@@ -45,19 +46,20 @@
  */
 public class WelcomeShell implements Factory<Command> {
 
-	private final IStoredSettings settings;
+	private final IGitblit gitblit;
 
-	public WelcomeShell(IStoredSettings settings) {
-		this.settings = settings;
+	public WelcomeShell(IGitblit gitblit) {
+		this.gitblit = gitblit;
 	}
 
 	@Override
 	public Command create() {
-		return new SendMessage(settings);
+		return new SendMessage(gitblit);
 	}
 
 	private static class SendMessage implements Command, SessionAware {
 
+		private final IPublicKeyManager km;
 		private final IStoredSettings settings;
 		private ServerSession session;
 
@@ -66,8 +68,9 @@
 		private OutputStream err;
 		private ExitCallback exit;
 
-		SendMessage(IStoredSettings settings) {
-			this.settings = settings;
+		SendMessage(IGitblit gitblit) {
+			this.km = gitblit.getPublicKeyManager();
+			this.settings = gitblit.getSettings();
 		}
 
 		@Override
@@ -116,6 +119,10 @@
 			UserModel user = client.getUser();
 			String hostname = getHostname();
 			int port = settings.getInteger(Keys.git.sshPort, 0);
+			boolean writeKeysIsSupported = true;
+			if (km != null) {
+				writeKeysIsSupported = km.supportsWritingKeys(user);
+			}
 
 			final String b1 = StringUtils.rightPad("", 72, '═');
 			final String b2 = StringUtils.rightPad("", 72, '─');
@@ -159,7 +166,7 @@
 			msg.append(nl);
 			msg.append(nl);
 
-			if (client.getKey() == null) {
+			if (writeKeysIsSupported && client.getKey() == null) {
 				// user has authenticated with a password
 				// display add public key instructions
 				msg.append(" You may upload an SSH public key with the following syntax:");
diff --git a/src/main/java/com/gitblit/transport/ssh/keys/KeysDispatcher.java b/src/main/java/com/gitblit/transport/ssh/keys/KeysDispatcher.java
index da58584..817a98f 100644
--- a/src/main/java/com/gitblit/transport/ssh/keys/KeysDispatcher.java
+++ b/src/main/java/com/gitblit/transport/ssh/keys/KeysDispatcher.java
@@ -25,6 +25,7 @@
 import org.slf4j.LoggerFactory;
 
 import com.gitblit.Constants.AccessPermission;
+import com.gitblit.models.UserModel;
 import com.gitblit.transport.ssh.IPublicKeyManager;
 import com.gitblit.transport.ssh.SshKey;
 import com.gitblit.transport.ssh.commands.CommandMetaData;
@@ -47,12 +48,20 @@
 
 	@Override
 	protected void setup() {
-		register(AddKey.class);
-		register(RemoveKey.class);
+		IPublicKeyManager km = getContext().getGitblit().getPublicKeyManager();
+		UserModel user = getContext().getClient().getUser();
+		if (km != null && km.supportsWritingKeys(user)) {
+			register(AddKey.class);
+			register(RemoveKey.class);
+		}
 		register(ListKeys.class);
 		register(WhichKey.class);
-		register(CommentKey.class);
-		register(PermissionKey.class);
+		if (km != null && km.supportsCommentChanges(user)) {
+			register(CommentKey.class);
+		}
+		if (km != null && km.supportsPermissionChanges(user)) {
+			register(PermissionKey.class);
+		}
 	}
 
 	@CommandMetaData(name = "add", description = "Add an SSH public key to your account")
diff --git a/src/main/java/com/gitblit/wicket/panels/SshKeysPanel.java b/src/main/java/com/gitblit/wicket/panels/SshKeysPanel.java
index 15ebd67..4b87876 100644
--- a/src/main/java/com/gitblit/wicket/panels/SshKeysPanel.java
+++ b/src/main/java/com/gitblit/wicket/panels/SshKeysPanel.java
@@ -48,11 +48,13 @@
 	private static final long serialVersionUID = 1L;

 

 	private final UserModel user;

+	private final boolean canWriteKeys;

 

 	public SshKeysPanel(String wicketId, UserModel user) {

 		super(wicketId);

 

 		this.user = user;

+		this.canWriteKeys = app().keys().supportsWritingKeys(user);

 	}

 

 	@Override

@@ -90,6 +92,9 @@
 						}

 					}

 				};

+				if (!canWriteKeys) {

+					delete.setVisibilityAllowed(false);

+				}

 				item.add(delete);

 			}

 		};

@@ -164,6 +169,10 @@
 			}

 		});

 

+		if (! canWriteKeys) {

+			addKeyForm.setVisibilityAllowed(false);

+		}

+

 		add(addKeyForm);

 	}

 }