| | |
| | | */ |
| | | package com.gitblit.transport.ssh.gitblit; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | | import java.util.List; |
| | | |
| | | import org.kohsuke.args4j.Argument; |
| | | |
| | | import com.gitblit.GitBlitException; |
| | | import com.gitblit.Keys; |
| | | import com.gitblit.Constants.AccessRestrictionType; |
| | | import com.gitblit.Constants.AuthorizationControl; |
| | | import com.gitblit.manager.IGitblit; |
| | | import com.gitblit.models.RegistrantAccessPermission; |
| | | import com.gitblit.models.RepositoryModel; |
| | | import com.gitblit.models.UserModel; |
| | | import com.gitblit.transport.ssh.commands.CommandMetaData; |
| | | import com.gitblit.transport.ssh.commands.DispatchCommand; |
| | | import com.gitblit.transport.ssh.commands.ListFilterCommand; |
| | | import com.gitblit.transport.ssh.commands.SshCommand; |
| | | import com.gitblit.transport.ssh.commands.UsageExample; |
| | | import com.gitblit.utils.ArrayUtils; |
| | | import com.gitblit.utils.FlipTable; |
| | | import com.gitblit.utils.FlipTable.Borders; |
| | | import com.gitblit.utils.StringUtils; |
| | | import com.google.common.base.Joiner; |
| | | |
| | | @CommandMetaData(name = "repositories", aliases = { "repos" }, description = "Repository management commands") |
| | |
| | | |
| | | @Override |
| | | protected void setup(UserModel user) { |
| | | // primary commands |
| | | register(user, NewRepository.class); |
| | | register(user, RenameRepository.class); |
| | | register(user, RemoveRepository.class); |
| | | register(user, ShowRepository.class); |
| | | register(user, ListRepositories.class); |
| | | |
| | | // repository-specific commands |
| | | register(user, SetField.class); |
| | | } |
| | | |
| | | public static abstract class RepositoryCommand extends SshCommand { |
| | | @Argument(index = 0, required = true, metaVar = "REPOSITORY", usage = "repository") |
| | | protected String repository; |
| | | |
| | | protected RepositoryModel getRepository(boolean requireRepository) throws UnloggedFailure { |
| | | IGitblit gitblit = getContext().getGitblit(); |
| | | RepositoryModel repo = gitblit.getRepositoryModel(repository); |
| | | if (requireRepository && repo == null) { |
| | | throw new UnloggedFailure(1, String.format("Repository %s does not exist!", repository)); |
| | | } |
| | | return repo; |
| | | } |
| | | |
| | | protected String sanitize(String name) throws UnloggedFailure { |
| | | // automatically convert backslashes to forward slashes |
| | | name = name.replace('\\', '/'); |
| | | // Automatically replace // with / |
| | | name = name.replace("//", "/"); |
| | | |
| | | // prohibit folder paths |
| | | if (name.startsWith("/")) { |
| | | throw new UnloggedFailure(1, "Illegal leading slash"); |
| | | } |
| | | if (name.startsWith("../")) { |
| | | throw new UnloggedFailure(1, "Illegal relative slash"); |
| | | } |
| | | if (name.contains("/../")) { |
| | | throw new UnloggedFailure(1, "Illegal relative slash"); |
| | | } |
| | | if (name.endsWith("/")) { |
| | | name = name.substring(0, name.length() - 1); |
| | | } |
| | | return name; |
| | | } |
| | | } |
| | | |
| | | @CommandMetaData(name = "new", aliases = { "add" }, description = "Create a new repository") |
| | | @UsageExample(syntax = "${cmd} myRepo") |
| | | public static class NewRepository extends RepositoryCommand { |
| | | |
| | | @Override |
| | | public void run() throws UnloggedFailure { |
| | | |
| | | UserModel user = getContext().getClient().getUser(); |
| | | |
| | | String name = sanitize(repository); |
| | | |
| | | if (!user.canCreate(name)) { |
| | | // try to prepend personal path |
| | | String path = StringUtils.getFirstPathElement(name); |
| | | if ("".equals(path)) { |
| | | name = user.getPersonalPath() + "/" + name; |
| | | } |
| | | } |
| | | |
| | | if (getRepository(false) != null) { |
| | | throw new UnloggedFailure(1, String.format("Repository %s already exists!", name)); |
| | | } |
| | | |
| | | if (!user.canCreate(name)) { |
| | | throw new UnloggedFailure(1, String.format("Sorry, you do not have permission to create %s", name)); |
| | | } |
| | | |
| | | IGitblit gitblit = getContext().getGitblit(); |
| | | |
| | | RepositoryModel repo = new RepositoryModel(); |
| | | repo.name = name; |
| | | repo.projectPath = StringUtils.getFirstPathElement(name); |
| | | String restriction = gitblit.getSettings().getString(Keys.git.defaultAccessRestriction, "PUSH"); |
| | | repo.accessRestriction = AccessRestrictionType.fromName(restriction); |
| | | String authorization = gitblit.getSettings().getString(Keys.git.defaultAuthorizationControl, null); |
| | | repo.authorizationControl = AuthorizationControl.fromName(authorization); |
| | | |
| | | if (user.isMyPersonalRepository(name)) { |
| | | // personal repositories are private by default |
| | | repo.addOwner(user.username); |
| | | repo.accessRestriction = AccessRestrictionType.VIEW; |
| | | repo.authorizationControl = AuthorizationControl.NAMED; |
| | | } |
| | | |
| | | try { |
| | | gitblit.updateRepositoryModel(repository, repo, true); |
| | | stdout.println(String.format("%s created.", repo.name)); |
| | | } catch (GitBlitException e) { |
| | | log.error("Failed to add " + repository, e); |
| | | throw new UnloggedFailure(1, e.getMessage()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @CommandMetaData(name = "rename", aliases = { "mv" }, description = "Rename a repository") |
| | | @UsageExample(syntax = "${cmd} myRepo.git otherRepo.git", description = "Rename the repository from myRepo.git to otherRepo.git") |
| | | public static class RenameRepository extends RepositoryCommand { |
| | | @Argument(index = 1, required = true, metaVar = "NEWNAME", usage = "the new repository name") |
| | | protected String newRepositoryName; |
| | | |
| | | @Override |
| | | public void run() throws UnloggedFailure { |
| | | RepositoryModel repo = getRepository(true); |
| | | IGitblit gitblit = getContext().getGitblit(); |
| | | UserModel user = getContext().getClient().getUser(); |
| | | |
| | | String name = sanitize(newRepositoryName); |
| | | if (!user.canCreate(name)) { |
| | | // try to prepend personal path |
| | | String path = StringUtils.getFirstPathElement(name); |
| | | if ("".equals(path)) { |
| | | name = user.getPersonalPath() + "/" + name; |
| | | } |
| | | } |
| | | |
| | | if (null != gitblit.getRepositoryModel(name)) { |
| | | throw new UnloggedFailure(1, String.format("Repository %s already exists!", name)); |
| | | } |
| | | |
| | | if (repo.name.equalsIgnoreCase(name)) { |
| | | throw new UnloggedFailure(1, "Repository names are identical"); |
| | | } |
| | | |
| | | if (!user.canAdmin(repo)) { |
| | | throw new UnloggedFailure(1, String.format("Sorry, you do not have permission to rename %s", repository)); |
| | | } |
| | | |
| | | if (!user.canCreate(name)) { |
| | | throw new UnloggedFailure(1, String.format("Sorry, you don't have permission to move %s to %s/", repository, name)); |
| | | } |
| | | |
| | | // set the new name |
| | | repo.name = name; |
| | | |
| | | try { |
| | | gitblit.updateRepositoryModel(repository, repo, false); |
| | | stdout.println(String.format("Renamed repository %s to %s.", repository, name)); |
| | | } catch (GitBlitException e) { |
| | | String msg = String.format("Failed to rename repository from %s to %s", repository, name); |
| | | log.error(msg, e); |
| | | throw new UnloggedFailure(1, msg); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @CommandMetaData(name = "set", description = "Set the specified field of a repository") |
| | | @UsageExample(syntax = "${cmd} myRepo description John's personal projects", description = "Set the description of a repository") |
| | | public static class SetField extends RepositoryCommand { |
| | | |
| | | @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 { |
| | | description; |
| | | |
| | | 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 { |
| | | RepositoryModel repo = getRepository(true); |
| | | |
| | | Field field = Field.fromString(fieldName); |
| | | if (field == null) { |
| | | throw new UnloggedFailure(1, String.format("Unknown field %s", fieldName)); |
| | | } |
| | | |
| | | if (!getContext().getClient().getUser().canAdmin(repo)) { |
| | | throw new UnloggedFailure(1, String.format("Sorry, you do not have permission to administer %s", repository)); |
| | | } |
| | | |
| | | String value = Joiner.on(" ").join(fieldValues).trim(); |
| | | IGitblit gitblit = getContext().getGitblit(); |
| | | |
| | | switch(field) { |
| | | case description: |
| | | repo.description = value; |
| | | break; |
| | | default: |
| | | throw new UnloggedFailure(1, String.format("Field %s was not properly handled by the set command.", fieldName)); |
| | | } |
| | | |
| | | try { |
| | | gitblit.updateRepositoryModel(repo.name, repo, false); |
| | | stdout.println(String.format("Set %s.%s = %s", repo.name, fieldName, value)); |
| | | } catch (GitBlitException e) { |
| | | String msg = String.format("Failed to set %s.%s = %s", repo.name, 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)); |
| | | } |
| | | } |
| | | |
| | | @CommandMetaData(name = "remove", aliases = { "rm" }, description = "Remove a repository") |
| | | @UsageExample(syntax = "${cmd} myRepo.git", description = "Delete myRepo.git") |
| | | public static class RemoveRepository extends RepositoryCommand { |
| | | |
| | | @Override |
| | | public void run() throws UnloggedFailure { |
| | | |
| | | RepositoryModel repo = getRepository(true); |
| | | |
| | | if (!getContext().getClient().getUser().canAdmin(repo)) { |
| | | throw new UnloggedFailure(1, String.format("Sorry, you do not have permission to delete %s", repository)); |
| | | } |
| | | |
| | | IGitblit gitblit = getContext().getGitblit(); |
| | | if (gitblit.deleteRepositoryModel(repo)) { |
| | | stdout.println(String.format("%s has been deleted.", repository)); |
| | | } else { |
| | | throw new UnloggedFailure(1, String.format("Failed to delete %s!", repository)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @CommandMetaData(name = "show", description = "Show the details of a repository") |
| | | @UsageExample(syntax = "${cmd} myRepo.git", description = "Display myRepo.git") |
| | | public static class ShowRepository extends RepositoryCommand { |
| | | |
| | | @Override |
| | | public void run() throws UnloggedFailure { |
| | | |
| | | RepositoryModel r = getRepository(true); |
| | | |
| | | if (!getContext().getClient().getUser().canAdmin(r)) { |
| | | throw new UnloggedFailure(1, String.format("Sorry, you do not have permission to see the %s settings.", repository)); |
| | | } |
| | | |
| | | IGitblit gitblit = getContext().getGitblit(); |
| | | |
| | | // fields |
| | | StringBuilder fb = new StringBuilder(); |
| | | fb.append("Description : ").append(toString(r.description)).append('\n'); |
| | | fb.append("Origin : ").append(toString(r.origin)).append('\n'); |
| | | fb.append("Default Branch : ").append(toString(r.HEAD)).append('\n'); |
| | | fb.append('\n'); |
| | | fb.append("GC Period : ").append(r.gcPeriod).append('\n'); |
| | | fb.append("GC Threshold : ").append(r.gcThreshold).append('\n'); |
| | | fb.append('\n'); |
| | | fb.append("Accept Tickets : ").append(toString(r.acceptNewTickets)).append('\n'); |
| | | fb.append("Accept Patchsets : ").append(toString(r.acceptNewPatchsets)).append('\n'); |
| | | fb.append("Require Approval : ").append(toString(r.requireApproval)).append('\n'); |
| | | fb.append("Merge To : ").append(toString(r.mergeTo)).append('\n'); |
| | | fb.append('\n'); |
| | | fb.append("Incremental push tags : ").append(toString(r.useIncrementalPushTags)).append('\n'); |
| | | fb.append("Show remote branches : ").append(toString(r.showRemoteBranches)).append('\n'); |
| | | fb.append("Skip size calculations : ").append(toString(r.skipSizeCalculation)).append('\n'); |
| | | fb.append("Skip summary metrics : ").append(toString(r.skipSummaryMetrics)).append('\n'); |
| | | fb.append("Max activity commits : ").append(r.maxActivityCommits).append('\n'); |
| | | fb.append("Author metric exclusions : ").append(toString(r.metricAuthorExclusions)).append('\n'); |
| | | fb.append("Commit Message Renderer : ").append(r.commitMessageRenderer).append('\n'); |
| | | fb.append("Mailing Lists : ").append(toString(r.mailingLists)).append('\n'); |
| | | fb.append('\n'); |
| | | fb.append("Access Restriction : ").append(r.accessRestriction).append('\n'); |
| | | fb.append("Authorization Control : ").append(r.authorizationControl).append('\n'); |
| | | fb.append('\n'); |
| | | fb.append("Is Frozen : ").append(toString(r.isFrozen)).append('\n'); |
| | | fb.append("Allow Forks : ").append(toString(r.allowForks)).append('\n'); |
| | | fb.append("Verify Committer : ").append(toString(r.verifyCommitter)).append('\n'); |
| | | fb.append('\n'); |
| | | fb.append("Federation Strategy : ").append(r.federationStrategy).append('\n'); |
| | | fb.append("Federation Sets : ").append(toString(r.federationSets)).append('\n'); |
| | | fb.append('\n'); |
| | | fb.append("Indexed Branches : ").append(toString(r.indexedBranches)).append('\n'); |
| | | fb.append('\n'); |
| | | fb.append("Pre-Receive Scripts : ").append(toString(r.preReceiveScripts)).append('\n'); |
| | | fb.append(" inherited : ").append(toString(gitblit.getPreReceiveScriptsInherited(r))).append('\n'); |
| | | fb.append("Post-Receive Scripts : ").append(toString(r.postReceiveScripts)).append('\n'); |
| | | fb.append(" inherited : ").append(toString(gitblit.getPostReceiveScriptsInherited(r))).append('\n'); |
| | | String fields = fb.toString(); |
| | | |
| | | // owners |
| | | String owners; |
| | | if (r.owners.isEmpty()) { |
| | | owners = FlipTable.EMPTY; |
| | | } else { |
| | | String[] pheaders = { "Account", "Name" }; |
| | | Object [][] pdata = new Object[r.owners.size()][]; |
| | | for (int i = 0; i < r.owners.size(); i++) { |
| | | String owner = r.owners.get(i); |
| | | UserModel u = gitblit.getUserModel(owner); |
| | | pdata[i] = new Object[] { owner, u == null ? "" : u.getDisplayName() }; |
| | | } |
| | | owners = FlipTable.of(pheaders, pdata, Borders.COLS); |
| | | } |
| | | |
| | | // team permissions |
| | | List<RegistrantAccessPermission> tperms = gitblit.getTeamAccessPermissions(r); |
| | | String tpermissions; |
| | | if (tperms.isEmpty()) { |
| | | tpermissions = FlipTable.EMPTY; |
| | | } else { |
| | | String[] pheaders = { "Team", "Permission", "Type" }; |
| | | Object [][] pdata = new Object[tperms.size()][]; |
| | | for (int i = 0; i < tperms.size(); i++) { |
| | | RegistrantAccessPermission ap = tperms.get(i); |
| | | pdata[i] = new Object[] { ap.registrant, ap.permission, ap.permissionType }; |
| | | } |
| | | tpermissions = FlipTable.of(pheaders, pdata, Borders.COLS); |
| | | } |
| | | |
| | | // user permissions |
| | | List<RegistrantAccessPermission> uperms = gitblit.getUserAccessPermissions(r); |
| | | String upermissions; |
| | | if (uperms.isEmpty()) { |
| | | upermissions = FlipTable.EMPTY; |
| | | } else { |
| | | String[] pheaders = { "Account", "Name", "Permission", "Type", "Source", "Mutable" }; |
| | | Object [][] pdata = new Object[uperms.size()][]; |
| | | for (int i = 0; i < uperms.size(); i++) { |
| | | RegistrantAccessPermission ap = uperms.get(i); |
| | | String name = ""; |
| | | try { |
| | | String dn = gitblit.getUserModel(ap.registrant).displayName; |
| | | if (dn != null) { |
| | | name = dn; |
| | | } |
| | | } catch (Exception e) { |
| | | } |
| | | pdata[i] = new Object[] { ap.registrant, name, ap.permission, ap.permissionType, ap.source, ap.mutable ? "Y":"" }; |
| | | } |
| | | upermissions = FlipTable.of(pheaders, pdata, Borders.COLS); |
| | | } |
| | | |
| | | // assemble table |
| | | String title = r.name; |
| | | String [] headers = new String[] { title }; |
| | | String[][] data = new String[8][]; |
| | | data[0] = new String [] { "FIELDS" }; |
| | | data[1] = new String [] {fields }; |
| | | data[2] = new String [] { "OWNERS" }; |
| | | data[3] = new String [] { owners }; |
| | | data[4] = new String [] { "TEAM PERMISSIONS" }; |
| | | data[5] = new String [] { tpermissions }; |
| | | data[6] = new String [] { "USER PERMISSIONS" }; |
| | | data[7] = new String [] { upermissions }; |
| | | stdout.println(FlipTable.of(headers, data)); |
| | | } |
| | | |
| | | protected String toString(String val) { |
| | | if (val == null) { |
| | | return ""; |
| | | } |
| | | return val; |
| | | } |
| | | |
| | | protected String toString(Collection<?> collection) { |
| | | if (collection == null) { |
| | | return ""; |
| | | } |
| | | return Joiner.on(", ").join(collection); |
| | | } |
| | | |
| | | protected String toString(boolean val) { |
| | | if (val) { |
| | | return "Y"; |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | } |
| | | |
| | | /* List repositories */ |