ls-projects: Add option to list projects on which a certain group is used

Add a --has-acl-for option to the ls-projects command that allows to
list only projects on which access rights for the specified group are
assigned directly. Projects which only inherit access rights for this
group are not listed.

With this option you can find out on which projects a group is (still)
used, e.g. before removing a group (by deletion in the database).

Change-Id: Iab139425808766fbf4e352584b0035fe1c071d93
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
This commit is contained in:
Edwin Kempin
2012-08-24 15:34:20 +02:00
parent 96a10ed60e
commit f0c7042b17
4 changed files with 58 additions and 5 deletions

View File

@@ -16,6 +16,7 @@ SYNOPSIS
[--format {text | json | json_compact}] [--format {text | json | json_compact}]
[--all] [--all]
[--limit <N>] [--limit <N>]
[--has-acl-for GROUP]
DESCRIPTION DESCRIPTION
----------- -----------
@@ -91,6 +92,13 @@ used to unescape the output.
--limit:: --limit::
Cap the number of results to the first N matches. Cap the number of results to the first N matches.
--has-acl-for::
Display only projects on which access rights for this group are
directly assigned. Projects which only inherit access rights for
this group are not listed.
+
With this option you can find out on which projects a group is used.
HTTP HTTP
---- ----
This command is also available over HTTP, as `/projects/` for This command is also available over HTTP, as `/projects/` for

View File

@@ -15,11 +15,16 @@
package com.google.gerrit.server.project; package com.google.gerrit.server.project;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.errors.NoSuchGroupException;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.NameKey; import com.google.gerrit.reviewdb.client.Project.NameKey;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.OutputFormat; import com.google.gerrit.server.OutputFormat;
import com.google.gerrit.server.StringUtil; import com.google.gerrit.server.StringUtil;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.util.TreeFormatter; import com.google.gerrit.server.util.TreeFormatter;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
@@ -86,6 +91,8 @@ public class ListProjects {
private final CurrentUser currentUser; private final CurrentUser currentUser;
private final ProjectCache projectCache; private final ProjectCache projectCache;
private final GroupCache groupCache;
private final GroupControl.Factory groupControlFactory;
private final GitRepositoryManager repoManager; private final GitRepositoryManager repoManager;
private final ProjectNode.Factory projectNodeFactory; private final ProjectNode.Factory projectNodeFactory;
@@ -115,12 +122,18 @@ public class ListProjects {
private String matchPrefix; private String matchPrefix;
@Option(name = "--has-acl-for", metaVar = "GROUP", usage =
"displays only projects on which access rights for this group are directly assigned")
private AccountGroup.Id groupId;
@Inject @Inject
protected ListProjects(CurrentUser currentUser, ProjectCache projectCache, protected ListProjects(CurrentUser currentUser, ProjectCache projectCache,
GitRepositoryManager repoManager, GroupCache groupCache, GroupControl.Factory groupControlFactory,
ProjectNode.Factory projectNodeFactory) { GitRepositoryManager repoManager, ProjectNode.Factory projectNodeFactory) {
this.currentUser = currentUser; this.currentUser = currentUser;
this.projectCache = projectCache; this.projectCache = projectCache;
this.groupCache = groupCache;
this.groupControlFactory = groupControlFactory;
this.repoManager = repoManager; this.repoManager = repoManager;
this.projectNodeFactory = projectNodeFactory; this.projectNodeFactory = projectNodeFactory;
} }
@@ -175,6 +188,22 @@ public class ListProjects {
// //
continue; continue;
} }
final ProjectControl pctl = e.controlFor(currentUser);
if (groupId != null) {
try {
if (!groupControlFactory.controlFor(groupId).isVisible()) {
break;
}
} catch (NoSuchGroupException ex) {
break;
}
if (!pctl.getLocalGroups().contains(
GroupReference.forGroup(groupCache.get(groupId)))) {
continue;
}
}
ProjectInfo info = new ProjectInfo(); ProjectInfo info = new ProjectInfo();
if (type == FilterType.PARENT_CANDIDATES) { if (type == FilterType.PARENT_CANDIDATES) {
ProjectState parentState = e.getParentState(); ProjectState parentState = e.getParentState();
@@ -194,7 +223,6 @@ public class ListProjects {
} }
} else { } else {
final ProjectControl pctl = e.controlFor(currentUser);
final boolean isVisible = pctl.isVisible() || (all && pctl.isOwner()); final boolean isVisible = pctl.isVisible() || (all && pctl.isOwner());
if (showTree && !format.isJson()) { if (showTree && !format.isJson()) {
treeMap.put(projectName, treeMap.put(projectName,

View File

@@ -120,6 +120,7 @@ public class ProjectControl {
private final Collection<ContributorAgreement> contributorAgreements; private final Collection<ContributorAgreement> contributorAgreements;
private List<SectionMatcher> allSections; private List<SectionMatcher> allSections;
private List<SectionMatcher> localSections;
private Map<String, RefControl> refControls; private Map<String, RefControl> refControls;
private Boolean declaredOwner; private Boolean declaredOwner;
@@ -239,8 +240,17 @@ public class ProjectControl {
} }
public Set<GroupReference> getAllGroups() { public Set<GroupReference> getAllGroups() {
return getGroups(access());
}
public Set<GroupReference> getLocalGroups() {
return getGroups(localAccess());
}
private static Set<GroupReference> getGroups(
final List<SectionMatcher> sectionMatcherList) {
final Set<GroupReference> all = new HashSet<GroupReference>(); final Set<GroupReference> all = new HashSet<GroupReference>();
for (final SectionMatcher matcher : access()) { for (final SectionMatcher matcher : sectionMatcherList) {
final AccessSection section = matcher.section; final AccessSection section = matcher.section;
for (final Permission permission : section.getPermissions()) { for (final Permission permission : section.getPermissions()) {
for (final PermissionRule rule : permission.getRules()) { for (final PermissionRule rule : permission.getRules()) {
@@ -392,6 +402,13 @@ public class ProjectControl {
return allSections; return allSections;
} }
private List<SectionMatcher> localAccess() {
if (localSections == null) {
localSections = state.getLocalAccessSections();
}
return localSections;
}
boolean match(PermissionRule rule) { boolean match(PermissionRule rule) {
return match(rule.getGroup().getUUID()); return match(rule.getGroup().getUUID());
} }

View File

@@ -174,7 +174,7 @@ public class ProjectState {
} }
/** Get the sections that pertain only to this project. */ /** Get the sections that pertain only to this project. */
private List<SectionMatcher> getLocalAccessSections() { List<SectionMatcher> getLocalAccessSections() {
List<SectionMatcher> sm = localAccessSections; List<SectionMatcher> sm = localAccessSections;
if (sm == null) { if (sm == null) {
Collection<AccessSection> fromConfig = config.getAccessSections(); Collection<AccessSection> fromConfig = config.getAccessSections();