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.

PushCertificateStoreTest.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. /*
  2. * Copyright (C) 2015, Google Inc. 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.transport;
  11. import static java.nio.charset.StandardCharsets.UTF_8;
  12. import static org.eclipse.jgit.lib.ObjectId.zeroId;
  13. import static org.eclipse.jgit.lib.RefUpdate.Result.FAST_FORWARD;
  14. import static org.eclipse.jgit.lib.RefUpdate.Result.LOCK_FAILURE;
  15. import static org.eclipse.jgit.lib.RefUpdate.Result.NEW;
  16. import static org.eclipse.jgit.lib.RefUpdate.Result.NO_CHANGE;
  17. import static org.junit.Assert.assertEquals;
  18. import static org.junit.Assert.assertFalse;
  19. import static org.junit.Assert.assertTrue;
  20. import java.io.ByteArrayInputStream;
  21. import java.io.IOException;
  22. import java.io.InputStreamReader;
  23. import java.util.ArrayList;
  24. import java.util.Arrays;
  25. import java.util.Collections;
  26. import java.util.List;
  27. import java.util.concurrent.atomic.AtomicInteger;
  28. import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
  29. import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
  30. import org.eclipse.jgit.lib.BatchRefUpdate;
  31. import org.eclipse.jgit.lib.Constants;
  32. import org.eclipse.jgit.lib.NullProgressMonitor;
  33. import org.eclipse.jgit.lib.ObjectId;
  34. import org.eclipse.jgit.lib.PersonIdent;
  35. import org.eclipse.jgit.revwalk.RevCommit;
  36. import org.eclipse.jgit.revwalk.RevWalk;
  37. import org.junit.Before;
  38. import org.junit.Test;
  39. public class PushCertificateStoreTest {
  40. private static final ObjectId ID1 =
  41. ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
  42. private static final ObjectId ID2 =
  43. ObjectId.fromString("badc0ffebadc0ffebadc0ffebadc0ffebadc0ffe");
  44. private static PushCertificate newCert(String... updateLines) {
  45. StringBuilder cert = new StringBuilder(
  46. "certificate version 0.1\n"
  47. + "pusher Dave Borowitz <dborowitz@google.com> 1433954361 -0700\n"
  48. + "pushee git://localhost/repo.git\n"
  49. + "nonce 1433954361-bde756572d665bba81d8\n"
  50. + "\n");
  51. for (String updateLine : updateLines) {
  52. cert.append(updateLine).append('\n');
  53. }
  54. cert.append(
  55. "-----BEGIN PGP SIGNATURE-----\n"
  56. + "DUMMY/SIGNATURE\n"
  57. + "-----END PGP SIGNATURE-----\n");
  58. try {
  59. return PushCertificateParser.fromReader(new InputStreamReader(
  60. new ByteArrayInputStream(
  61. Constants.encode(cert.toString())),
  62. UTF_8));
  63. } catch (IOException e) {
  64. throw new IllegalArgumentException(e);
  65. }
  66. }
  67. private static String command(ObjectId oldId, ObjectId newId, String ref) {
  68. return oldId.name() + " " + newId.name() + " " + ref;
  69. }
  70. private AtomicInteger ts = new AtomicInteger(1433954361);
  71. private InMemoryRepository repo;
  72. private PushCertificateStore store;
  73. @Before
  74. public void setUp() throws Exception {
  75. repo = new InMemoryRepository(new DfsRepositoryDescription("repo"));
  76. store = newStore();
  77. }
  78. @Test
  79. public void missingRef() throws Exception {
  80. assertCerts("refs/heads/master");
  81. }
  82. @Test
  83. public void saveNoChange() throws Exception {
  84. assertEquals(NO_CHANGE, store.save());
  85. }
  86. @Test
  87. public void saveOneCertOnOneRef() throws Exception {
  88. PersonIdent ident = newIdent();
  89. PushCertificate addMaster = newCert(
  90. command(zeroId(), ID1, "refs/heads/master"));
  91. store.put(addMaster, ident);
  92. assertEquals(NEW, store.save());
  93. assertCerts("refs/heads/master", addMaster);
  94. assertCerts("refs/heads/branch");
  95. try (RevWalk rw = new RevWalk(repo)) {
  96. RevCommit c = rw.parseCommit(repo.resolve(PushCertificateStore.REF_NAME));
  97. rw.parseBody(c);
  98. assertEquals("Store push certificate for refs/heads/master\n",
  99. c.getFullMessage());
  100. assertEquals(ident, c.getAuthorIdent());
  101. assertEquals(ident, c.getCommitterIdent());
  102. }
  103. }
  104. @Test
  105. public void saveTwoCertsOnSameRefInTwoUpdates() throws Exception {
  106. PushCertificate addMaster = newCert(
  107. command(zeroId(), ID1, "refs/heads/master"));
  108. store.put(addMaster, newIdent());
  109. assertEquals(NEW, store.save());
  110. PushCertificate updateMaster = newCert(
  111. command(ID1, ID2, "refs/heads/master"));
  112. store.put(updateMaster, newIdent());
  113. assertEquals(FAST_FORWARD, store.save());
  114. assertCerts("refs/heads/master", updateMaster, addMaster);
  115. }
  116. @Test
  117. public void saveTwoCertsOnSameRefInOneUpdate() throws Exception {
  118. PersonIdent ident1 = newIdent();
  119. PersonIdent ident2 = newIdent();
  120. PushCertificate updateMaster = newCert(
  121. command(ID1, ID2, "refs/heads/master"));
  122. store.put(updateMaster, ident2);
  123. PushCertificate addMaster = newCert(
  124. command(zeroId(), ID1, "refs/heads/master"));
  125. store.put(addMaster, ident1);
  126. assertEquals(NEW, store.save());
  127. assertCerts("refs/heads/master", updateMaster, addMaster);
  128. }
  129. @Test
  130. public void saveTwoCertsOnDifferentRefsInOneUpdate() throws Exception {
  131. PersonIdent ident1 = newIdent();
  132. PersonIdent ident3 = newIdent();
  133. PushCertificate addBranch = newCert(
  134. command(zeroId(), ID1, "refs/heads/branch"));
  135. store.put(addBranch, ident3);
  136. PushCertificate addMaster = newCert(
  137. command(zeroId(), ID1, "refs/heads/master"));
  138. store.put(addMaster, ident1);
  139. assertEquals(NEW, store.save());
  140. assertCerts("refs/heads/master", addMaster);
  141. assertCerts("refs/heads/branch", addBranch);
  142. }
  143. @Test
  144. public void saveTwoCertsOnDifferentRefsInTwoUpdates() throws Exception {
  145. PushCertificate addMaster = newCert(
  146. command(zeroId(), ID1, "refs/heads/master"));
  147. store.put(addMaster, newIdent());
  148. assertEquals(NEW, store.save());
  149. PushCertificate addBranch = newCert(
  150. command(zeroId(), ID1, "refs/heads/branch"));
  151. store.put(addBranch, newIdent());
  152. assertEquals(FAST_FORWARD, store.save());
  153. assertCerts("refs/heads/master", addMaster);
  154. assertCerts("refs/heads/branch", addBranch);
  155. }
  156. @Test
  157. public void saveOneCertOnMultipleRefs() throws Exception {
  158. PersonIdent ident = newIdent();
  159. PushCertificate addMasterAndBranch = newCert(
  160. command(zeroId(), ID1, "refs/heads/branch"),
  161. command(zeroId(), ID2, "refs/heads/master"));
  162. store.put(addMasterAndBranch, ident);
  163. assertEquals(NEW, store.save());
  164. assertCerts("refs/heads/master", addMasterAndBranch);
  165. assertCerts("refs/heads/branch", addMasterAndBranch);
  166. try (RevWalk rw = new RevWalk(repo)) {
  167. RevCommit c = rw.parseCommit(repo.resolve(PushCertificateStore.REF_NAME));
  168. rw.parseBody(c);
  169. assertEquals("Store push certificate for 2 refs\n", c.getFullMessage());
  170. assertEquals(ident, c.getAuthorIdent());
  171. assertEquals(ident, c.getCommitterIdent());
  172. }
  173. }
  174. @Test
  175. public void changeRefFileToDirectory() throws Exception {
  176. PushCertificate deleteRefsHeads = newCert(
  177. command(ID1, zeroId(), "refs/heads"));
  178. store.put(deleteRefsHeads, newIdent());
  179. PushCertificate addMaster = newCert(
  180. command(zeroId(), ID1, "refs/heads/master"));
  181. store.put(addMaster, newIdent());
  182. assertEquals(NEW, store.save());
  183. assertCerts("refs/heads", deleteRefsHeads);
  184. assertCerts("refs/heads/master", addMaster);
  185. }
  186. @Test
  187. public void getBeforeSaveDoesNotIncludePending() throws Exception {
  188. PushCertificate addMaster = newCert(
  189. command(zeroId(), ID1, "refs/heads/master"));
  190. store.put(addMaster, newIdent());
  191. assertEquals(NEW, store.save());
  192. PushCertificate updateMaster = newCert(
  193. command(ID1, ID2, "refs/heads/master"));
  194. store.put(updateMaster, newIdent());
  195. assertCerts("refs/heads/master", addMaster);
  196. assertEquals(FAST_FORWARD, store.save());
  197. assertCerts("refs/heads/master", updateMaster, addMaster);
  198. }
  199. @Test
  200. public void lockFailure() throws Exception {
  201. PushCertificateStore store1 = store;
  202. PushCertificateStore store2 = newStore();
  203. store2.get("refs/heads/master");
  204. PushCertificate addMaster = newCert(
  205. command(zeroId(), ID1, "refs/heads/master"));
  206. store1.put(addMaster, newIdent());
  207. assertEquals(NEW, store1.save());
  208. PushCertificate addBranch = newCert(
  209. command(zeroId(), ID2, "refs/heads/branch"));
  210. store2.put(addBranch, newIdent());
  211. assertEquals(LOCK_FAILURE, store2.save());
  212. // Reread ref after lock failure.
  213. assertCerts(store2, "refs/heads/master", addMaster);
  214. assertCerts(store2, "refs/heads/branch");
  215. assertEquals(FAST_FORWARD, store2.save());
  216. assertCerts(store2, "refs/heads/master", addMaster);
  217. assertCerts(store2, "refs/heads/branch", addBranch);
  218. }
  219. @Test
  220. public void saveInBatch() throws Exception {
  221. BatchRefUpdate batch = repo.getRefDatabase().newBatchUpdate();
  222. assertFalse(store.save(batch));
  223. assertEquals(0, batch.getCommands().size());
  224. PushCertificate addMaster = newCert(
  225. command(zeroId(), ID1, "refs/heads/master"));
  226. store.put(addMaster, newIdent());
  227. assertTrue(store.save(batch));
  228. List<ReceiveCommand> commands = batch.getCommands();
  229. assertEquals(1, commands.size());
  230. ReceiveCommand cmd = commands.get(0);
  231. assertEquals("refs/meta/push-certs", cmd.getRefName());
  232. assertEquals(ReceiveCommand.Result.NOT_ATTEMPTED, cmd.getResult());
  233. try (RevWalk rw = new RevWalk(repo)) {
  234. batch.execute(rw, NullProgressMonitor.INSTANCE);
  235. assertEquals(ReceiveCommand.Result.OK, cmd.getResult());
  236. }
  237. }
  238. @Test
  239. public void putMatchingWithNoMatchingRefs() throws Exception {
  240. PushCertificate addMaster = newCert(
  241. command(zeroId(), ID1, "refs/heads/master"),
  242. command(zeroId(), ID2, "refs/heads/branch"));
  243. store.put(addMaster, newIdent(), Collections.<ReceiveCommand> emptyList());
  244. assertEquals(NO_CHANGE, store.save());
  245. }
  246. @Test
  247. public void putMatchingWithNoMatchingRefsInBatchOnEmptyRef()
  248. throws Exception {
  249. PushCertificate addMaster = newCert(
  250. command(zeroId(), ID1, "refs/heads/master"),
  251. command(zeroId(), ID2, "refs/heads/branch"));
  252. store.put(addMaster, newIdent(), Collections.<ReceiveCommand> emptyList());
  253. BatchRefUpdate batch = repo.getRefDatabase().newBatchUpdate();
  254. assertFalse(store.save(batch));
  255. assertEquals(0, batch.getCommands().size());
  256. }
  257. @Test
  258. public void putMatchingWithNoMatchingRefsInBatchOnNonEmptyRef()
  259. throws Exception {
  260. PushCertificate addMaster = newCert(
  261. command(zeroId(), ID1, "refs/heads/master"));
  262. store.put(addMaster, newIdent());
  263. assertEquals(NEW, store.save());
  264. PushCertificate addBranch = newCert(
  265. command(zeroId(), ID2, "refs/heads/branch"));
  266. store.put(addBranch, newIdent(), Collections.<ReceiveCommand> emptyList());
  267. BatchRefUpdate batch = repo.getRefDatabase().newBatchUpdate();
  268. assertFalse(store.save(batch));
  269. assertEquals(0, batch.getCommands().size());
  270. }
  271. @Test
  272. public void putMatchingWithSomeMatchingRefs() throws Exception {
  273. PushCertificate addMasterAndBranch = newCert(
  274. command(zeroId(), ID1, "refs/heads/master"),
  275. command(zeroId(), ID2, "refs/heads/branch"));
  276. store.put(addMasterAndBranch, newIdent(),
  277. Collections.singleton(addMasterAndBranch.getCommands().get(0)));
  278. assertEquals(NEW, store.save());
  279. assertCerts("refs/heads/master", addMasterAndBranch);
  280. assertCerts("refs/heads/branch");
  281. }
  282. private PersonIdent newIdent() {
  283. return new PersonIdent(
  284. "A U. Thor", "author@example.com", ts.getAndIncrement(), 0);
  285. }
  286. private PushCertificateStore newStore() {
  287. return new PushCertificateStore(repo);
  288. }
  289. private void assertCerts(String refName, PushCertificate... expected)
  290. throws Exception {
  291. assertCerts(store, refName, expected);
  292. assertCerts(newStore(), refName, expected);
  293. }
  294. private static void assertCerts(PushCertificateStore store, String refName,
  295. PushCertificate... expected) throws Exception {
  296. List<PushCertificate> ex = Arrays.asList(expected);
  297. PushCertificate first = !ex.isEmpty() ? ex.get(0) : null;
  298. assertEquals(first, store.get(refName));
  299. assertEquals(ex, toList(store.getAll(refName)));
  300. }
  301. private static <T> List<T> toList(Iterable<T> it) {
  302. List<T> list = new ArrayList<>();
  303. for (T t : it) {
  304. list.add(t);
  305. }
  306. return list;
  307. }
  308. }