From 3ad13e644f00b01010002f6d9d98343d4f2a4ea3 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Thu, 10 Apr 2014 19:00:05 -0400
Subject: [PATCH] Implement rename user/team and set field user/team commands

---
 src/main/java/com/gitblit/transport/ssh/gitblit/TeamsDispatcher.java |  144 +++++++++++++++++++++++
 src/main/java/com/gitblit/transport/ssh/commands/BaseCommand.java    |    9 +
 src/main/java/com/gitblit/transport/ssh/gitblit/UsersDispatcher.java |  176 ++++++++++++++++++++++++++--
 src/main/java/com/gitblit/service/LuceneService.java                 |    2 
 4 files changed, 312 insertions(+), 19 deletions(-)

diff --git a/src/main/java/com/gitblit/service/LuceneService.java b/src/main/java/com/gitblit/service/LuceneService.java
index 714a1e2..868a295 100644
--- a/src/main/java/com/gitblit/service/LuceneService.java
+++ b/src/main/java/com/gitblit/service/LuceneService.java
@@ -194,7 +194,7 @@
 	 * Synchronously indexes a repository. This may build a complete index of a
 	 * repository or it may update an existing index.
 	 *
-	 * @param name
+	 * @param displayName
 	 *            the name of the repository
 	 * @param repository
 	 *            the repository object
diff --git a/src/main/java/com/gitblit/transport/ssh/commands/BaseCommand.java b/src/main/java/com/gitblit/transport/ssh/commands/BaseCommand.java
index 6662a32..d6aa929 100644
--- a/src/main/java/com/gitblit/transport/ssh/commands/BaseCommand.java
+++ b/src/main/java/com/gitblit/transport/ssh/commands/BaseCommand.java
@@ -222,6 +222,11 @@
 			msg.write("  ");
 			clp.printSingleLineUsage(msg, null);
 			msg.write("\n\n");
+			String txt = getUsageText();
+			if (!StringUtils.isEmpty(txt)) {
+				msg.write(txt);
+				msg.write("\n\n");
+			}
 			msg.write("ARGUMENTS & OPTIONS\n");
 			msg.write("───────────────────\n");
 			clp.printUsage(msg, null);
@@ -254,6 +259,10 @@
 		return "";
 	}
 
