Add UploadValidationListener extension point.
Add pre-upload extension point to allow plugins to validate upload operations right before Gerrit begins to send a pack back to the git client. A plugin can interrupt the upload by throwing an exception which will cause the upload to fail and the exception’s message text will be reported to the git client. For example, a plugin may want to deny fetches for a specific project. Change-Id: I5d0aeac51b83bf14150fa3036f71965cf2051243
This commit is contained in:
parent
572d542b3d
commit
0c14990f6d
@ -32,6 +32,19 @@ are merged to the git repository.
|
||||
If the commit fails the validation, the plugin can throw an exception
|
||||
which will cause the merge to fail.
|
||||
|
||||
[[pre-upload-validation]]
|
||||
== Pre-upload validation
|
||||
|
||||
|
||||
Plugins implementing the `UploadValidationListener` interface can
|
||||
perform additional validation checks before any upload operations
|
||||
(clone, fetch, pull). The validation is executed right before Gerrit
|
||||
begins to send a pack back to the git client.
|
||||
|
||||
If upload fails the validation, the plugin can throw an exception
|
||||
which will cause the upload to fail and the exception's message text
|
||||
will be reported to the git client.
|
||||
|
||||
[[new-project-validation]]
|
||||
== New project validation
|
||||
|
||||
|
@ -33,6 +33,7 @@ import com.google.gerrit.server.git.ReceivePackInitializer;
|
||||
import com.google.gerrit.server.git.TagCache;
|
||||
import com.google.gerrit.server.git.TransferConfig;
|
||||
import com.google.gerrit.server.git.VisibleRefFilter;
|
||||
import com.google.gerrit.server.git.validators.UploadValidators;
|
||||
import com.google.gerrit.server.project.NoSuchProjectException;
|
||||
import com.google.gerrit.server.project.ProjectControl;
|
||||
import com.google.inject.AbstractModule;
|
||||
@ -213,12 +214,15 @@ public class GitOverHttpServlet extends GitServlet {
|
||||
private final Provider<ReviewDb> db;
|
||||
private final TagCache tagCache;
|
||||
private final ChangeCache changeCache;
|
||||
private final UploadValidators.Factory uploadValidatorsFactory;
|
||||
|
||||
@Inject
|
||||
UploadFilter(Provider<ReviewDb> db, TagCache tagCache, ChangeCache changeCache) {
|
||||
UploadFilter(Provider<ReviewDb> db, TagCache tagCache, ChangeCache changeCache,
|
||||
UploadValidators.Factory uploadValidatorsFactory) {
|
||||
this.db = db;
|
||||
this.tagCache = tagCache;
|
||||
this.changeCache = changeCache;
|
||||
this.uploadValidatorsFactory = uploadValidatorsFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -235,9 +239,15 @@ public class GitOverHttpServlet extends GitServlet {
|
||||
"upload-pack not permitted on this server");
|
||||
return;
|
||||
}
|
||||
|
||||
// We use getRemoteHost() here instead of getRemoteAddr() because REMOTE_ADDR
|
||||
// may have been overridden by a proxy server -- we'll try to avoid this.
|
||||
UploadValidators uploadValidators =
|
||||
uploadValidatorsFactory.create(pc.getProject(), repo, request.getRemoteHost());
|
||||
up.setPreUploadHook(PreUploadHookChain.newChain(
|
||||
Lists.newArrayList(up.getPreUploadHook(), uploadValidators)));
|
||||
if (!pc.allRefsAreVisible()) {
|
||||
up.setAdvertiseRefsHook(new VisibleRefFilter(tagCache, changeCache, repo, pc, db.get(), true));
|
||||
up.setAdvertiseRefsHook(new VisibleRefFilter(tagCache, changeCache,
|
||||
repo, pc, db.get(), true));
|
||||
}
|
||||
|
||||
next.doFilter(request, response);
|
||||
|
@ -89,6 +89,8 @@ import com.google.gerrit.server.git.validators.CommitValidators;
|
||||
import com.google.gerrit.server.git.validators.MergeValidationListener;
|
||||
import com.google.gerrit.server.git.validators.MergeValidators;
|
||||
import com.google.gerrit.server.git.validators.MergeValidators.ProjectConfigValidator;
|
||||
import com.google.gerrit.server.git.validators.UploadValidationListener;
|
||||
import com.google.gerrit.server.git.validators.UploadValidators;
|
||||
import com.google.gerrit.server.group.GroupModule;
|
||||
import com.google.gerrit.server.mail.AddReviewerSender;
|
||||
import com.google.gerrit.server.mail.CreateChangeSender;
|
||||
@ -276,6 +278,9 @@ public class GerritGlobalModule extends FactoryModule {
|
||||
DynamicSet.setOf(binder(), PatchSetWebLink.class);
|
||||
DynamicSet.setOf(binder(), ProjectWebLink.class);
|
||||
|
||||
factory(UploadValidators.Factory.class);
|
||||
DynamicSet.setOf(binder(), UploadValidationListener.class);
|
||||
|
||||
bind(AnonymousUser.class);
|
||||
|
||||
factory(CommitValidators.Factory.class);
|
||||
|
@ -0,0 +1,30 @@
|
||||
// Copyright (C) 2014 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.validators;
|
||||
|
||||
import org.eclipse.jgit.transport.ServiceMayNotContinueException;
|
||||
|
||||
public class UploadValidationException extends ServiceMayNotContinueException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public UploadValidationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public UploadValidationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
// Copyright (C) 2014 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.validators;
|
||||
|
||||
import com.google.gerrit.extensions.annotations.ExtensionPoint;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.validators.ValidationException;
|
||||
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.transport.UploadPack;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Listener to provide validation for upload operations.
|
||||
*
|
||||
* Invoked by Gerrit before it begins to send a pack to the client.
|
||||
*
|
||||
* Implementors can block the upload operation by throwing a
|
||||
* ValidationException. The exception's message text will be reported to
|
||||
* the end-user over the client's protocol connection.
|
||||
*/
|
||||
@ExtensionPoint
|
||||
public interface UploadValidationListener {
|
||||
|
||||
/**
|
||||
* Validate an upload before it begins.
|
||||
*
|
||||
* @param repository The repository
|
||||
* @param project The project
|
||||
* @param remoteHost Remote address/hostname of the user
|
||||
* @param wants The list of wanted objects. These may be RevObject or
|
||||
* RevCommit if the processor parsed them. Implementors should not rely
|
||||
* on the values being parsed.
|
||||
* @param haves The list of common objects. Empty on an initial clone request.
|
||||
* These may be RevObject or RevCommit if the processor parsed them.
|
||||
* Implementors should not rely on the values being parsed.
|
||||
* @throws ValidationException to block the upload and send a message
|
||||
* back to the end-used over the client's protocol connection.
|
||||
*/
|
||||
public void onPreUpload(Repository repository, Project project,
|
||||
String remoteHost, UploadPack up, Collection<? extends ObjectId> wants,
|
||||
Collection<? extends ObjectId> haves)
|
||||
throws ValidationException;
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
// Copyright (C) 2014 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.validators;
|
||||
|
||||
import com.google.gerrit.extensions.registration.DynamicSet;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.validators.ValidationException;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.transport.PreUploadHook;
|
||||
import org.eclipse.jgit.transport.ServiceMayNotContinueException;
|
||||
import org.eclipse.jgit.transport.UploadPack;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class UploadValidators implements PreUploadHook {
|
||||
|
||||
private final DynamicSet<UploadValidationListener> uploadValidationListeners;
|
||||
private final Project project;
|
||||
private final Repository repository;
|
||||
private final String remoteHost;
|
||||
|
||||
public interface Factory {
|
||||
UploadValidators create(Project project, Repository repository,
|
||||
String remoteAddress);
|
||||
}
|
||||
|
||||
@Inject
|
||||
UploadValidators(
|
||||
DynamicSet<UploadValidationListener> uploadValidationListeners,
|
||||
@Assisted Project project, @Assisted Repository repository,
|
||||
@Assisted String remoteHost) {
|
||||
this.uploadValidationListeners = uploadValidationListeners;
|
||||
this.project = project;
|
||||
this.repository = repository;
|
||||
this.remoteHost = remoteHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendPack(UploadPack up, Collection<? extends ObjectId> wants,
|
||||
Collection<? extends ObjectId> haves)
|
||||
throws ServiceMayNotContinueException {
|
||||
for (UploadValidationListener validator : uploadValidationListeners) {
|
||||
try {
|
||||
validator.onPreUpload(repository, project, remoteHost, up, wants, haves);
|
||||
} catch (ValidationException e) {
|
||||
throw new UploadValidationException(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBeginNegotiateRound(UploadPack up,
|
||||
Collection<? extends ObjectId> wants, int cntOffered)
|
||||
throws ServiceMayNotContinueException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEndNegotiateRound(UploadPack up,
|
||||
Collection<? extends ObjectId> wants, int cntCommon, int cntNotFound,
|
||||
boolean ready) throws ServiceMayNotContinueException {
|
||||
}
|
||||
}
|
@ -64,6 +64,14 @@ public class SshSession {
|
||||
return identity;
|
||||
}
|
||||
|
||||
public SocketAddress getRemoteAddress() {
|
||||
return remoteAddress;
|
||||
}
|
||||
|
||||
public String getRemoteAddressAsString() {
|
||||
return remoteAsString;
|
||||
}
|
||||
|
||||
String getUsername() {
|
||||
return username;
|
||||
}
|
||||
@ -94,14 +102,6 @@ public class SshSession {
|
||||
return authError != null;
|
||||
}
|
||||
|
||||
SocketAddress getRemoteAddress() {
|
||||
return remoteAddress;
|
||||
}
|
||||
|
||||
String getRemoteAddressAsString() {
|
||||
return remoteAsString;
|
||||
}
|
||||
|
||||
private static String format(final SocketAddress remote) {
|
||||
if (remote instanceof InetSocketAddress) {
|
||||
final InetSocketAddress sa = (InetSocketAddress) remote;
|
||||
|
@ -21,7 +21,10 @@ import com.google.gerrit.server.git.ChangeCache;
|
||||
import com.google.gerrit.server.git.TagCache;
|
||||
import com.google.gerrit.server.git.TransferConfig;
|
||||
import com.google.gerrit.server.git.VisibleRefFilter;
|
||||
import com.google.gerrit.server.git.validators.UploadValidationException;
|
||||
import com.google.gerrit.server.git.validators.UploadValidators;
|
||||
import com.google.gerrit.sshd.AbstractGitCommand;
|
||||
import com.google.gerrit.sshd.SshSession;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
@ -30,6 +33,7 @@ import org.eclipse.jgit.transport.PreUploadHookChain;
|
||||
import org.eclipse.jgit.transport.UploadPack;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/** Publishes Git repositories over SSH using the Git upload-pack protocol. */
|
||||
final class Upload extends AbstractGitCommand {
|
||||
@ -48,6 +52,12 @@ final class Upload extends AbstractGitCommand {
|
||||
@Inject
|
||||
private DynamicSet<PreUploadHook> preUploadHooks;
|
||||
|
||||
@Inject
|
||||
private UploadValidators.Factory uploadValidatorsFactory;
|
||||
|
||||
@Inject
|
||||
private SshSession session;
|
||||
|
||||
@Override
|
||||
protected void runImpl() throws IOException, Failure {
|
||||
if (!projectControl.canRunUploadPack()) {
|
||||
@ -61,8 +71,21 @@ final class Upload extends AbstractGitCommand {
|
||||
}
|
||||
up.setPackConfig(config.getPackConfig());
|
||||
up.setTimeout(config.getTimeout());
|
||||
up.setPreUploadHook(PreUploadHookChain.newChain(
|
||||
Lists.newArrayList(preUploadHooks)));
|
||||
up.upload(in, out, err);
|
||||
|
||||
List<PreUploadHook> allPreUploadHooks = Lists.newArrayList(preUploadHooks);
|
||||
allPreUploadHooks.add(uploadValidatorsFactory.create(project, repo,
|
||||
session.getRemoteAddressAsString()));
|
||||
up.setPreUploadHook(PreUploadHookChain.newChain(allPreUploadHooks));
|
||||
try {
|
||||
up.upload(in, out, err);
|
||||
} catch (UploadValidationException e) {
|
||||
// UploadValidationException is used by the UploadValidators to
|
||||
// stop the uploadPack. We do not want this exception to go beyond this
|
||||
// point otherwise it would print a stacktrace in the logs and return an
|
||||
// internal server error to the client.
|
||||
if (!e.isOutput()) {
|
||||
up.sendMessage(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user