Added support for included groups

This change adds a new item to the group configuration: a list of
groups whose members should be included in this one.  This makes it
possible to set up a hierarchy of included groups, which can make it
easier to maintain complex access control lists.

To accomplish this, two new database tables were added,
called AccountGroupIncludes and AccountGroupIncludesAudit.
The relevant support code was added around them, largely based on
the existing code for handling indivdual account membership.  In
addition, caches for group information were added, paralleling the
caches that already exist for accounts.

Change-Id: Ib6990c17739f28f38bc13961143db7ce79251567
This commit is contained in:
Matt Fischer
2011-03-22 14:28:23 -05:00
committed by Shawn O. Pearce
parent 0860e1b13c
commit 620255aef7
33 changed files with 1201 additions and 34 deletions

View File

@@ -23,6 +23,7 @@ import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.reviewdb.StarredChange;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.GroupIncludeCache;
import com.google.gerrit.server.account.Realm;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.CanonicalWebUrl;
@@ -46,7 +47,9 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.TimeZone;
@@ -61,15 +64,18 @@ public class IdentifiedUser extends CurrentUser {
private final Provider<String> canonicalUrl;
private final Realm realm;
private final AccountCache accountCache;
private final GroupIncludeCache groupIncludeCache;
@Inject
GenericFactory(final AuthConfig authConfig,
final @CanonicalWebUrl Provider<String> canonicalUrl,
final Realm realm, final AccountCache accountCache) {
final Realm realm, final AccountCache accountCache,
final GroupIncludeCache groupIncludeCache) {
this.authConfig = authConfig;
this.canonicalUrl = canonicalUrl;
this.realm = realm;
this.accountCache = accountCache;
this.groupIncludeCache = groupIncludeCache;
}
public IdentifiedUser create(final Account.Id id) {
@@ -78,13 +84,13 @@ public class IdentifiedUser extends CurrentUser {
public IdentifiedUser create(Provider<ReviewDb> db, Account.Id id) {
return new IdentifiedUser(AccessPath.UNKNOWN, authConfig, canonicalUrl,
realm, accountCache, null, db, id);
realm, accountCache, groupIncludeCache, null, db, id);
}
public IdentifiedUser create(AccessPath accessPath,
Provider<SocketAddress> remotePeerProvider, Account.Id id) {
return new IdentifiedUser(accessPath, authConfig, canonicalUrl, realm,
accountCache, remotePeerProvider, null, id);
accountCache, groupIncludeCache, remotePeerProvider, null, id);
}
}
@@ -100,6 +106,7 @@ public class IdentifiedUser extends CurrentUser {
private final Provider<String> canonicalUrl;
private final Realm realm;
private final AccountCache accountCache;
private final GroupIncludeCache groupIncludeCache;
private final Provider<SocketAddress> remotePeerProvider;
private final Provider<ReviewDb> dbProvider;
@@ -108,6 +115,7 @@ public class IdentifiedUser extends CurrentUser {
RequestFactory(final AuthConfig authConfig,
final @CanonicalWebUrl Provider<String> canonicalUrl,
final Realm realm, final AccountCache accountCache,
final GroupIncludeCache groupIncludeCache,
final @RemotePeer Provider<SocketAddress> remotePeerProvider,
final Provider<ReviewDb> dbProvider) {
@@ -115,6 +123,7 @@ public class IdentifiedUser extends CurrentUser {
this.canonicalUrl = canonicalUrl;
this.realm = realm;
this.accountCache = accountCache;
this.groupIncludeCache = groupIncludeCache;
this.remotePeerProvider = remotePeerProvider;
this.dbProvider = dbProvider;
@@ -123,7 +132,7 @@ public class IdentifiedUser extends CurrentUser {
public IdentifiedUser create(final AccessPath accessPath,
final Account.Id id) {
return new IdentifiedUser(accessPath, authConfig, canonicalUrl, realm,
accountCache, remotePeerProvider, dbProvider, id);
accountCache, groupIncludeCache, remotePeerProvider, dbProvider, id);
}
}
@@ -133,6 +142,7 @@ public class IdentifiedUser extends CurrentUser {
private final Provider<String> canonicalUrl;
private final Realm realm;
private final AccountCache accountCache;
private final GroupIncludeCache groupIncludeCache;
@Nullable
private final Provider<SocketAddress> remotePeerProvider;
@@ -151,12 +161,14 @@ public class IdentifiedUser extends CurrentUser {
private IdentifiedUser(final AccessPath accessPath,
final AuthConfig authConfig, final Provider<String> canonicalUrl,
final Realm realm, final AccountCache accountCache,
final GroupIncludeCache groupIncludeCache,
@Nullable final Provider<SocketAddress> remotePeerProvider,
@Nullable final Provider<ReviewDb> dbProvider, final Account.Id id) {
super(accessPath, authConfig);
this.canonicalUrl = canonicalUrl;
this.realm = realm;
this.accountCache = accountCache;
this.groupIncludeCache = groupIncludeCache;
this.remotePeerProvider = remotePeerProvider;
this.dbProvider = dbProvider;
this.accountId = id;
@@ -207,14 +219,35 @@ public class IdentifiedUser extends CurrentUser {
@Override
public Set<AccountGroup.Id> getEffectiveGroups() {
if (effectiveGroups == null) {
if (authConfig.isIdentityTrustable(state().getExternalIds())) {
effectiveGroups = realm.groups(state());
Set<AccountGroup.Id> seedGroups;
if (authConfig.isIdentityTrustable(state().getExternalIds())) {
seedGroups = realm.groups(state());
} else {
effectiveGroups = authConfig.getRegisteredGroups();
seedGroups = authConfig.getRegisteredGroups();
}
effectiveGroups = getIncludedGroups(seedGroups);
}
return effectiveGroups;
}
private Set<AccountGroup.Id> getIncludedGroups(Set<AccountGroup.Id> seedGroups) {
Set<AccountGroup.Id> includes = new HashSet<AccountGroup.Id> (seedGroups);
Queue<AccountGroup.Id> groupQueue = new LinkedList<AccountGroup.Id> (seedGroups);
while (groupQueue.size() > 0) {
AccountGroup.Id id = groupQueue.remove();
for (final AccountGroup.Id groupId : groupIncludeCache.getByInclude(id)) {
if (includes.add(groupId)) {
groupQueue.add(groupId);
}
}
}
return effectiveGroups;
return Collections.unmodifiableSet(includes);
}
@Override