Access control for branch reads

Enable ref level READ +1 access control, so permissions can be
fine-grained down to the branch level within the same project.

There are two parts to this change:

 - Filter the branches that the user can see in Upload and
   ReceiveCommits using the new RefFilter interface in JGit.
   This prevents a user from fetching something they are not
   allowed to read.

 - Ensure that any object created by the user only points to
   objects they can already reach.  This prevents the user
   from being able to discover objects they can't read by
   uploading a change that points at them.

Change-Id: I55a1811694e8f568e3404f625c5f0a8bf7000cac
This commit is contained in:
Nico Sallembien 2010-03-12 10:31:08 -08:00 committed by Shawn O. Pearce
parent ee1ad9b604
commit c4c51feff4
17 changed files with 451 additions and 63 deletions

View File

@ -321,14 +321,6 @@ public class ProjectRightsPanel extends Composite {
return;
}
// TODO Support per-branch READ access.
if (ApprovalCategory.READ.equals(at.getCategory().getId())) {
referenceTxt.setText("");
referenceTxt.setEnabled(false);
} else {
referenceTxt.setEnabled(true);
}
int curIndex = 0, minIndex = -1, maxIndex = -1;
rangeMinBox.clear();
rangeMaxBox.clear();

View File

@ -327,6 +327,10 @@ class GitWebServlet extends HttpServlet {
try {
final Project.NameKey nameKey = new Project.NameKey(name);
project = projectControl.validateFor(nameKey);
if (!project.allRefsAreVisible()) {
// Pretend the project doesn't exist
throw new NoSuchProjectException(nameKey);
}
} catch (NoSuchProjectException e) {
rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
return;

View File

@ -156,13 +156,6 @@ class AddRefRight extends Handler<ProjectDetail> {
throw new NoSuchRefException(refPattern);
}
// TODO Support per-branch READ access.
if (ApprovalCategory.READ.equals(categoryId)
&& !refPattern.equals("refs/*")) {
throw new UnsupportedOperationException("READ on " + refPattern
+ " not yet supported.");
}
final AccountGroup group = groupCache.get(groupName);
if (group == null) {
throw new NoSuchGroupException(groupName);

View File

@ -60,8 +60,9 @@ class ListBranches extends Handler<List<Branch>> {
@Override
public List<Branch> call() throws NoSuchProjectException,
RepositoryNotFoundException {
projectControlFactory.validateFor(projectName, ProjectControl.OWNER
| ProjectControl.VISIBLE);
final ProjectControl pctl = projectControlFactory.validateFor( //
projectName, //
ProjectControl.OWNER | ProjectControl.VISIBLE);
final List<Branch> branches = new ArrayList<Branch>();
Branch headBranch = null;
@ -85,20 +86,30 @@ class ListBranches extends Handler<List<Branch>> {
}
for (final Ref ref : all.values()) {
if (Constants.HEAD.equals(ref.getName()) && ref.isSymbolic()) {
// HEAD is a symbolic reference to another branch, instead of
if (ref.isSymbolic()) {
// A symbolic reference to another branch, instead of
// showing the resolved value, show the name it references.
//
headBranch = createBranch(Constants.HEAD);
String target = ref.getTarget().getName();
if (!pctl.controlForRef(target).isVisible()) {
continue;
}
if (target.startsWith(Constants.R_HEADS)) {
target = target.substring(Constants.R_HEADS.length());
}
headBranch.setRevision(new RevId(target));
Branch b = createBranch(Constants.HEAD);
b.setRevision(new RevId(target));
if (Constants.HEAD.equals(ref.getName())) {
headBranch = b;
} else {
branches.add(b);
}
continue;
}
if (ref.getName().startsWith(Constants.R_HEADS)) {
if (ref.getName().startsWith(Constants.R_HEADS)
&& pctl.controlForRef(ref.getName()).isVisible()) {
final Branch b = createBranch(ref.getName());
if (ref.getObjectId() != null) {
b.setRevision(new RevId(ref.getObjectId().name()));

View File

@ -29,6 +29,7 @@ import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.RefControl;
import com.google.gwtorm.client.KeyUtil;
import com.google.gwtorm.server.StandardKeyEncoder;
@ -42,6 +43,7 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.SymbolicRef;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@ -58,6 +60,7 @@ public class ListBranchesTest extends LocalDiskRepositoryTestCase {
private ProjectControl.Factory pcf;
private ProjectControl pc;
private GitRepositoryManager grm;
private List<RefControl> refMocks;
@Override
protected void setUp() throws Exception {
@ -71,6 +74,7 @@ public class ListBranchesTest extends LocalDiskRepositoryTestCase {
pc = createStrictMock(ProjectControl.class);
pcf = createStrictMock(ProjectControl.Factory.class);
grm = createStrictMock(GitRepositoryManager.class);
refMocks = new ArrayList<RefControl>();
}
private IExpectationSetters<ProjectControl> validate()
@ -81,10 +85,12 @@ public class ListBranchesTest extends LocalDiskRepositoryTestCase {
private void doReplay() {
replay(mockDb, pc, pcf, grm);
replay(refMocks.toArray());
}
private void doVerify() {
verify(mockDb, pc, pcf, grm);
verify(refMocks.toArray());
}
private void set(String branch, ObjectId id) throws IOException {
@ -117,12 +123,22 @@ public class ListBranchesTest extends LocalDiskRepositoryTestCase {
private List<Branch> permitted(boolean getHead)
throws NoSuchProjectException, IOException {
Map<String, Ref> refs = realDb.getAllRefs();
validate().andReturn(pc);
expect(grm.openRepository(eq(name.get()))).andReturn(mockDb);
expect(mockDb.getAllRefs()).andDelegateTo(realDb);
if (getHead) {
expect(mockDb.getRef(HEAD)).andDelegateTo(realDb);
if (!refs.containsKey(HEAD) && realDb.getRef(HEAD) != null) {
refs.put(HEAD, realDb.getRef(HEAD));
}
}
for (Ref ref : refs.values()) {
assumeVisible(ref, true);
}
mockDb.close();
expectLastCall();
@ -133,6 +149,18 @@ public class ListBranchesTest extends LocalDiskRepositoryTestCase {
return r;
}
private void assumeVisible(Ref ref, boolean visible) {
RefControl rc = createStrictMock(RefControl.class);
refMocks.add(rc);
expect(rc.isVisible()).andReturn(visible);
if (ref.isSymbolic()) {
expect(pc.controlForRef(ref.getTarget().getName())).andReturn(rc);
} else {
expect(pc.controlForRef(ref.getName())).andReturn(rc);
}
}
public void testEmptyProject() throws Exception {
List<Branch> r = permitted(true);
assertEquals(1, r.size());
@ -227,6 +255,9 @@ public class ListBranchesTest extends LocalDiskRepositoryTestCase {
validate().andReturn(pc);
expect(grm.openRepository(eq(name.get()))).andReturn(mockDb);
expect(mockDb.getAllRefs()).andReturn(u);
for (Ref ref : u.values()) {
assumeVisible(ref, true);
}
mockDb.close();
expectLastCall();
@ -240,4 +271,56 @@ public class ListBranchesTest extends LocalDiskRepositoryTestCase {
assertEquals("bar", r.get(1).getShortName());
assertEquals("foo", r.get(2).getShortName());
}
public void testHeadNotVisible() throws Exception {
ObjectIdRef.Unpeeled bar =
new ObjectIdRef.Unpeeled(LOOSE, R_HEADS + "bar", idA);
Map<String, Ref> u = new LinkedHashMap<String, Ref>();
u.put(bar.getName(), bar);
u.put(HEAD, new SymbolicRef(HEAD, bar));
validate().andReturn(pc);
expect(grm.openRepository(eq(name.get()))).andReturn(mockDb);
expect(mockDb.getAllRefs()).andReturn(u);
assumeVisible(bar, false);
assumeVisible(bar, false);
mockDb.close();
expectLastCall();
doReplay();
final List<Branch> r = new ListBranches(pcf, grm, name).call();
doVerify();
assertNotNull(r);
assertTrue(r.isEmpty());
}
public void testHeadVisibleButBranchHidden() throws Exception {
ObjectIdRef.Unpeeled bar =
new ObjectIdRef.Unpeeled(LOOSE, R_HEADS + "bar", idA);
ObjectIdRef.Unpeeled foo =
new ObjectIdRef.Unpeeled(LOOSE, R_HEADS + "foo", idA);
Map<String, Ref> u = new LinkedHashMap<String, Ref>();
u.put(bar.getName(), bar);
u.put(HEAD, new SymbolicRef(HEAD, bar));
u.put(foo.getName(), foo);
validate().andReturn(pc);
expect(grm.openRepository(eq(name.get()))).andReturn(mockDb);
expect(mockDb.getAllRefs()).andReturn(u);
assumeVisible(bar, true);
assumeVisible(bar, true);
assumeVisible(foo, false);
mockDb.close();
expectLastCall();
doReplay();
final List<Branch> r = new ListBranches(pcf, grm, name).call();
doVerify();
assertNotNull(r);
assertEquals(2, r.size());
assertEquals(HEAD, r.get(0).getShortName());
assertEquals("bar", r.get(1).getShortName());
}
}

View File

@ -127,6 +127,10 @@ public final class Change {
r.fromString(str);
return r;
}
public static Id fromRef(final String ref) {
return PatchSet.Id.fromRef(ref).getParentKey();
}
}
/** Globally unique identification of this change. */

View File

@ -35,6 +35,9 @@ public interface ChangeAccess extends Access<Change, Change.Id> {
ResultSet<Change> byProjectKey(Project.NameKey p, Change.Key key)
throws OrmException;
@Query("WHERE dest.projectName = ?")
ResultSet<Change> byProject(Project.NameKey p) throws OrmException;
@Query("WHERE owner = ? AND open = true ORDER BY createdOn, changeId")
ResultSet<Change> byOwnerOpen(Account.Id id) throws OrmException;

View File

@ -102,6 +102,10 @@ ON changes (open, sort_key);
CREATE INDEX changes_byProjectOpen
ON changes (open, dest_project_name, sort_key);
-- covers: byProject
CREATE INDEX changes_byProject
ON changes (dest_project_name);
-- covers: allClosedPrev, allClosedNext
CREATE INDEX changes_allClosed
ON changes (open, status, sort_key);

View File

@ -154,6 +154,10 @@ CREATE INDEX changes_allClosed
ON changes (status, sort_key)
WHERE open = 'N';
-- covers: byProject
CREATE INDEX changes_byProject
ON changes (dest_project_name);
CREATE INDEX changes_key
ON changes (change_key);

View File

@ -192,6 +192,12 @@ public class ReceiveCommits implements PreReceiveHook, PostReceiveHook {
rp.setAllowDeletes(true);
rp.setAllowNonFastForwards(true);
rp.setCheckReceivedObjects(true);
if (!projectControl.allRefsAreVisible()) {
rp.setCheckReferencedObjectsAreReachable(true);
rp.setRefFilter(new VisibleRefFilter(repo, projectControl, db));
}
rp.setPreReceiveHook(this);
rp.setPostReceiveHook(this);
}
@ -230,6 +236,7 @@ public class ReceiveCommits implements PreReceiveHook, PostReceiveHook {
}
}
@Override
public void onPreReceive(final ReceivePack arg0,
final Collection<ReceiveCommand> commands) {
parseCommands(commands);
@ -240,6 +247,7 @@ public class ReceiveCommits implements PreReceiveHook, PostReceiveHook {
doReplaces();
}
@Override
public void onPostReceive(final ReceivePack arg0,
final Collection<ReceiveCommand> commands) {
for (final ReceiveCommand c : commands) {

View File

@ -0,0 +1,224 @@
// Copyright (C) 2010 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.git;
import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.PatchSet;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gwtorm.client.OrmException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevFlag;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.RefFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class VisibleRefFilter implements RefFilter {
private static final Logger log =
LoggerFactory.getLogger(VisibleRefFilter.class);
private final Repository db;
private final ProjectControl projectCtl;
private final ReviewDb reviewDb;
public VisibleRefFilter(final Repository db,
final ProjectControl projectControl, final ReviewDb reviewDb) {
this.db = db;
this.projectCtl = projectControl;
this.reviewDb = reviewDb;
}
@Override
public Map<String, Ref> filter(Map<String, Ref> refs) {
final Set<Change.Id> visibleChanges = visibleChanges();
final Map<String, Ref> result = new HashMap<String, Ref>();
final List<Ref> deferredTags = new ArrayList<Ref>();
for (Ref ref : refs.values()) {
if (PatchSet.isRef(ref.getName())) {
// Reference to a patch set is visible if the change is visible.
//
if (visibleChanges.contains(Change.Id.fromRef(ref.getName()))) {
result.put(ref.getName(), ref);
}
} else if (isTag(ref)) {
// If its a tag, consider it later.
//
deferredTags.add(ref);
} else if (projectCtl.controlForRef(ref.getLeaf().getName()).isVisible()) {
// Use the leaf to lookup the control data. If the reference is
// symbolic we want the control around the final target. If its
// not symbolic then getLeaf() is a no-op returning ref itself.
//
result.put(ref.getName(), ref);
}
}
// If we have tags that were deferred, we need to do a revision walk
// to identify what tags we can actually reach, and what we cannot.
//
if (!deferredTags.isEmpty() && !result.isEmpty()) {
addVisibleTags(result, deferredTags);
}
return result;
}
private Set<Change.Id> visibleChanges() {
final Project project = projectCtl.getProject();
try {
final Set<Change.Id> visibleChanges = new HashSet<Change.Id>();
for (Change change : reviewDb.changes().byProject(project.getNameKey())) {
if (projectCtl.controlFor(change).isVisible()) {
visibleChanges.add(change.getId());
}
}
return visibleChanges;
} catch (OrmException e) {
log.error("Cannot load changes for project " + project.getName()
+ ", assuming no changes are visible", e);
return Collections.emptySet();
}
}
private void addVisibleTags(final Map<String, Ref> result,
final List<Ref> tags) {
final RevWalk rw = new RevWalk(db);
final RevFlag VISIBLE = rw.newFlag("VISIBLE");
final List<RevCommit> starts;
rw.carry(VISIBLE);
starts = lookupVisibleCommits(result, rw, VISIBLE);
for (Ref tag : tags) {
if (isTagVisible(rw, VISIBLE, starts, tag)) {
result.put(tag.getName(), tag);
}
}
}
private List<RevCommit> lookupVisibleCommits(final Map<String, Ref> result,
final RevWalk rw, final RevFlag VISIBLE) {
// Lookup and cache the roots of the graph that we know we can see.
//
final List<RevCommit> roots = new ArrayList<RevCommit>(result.size());
for (Ref ref : result.values()) {
try {
RevObject c = rw.parseAny(ref.getObjectId());
c.add(VISIBLE);
if (c instanceof RevCommit) {
roots.add((RevCommit) c);
} else if (c instanceof RevTag) {
roots.add(rw.parseCommit(c));
}
} catch (IOException e) {
}
}
return roots;
}
private boolean isTagVisible(final RevWalk rw, final RevFlag VISIBLE,
final List<RevCommit> starts, Ref tag) {
try {
final RevObject obj = peelTag(rw, tag);
if (obj.has(VISIBLE)) {
// If the target is immediately visible, continue on. This case
// is quite common as tags are often sorted alphabetically by the
// version number, so earlier tags usually compute the data needed
// to answer later tags with no additional effort.
//
return true;
}
if (obj instanceof RevCommit) {
// Cast to a commit and traverse the history to determine if
// the commit is reachable through one or more references.
//
final RevCommit c = (RevCommit) obj;
walk(rw, VISIBLE, c, starts);
return c.has(VISIBLE);
}
return false;
} catch (IOException e) {
return false;
}
}
private RevObject peelTag(final RevWalk rw, final Ref tag)
throws MissingObjectException, IOException {
// Try to use the peeled object identity, because it may be
// able to save us from parsing the tag object itself.
//
ObjectId target = tag.getPeeledObjectId();
if (target == null) {
target = tag.getObjectId();
}
RevObject o = rw.parseAny(target);
while (o instanceof RevTag) {
o = ((RevTag) o).getObject();
rw.parseHeaders(o);
}
return o;
}
private void walk(final RevWalk rw, final RevFlag VISIBLE,
final RevCommit tagged, final List<RevCommit> starts)
throws MissingObjectException, IncorrectObjectTypeException, IOException {
// Reset the traversal, but keep VISIBLE flags live as they aren't
// invalidated by the change in starting points.
//
rw.resetRetain(VISIBLE);
for (RevCommit o : starts) {
try {
rw.markStart(o);
} catch (IOException e) {
}
}
// Traverse the history until the tag is found.
//
rw.markUninteresting(tagged);
RevCommit c;
while ((c = rw.next()) != null) {
}
}
private static boolean isTag(Ref ref) {
return ref.getLeaf().getName().startsWith(Constants.R_TAGS);
}
}

View File

@ -119,6 +119,11 @@ public class ProjectControl {
return canPerformOnAnyRef(ApprovalCategory.READ, (short) 1);
}
/** Can this user see all the refs in this projects? */
public boolean allRefsAreVisible() {
return canPerformOnAllRefs(ApprovalCategory.READ, (short) 1);
}
/** Is this user a project owner? Ownership does not imply {@link #isVisible()} */
public boolean isOwner() {
return controlForRef(RefRight.ALL).isOwner()

View File

@ -32,7 +32,7 @@ import java.util.List;
/** A version of the database schema. */
public abstract class SchemaVersion {
/** The current schema version. */
private static final Class<? extends SchemaVersion> C = Schema_30.class;
private static final Class<? extends SchemaVersion> C = Schema_31.class;
public static class Module extends AbstractModule {
@Override

View File

@ -0,0 +1,41 @@
// Copyright (C) 2010 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.schema;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gwtorm.jdbc.JdbcSchema;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.sql.SQLException;
import java.sql.Statement;
class Schema_31 extends SchemaVersion {
@Inject
Schema_31(Provider<Schema_30> prior) {
super(prior);
}
@Override
protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException {
Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
try {
stmt.execute("CREATE INDEX changes_byProject"
+ " ON changes (dest_project_name)");
} finally {
stmt.close();
}
}
}

View File

@ -14,9 +14,7 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.reviewdb.Branch;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.RevId;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.WildProjectName;
@ -29,20 +27,12 @@ import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import org.apache.sshd.server.Environment;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.kohsuke.args4j.Option;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
final class ListProjects extends BaseCommand {
@Inject
@ -61,8 +51,7 @@ final class ListProjects extends BaseCommand {
@WildProjectName
private Project.NameKey wildProject;
@Option(name = "--show-branch", aliases = {"-b"},
usage = "displays the sha of each project in the specified branch")
@Option(name = "--show-branch", aliases = {"-b"}, usage = "displays the sha of each project in the specified branch")
private String showBranch;
@Override
@ -76,21 +65,6 @@ final class ListProjects extends BaseCommand {
});
}
private final ObjectId getObjectIdForBranch(Project.NameKey projectName,
String branch) {
try {
final Repository r = repoManager.openRepository(projectName.get());
try {
Ref ref = r.getRef(branch);
return ref == null ? null : ref.getObjectId();
} finally {
r.close();
}
} catch (IOException ioe) {
return null;
}
}
private void display() throws Failure {
final PrintWriter stdout = toPrintWriter(out);
try {
@ -102,19 +76,34 @@ final class ListProjects extends BaseCommand {
}
final ProjectState e = projectCache.get(p.getNameKey());
if (e != null && e.controlFor(currentUser).isVisible()) {
if (showBranch != null) {
ObjectId id = getObjectIdForBranch(p.getNameKey(), showBranch);
if (id != null) {
stdout.print(id.name() + " ");
stdout.print(p.getName());
stdout.println();
}
} else {
stdout.print(p.getName());
stdout.println();
}
if (e == null) {
// If we can't get it from the cache, pretend its not present.
//
continue;
}
final ProjectControl pctl = e.controlFor(currentUser);
if (!pctl.isVisible()) {
// Require the project itself to be visible to the user.
//
continue;
}
if (showBranch != null) {
final Ref ref = getBranchRef(p.getNameKey());
if (ref == null || ref.getObjectId() == null
|| !pctl.controlForRef(ref.getLeaf().getName()).isVisible()) {
// No branch, or the user can't see this branch, so skip it.
//
continue;
}
stdout.print(ref.getObjectId().name());
stdout.print(' ');
}
stdout.print(p.getName());
stdout.println();
}
} catch (OrmException e) {
throw new Failure(1, "fatal: database error", e);
@ -122,4 +111,17 @@ final class ListProjects extends BaseCommand {
stdout.flush();
}
}
private Ref getBranchRef(Project.NameKey projectName) {
try {
final Repository r = repoManager.openRepository(projectName.get());
try {
return r.getRef(showBranch);
} finally {
r.close();
}
} catch (IOException ioe) {
return null;
}
}
}

View File

@ -14,7 +14,10 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.git.VisibleRefFilter;
import com.google.gerrit.sshd.AbstractGitCommand;
import com.google.inject.Inject;
import org.eclipse.jgit.transport.UploadPack;
@ -22,9 +25,16 @@ import java.io.IOException;
/** Publishes Git repositories over SSH using the Git upload-pack protocol. */
final class Upload extends AbstractGitCommand {
@Inject
private ReviewDb db;
@Override
protected void runImpl() throws IOException {
final UploadPack up = new UploadPack(repo);
if (!projectControl.allRefsAreVisible()) {
up.setRefFilter(new VisibleRefFilter(repo, projectControl, db));
}
up.upload(in, out, err);
}
}

View File

@ -46,7 +46,7 @@ limitations under the License.
</issueManagement>
<properties>
<jgitVersion>0.7.1.11-g2b6c555</jgitVersion>
<jgitVersion>0.7.1.34-gf36df5d</jgitVersion>
<gwtormVersion>1.1.4</gwtormVersion>
<gwtjsonrpcVersion>1.2.2</gwtjsonrpcVersion>
<gwtexpuiVersion>1.2.1</gwtexpuiVersion>