diff options
5 files changed, 482 insertions, 5 deletions
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitServlet.java index 1ce2776e4d..1e8a51192b 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitServlet.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitServlet.java @@ -47,14 +47,19 @@ import java.io.File; import javax.servlet.ServletConfig; import javax.servlet.ServletException; +import javax.servlet.http.HttpServletResponse; +import org.eclipse.jgit.http.server.glue.ErrorServlet; import org.eclipse.jgit.http.server.glue.MetaServlet; import org.eclipse.jgit.http.server.glue.RegexGroupFilter; import org.eclipse.jgit.http.server.glue.ServletBinder; +import org.eclipse.jgit.http.server.resolver.DefaultReceivePackFactory; import org.eclipse.jgit.http.server.resolver.FileResolver; import org.eclipse.jgit.http.server.resolver.AsIsFileService; +import org.eclipse.jgit.http.server.resolver.ReceivePackFactory; import org.eclipse.jgit.http.server.resolver.RepositoryResolver; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.transport.ReceivePack; import org.eclipse.jgit.util.StringUtils; /** @@ -100,6 +105,8 @@ public class GitServlet extends MetaServlet { private AsIsFileService asIs = new AsIsFileService(); + private ReceivePackFactory receivePackFactory = new DefaultReceivePackFactory(); + /** * New servlet that will load its base directory from {@code web.xml}. * <p> @@ -135,6 +142,16 @@ public class GitServlet extends MetaServlet { this.asIs = f != null ? f : AsIsFileService.DISABLED; } + /** + * @param f + * the factory to construct and configure a {@link ReceivePack} + * session when a push is requested by a client. + */ + public void setReceivePackFactory(ReceivePackFactory f) { + assertNotInitialized(); + this.receivePackFactory = f != null ? f : ReceivePackFactory.DISABLED; + } + private void assertNotInitialized() { if (initialized) throw new IllegalStateException("Already initialized by container"); @@ -152,15 +169,27 @@ public class GitServlet extends MetaServlet { initialized = true; + if (receivePackFactory != ReceivePackFactory.DISABLED) { + serve("*/git-receive-pack")// + .with(new ReceivePackServlet(receivePackFactory)); + } + + ServletBinder refs = serve("*/" + Constants.INFO_REFS); + if (receivePackFactory != ReceivePackFactory.DISABLED) { + refs = refs.through(// + new ReceivePackServlet.InfoRefs(receivePackFactory)); + } + if (asIs != AsIsFileService.DISABLED) { + refs = refs.through(new IsLocalFilter()); + refs = refs.through(new AsIsFileFilter(asIs)); + refs.with(new InfoRefsServlet()); + } else + refs.with(new ErrorServlet(HttpServletResponse.SC_FORBIDDEN)); + if (asIs != AsIsFileService.DISABLED) { final IsLocalFilter mustBeLocal = new IsLocalFilter(); final AsIsFileFilter enabled = new AsIsFileFilter(asIs); - serve("*/" + Constants.INFO_REFS)// - .through(mustBeLocal)// - .through(enabled)// - .with(new InfoRefsServlet()); - serve("*/" + Constants.HEAD)// .through(mustBeLocal)// .through(enabled)// diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java new file mode 100644 index 0000000000..1a426af96d --- /dev/null +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2009-2010, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.http.server; + +import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; +import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; +import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; +import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE; +import static org.eclipse.jgit.http.server.ServletUtils.getInputStream; +import static org.eclipse.jgit.http.server.ServletUtils.getRepository; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jgit.http.server.resolver.ReceivePackFactory; +import org.eclipse.jgit.http.server.resolver.ServiceNotAuthorizedException; +import org.eclipse.jgit.http.server.resolver.ServiceNotEnabledException; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.ReceivePack; +import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser; + +/** Server side implementation of smart push over HTTP. */ +class ReceivePackServlet extends HttpServlet { + private static final String REQ_TYPE = "application/x-git-receive-pack-request"; + + private static final String RSP_TYPE = "application/x-git-receive-pack-result"; + + private static final long serialVersionUID = 1L; + + static class InfoRefs extends SmartServiceInfoRefs { + private final ReceivePackFactory receivePackFactory; + + InfoRefs(final ReceivePackFactory receivePackFactory) { + super("git-receive-pack"); + this.receivePackFactory = receivePackFactory; + } + + @Override + protected void advertise(HttpServletRequest req, Repository db, + PacketLineOutRefAdvertiser pck) throws IOException, + ServiceNotEnabledException, ServiceNotAuthorizedException { + receivePackFactory.create(req, db).sendAdvertisedRefs(pck); + } + } + + private final ReceivePackFactory receivePackFactory; + + ReceivePackServlet(final ReceivePackFactory receivePackFactory) { + this.receivePackFactory = receivePackFactory; + } + + @Override + public void doPost(final HttpServletRequest req, + final HttpServletResponse rsp) throws IOException { + if (!REQ_TYPE.equals(req.getContentType())) { + rsp.sendError(SC_UNSUPPORTED_MEDIA_TYPE); + return; + } + + final Repository db = getRepository(req); + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + final ReceivePack rp = receivePackFactory.create(req, db); + rp.setBiDirectionalPipe(false); + rp.receive(getInputStream(req), out, null); + + } catch (ServiceNotAuthorizedException e) { + rsp.sendError(SC_UNAUTHORIZED); + return; + + } catch (ServiceNotEnabledException e) { + rsp.sendError(SC_FORBIDDEN); + return; + + } catch (IOException e) { + getServletContext().log("Internal error during receive-pack", e); + rsp.sendError(SC_INTERNAL_SERVER_ERROR); + return; + } + + reply(rsp, out.toByteArray()); + } + + private void reply(final HttpServletResponse rsp, final byte[] result) + throws IOException { + rsp.setContentType(RSP_TYPE); + rsp.setContentLength(result.length); + final OutputStream os = rsp.getOutputStream(); + try { + os.write(result); + os.flush(); + } finally { + os.close(); + } + } +} diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java new file mode 100644 index 0000000000..96716d0703 --- /dev/null +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2009-2010, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.http.server; + +import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; +import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; +import static org.eclipse.jgit.http.server.ServletUtils.getRepository; +import static org.eclipse.jgit.http.server.ServletUtils.send; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jgit.http.server.resolver.ServiceNotAuthorizedException; +import org.eclipse.jgit.http.server.resolver.ServiceNotEnabledException; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.PacketLineOut; +import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser; + +/** Filter in front of {@link InfoRefsServlet} to catch smart service requests. */ +abstract class SmartServiceInfoRefs implements Filter { + private final String svc; + + SmartServiceInfoRefs(final String service) { + this.svc = service; + } + + public void init(FilterConfig config) throws ServletException { + // Do nothing. + } + + public void destroy() { + // Do nothing. + } + + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + final HttpServletRequest req = (HttpServletRequest) request; + + if (svc.equals(req.getParameter("service"))) { + final HttpServletResponse rsp = (HttpServletResponse) response; + try { + final Repository db = getRepository(req); + final ByteArrayOutputStream buf = new ByteArrayOutputStream(); + final PacketLineOut out = new PacketLineOut(buf); + out.writeString("# service=" + svc + "\n"); + out.end(); + advertise(req, db, new PacketLineOutRefAdvertiser(out)); + rsp.setContentType("application/x-" + svc + "-advertisement"); + send(buf.toByteArray(), req, rsp); + } catch (ServiceNotAuthorizedException e) { + rsp.sendError(SC_UNAUTHORIZED); + + } catch (ServiceNotEnabledException e) { + rsp.sendError(SC_FORBIDDEN); + } + } else { + chain.doFilter(request, response); + } + } + + protected abstract void advertise(HttpServletRequest req, Repository db, + PacketLineOutRefAdvertiser pck) throws IOException, + ServiceNotEnabledException, ServiceNotAuthorizedException; +} diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/DefaultReceivePackFactory.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/DefaultReceivePackFactory.java new file mode 100644 index 0000000000..2495751da2 --- /dev/null +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/DefaultReceivePackFactory.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2009-2010, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.http.server.resolver; + +import javax.servlet.http.HttpServletRequest; + +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.Config.SectionParser; +import org.eclipse.jgit.transport.ReceivePack; + +/** + * Create and configure {@link ReceivePack} service instance. + * <p> + * Writing by receive-pack is permitted if any of the following is true: + * <ul> + * <li>The container has authenticated the user and set + * {@link HttpServletRequest#getRemoteUser()} to the authenticated name. + * <li>The repository configuration file has {@code http.receivepack} explicitly + * set to true. + * </ul> + * and explicitly rejected otherwise. + */ +public class DefaultReceivePackFactory implements ReceivePackFactory { + private static final SectionParser<ServiceConfig> CONFIG = new SectionParser<ServiceConfig>() { + public ServiceConfig parse(final Config cfg) { + return new ServiceConfig(cfg); + } + }; + + private static class ServiceConfig { + final boolean set; + + final boolean enabled; + + ServiceConfig(final Config cfg) { + set = cfg.getString("http", null, "receivepack") != null; + enabled = cfg.getBoolean("http", "receivepack", false); + } + } + + public ReceivePack create(final HttpServletRequest req, final Repository db) + throws ServiceNotEnabledException, ServiceNotAuthorizedException { + final ServiceConfig cfg = db.getConfig().get(CONFIG); + String user = req.getRemoteUser(); + + if (cfg.set) { + if (cfg.enabled) { + if (user == null || "".equals(user)) + user = "anonymous"; + return createFor(req, db, user); + } + throw new ServiceNotEnabledException(); + } + + if (user != null && !"".equals(user)) + return createFor(req, db, user); + throw new ServiceNotAuthorizedException(); + } + + private ReceivePack createFor(final HttpServletRequest req, + final Repository db, final String user) { + final ReceivePack rp = new ReceivePack(db); + rp.setRefLogIdent(toPersonIdent(req, user)); + return rp; + } + + private static PersonIdent toPersonIdent(HttpServletRequest req, String user) { + return new PersonIdent(user, user + "@" + req.getRemoteHost()); + } +} diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/ReceivePackFactory.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/ReceivePackFactory.java new file mode 100644 index 0000000000..2432632c01 --- /dev/null +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/ReceivePackFactory.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2009-2010, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.http.server.resolver; + +import javax.servlet.http.HttpServletRequest; + +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.ReceivePack; + +/** Create and configure {@link ReceivePack} service instance. */ +public interface ReceivePackFactory { + /** A factory disabling the ReceivePack service for all repositories. */ + public static final ReceivePackFactory DISABLED = new ReceivePackFactory() { + public ReceivePack create(HttpServletRequest req, Repository db) + throws ServiceNotEnabledException { + throw new ServiceNotEnabledException(); + } + }; + + /** + * Create and configure a new ReceivePack instance for a repository. + * + * @param req + * current HTTP request, in case information from the request may + * help configure the ReceivePack instance. + * @param db + * the repository the receive would write into. + * @return the newly configured ReceivePack instance, must not be null. + * @throws ServiceNotEnabledException + * this factory refuses to create the instance because it is not + * allowed on the target repository, by any user. + * @throws ServiceNotAuthorizedException + * this factory refuses to create the instance for this HTTP + * request and repository, such as due to a permission error. + */ + ReceivePack create(HttpServletRequest req, Repository db) + throws ServiceNotEnabledException, ServiceNotAuthorizedException; +} |