Honor reference level READ during replication

If reference level READ access is used in a repository, we should
also honor it during replication based upon what the replication
user can see within the repository.

Change-Id: Ica62739cb17f497b847eeb8c1e7bf23dd978834e
Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2010-05-10 15:27:19 -07:00
parent d875e38b25
commit 9ee35ef724
5 changed files with 85 additions and 13 deletions

View File

@@ -25,6 +25,10 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
public class ReplicationUser extends CurrentUser { public class ReplicationUser extends CurrentUser {
/** Magic set of groups enabling read of any project and reference. */
public static final Set<AccountGroup.Id> EVERYTHING_VISIBLE =
Collections.unmodifiableSet(new HashSet<AccountGroup.Id>(0));
public interface Factory { public interface Factory {
ReplicationUser create(@Assisted Set<AccountGroup.Id> authGroups); ReplicationUser create(@Assisted Set<AccountGroup.Id> authGroups);
} }
@@ -36,7 +40,10 @@ public class ReplicationUser extends CurrentUser {
@Assisted Set<AccountGroup.Id> authGroups) { @Assisted Set<AccountGroup.Id> authGroups) {
super(AccessPath.REPLICATION, authConfig); super(AccessPath.REPLICATION, authConfig);
if (authGroups.isEmpty()) { if (authGroups == EVERYTHING_VISIBLE) {
effectiveGroups = EVERYTHING_VISIBLE;
} else if (authGroups.isEmpty()) {
// Only include the registered groups if no specific groups // Only include the registered groups if no specific groups
// were provided. This allows an administrator to configure // were provided. This allows an administrator to configure
// a replication user with a narrower view of the system than // a replication user with a narrower view of the system than
@@ -63,4 +70,8 @@ public class ReplicationUser extends CurrentUser {
public Set<Change.Id> getStarredChanges() { public Set<Change.Id> getStarredChanges() {
return Collections.emptySet(); return Collections.emptySet();
} }
public boolean isEverythingVisible() {
return getEffectiveGroups() == EVERYTHING_VISIBLE;
}
} }

View File

@@ -15,7 +15,12 @@
package com.google.gerrit.server.git; package com.google.gerrit.server.git;
import com.google.gerrit.reviewdb.Project; import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.reviewdb.Project.NameKey; import com.google.gerrit.reviewdb.Project.NameKey;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.SchemaFactory;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
@@ -40,6 +45,8 @@ import org.slf4j.Logger;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -60,6 +67,7 @@ class PushOp implements ProjectRunnable {
static final String MIRROR_ALL = "..all.."; static final String MIRROR_ALL = "..all..";
private final GitRepositoryManager repoManager; private final GitRepositoryManager repoManager;
private final SchemaFactory<ReviewDb> schema;
private final PushReplication.ReplicationConfig pool; private final PushReplication.ReplicationConfig pool;
private final RemoteConfig config; private final RemoteConfig config;
@@ -71,10 +79,11 @@ class PushOp implements ProjectRunnable {
private Repository db; private Repository db;
@Inject @Inject
PushOp(final GitRepositoryManager grm, PushOp(final GitRepositoryManager grm, final SchemaFactory<ReviewDb> s,
final PushReplication.ReplicationConfig p, final RemoteConfig c, final PushReplication.ReplicationConfig p, final RemoteConfig c,
@Assisted final Project.NameKey d, @Assisted final URIish u) { @Assisted final Project.NameKey d, @Assisted final URIish u) {
repoManager = grm; repoManager = grm;
schema = s;
pool = p; pool = p;
config = c; config = c;
projectName = d; projectName = d;
@@ -198,9 +207,44 @@ class PushOp implements ProjectRunnable {
private List<RemoteRefUpdate> generateUpdates(final Transport tn) private List<RemoteRefUpdate> generateUpdates(final Transport tn)
throws IOException { throws IOException {
final List<RemoteRefUpdate> cmds = new ArrayList<RemoteRefUpdate>(); final ProjectControl pc;
final Map<String, Ref> local = db.getAllRefs(); try {
pc = pool.controlFor(projectName);
} catch (NoSuchProjectException e) {
return Collections.emptyList();
}
Map<String, Ref> local = db.getAllRefs();
if (!pc.allRefsAreVisible()) {
if (!mirror) {
// If we aren't mirroring, reduce the space we need to filter
// to only the references we will update during this operation.
//
Map<String, Ref> n = new HashMap<String, Ref>();
for (String src : delta) {
Ref r = local.get(src);
if (r != null) {
n.put(src, r);
}
}
local = n;
}
final ReviewDb meta;
try {
meta = schema.open();
} catch (OrmException e) {
log.error("Cannot read database to replicate to " + projectName, e);
return Collections.emptyList();
}
try {
local = new VisibleRefFilter(db, pc, meta).filter(local);
} finally {
meta.close();
}
}
final List<RemoteRefUpdate> cmds = new ArrayList<RemoteRefUpdate>();
if (mirror) { if (mirror) {
final Map<String, Ref> remote = listRemote(tn); final Map<String, Ref> remote = listRemote(tn);

View File

@@ -304,7 +304,6 @@ public class PushReplication implements ReplicationQueue {
private final Map<URIish, PushOp> pending = new HashMap<URIish, PushOp>(); private final Map<URIish, PushOp> pending = new HashMap<URIish, PushOp>();
private final PushOp.Factory opFactory; private final PushOp.Factory opFactory;
private final ProjectControl.Factory projectControlFactory; private final ProjectControl.Factory projectControlFactory;
private final boolean authEnabled;
ReplicationConfig(final Injector injector, final WorkQueue workQueue, ReplicationConfig(final Injector injector, final WorkQueue workQueue,
final RemoteConfig rc, final Config cfg, SchemaFactory<ReviewDb> db, final RemoteConfig rc, final Config cfg, SchemaFactory<ReviewDb> db,
@@ -319,9 +318,13 @@ public class PushReplication implements ReplicationQueue {
String[] authGroupNames = String[] authGroupNames =
cfg.getStringList("remote", rc.getName(), "authGroup"); cfg.getStringList("remote", rc.getName(), "authGroup");
authEnabled = authGroupNames.length > 0; final Set<AccountGroup.Id> authGroups;
Set<AccountGroup.Id> authGroups = ConfigUtil.groupsFor(db, authGroupNames, log, if (authGroupNames.length > 0) {
"Group \"{0}\" not in database, removing from authGroup"); authGroups = ConfigUtil.groupsFor(db, authGroupNames, //
log, "Group \"{0}\" not in database, removing from authGroup");
} else {
authGroups = ReplicationUser.EVERYTHING_VISIBLE;
}
final ReplicationUser remoteUser = final ReplicationUser remoteUser =
replicationUserFactory.create(authGroups); replicationUserFactory.create(authGroups);
@@ -354,8 +357,7 @@ public class PushReplication implements ReplicationQueue {
void schedule(final Project.NameKey project, final String ref, void schedule(final Project.NameKey project, final String ref,
final URIish uri) { final URIish uri) {
try { try {
if (authEnabled if (!controlFor(project).isVisible()) {
&& !projectControlFactory.controlFor(project).isVisible()) {
return; return;
} }
} catch (NoSuchProjectException e1) { } catch (NoSuchProjectException e1) {
@@ -374,6 +376,11 @@ public class PushReplication implements ReplicationQueue {
} }
} }
ProjectControl controlFor(final Project.NameKey project)
throws NoSuchProjectException {
return projectControlFactory.controlFor(project);
}
void notifyStarting(final PushOp op) { void notifyStarting(final PushOp op) {
synchronized (pending) { synchronized (pending) {
pending.remove(op.getURI()); pending.remove(op.getURI());

View File

@@ -21,6 +21,7 @@ import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.Project; import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.RefRight; import com.google.gerrit.reviewdb.RefRight;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.ReplicationUser;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
@@ -116,12 +117,20 @@ public class ProjectControl {
/** Can this user see this project exists? */ /** Can this user see this project exists? */
public boolean isVisible() { public boolean isVisible() {
return canPerformOnAnyRef(ApprovalCategory.READ, (short) 1); return visibleForReplication()
|| canPerformOnAnyRef(ApprovalCategory.READ, (short) 1);
} }
/** Can this user see all the refs in this projects? */ /** Can this user see all the refs in this projects? */
public boolean allRefsAreVisible() { public boolean allRefsAreVisible() {
return canPerformOnAllRefs(ApprovalCategory.READ, (short) 1); return visibleForReplication()
|| canPerformOnAllRefs(ApprovalCategory.READ, (short) 1);
}
/** Is this project completely visible for replication? */
boolean visibleForReplication() {
return getCurrentUser() instanceof ReplicationUser
&& ((ReplicationUser) getCurrentUser()).isEverythingVisible();
} }
/** Is this user a project owner? Ownership does not imply {@link #isVisible()} */ /** Is this user a project owner? Ownership does not imply {@link #isVisible()} */

View File

@@ -100,7 +100,8 @@ public class RefControl {
/** Can this user see this reference exists? */ /** Can this user see this reference exists? */
public boolean isVisible() { public boolean isVisible() {
return canPerform(READ, (short) 1); return getProjectControl().visibleForReplication()
|| canPerform(READ, (short) 1);
} }
/** /**