diff options
Diffstat (limited to 'gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java')
-rw-r--r-- | gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java | 612 |
1 files changed, 0 insertions, 612 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java deleted file mode 100644 index 9ed04b0c43..0000000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java +++ /dev/null @@ -1,612 +0,0 @@ -// Copyright (C) 2009 The Android Open Source Project -// -// 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.google.gerrit.server.project; - -import static com.google.gerrit.extensions.client.ProjectState.HIDDEN; -import static java.nio.charset.StandardCharsets.UTF_8; - -import com.google.common.base.Strings; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; -import com.google.gerrit.common.Nullable; -import com.google.gerrit.common.data.GroupReference; -import com.google.gerrit.common.errors.NoSuchGroupException; -import com.google.gerrit.extensions.common.ProjectInfo; -import com.google.gerrit.extensions.common.WebLinkInfo; -import com.google.gerrit.extensions.restapi.AuthException; -import com.google.gerrit.extensions.restapi.BadRequestException; -import com.google.gerrit.extensions.restapi.BinaryResult; -import com.google.gerrit.extensions.restapi.RestReadView; -import com.google.gerrit.extensions.restapi.TopLevelResource; -import com.google.gerrit.extensions.restapi.Url; -import com.google.gerrit.reviewdb.client.AccountGroup; -import com.google.gerrit.reviewdb.client.Project; -import com.google.gerrit.reviewdb.client.RefNames; -import com.google.gerrit.server.CurrentUser; -import com.google.gerrit.server.OutputFormat; -import com.google.gerrit.server.StringUtil; -import com.google.gerrit.server.WebLinks; -import com.google.gerrit.server.account.GroupControl; -import com.google.gerrit.server.git.GitRepositoryManager; -import com.google.gerrit.server.group.GroupsCollection; -import com.google.gerrit.server.permissions.PermissionBackend; -import com.google.gerrit.server.permissions.PermissionBackendException; -import com.google.gerrit.server.permissions.ProjectPermission; -import com.google.gerrit.server.permissions.RefPermission; -import com.google.gerrit.server.util.RegexListSearcher; -import com.google.gerrit.server.util.TreeFormatter; -import com.google.gson.reflect.TypeToken; -import com.google.inject.Inject; -import java.io.BufferedWriter; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.SortedMap; -import java.util.SortedSet; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; -import org.eclipse.jgit.errors.RepositoryNotFoundException; -import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.Ref; -import org.eclipse.jgit.lib.Repository; -import org.kohsuke.args4j.Option; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** List projects visible to the calling user. */ -public class ListProjects implements RestReadView<TopLevelResource> { - private static final Logger log = LoggerFactory.getLogger(ListProjects.class); - - public enum FilterType { - CODE { - @Override - boolean matches(Repository git) throws IOException { - return !PERMISSIONS.matches(git); - } - - @Override - boolean useMatch() { - return true; - } - }, - PARENT_CANDIDATES { - @Override - boolean matches(Repository git) { - return true; - } - - @Override - boolean useMatch() { - return false; - } - }, - PERMISSIONS { - @Override - boolean matches(Repository git) throws IOException { - Ref head = git.getRefDatabase().exactRef(Constants.HEAD); - return head != null - && head.isSymbolic() - && RefNames.REFS_CONFIG.equals(head.getLeaf().getName()); - } - - @Override - boolean useMatch() { - return true; - } - }, - ALL { - @Override - boolean matches(Repository git) { - return true; - } - - @Override - boolean useMatch() { - return false; - } - }; - - abstract boolean matches(Repository git) throws IOException; - - abstract boolean useMatch(); - } - - private final CurrentUser currentUser; - private final ProjectCache projectCache; - private final GroupsCollection groupsCollection; - private final GroupControl.Factory groupControlFactory; - private final GitRepositoryManager repoManager; - private final PermissionBackend permissionBackend; - private final ProjectNode.Factory projectNodeFactory; - private final WebLinks webLinks; - - @Deprecated - @Option(name = "--format", usage = "(deprecated) output format") - private OutputFormat format = OutputFormat.TEXT; - - @Option( - name = "--show-branch", - aliases = {"-b"}, - usage = "displays the sha of each project in the specified branch") - public void addShowBranch(String branch) { - showBranch.add(branch); - } - - @Option( - name = "--tree", - aliases = {"-t"}, - usage = - "displays project inheritance in a tree-like format\n" - + "this option does not work together with the show-branch option") - public void setShowTree(boolean showTree) { - this.showTree = showTree; - } - - @Option(name = "--type", usage = "type of project") - public void setFilterType(FilterType type) { - this.type = type; - } - - @Option( - name = "--description", - aliases = {"-d"}, - usage = "include description of project in list") - public void setShowDescription(boolean showDescription) { - this.showDescription = showDescription; - } - - @Option(name = "--all", usage = "display all projects that are accessible by the calling user") - public void setAll(boolean all) { - this.all = all; - } - - @Option( - name = "--limit", - aliases = {"-n"}, - metaVar = "CNT", - usage = "maximum number of projects to list") - public void setLimit(int limit) { - this.limit = limit; - } - - @Option( - name = "--start", - aliases = {"-S"}, - metaVar = "CNT", - usage = "number of projects to skip") - public void setStart(int start) { - this.start = start; - } - - @Option( - name = "--prefix", - aliases = {"-p"}, - metaVar = "PREFIX", - usage = "match project prefix") - public void setMatchPrefix(String matchPrefix) { - this.matchPrefix = matchPrefix; - } - - @Option( - name = "--match", - aliases = {"-m"}, - metaVar = "MATCH", - usage = "match project substring") - public void setMatchSubstring(String matchSubstring) { - this.matchSubstring = matchSubstring; - } - - @Option(name = "-r", metaVar = "REGEX", usage = "match project regex") - public void setMatchRegex(String matchRegex) { - this.matchRegex = matchRegex; - } - - @Option( - name = "--has-acl-for", - metaVar = "GROUP", - usage = "displays only projects on which access rights for this group are directly assigned") - public void setGroupUuid(AccountGroup.UUID groupUuid) { - this.groupUuid = groupUuid; - } - - private final List<String> showBranch = new ArrayList<>(); - private boolean showTree; - private FilterType type = FilterType.ALL; - private boolean showDescription; - private boolean all; - private int limit; - private int start; - private String matchPrefix; - private String matchSubstring; - private String matchRegex; - private AccountGroup.UUID groupUuid; - - @Inject - protected ListProjects( - CurrentUser currentUser, - ProjectCache projectCache, - GroupsCollection groupsCollection, - GroupControl.Factory groupControlFactory, - GitRepositoryManager repoManager, - PermissionBackend permissionBackend, - ProjectNode.Factory projectNodeFactory, - WebLinks webLinks) { - this.currentUser = currentUser; - this.projectCache = projectCache; - this.groupsCollection = groupsCollection; - this.groupControlFactory = groupControlFactory; - this.repoManager = repoManager; - this.permissionBackend = permissionBackend; - this.projectNodeFactory = projectNodeFactory; - this.webLinks = webLinks; - } - - public List<String> getShowBranch() { - return showBranch; - } - - public boolean isShowTree() { - return showTree; - } - - public boolean isShowDescription() { - return showDescription; - } - - public OutputFormat getFormat() { - return format; - } - - public ListProjects setFormat(OutputFormat fmt) { - format = fmt; - return this; - } - - @Override - public Object apply(TopLevelResource resource) - throws BadRequestException, PermissionBackendException { - if (format == OutputFormat.TEXT) { - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - display(buf); - return BinaryResult.create(buf.toByteArray()) - .setContentType("text/plain") - .setCharacterEncoding(UTF_8); - } - return apply(); - } - - public SortedMap<String, ProjectInfo> apply() - throws BadRequestException, PermissionBackendException { - format = OutputFormat.JSON; - return display(null); - } - - public SortedMap<String, ProjectInfo> display(@Nullable OutputStream displayOutputStream) - throws BadRequestException, PermissionBackendException { - if (groupUuid != null) { - try { - if (!groupControlFactory.controlFor(groupUuid).isVisible()) { - return Collections.emptySortedMap(); - } - } catch (NoSuchGroupException ex) { - return Collections.emptySortedMap(); - } - } - - PrintWriter stdout = null; - if (displayOutputStream != null) { - stdout = - new PrintWriter(new BufferedWriter(new OutputStreamWriter(displayOutputStream, UTF_8))); - } - - if (type == FilterType.PARENT_CANDIDATES) { - // Historically, PARENT_CANDIDATES implied showDescription. - showDescription = true; - } - - int foundIndex = 0; - int found = 0; - TreeMap<String, ProjectInfo> output = new TreeMap<>(); - Map<String, String> hiddenNames = new HashMap<>(); - Map<Project.NameKey, Boolean> accessibleParents = new HashMap<>(); - PermissionBackend.WithUser perm = permissionBackend.user(currentUser); - final TreeMap<Project.NameKey, ProjectNode> treeMap = new TreeMap<>(); - try { - Iterable<Project.NameKey> projectNames = filter(perm)::iterator; - for (Project.NameKey projectName : projectNames) { - final ProjectState e = projectCache.get(projectName); - if (e == null || (!all && e.getProject().getState() == HIDDEN)) { - // If we can't get it from the cache, pretend its not present. - // If all wasn't selected, and its HIDDEN, pretend its not present. - continue; - } - - final ProjectControl pctl = e.controlFor(currentUser); - if (groupUuid != null - && !pctl.getProjectState() - .getLocalGroups() - .contains(GroupReference.forGroup(groupsCollection.parseId(groupUuid.get())))) { - continue; - } - - ProjectInfo info = new ProjectInfo(); - if (showTree && !format.isJson()) { - treeMap.put(projectName, projectNodeFactory.create(pctl.getProject(), true)); - continue; - } - - info.name = projectName.get(); - if (showTree && format.isJson()) { - ProjectState parent = Iterables.getFirst(e.parents(), null); - if (parent != null) { - if (isParentAccessible(accessibleParents, perm, parent)) { - info.parent = parent.getName(); - } else { - info.parent = hiddenNames.get(parent.getName()); - if (info.parent == null) { - info.parent = "?-" + (hiddenNames.size() + 1); - hiddenNames.put(parent.getName(), info.parent); - } - } - } - } - - if (showDescription) { - info.description = Strings.emptyToNull(e.getProject().getDescription()); - } - info.state = e.getProject().getState(); - - try { - if (!showBranch.isEmpty()) { - try (Repository git = repoManager.openRepository(projectName)) { - if (!type.matches(git)) { - continue; - } - - List<Ref> refs = getBranchRefs(projectName, pctl); - if (!hasValidRef(refs)) { - continue; - } - - for (int i = 0; i < showBranch.size(); i++) { - Ref ref = refs.get(i); - if (ref != null && ref.getObjectId() != null) { - if (info.branches == null) { - info.branches = new LinkedHashMap<>(); - } - info.branches.put(showBranch.get(i), ref.getObjectId().name()); - } - } - } - } else if (!showTree && type.useMatch()) { - try (Repository git = repoManager.openRepository(projectName)) { - if (!type.matches(git)) { - continue; - } - } - } - } catch (RepositoryNotFoundException err) { - // If the Git repository is gone, the project doesn't actually exist anymore. - continue; - } catch (IOException err) { - log.warn("Unexpected error reading " + projectName, err); - continue; - } - - if (type != FilterType.PARENT_CANDIDATES) { - List<WebLinkInfo> links = webLinks.getProjectLinks(projectName.get()); - info.webLinks = links.isEmpty() ? null : links; - } - - if (foundIndex++ < start) { - continue; - } - if (limit > 0 && ++found > limit) { - break; - } - - if (stdout == null || format.isJson()) { - output.put(info.name, info); - continue; - } - - if (!showBranch.isEmpty()) { - for (String name : showBranch) { - String ref = info.branches != null ? info.branches.get(name) : null; - if (ref == null) { - // Print stub (forty '-' symbols) - ref = "----------------------------------------"; - } - stdout.print(ref); - stdout.print(' '); - } - } - stdout.print(info.name); - - if (info.description != null) { - // We still want to list every project as one-liners, hence escaping \n. - stdout.print(" - " + StringUtil.escapeString(info.description)); - } - stdout.print('\n'); - } - - for (ProjectInfo info : output.values()) { - info.id = Url.encode(info.name); - info.name = null; - } - if (stdout == null) { - return output; - } else if (format.isJson()) { - format - .newGson() - .toJson(output, new TypeToken<Map<String, ProjectInfo>>() {}.getType(), stdout); - stdout.print('\n'); - } else if (showTree && treeMap.size() > 0) { - printProjectTree(stdout, treeMap); - } - return null; - } finally { - if (stdout != null) { - stdout.flush(); - } - } - } - - private Stream<Project.NameKey> filter(PermissionBackend.WithUser perm) - throws BadRequestException { - Stream<Project.NameKey> matches = StreamSupport.stream(scan().spliterator(), false); - if (type == FilterType.PARENT_CANDIDATES) { - matches = - matches.map(projectCache::get).map(this::parentOf).filter(Objects::nonNull).sorted(); - } - return matches.filter(p -> perm.project(p).testOrFalse(ProjectPermission.ACCESS)); - } - - private Project.NameKey parentOf(ProjectState ps) { - if (ps == null) { - return null; - } - Project.NameKey parent = ps.getProject().getParent(); - if (parent != null) { - if (projectCache.get(parent) != null) { - return parent; - } - log.warn("parent project {} of project {} not found", parent.get(), ps.getName()); - } - return null; - } - - private boolean isParentAccessible( - Map<Project.NameKey, Boolean> checked, PermissionBackend.WithUser perm, ProjectState p) - throws PermissionBackendException { - Project.NameKey name = p.getNameKey(); - Boolean b = checked.get(name); - if (b == null) { - try { - perm.project(name).check(ProjectPermission.ACCESS); - b = true; - } catch (AuthException denied) { - b = false; - } - checked.put(name, b); - } - return b; - } - - private Iterable<Project.NameKey> scan() throws BadRequestException { - if (matchPrefix != null) { - checkMatchOptions(matchSubstring == null && matchRegex == null); - return projectCache.byName(matchPrefix); - } else if (matchSubstring != null) { - checkMatchOptions(matchPrefix == null && matchRegex == null); - return Iterables.filter( - projectCache.all(), - p -> p.get().toLowerCase(Locale.US).contains(matchSubstring.toLowerCase(Locale.US))); - } else if (matchRegex != null) { - checkMatchOptions(matchPrefix == null && matchSubstring == null); - RegexListSearcher<Project.NameKey> searcher; - try { - searcher = - new RegexListSearcher<Project.NameKey>(matchRegex) { - @Override - public String apply(Project.NameKey in) { - return in.get(); - } - }; - } catch (IllegalArgumentException e) { - throw new BadRequestException(e.getMessage()); - } - return searcher.search(ImmutableList.copyOf(projectCache.all())); - } else { - return projectCache.all(); - } - } - - private static void checkMatchOptions(boolean cond) throws BadRequestException { - if (!cond) { - throw new BadRequestException("specify exactly one of p/m/r"); - } - } - - private void printProjectTree( - final PrintWriter stdout, TreeMap<Project.NameKey, ProjectNode> treeMap) { - final SortedSet<ProjectNode> sortedNodes = new TreeSet<>(); - - // Builds the inheritance tree using a list. - // - for (ProjectNode key : treeMap.values()) { - if (key.isAllProjects()) { - sortedNodes.add(key); - continue; - } - - ProjectNode node = treeMap.get(key.getParentName()); - if (node != null) { - node.addChild(key); - } else { - sortedNodes.add(key); - } - } - - final TreeFormatter treeFormatter = new TreeFormatter(stdout); - treeFormatter.printTree(sortedNodes); - stdout.flush(); - } - - private List<Ref> getBranchRefs(Project.NameKey projectName, ProjectControl projectControl) { - Ref[] result = new Ref[showBranch.size()]; - try (Repository git = repoManager.openRepository(projectName)) { - PermissionBackend.ForProject perm = permissionBackend.user(currentUser).project(projectName); - for (int i = 0; i < showBranch.size(); i++) { - Ref ref = git.findRef(showBranch.get(i)); - if (all && projectControl.isOwner()) { - result[i] = ref; - } else if (ref != null && ref.getObjectId() != null) { - try { - perm.ref(ref.getLeaf().getName()).check(RefPermission.READ); - result[i] = ref; - } catch (AuthException e) { - continue; - } - } - } - } catch (IOException | PermissionBackendException e) { - // Fall through and return what is available. - } - return Arrays.asList(result); - } - - private static boolean hasValidRef(List<Ref> refs) { - for (Ref ref : refs) { - if (ref != null) { - return true; - } - } - return false; - } -} |