You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SmartClientSmartServerSslTest.java 9.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. /*
  2. * Copyright (C) 2017, 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.http.test;
  11. import static org.junit.Assert.assertEquals;
  12. import static org.junit.Assert.assertFalse;
  13. import static org.junit.Assert.assertTrue;
  14. import static org.junit.Assert.fail;
  15. import java.io.IOException;
  16. import java.util.EnumSet;
  17. import java.util.List;
  18. import javax.servlet.DispatcherType;
  19. import javax.servlet.Filter;
  20. import javax.servlet.FilterChain;
  21. import javax.servlet.FilterConfig;
  22. import javax.servlet.ServletException;
  23. import javax.servlet.ServletRequest;
  24. import javax.servlet.ServletResponse;
  25. import javax.servlet.http.HttpServletRequest;
  26. import javax.servlet.http.HttpServletResponse;
  27. import org.eclipse.jetty.servlet.FilterHolder;
  28. import org.eclipse.jetty.servlet.ServletContextHandler;
  29. import org.eclipse.jetty.servlet.ServletHolder;
  30. import org.eclipse.jgit.errors.TransportException;
  31. import org.eclipse.jgit.errors.UnsupportedCredentialItem;
  32. import org.eclipse.jgit.http.server.GitServlet;
  33. import org.eclipse.jgit.junit.TestRepository;
  34. import org.eclipse.jgit.junit.http.AccessEvent;
  35. import org.eclipse.jgit.junit.http.AppServer;
  36. import org.eclipse.jgit.lib.ConfigConstants;
  37. import org.eclipse.jgit.lib.NullProgressMonitor;
  38. import org.eclipse.jgit.lib.Repository;
  39. import org.eclipse.jgit.revwalk.RevBlob;
  40. import org.eclipse.jgit.revwalk.RevCommit;
  41. import org.eclipse.jgit.transport.CredentialItem;
  42. import org.eclipse.jgit.transport.CredentialsProvider;
  43. import org.eclipse.jgit.transport.Transport;
  44. import org.eclipse.jgit.transport.URIish;
  45. import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
  46. import org.eclipse.jgit.util.HttpSupport;
  47. import org.junit.Before;
  48. import org.junit.Test;
  49. import org.junit.runner.RunWith;
  50. import org.junit.runners.Parameterized;
  51. @RunWith(Parameterized.class)
  52. public class SmartClientSmartServerSslTest extends AllProtocolsHttpTestCase {
  53. // We run these tests with a server on localhost with a self-signed
  54. // certificate. We don't do authentication tests here, so there's no need
  55. // for username and password.
  56. //
  57. // But the server certificate will not validate. We know that Transport will
  58. // ask whether we trust the server all the same. This credentials provider
  59. // blindly trusts the self-signed certificate by answering "Yes" to all
  60. // questions.
  61. private CredentialsProvider testCredentials = new CredentialsProvider() {
  62. @Override
  63. public boolean isInteractive() {
  64. return false;
  65. }
  66. @Override
  67. public boolean supports(CredentialItem... items) {
  68. for (CredentialItem item : items) {
  69. if (item instanceof CredentialItem.InformationalMessage) {
  70. continue;
  71. }
  72. if (item instanceof CredentialItem.YesNoType) {
  73. continue;
  74. }
  75. return false;
  76. }
  77. return true;
  78. }
  79. @Override
  80. public boolean get(URIish uri, CredentialItem... items)
  81. throws UnsupportedCredentialItem {
  82. for (CredentialItem item : items) {
  83. if (item instanceof CredentialItem.InformationalMessage) {
  84. continue;
  85. }
  86. if (item instanceof CredentialItem.YesNoType) {
  87. ((CredentialItem.YesNoType) item).setValue(true);
  88. continue;
  89. }
  90. return false;
  91. }
  92. return true;
  93. }
  94. };
  95. private URIish remoteURI;
  96. private URIish secureURI;
  97. private RevBlob A_txt;
  98. private RevCommit A, B;
  99. public SmartClientSmartServerSslTest(TestParameters params) {
  100. super(params);
  101. }
  102. @Override
  103. protected AppServer createServer() {
  104. return new AppServer(0, 0);
  105. }
  106. @Override
  107. @Before
  108. public void setUp() throws Exception {
  109. super.setUp();
  110. final TestRepository<Repository> src = createTestRepository();
  111. final String srcName = src.getRepository().getDirectory().getName();
  112. src.getRepository()
  113. .getConfig()
  114. .setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
  115. ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true);
  116. if (enableProtocolV2) {
  117. src.getRepository().getConfig().setInt("protocol", null, "version",
  118. 2);
  119. }
  120. GitServlet gs = new GitServlet();
  121. ServletContextHandler app = addNormalContext(gs, src, srcName);
  122. server.setUp();
  123. remoteURI = toURIish(app, srcName);
  124. secureURI = new URIish(rewriteUrl(remoteURI.toString(), "https",
  125. server.getSecurePort()));
  126. A_txt = src.blob("A");
  127. A = src.commit().add("A_txt", A_txt).create();
  128. B = src.commit().parent(A).add("A_txt", "C").add("B", "B").create();
  129. src.update(master, B);
  130. src.update("refs/garbage/a/very/long/ref/name/to/compress", B);
  131. }
  132. private ServletContextHandler addNormalContext(GitServlet gs, TestRepository<Repository> src, String srcName) {
  133. ServletContextHandler app = server.addContext("/git");
  134. app.addFilter(new FilterHolder(new Filter() {
  135. @Override
  136. public void init(FilterConfig filterConfig)
  137. throws ServletException {
  138. // empty
  139. }
  140. // Redirects http to https for requests containing "/https/".
  141. @Override
  142. public void doFilter(ServletRequest request,
  143. ServletResponse response, FilterChain chain)
  144. throws IOException, ServletException {
  145. final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
  146. final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  147. final StringBuffer fullUrl = httpServletRequest.getRequestURL();
  148. if (httpServletRequest.getQueryString() != null) {
  149. fullUrl.append("?")
  150. .append(httpServletRequest.getQueryString());
  151. }
  152. String urlString = rewriteUrl(fullUrl.toString(), "https",
  153. server.getSecurePort());
  154. httpServletResponse
  155. .setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
  156. httpServletResponse.setHeader(HttpSupport.HDR_LOCATION,
  157. urlString.replace("/https/", "/"));
  158. }
  159. @Override
  160. public void destroy() {
  161. // empty
  162. }
  163. }), "/https/*", EnumSet.of(DispatcherType.REQUEST));
  164. app.addFilter(new FilterHolder(new Filter() {
  165. @Override
  166. public void init(FilterConfig filterConfig)
  167. throws ServletException {
  168. // empty
  169. }
  170. // Redirects https back to http for requests containing "/back/".
  171. @Override
  172. public void doFilter(ServletRequest request,
  173. ServletResponse response, FilterChain chain)
  174. throws IOException, ServletException {
  175. final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
  176. final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  177. final StringBuffer fullUrl = httpServletRequest.getRequestURL();
  178. if (httpServletRequest.getQueryString() != null) {
  179. fullUrl.append("?")
  180. .append(httpServletRequest.getQueryString());
  181. }
  182. String urlString = rewriteUrl(fullUrl.toString(), "http",
  183. server.getPort());
  184. httpServletResponse
  185. .setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
  186. httpServletResponse.setHeader(HttpSupport.HDR_LOCATION,
  187. urlString.replace("/back/", "/"));
  188. }
  189. @Override
  190. public void destroy() {
  191. // empty
  192. }
  193. }), "/back/*", EnumSet.of(DispatcherType.REQUEST));
  194. gs.setRepositoryResolver(new TestRepositoryResolver(src, srcName));
  195. app.addServlet(new ServletHolder(gs), "/*");
  196. return app;
  197. }
  198. @Test
  199. public void testInitialClone_ViaHttps() throws Exception {
  200. Repository dst = createBareRepository();
  201. assertFalse(dst.getObjectDatabase().has(A_txt));
  202. try (Transport t = Transport.open(dst, secureURI)) {
  203. t.setCredentialsProvider(testCredentials);
  204. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  205. }
  206. assertTrue(dst.getObjectDatabase().has(A_txt));
  207. assertEquals(B, dst.exactRef(master).getObjectId());
  208. fsck(dst, B);
  209. List<AccessEvent> requests = getRequests();
  210. assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
  211. }
  212. @Test
  213. public void testInitialClone_RedirectToHttps() throws Exception {
  214. Repository dst = createBareRepository();
  215. assertFalse(dst.getObjectDatabase().has(A_txt));
  216. URIish cloneFrom = extendPath(remoteURI, "/https");
  217. try (Transport t = Transport.open(dst, cloneFrom)) {
  218. t.setCredentialsProvider(testCredentials);
  219. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  220. }
  221. assertTrue(dst.getObjectDatabase().has(A_txt));
  222. assertEquals(B, dst.exactRef(master).getObjectId());
  223. fsck(dst, B);
  224. List<AccessEvent> requests = getRequests();
  225. assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
  226. }
  227. @Test
  228. public void testInitialClone_RedirectBackToHttp() throws Exception {
  229. Repository dst = createBareRepository();
  230. assertFalse(dst.getObjectDatabase().has(A_txt));
  231. URIish cloneFrom = extendPath(secureURI, "/back");
  232. try (Transport t = Transport.open(dst, cloneFrom)) {
  233. t.setCredentialsProvider(testCredentials);
  234. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  235. fail("Should have failed (redirect from https to http)");
  236. } catch (TransportException e) {
  237. assertTrue(e.getMessage().contains("not allowed"));
  238. }
  239. }
  240. @Test
  241. public void testInitialClone_SslFailure() throws Exception {
  242. Repository dst = createBareRepository();
  243. assertFalse(dst.getObjectDatabase().has(A_txt));
  244. try (Transport t = Transport.open(dst, secureURI)) {
  245. // Set a credentials provider that doesn't handle questions
  246. t.setCredentialsProvider(
  247. new UsernamePasswordCredentialsProvider("any", "anypwd"));
  248. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  249. fail("Should have failed (SSL certificate not trusted)");
  250. } catch (TransportException e) {
  251. assertTrue(e.getMessage().contains("Secure connection"));
  252. }
  253. }
  254. }