+	protected String getUsageText() {
+		return "";
+	}
+
 	protected String examples(UsageExample... examples) {
 		int sshPort = getContext().getGitblit().getSettings().getInteger(Keys.git.sshPort, 29418);
 		String username = getContext().getClient().getUsername();
diff --git a/src/main/java/com/gitblit/transport/ssh/gitblit/TeamsDispatcher.java b/src/main/java/com/gitblit/transport/ssh/gitblit/TeamsDispatcher.java
index 491cf6a..710ec4e 100644
--- a/src/main/java/com/gitblit/transport/ssh/gitblit/TeamsDispatcher.java
+++ b/src/main/java/com/gitblit/transport/ssh/gitblit/TeamsDispatcher.java
@@ -15,12 +15,14 @@
  */
 package com.gitblit.transport.ssh.gitblit;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import org.kohsuke.args4j.Argument;
 import org.kohsuke.args4j.Option;
 
 import com.gitblit.Constants.AccessPermission;
+import com.gitblit.GitBlitException;
 import com.gitblit.manager.IGitblit;
 import com.gitblit.models.RegistrantAccessPermission;
 import com.gitblit.models.RepositoryModel;
@@ -45,11 +47,13 @@
 	protected void setup(UserModel user) {
 		// primary team commands
 		register(user, NewTeam.class);
+		register(user, RenameTeam.class);
 		register(user, RemoveTeam.class);
 		register(user, ShowTeam.class);
 		register(user, ListTeams.class);
 
 		// team-specific commands
+		register(user, SetField.class);
 		register(user, Permissions.class);
 		register(user, Members.class);
 	}
@@ -94,11 +98,134 @@
 			team.canCreate = canCreate;
 
 			IGitblit gitblit = getContext().getGitblit();
-			if (gitblit.updateTeamModel(teamname, team)) {
+			try {
+				gitblit.addTeam(team);
 				stdout.println(String.format("%s created.", teamname));
-			} else {
-				throw new UnloggedFailure(1, String.format("Failed to create %s!", teamname));
+			} catch (GitBlitException e) {
+				String msg = String.format("Failed to create %s!", teamname);
+				log.error(msg, e);
+				throw new UnloggedFailure(1, msg);
 			}
+		}
+	}
+
+	@CommandMetaData(name = "rename", aliases = { "mv" }, description = "Rename a team")
+	@UsageExample(syntax = "${cmd} contributors friends", description = "Rename the contributors team to the friends team")
+	public static class RenameTeam extends TeamCommand {
+		@Argument(index = 1, required = true, metaVar = "NEWNAME", usage = "the new team name")
+		protected String newTeamName;
+
+				@Override
+		public void run() throws UnloggedFailure {
+			TeamModel team = getTeam(true);
+			IGitblit gitblit = getContext().getGitblit();
+			if (null != gitblit.getTeamModel(newTeamName)) {
+				throw new UnloggedFailure(1, String.format("Team %s already exists!", newTeamName));
+			}
+
+			// set the new team name
+			team.name = newTeamName;
+
+			try {
+				gitblit.reviseTeam(teamname, team);
+				stdout.println(String.format("Renamed team %s to %s.", teamname, newTeamName));
+			} catch (GitBlitException e) {
+				String msg = String.format("Failed to rename team from %s to %s", teamname, newTeamName);
+				log.error(msg, e);
+				throw new UnloggedFailure(1, msg);
+			}
+		}
+	}
+
+	@CommandMetaData(name = "set", description = "Set the specified field of a team")
+	@UsageExample(syntax = "${cmd} contributors canFork true", description = "Allow the contributors team to fork repositories")
+	public static class SetField extends TeamCommand {
+
+		@Argument(index = 1, required = true, metaVar = "FIELD", usage = "the field to update")
+		protected String fieldName;
+
+		@Argument(index = 2, required = true, metaVar = "VALUE", usage = "the new value")
+		protected List<String> fieldValues = new ArrayList<String>();
+
+		protected enum Field {
+			mailingList, canAdmin, canFork, canCreate;
+
+			static Field fromString(String name) {
+				for (Field field : values()) {
+					if (field.name().equalsIgnoreCase(name)) {
+						return field;
+					}
+				}
+				return null;
+			}
+		}
+
+		@Override
+		protected String getUsageText() {
+			String fields = Joiner.on(", ").join(Field.values());
+			StringBuilder sb = new StringBuilder();
+			sb.append("Valid fields are:\n    ").append(fields);
+			return sb.toString();
+		}
+
+		@Override
+		public void run() throws UnloggedFailure {
+			TeamModel team = getTeam(true);
+
+			Field field = Field.fromString(fieldName);
+			if (field == null) {
+				throw new UnloggedFailure(1, String.format("Unknown field %s", fieldName));
+			}
+
+			String value = Joiner.on(" ").join(fieldValues);
+			IGitblit gitblit = getContext().getGitblit();
+
+			switch(field) {
+			case mailingList:
+				team.mailingLists.clear();
+				team.mailingLists.addAll(fieldValues);
+				break;
+			case canAdmin:
+				team.canAdmin = toBool(value);
+				break;
+			case canFork:
+				team.canFork = toBool(value);
+				break;
+			case canCreate:
+				team.canCreate = toBool(value);
+				break;
+			default:
+				throw new UnloggedFailure(1,  String.format("Field %s was not properly handled by the set command.", fieldName));
+			}
+
+			try {
+				gitblit.reviseTeam(teamname, team);
+				stdout.println(String.format("Set %s.%s = %s", teamname, fieldName, value));
+			} catch (GitBlitException e) {
+				String msg = String.format("Failed to set %s.%s = %s", teamname, fieldName, value);
+				log.error(msg, e);
+				throw new UnloggedFailure(1, msg);
+			}
+		}
+
+		protected boolean toBool(String value) throws UnloggedFailure {
+			String v = value.toLowerCase();
+			if (v.equals("t")
+					|| v.equals("true")
+					|| v.equals("yes")
+					|| v.equals("on")
+					|| v.equals("y")
+					|| v.equals("1")) {
+				return true;
+			} else if (v.equals("f")
+					|| v.equals("false")
+					|| v.equals("no")
+					|| v.equals("off")
+					|| v.equals("n")
+					|| v.equals("0")) {
+				return false;
+			}
+			throw new UnloggedFailure(1,  String.format("Invalid boolean value %s", value));
 		}
 	}
 
@@ -182,6 +309,12 @@
 			IGitblit gitblit = getContext().getGitblit();
 			TeamModel team = getTeam(true);
 
