Use custom error messages for Git-over-HTTP
Ensure clients see messages related to contributor agreement not being activated even if they push over HTTP. These used to be put into the server error log, but the new GitSmartHttpTools class in JGit allows formatting it directly to the client. Refactor other things like the server-level receive and upload controls to also report a custom error message that matches with the SSH version of the same code. Change-Id: Idd35853198fcb3103ebb099bab185c0620573e0f
This commit is contained in:
@@ -40,6 +40,8 @@ import com.google.inject.name.Named;
|
|||||||
|
|
||||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||||
import org.eclipse.jgit.http.server.GitServlet;
|
import org.eclipse.jgit.http.server.GitServlet;
|
||||||
|
import org.eclipse.jgit.http.server.GitSmartHttpTools;
|
||||||
|
import org.eclipse.jgit.http.server.ServletUtils;
|
||||||
import org.eclipse.jgit.http.server.resolver.AsIsFileService;
|
import org.eclipse.jgit.http.server.resolver.AsIsFileService;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
@@ -51,8 +53,6 @@ import org.eclipse.jgit.transport.resolver.RepositoryResolver;
|
|||||||
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
|
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
|
||||||
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
|
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
|
||||||
import org.eclipse.jgit.transport.resolver.UploadPackFactory;
|
import org.eclipse.jgit.transport.resolver.UploadPackFactory;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -76,8 +76,6 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class ProjectServlet extends GitServlet {
|
public class ProjectServlet extends GitServlet {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private static final Logger log =
|
|
||||||
LoggerFactory.getLogger(ProjectServlet.class);
|
|
||||||
|
|
||||||
private static final String ATT_CONTROL = ProjectControl.class.getName();
|
private static final String ATT_CONTROL = ProjectControl.class.getName();
|
||||||
private static final String ATT_RC = ReceiveCommits.class.getName();
|
private static final String ATT_RC = ReceiveCommits.class.getName();
|
||||||
@@ -87,8 +85,9 @@ public class ProjectServlet extends GitServlet {
|
|||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
bind(Resolver.class);
|
bind(Resolver.class);
|
||||||
bind(Upload.class);
|
bind(UploadFactory.class);
|
||||||
bind(Receive.class);
|
bind(UploadFilter.class);
|
||||||
|
bind(ReceiveFactory.class);
|
||||||
bind(ReceiveFilter.class);
|
bind(ReceiveFilter.class);
|
||||||
install(new CacheModule() {
|
install(new CacheModule() {
|
||||||
@Override
|
@Override
|
||||||
@@ -103,28 +102,21 @@ public class ProjectServlet extends GitServlet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ProjectControl getProjectControl(ServletRequest req)
|
|
||||||
throws ServiceNotEnabledException {
|
|
||||||
ProjectControl pc = (ProjectControl) req.getAttribute(ATT_CONTROL);
|
|
||||||
if (pc == null) {
|
|
||||||
log.error("No " + ATT_CONTROL + " in request", new Exception("here"));
|
|
||||||
throw new ServiceNotEnabledException();
|
|
||||||
}
|
|
||||||
return pc;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Provider<String> urlProvider;
|
private final Provider<String> urlProvider;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ProjectServlet(final Resolver resolver, final Upload upload,
|
ProjectServlet(final Resolver resolver,
|
||||||
final Receive receive,
|
final UploadFactory upload, final UploadFilter uploadFilter,
|
||||||
final ReceiveFilter receiveFilter,
|
final ReceiveFactory receive, final ReceiveFilter receiveFilter,
|
||||||
@CanonicalWebUrl @Nullable Provider<String> urlProvider) {
|
@CanonicalWebUrl @Nullable Provider<String> urlProvider) {
|
||||||
this.urlProvider = urlProvider;
|
this.urlProvider = urlProvider;
|
||||||
|
|
||||||
setRepositoryResolver(resolver);
|
setRepositoryResolver(resolver);
|
||||||
setAsIsFileService(AsIsFileService.DISABLED);
|
setAsIsFileService(AsIsFileService.DISABLED);
|
||||||
|
|
||||||
setUploadPackFactory(upload);
|
setUploadPackFactory(upload);
|
||||||
|
addUploadPackFilter(uploadFilter);
|
||||||
|
|
||||||
setReceivePackFactory(receive);
|
setReceivePackFactory(receive);
|
||||||
addReceivePackFilter(receiveFilter);
|
addReceivePackFilter(receiveFilter);
|
||||||
}
|
}
|
||||||
@@ -133,20 +125,13 @@ public class ProjectServlet extends GitServlet {
|
|||||||
public void init(ServletConfig config) throws ServletException {
|
public void init(ServletConfig config) throws ServletException {
|
||||||
super.init(config);
|
super.init(config);
|
||||||
|
|
||||||
serveRegex("^/(.*?)/?$").with(new HttpServlet() {
|
serve("*").with(new HttpServlet() {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doGet(HttpServletRequest req, HttpServletResponse rsp)
|
protected void doGet(HttpServletRequest req, HttpServletResponse rsp)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
ProjectControl pc;
|
ProjectControl pc = (ProjectControl) req.getAttribute(ATT_CONTROL);
|
||||||
try {
|
|
||||||
pc = getProjectControl(req);
|
|
||||||
} catch (ServiceNotEnabledException e) {
|
|
||||||
rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Project.NameKey dst = pc.getProject().getNameKey();
|
Project.NameKey dst = pc.getProject().getNameKey();
|
||||||
StringBuilder r = new StringBuilder();
|
StringBuilder r = new StringBuilder();
|
||||||
r.append(urlProvider.get());
|
r.append(urlProvider.get());
|
||||||
@@ -198,8 +183,7 @@ public class ProjectServlet extends GitServlet {
|
|||||||
|
|
||||||
final ProjectControl pc;
|
final ProjectControl pc;
|
||||||
try {
|
try {
|
||||||
final Project.NameKey nameKey = new Project.NameKey(projectName);
|
pc = projectControlFactory.controlFor(new Project.NameKey(projectName));
|
||||||
pc = projectControlFactory.controlFor(nameKey);
|
|
||||||
} catch (NoSuchProjectException err) {
|
} catch (NoSuchProjectException err) {
|
||||||
throw new RepositoryNotFoundException(projectName);
|
throw new RepositoryNotFoundException(projectName);
|
||||||
}
|
}
|
||||||
@@ -216,75 +200,86 @@ public class ProjectServlet extends GitServlet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Upload implements UploadPackFactory<HttpServletRequest> {
|
static class UploadFactory implements UploadPackFactory<HttpServletRequest> {
|
||||||
private final Provider<ReviewDb> db;
|
|
||||||
private final PackConfig packConfig;
|
private final PackConfig packConfig;
|
||||||
private final TagCache tagCache;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
Upload(final Provider<ReviewDb> db, final TransferConfig tc,
|
UploadFactory(final TransferConfig tc) {
|
||||||
final TagCache tagCache) {
|
|
||||||
this.db = db;
|
|
||||||
this.packConfig = tc.getPackConfig();
|
this.packConfig = tc.getPackConfig();
|
||||||
this.tagCache = tagCache;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UploadPack create(HttpServletRequest req, Repository repo)
|
public UploadPack create(HttpServletRequest req, Repository repo) {
|
||||||
throws ServiceNotEnabledException, ServiceNotAuthorizedException {
|
|
||||||
ProjectControl pc = getProjectControl(req);
|
|
||||||
if (!pc.canRunUploadPack()) {
|
|
||||||
throw new ServiceNotAuthorizedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
// The Resolver above already checked READ access for us.
|
|
||||||
//
|
|
||||||
UploadPack up = new UploadPack(repo);
|
UploadPack up = new UploadPack(repo);
|
||||||
up.setPackConfig(packConfig);
|
up.setPackConfig(packConfig);
|
||||||
if (!pc.allRefsAreVisible()) {
|
|
||||||
up.setRefFilter(new VisibleRefFilter(tagCache, repo, pc, db.get(), true));
|
|
||||||
}
|
|
||||||
return up;
|
return up;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Receive implements ReceivePackFactory<HttpServletRequest> {
|
static class UploadFilter implements Filter {
|
||||||
|
private final Provider<ReviewDb> db;
|
||||||
|
private final TagCache tagCache;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
UploadFilter(Provider<ReviewDb> db, TagCache tagCache) {
|
||||||
|
this.db = db;
|
||||||
|
this.tagCache = tagCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest request, ServletResponse response,
|
||||||
|
FilterChain next) throws IOException, ServletException {
|
||||||
|
// The Resolver above already checked READ access for us.
|
||||||
|
Repository repo = ServletUtils.getRepository(request);
|
||||||
|
ProjectControl pc = (ProjectControl) request.getAttribute(ATT_CONTROL);
|
||||||
|
UploadPack up = (UploadPack) request.getAttribute(ServletUtils.ATTRIBUTE_HANDLER);
|
||||||
|
|
||||||
|
if (!pc.canRunUploadPack()) {
|
||||||
|
GitSmartHttpTools.sendError((HttpServletRequest) request,
|
||||||
|
(HttpServletResponse) response, HttpServletResponse.SC_FORBIDDEN,
|
||||||
|
"upload-pack not permitted on this server");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pc.allRefsAreVisible()) {
|
||||||
|
up.setRefFilter(new VisibleRefFilter(tagCache, repo, pc, db.get(), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
next.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(FilterConfig config) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ReceiveFactory implements ReceivePackFactory<HttpServletRequest> {
|
||||||
private final ReceiveCommits.Factory factory;
|
private final ReceiveCommits.Factory factory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
Receive(final ReceiveCommits.Factory factory) {
|
ReceiveFactory(final ReceiveCommits.Factory factory) {
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReceivePack create(HttpServletRequest req, Repository db)
|
public ReceivePack create(HttpServletRequest req, Repository db)
|
||||||
throws ServiceNotEnabledException, ServiceNotAuthorizedException {
|
throws ServiceNotAuthorizedException {
|
||||||
final ProjectControl pc = getProjectControl(req);
|
final ProjectControl pc = (ProjectControl) req.getAttribute(ATT_CONTROL);
|
||||||
if (!pc.canRunReceivePack()) {
|
|
||||||
|
if (!(pc.getCurrentUser() instanceof IdentifiedUser)) {
|
||||||
|
// Anonymous users are not permitted to push.
|
||||||
throw new ServiceNotAuthorizedException();
|
throw new ServiceNotAuthorizedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pc.getCurrentUser() instanceof IdentifiedUser) {
|
final IdentifiedUser user = (IdentifiedUser) pc.getCurrentUser();
|
||||||
final IdentifiedUser user = (IdentifiedUser) pc.getCurrentUser();
|
final ReceiveCommits rc = factory.create(pc, db);
|
||||||
final ReceiveCommits rc = factory.create(pc, db);
|
rc.getReceivePack().setRefLogIdent(user.newRefLogIdent());
|
||||||
final Capable s = rc.canUpload();
|
req.setAttribute(ATT_RC, rc);
|
||||||
if (s != Capable.OK) {
|
return rc.getReceivePack();
|
||||||
// TODO We should alert the user to this message on the HTTP
|
|
||||||
// response channel, assuming Git will even report it to them.
|
|
||||||
//
|
|
||||||
final String who = user.getUserName();
|
|
||||||
final String why = s.getMessage();
|
|
||||||
log.warn("Rejected push from " + who + ": " + why);
|
|
||||||
throw new ServiceNotEnabledException();
|
|
||||||
}
|
|
||||||
|
|
||||||
rc.getReceivePack().setRefLogIdent(user.newRefLogIdent());
|
|
||||||
req.setAttribute(ATT_RC, rc);
|
|
||||||
return rc.getReceivePack();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
throw new ServiceNotAuthorizedException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,15 +300,24 @@ public class ProjectServlet extends GitServlet {
|
|||||||
|
|
||||||
ReceiveCommits rc = (ReceiveCommits) request.getAttribute(ATT_RC);
|
ReceiveCommits rc = (ReceiveCommits) request.getAttribute(ATT_RC);
|
||||||
ReceivePack rp = rc.getReceivePack();
|
ReceivePack rp = rc.getReceivePack();
|
||||||
ProjectControl pc;
|
ProjectControl pc = (ProjectControl) request.getAttribute(ATT_CONTROL);
|
||||||
try {
|
IdentifiedUser user = (IdentifiedUser) pc.getCurrentUser();
|
||||||
pc = getProjectControl(request);
|
Project.NameKey projectName = pc.getProject().getNameKey();
|
||||||
} catch (ServiceNotEnabledException e) {
|
|
||||||
// This shouldn't occur, the parent should have stopped processing.
|
if (!pc.canRunReceivePack()) {
|
||||||
throw new ServletException(e);
|
GitSmartHttpTools.sendError((HttpServletRequest) request,
|
||||||
|
(HttpServletResponse) response, HttpServletResponse.SC_FORBIDDEN,
|
||||||
|
"receive-pack not permitted on this server");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Project.NameKey projectName = pc.getProject().getNameKey();
|
final Capable s = rc.canUpload();
|
||||||
|
if (s != Capable.OK) {
|
||||||
|
GitSmartHttpTools.sendError((HttpServletRequest) request,
|
||||||
|
(HttpServletResponse) response, HttpServletResponse.SC_FORBIDDEN,
|
||||||
|
s.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!rp.isCheckReferencedObjectsAreReachable()) {
|
if (!rp.isCheckReferencedObjectsAreReachable()) {
|
||||||
if (isGet) {
|
if (isGet) {
|
||||||
@@ -352,7 +356,7 @@ public class ProjectServlet extends GitServlet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(FilterConfig arg0) throws ServletException {
|
public void init(FilterConfig arg0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user