Allow group backends to guess on relevant UUIDs
Expose all cheaply known group UUIDs from the ProjectCache, enumerating groups used by access controls. This allows a backend that has a large number of groups to filter its getKnownGroups() output to only groups that may be relevant for this Gerrit server. The best use case to consider is an LDAP server at a large organization. A typical user may belong to 50 LDAP groups, but only 3 are relevant to this Gerrit server. Taking the intersection of the two groups limits the output Gerrit displays to users, or uses when considering same group visibility. Change-Id: I0fb5053f24af52014fd860e795d08cd38fc25f1c
This commit is contained in:
@@ -241,7 +241,7 @@ import javax.security.auth.login.LoginException;
|
|||||||
if (actual.isEmpty()) {
|
if (actual.isEmpty()) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
} else {
|
} else {
|
||||||
return Collections.unmodifiableSet(actual);
|
return ImmutableSet.copyOf(actual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -32,6 +32,7 @@ import com.google.gerrit.server.account.GroupBackend;
|
|||||||
import com.google.gerrit.server.account.GroupMembership;
|
import com.google.gerrit.server.account.GroupMembership;
|
||||||
import com.google.gerrit.server.account.ListGroupMembership;
|
import com.google.gerrit.server.account.ListGroupMembership;
|
||||||
import com.google.gerrit.server.auth.ldap.Helper.LdapSchema;
|
import com.google.gerrit.server.auth.ldap.Helper.LdapSchema;
|
||||||
|
import com.google.gerrit.server.project.ProjectCache;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.name.Named;
|
import com.google.inject.name.Named;
|
||||||
@@ -65,6 +66,7 @@ public class LdapGroupBackend implements GroupBackend {
|
|||||||
private final Helper helper;
|
private final Helper helper;
|
||||||
private final LoadingCache<String, Set<AccountGroup.UUID>> membershipCache;
|
private final LoadingCache<String, Set<AccountGroup.UUID>> membershipCache;
|
||||||
private final LoadingCache<String, Boolean> existsCache;
|
private final LoadingCache<String, Boolean> existsCache;
|
||||||
|
private final ProjectCache projectCache;
|
||||||
private final Provider<CurrentUser> userProvider;
|
private final Provider<CurrentUser> userProvider;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@@ -72,9 +74,11 @@ public class LdapGroupBackend implements GroupBackend {
|
|||||||
Helper helper,
|
Helper helper,
|
||||||
@Named(GROUP_CACHE) LoadingCache<String, Set<AccountGroup.UUID>> membershipCache,
|
@Named(GROUP_CACHE) LoadingCache<String, Set<AccountGroup.UUID>> membershipCache,
|
||||||
@Named(GROUP_EXIST_CACHE) LoadingCache<String, Boolean> existsCache,
|
@Named(GROUP_EXIST_CACHE) LoadingCache<String, Boolean> existsCache,
|
||||||
|
ProjectCache projectCache,
|
||||||
Provider<CurrentUser> userProvider) {
|
Provider<CurrentUser> userProvider) {
|
||||||
this.helper = helper;
|
this.helper = helper;
|
||||||
this.membershipCache = membershipCache;
|
this.membershipCache = membershipCache;
|
||||||
|
this.projectCache = projectCache;
|
||||||
this.existsCache = existsCache;
|
this.existsCache = existsCache;
|
||||||
this.userProvider = userProvider;
|
this.userProvider = userProvider;
|
||||||
}
|
}
|
||||||
@@ -174,7 +178,15 @@ public class LdapGroupBackend implements GroupBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return new ListGroupMembership(membershipCache.get(id));
|
final Set<AccountGroup.UUID> groups = membershipCache.get(id);
|
||||||
|
return new ListGroupMembership(groups) {
|
||||||
|
@Override
|
||||||
|
public Set<AccountGroup.UUID> getKnownGroups() {
|
||||||
|
Set<AccountGroup.UUID> g = Sets.newHashSet(groups);
|
||||||
|
g.retainAll(projectCache.guessRelevantGroupUUIDs());
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
};
|
||||||
} catch (ExecutionException e) {
|
} catch (ExecutionException e) {
|
||||||
log.warn(String.format("Cannot lookup membershipsOf %s in LDAP", id), e);
|
log.warn(String.format("Cannot lookup membershipsOf %s in LDAP", id), e);
|
||||||
return GroupMembership.EMPTY;
|
return GroupMembership.EMPTY;
|
||||||
|
@@ -228,6 +228,11 @@ public class ProjectConfig extends VersionedMetaData {
|
|||||||
return groupsByUUID.get(uuid);
|
return groupsByUUID.get(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return set of all groups used by this configuration. */
|
||||||
|
public Set<AccountGroup.UUID> getAllGroupUUIDs() {
|
||||||
|
return Collections.unmodifiableSet(groupsByUUID.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the project's rules.pl ObjectId, if present in the branch.
|
* @return the project's rules.pl ObjectId, if present in the branch.
|
||||||
* Null if it doesn't exist.
|
* Null if it doesn't exist.
|
||||||
|
@@ -14,8 +14,11 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.project;
|
package com.google.gerrit.server.project;
|
||||||
|
|
||||||
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import com.google.gerrit.reviewdb.client.Project;
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/** Cache of project information, including access rights. */
|
/** Cache of project information, including access rights. */
|
||||||
public interface ProjectCache {
|
public interface ProjectCache {
|
||||||
/** @return the parent state for all projects on this server. */
|
/** @return the parent state for all projects on this server. */
|
||||||
@@ -41,6 +44,13 @@ public interface ProjectCache {
|
|||||||
/** @return sorted iteration of projects. */
|
/** @return sorted iteration of projects. */
|
||||||
public abstract Iterable<Project.NameKey> all();
|
public abstract Iterable<Project.NameKey> all();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return estimated set of relevant groups extracted from hot project access
|
||||||
|
* rules. If the cache is cold or too small for the entire project set
|
||||||
|
* of the server, this set may be incomplete.
|
||||||
|
*/
|
||||||
|
public abstract Set<AccountGroup.UUID> guessRelevantGroupUUIDs();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter the set of registered project names by common prefix.
|
* Filter the set of registered project names by common prefix.
|
||||||
*
|
*
|
||||||
|
@@ -17,6 +17,7 @@ package com.google.gerrit.server.project;
|
|||||||
import com.google.common.cache.CacheLoader;
|
import com.google.common.cache.CacheLoader;
|
||||||
import com.google.common.cache.LoadingCache;
|
import com.google.common.cache.LoadingCache;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
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.server.cache.CacheModule;
|
import com.google.gerrit.server.cache.CacheModule;
|
||||||
import com.google.gerrit.server.config.AllProjectsName;
|
import com.google.gerrit.server.config.AllProjectsName;
|
||||||
@@ -36,6 +37,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
@@ -170,6 +172,18 @@ public class ProjectCacheImpl implements ProjectCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<AccountGroup.UUID> guessRelevantGroupUUIDs() {
|
||||||
|
Set<AccountGroup.UUID> groups = Sets.newHashSet();
|
||||||
|
for (Project.NameKey n : all()) {
|
||||||
|
ProjectState p = byName.getIfPresent(n);
|
||||||
|
if (p != null) {
|
||||||
|
groups.addAll(p.getConfig().getAllGroupUUIDs());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterable<Project.NameKey> byName(final String pfx) {
|
public Iterable<Project.NameKey> byName(final String pfx) {
|
||||||
final Iterable<Project.NameKey> src;
|
final Iterable<Project.NameKey> src;
|
||||||
|
@@ -306,6 +306,11 @@ public class RefControlTest extends TestCase {
|
|||||||
@Override
|
@Override
|
||||||
public void onCreateProject(Project.NameKey newProjectName) {
|
public void onCreateProject(Project.NameKey newProjectName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<AccountGroup.UUID> guessRelevantGroupUUIDs() {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Injector injector = Guice.createInjector(new FactoryModule() {
|
Injector injector = Guice.createInjector(new FactoryModule() {
|
||||||
|
Reference in New Issue
Block a user