+			boolean canEditMemberships = gitblit.supportsTeamMembershipChanges(team);
+			if (!canEditMemberships) {
+				String msg = String.format("Team %s (%s) does not permit membership changes!", team.name, team.accountType);
+				throw new UnloggedFailure(1, msg);
+			}
+
 			boolean modified = false;
 			if (!ArrayUtils.isEmpty(removals)) {
 				if (removals.contains("ALL")) {
@@ -201,6 +334,11 @@
 					if (u == null) {
 						throw new UnloggedFailure(1,  String.format("Unknown user %s", username));
 					}
+					boolean canEditTeams = gitblit.supportsTeamMembershipChanges(u);
+					if (!canEditTeams) {
+						String msg = String.format("User %s (%s) does not allow team membership changes ", u.username, u.accountType);
+						throw new UnloggedFailure(1, msg);
+					}
 					team.addUser(username);
 				}
 				modified = true;
diff --git a/src/main/java/com/gitblit/transport/ssh/gitblit/UsersDispatcher.java b/src/main/java/com/gitblit/transport/ssh/gitblit/UsersDispatcher.java
index 732eba2..4f604a2 100644
--- a/src/main/java/com/gitblit/transport/ssh/gitblit/UsersDispatcher.java
+++ b/src/main/java/com/gitblit/transport/ssh/gitblit/UsersDispatcher.java
@@ -22,6 +22,8 @@
 import org.kohsuke.args4j.Option;
 
 import com.gitblit.Constants.AccessPermission;
+import com.gitblit.GitBlitException;
+import com.gitblit.Keys;
 import com.gitblit.manager.IGitblit;
 import com.gitblit.models.RegistrantAccessPermission;
 import com.gitblit.models.RepositoryModel;
@@ -46,12 +48,13 @@
 	protected void setup(UserModel user) {
 		// primary user commands
 		register(user, NewUser.class);
+		register(user, RenameUser.class);
 		register(user, RemoveUser.class);
 		register(user, ShowUser.class);
 		register(user, ListUsers.class);
 
 		// user-specific commands
-		register(user, SetName.class);
+		register(user, SetField.class);
 		register(user, Permissions.class);
 		register(user, DisableUser.class);
 		register(user, EnableUser.class);
@@ -113,32 +116,175 @@
 			user.disabled = disabled;
 
 			IGitblit gitblit = getContext().getGitblit();
-			if (gitblit.updateUserModel(username, user)) {
+			try {
+				gitblit.addUser(user);
 				stdout.println(String.format("%s created.", username));
-			} else {
-				throw new UnloggedFailure(1, String.format("Failed to create %s!", username));
+			} catch (GitBlitException e) {
+				log.error("Failed to add " + username, e);
+				throw new UnloggedFailure(1, e.getMessage());
 			}
 		}
 	}
 
-	@CommandMetaData(name = "set-name", description = "Set the display name of an account")
-	@UsageExample(syntax = "${cmd} john John Smith", description = "The display name to \"John Smith\" for john's account")
-	public static class SetName extends UserCommand {
+	@CommandMetaData(name = "rename", aliases = { "mv" }, description = "Rename an account")
+	@UsageExample(syntax = "${cmd} john frank", description = "Rename the account from john to frank")
+	public static class RenameUser extends UserCommand {
+		@Argument(index = 1, required = true, metaVar = "NEWNAME", usage = "the new account name")
+		protected String newUserName;
 
-		@Argument(index = 1, multiValued = true, required = true, metaVar = "NAME", usage = "display name")
-		protected List<String> displayName = new ArrayList<String>();
+				@Override
+		public void run() throws UnloggedFailure {
+			UserModel user = getUser(true);
+			IGitblit gitblit = getContext().getGitblit();
+			if (null != gitblit.getTeamModel(newUserName)) {
+				throw new UnloggedFailure(1, String.format("Team %s already exists!", newUserName));
+			}
+
+			// set the new name
+			user.username = newUserName;
+
+			try {
+				gitblit.reviseUser(username, user);
+				stdout.println(String.format("Renamed user %s to %s.", username, newUserName));
+			} catch (GitBlitException e) {
+				String msg = String.format("Failed to rename user from %s to %s", username, newUserName);
+				log.error(msg, e);
+				throw new UnloggedFailure(1, msg);
+			}
+		}
+	}
+
+	@CommandMetaData(name = "set", description = "Set the specified field of an account")
+	@UsageExample(syntax = "${cmd} john name John Smith", description = "Set the display name to \"John Smith\" for john's account")
+	public static class SetField extends UserCommand {
+
+		@Argument(index = 1, required = true, metaVar = "FIELD", usage = "the field to update")
+		protected String fieldName;
+
+		@Argument(index = 2, required = true, metaVar = "VALUE", usage = "the new value")
+		protected List<String> fieldValues = new ArrayList<String>();
+
+		protected enum Field {
+			name, displayName, email, password, canAdmin, canFork, canCreate;
+
+			static Field fromString(String name) {
+				for (Field field : values()) {
+					if (field.name().equalsIgnoreCase(name)) {
+						return field;
+					}
+				}
+				return null;
+			}
+		}
+
+		@Override
+		protected String getUsageText() {
+			String fields = Joiner.on(", ").join(Field.values());
+			StringBuilder sb = new StringBuilder();
+			sb.append("Valid fields are:\n   ").append(fields);
+			return sb.toString();
+		}
 
 		@Override
 		public void run() throws UnloggedFailure {
 			UserModel user = getUser(true);
 
-			IGitblit gitblit = getContext().getGitblit();
-			user.displayName = Joiner.on(" ").join(displayName);
-			if (gitblit.updateUserModel(username, user)) {
-				stdout.println(String.format("Set the display name of %s to \"%s\".", username, user.displayName));
-			} else {
-				throw new UnloggedFailure(1, String.format("Failed to set the display name of %s!", username));
+			Field field = Field.fromString(fieldName);
+			if (field == null) {
+				throw new UnloggedFailure(1, String.format("Unknown field %s", fieldName));
 			}
+
+			String value = Joiner.on(" ").join(fieldValues).trim();
+			IGitblit gitblit = getContext().getGitblit();
+
+			boolean editCredentials = gitblit.supportsCredentialChanges(user);
+			boolean editDisplayName = gitblit.supportsDisplayNameChanges(user);
+			boolean editEmailAddress = gitblit.supportsEmailAddressChanges(user);
+
+			String m = String.format("Can not edit %s for %s (%s)", field, user.username, user.accountType);
+
+			switch(field) {
+			case name:
+			case displayName:
+				if (!editDisplayName) {
+					throw new UnloggedFailure(1, m);
+				}
+				user.displayName = value;
+				break;
+			case email:
+				if (!editEmailAddress) {
+					throw new UnloggedFailure(1, m);
+				}
+				user.emailAddress = value;
+				break;
+			case password:
+				if (!editCredentials) {
+					throw new UnloggedFailure(1, m);
+				}
+				int minLength = gitblit.getSettings().getInteger(Keys.realm.minPasswordLength, 5);
+				if (minLength < 4) {
+					minLength = 4;
+				}
+				if (value.trim().length() < minLength) {
+					throw new UnloggedFailure(1,  "Password is too short.");
+				}
+
+				// Optionally store the password MD5 digest.
+				String type = gitblit.getSettings().getString(Keys.realm.passwordStorage, "md5");
+				if (type.equalsIgnoreCase("md5")) {
+					// store MD5 digest of password
+					user.password = StringUtils.MD5_TYPE + StringUtils.getMD5(value);
+				} else if (type.equalsIgnoreCase("combined-md5")) {
+					// store MD5 digest of username+password
+					user.password = StringUtils.COMBINED_MD5_TYPE + StringUtils.getMD5(username + value);
+				} else {
+					user.password = value;
+				}
+
+				// reset the cookie
+				user.cookie = StringUtils.getSHA1(user.username + value);
+				break;
+			case canAdmin:
+				user.canAdmin = toBool(value);
+				break;
+			case canFork:
+				user.canFork = toBool(value);
+				break;
+			case canCreate:
+				user.canCreate = toBool(value);
+				break;
+			default:
+				throw new UnloggedFailure(1,  String.format("Field %s was not properly handled by the set command.", fieldName));
+			}
+
+			try {
+				gitblit.reviseUser(username, user);
+				stdout.println(String.format("Set %s.%s = %s", username, fieldName, value));
+			} catch (GitBlitException e) {
+				String msg = String.format("Failed to set %s.%s = %s", username, fieldName, value);
+				log.error(msg, e);
+				throw new UnloggedFailure(1, msg);
+			}
+		}
+
+		protected boolean toBool(String value) throws UnloggedFailure {
+			String v = value.toLowerCase();
+			if (v.equals("t")
+					|| v.equals("true")
+					|| v.equals("yes")
+					|| v.equals("on")
+					|| v.equals("y")
+					|| v.equals("1")) {
+				return true;
+			} else if (v.equals("f")
+					|| v.equals("false")
+					|| v.equals("no")
+					|| v.equals("off")
+					|| v.equals("n")
+					|| v.equals("0")) {
+				return false;
+			}
+			throw new UnloggedFailure(1,  String.format("Invalid boolean value %s", value));
 		}
 	}
 

--
Gitblit v1.9.1