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.

GitServletTest.java 35KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928
  1. /*
  2. * Copyright 2011 gitblit.com.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.gitblit.tests;
  17. import java.io.BufferedWriter;
  18. import java.io.File;
  19. import java.io.FileOutputStream;
  20. import java.io.IOException;
  21. import java.io.OutputStreamWriter;
  22. import java.text.MessageFormat;
  23. import java.util.Date;
  24. import java.util.List;
  25. import java.util.concurrent.atomic.AtomicBoolean;
  26. import org.eclipse.jgit.api.CloneCommand;
  27. import org.eclipse.jgit.api.Git;
  28. import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
  29. import org.eclipse.jgit.api.MergeResult;
  30. import org.eclipse.jgit.api.ResetCommand.ResetType;
  31. import org.eclipse.jgit.api.errors.GitAPIException;
  32. import org.eclipse.jgit.lib.Constants;
  33. import org.eclipse.jgit.lib.Repository;
  34. import org.eclipse.jgit.revwalk.RevCommit;
  35. import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
  36. import org.eclipse.jgit.transport.CredentialsProvider;
  37. import org.eclipse.jgit.transport.PushResult;
  38. import org.eclipse.jgit.transport.RefSpec;
  39. import org.eclipse.jgit.transport.RemoteRefUpdate;
  40. import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
  41. import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
  42. import org.eclipse.jgit.util.FileUtils;
  43. import org.junit.AfterClass;
  44. import org.junit.BeforeClass;
  45. import org.junit.Test;
  46. import com.gitblit.Constants.AccessPermission;
  47. import com.gitblit.Constants.AccessRestrictionType;
  48. import com.gitblit.Constants.AuthorizationControl;
  49. import com.gitblit.Keys;
  50. import com.gitblit.models.RefLogEntry;
  51. import com.gitblit.models.RepositoryModel;
  52. import com.gitblit.models.UserModel;
  53. import com.gitblit.utils.ArrayUtils;
  54. import com.gitblit.utils.JGitUtils;
  55. import com.gitblit.utils.RefLogUtils;
  56. public class GitServletTest extends GitblitUnitTest {
  57. static File ticgitFolder = new File(GitBlitSuite.REPOSITORIES, "working/ticgit");
  58. static File ticgit2Folder = new File(GitBlitSuite.REPOSITORIES, "working/ticgit2");
  59. static File jgitFolder = new File(GitBlitSuite.REPOSITORIES, "working/jgit");
  60. static File jgit2Folder = new File(GitBlitSuite.REPOSITORIES, "working/jgit2");
  61. String url = GitBlitSuite.gitServletUrl;
  62. String account = GitBlitSuite.account;
  63. String password = GitBlitSuite.password;
  64. private static final AtomicBoolean started = new AtomicBoolean(false);
  65. private static UserModel getUser() {
  66. UserModel user = new UserModel("james");
  67. user.displayName = "James Moger";
  68. user.emailAddress = "james.moger@gmail.com";
  69. user.password = "james";
  70. return user;
  71. }
  72. private static void delete(UserModel user) {
  73. if (users().getUserModel(user.username) != null) {
  74. users().deleteUser(user.username);
  75. }
  76. }
  77. @BeforeClass
  78. public static void startGitblit() throws Exception {
  79. started.set(GitBlitSuite.startGitblit());
  80. delete(getUser());
  81. }
  82. @AfterClass
  83. public static void stopGitblit() throws Exception {
  84. if (started.get()) {
  85. GitBlitSuite.stopGitblit();
  86. deleteWorkingFolders();
  87. }
  88. delete(getUser());
  89. }
  90. public static void deleteWorkingFolders() throws Exception {
  91. if (ticgitFolder.exists()) {
  92. GitBlitSuite.close(ticgitFolder);
  93. FileUtils.delete(ticgitFolder, FileUtils.RECURSIVE);
  94. }
  95. if (ticgit2Folder.exists()) {
  96. GitBlitSuite.close(ticgit2Folder);
  97. FileUtils.delete(ticgit2Folder, FileUtils.RECURSIVE);
  98. }
  99. if (jgitFolder.exists()) {
  100. GitBlitSuite.close(jgitFolder);
  101. FileUtils.delete(jgitFolder, FileUtils.RECURSIVE);
  102. }
  103. if (jgit2Folder.exists()) {
  104. GitBlitSuite.close(jgit2Folder);
  105. FileUtils.delete(jgit2Folder, FileUtils.RECURSIVE);
  106. }
  107. }
  108. @Test
  109. public void testClone() throws Exception {
  110. GitBlitSuite.close(ticgitFolder);
  111. if (ticgitFolder.exists()) {
  112. FileUtils.delete(ticgitFolder, FileUtils.RECURSIVE | FileUtils.RETRY);
  113. }
  114. CloneCommand clone = Git.cloneRepository();
  115. clone.setURI(MessageFormat.format("{0}/ticgit.git", url));
  116. clone.setDirectory(ticgitFolder);
  117. clone.setBare(false);
  118. clone.setCloneAllBranches(true);
  119. clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password));
  120. GitBlitSuite.close(clone.call());
  121. assertTrue(true);
  122. }
  123. @Test
  124. public void testBogusLoginClone() throws Exception {
  125. // restrict repository access
  126. RepositoryModel model = repositories().getRepositoryModel("ticgit.git");
  127. model.accessRestriction = AccessRestrictionType.CLONE;
  128. repositories().updateRepositoryModel(model.name, model, false);
  129. // delete any existing working folder
  130. boolean cloned = false;
  131. try {
  132. CloneCommand clone = Git.cloneRepository();
  133. clone.setURI(MessageFormat.format("{0}/ticgit.git", url));
  134. clone.setDirectory(ticgit2Folder);
  135. clone.setBare(false);
  136. clone.setCloneAllBranches(true);
  137. clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider("bogus", "bogus"));
  138. GitBlitSuite.close(clone.call());
  139. cloned = true;
  140. } catch (Exception e) {
  141. // swallow the exception which we expect
  142. }
  143. // restore anonymous repository access
  144. model.accessRestriction = AccessRestrictionType.NONE;
  145. repositories().updateRepositoryModel(model.name, model, false);
  146. assertFalse("Bogus login cloned a repository?!", cloned);
  147. }
  148. @Test
  149. public void testUnauthorizedLoginClone() throws Exception {
  150. // restrict repository access
  151. RepositoryModel model = repositories().getRepositoryModel("ticgit.git");
  152. model.accessRestriction = AccessRestrictionType.CLONE;
  153. model.authorizationControl = AuthorizationControl.NAMED;
  154. UserModel user = new UserModel("james");
  155. user.password = "james";
  156. gitblit().updateUserModel(user.username, user, true);
  157. repositories().updateRepositoryModel(model.name, model, false);
  158. FileUtils.delete(ticgit2Folder, FileUtils.RECURSIVE);
  159. // delete any existing working folder
  160. boolean cloned = false;
  161. try {
  162. CloneCommand clone = Git.cloneRepository();
  163. clone.setURI(MessageFormat.format("{0}/ticgit.git", url));
  164. clone.setDirectory(ticgit2Folder);
  165. clone.setBare(false);
  166. clone.setCloneAllBranches(true);
  167. clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(user.username, user.password));
  168. GitBlitSuite.close(clone.call());
  169. cloned = true;
  170. } catch (Exception e) {
  171. // swallow the exception which we expect
  172. }
  173. assertFalse("Unauthorized login cloned a repository?!", cloned);
  174. FileUtils.delete(ticgit2Folder, FileUtils.RECURSIVE);
  175. // switch to authenticated
  176. model.authorizationControl = AuthorizationControl.AUTHENTICATED;
  177. repositories().updateRepositoryModel(model.name, model, false);
  178. // try clone again
  179. cloned = false;
  180. CloneCommand clone = Git.cloneRepository();
  181. clone.setURI(MessageFormat.format("{0}/ticgit.git", url));
  182. clone.setDirectory(ticgit2Folder);
  183. clone.setBare(false);
  184. clone.setCloneAllBranches(true);
  185. clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(user.username, user.password));
  186. GitBlitSuite.close(clone.call());
  187. cloned = true;
  188. assertTrue("Authenticated login could not clone!", cloned);
  189. FileUtils.delete(ticgit2Folder, FileUtils.RECURSIVE);
  190. // restore anonymous repository access
  191. model.accessRestriction = AccessRestrictionType.NONE;
  192. model.authorizationControl = AuthorizationControl.NAMED;
  193. repositories().updateRepositoryModel(model.name, model, false);
  194. delete(user);
  195. }
  196. @Test
  197. public void testAnonymousPush() throws Exception {
  198. GitBlitSuite.close(ticgitFolder);
  199. if (ticgitFolder.exists()) {
  200. FileUtils.delete(ticgitFolder, FileUtils.RECURSIVE | FileUtils.RETRY);
  201. }
  202. RepositoryModel model = repositories().getRepositoryModel("ticgit.git");
  203. model.accessRestriction = AccessRestrictionType.NONE;
  204. repositories().updateRepositoryModel(model.name, model, false);
  205. CloneCommand clone = Git.cloneRepository();
  206. clone.setURI(MessageFormat.format("{0}/ticgit.git", url));
  207. clone.setDirectory(ticgitFolder);
  208. clone.setBare(false);
  209. clone.setCloneAllBranches(true);
  210. clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password));
  211. GitBlitSuite.close(clone.call());
  212. assertTrue(true);
  213. Git git = Git.open(ticgitFolder);
  214. File file = new File(ticgitFolder, "TODO");
  215. OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(file, true), Constants.CHARSET);
  216. BufferedWriter w = new BufferedWriter(os);
  217. w.write("// hellol中文 " + new Date().toString() + "\n");
  218. w.close();
  219. git.add().addFilepattern(file.getName()).call();
  220. git.commit().setMessage("test commit").call();
  221. Iterable<PushResult> results = git.push().setPushAll().call();
  222. GitBlitSuite.close(git);
  223. for (PushResult result : results) {
  224. for (RemoteRefUpdate update : result.getRemoteUpdates()) {
  225. assertEquals(Status.OK, update.getStatus());
  226. }
  227. }
  228. }
  229. @Test
  230. public void testSubfolderPush() throws Exception {
  231. GitBlitSuite.close(jgitFolder);
  232. if (jgitFolder.exists()) {
  233. FileUtils.delete(jgitFolder, FileUtils.RECURSIVE | FileUtils.RETRY);
  234. }
  235. CloneCommand clone = Git.cloneRepository();
  236. clone.setURI(MessageFormat.format("{0}/test/jgit.git", url));
  237. clone.setDirectory(jgitFolder);
  238. clone.setBare(false);
  239. clone.setCloneAllBranches(true);
  240. clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password));
  241. GitBlitSuite.close(clone.call());
  242. assertTrue(true);
  243. Git git = Git.open(jgitFolder);
  244. File file = new File(jgitFolder, "TODO");
  245. OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(file, true), Constants.CHARSET);
  246. BufferedWriter w = new BufferedWriter(os);
  247. w.write("// " + new Date().toString() + "\n");
  248. w.close();
  249. git.add().addFilepattern(file.getName()).call();
  250. git.commit().setMessage("test commit").call();
  251. Iterable<PushResult> results = git.push().setPushAll().setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password)).call();
  252. GitBlitSuite.close(git);
  253. for (PushResult result : results) {
  254. for (RemoteRefUpdate update : result.getRemoteUpdates()) {
  255. assertEquals(Status.OK, update.getStatus());
  256. }
  257. }
  258. }
  259. @Test
  260. public void testPushToFrozenRepo() throws Exception {
  261. GitBlitSuite.close(jgitFolder);
  262. if (jgitFolder.exists()) {
  263. FileUtils.delete(jgitFolder, FileUtils.RECURSIVE | FileUtils.RETRY);
  264. }
  265. CloneCommand clone = Git.cloneRepository();
  266. clone.setURI(MessageFormat.format("{0}/test/jgit.git", url));
  267. clone.setDirectory(jgitFolder);
  268. clone.setBare(false);
  269. clone.setCloneAllBranches(true);
  270. clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password));
  271. GitBlitSuite.close(clone.call());
  272. assertTrue(true);
  273. // freeze repo
  274. RepositoryModel model = repositories().getRepositoryModel("test/jgit.git");
  275. model.isFrozen = true;
  276. repositories().updateRepositoryModel(model.name, model, false);
  277. Git git = Git.open(jgitFolder);
  278. File file = new File(jgitFolder, "TODO");
  279. OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(file, true), Constants.CHARSET);
  280. BufferedWriter w = new BufferedWriter(os);
  281. w.write("// " + new Date().toString() + "\n");
  282. w.close();
  283. git.add().addFilepattern(file.getName()).call();
  284. git.commit().setMessage("test commit").call();
  285. Iterable<PushResult> results = git.push().setPushAll().setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password)).call();
  286. for (PushResult result : results) {
  287. for (RemoteRefUpdate update : result.getRemoteUpdates()) {
  288. assertEquals(Status.REJECTED_OTHER_REASON, update.getStatus());
  289. }
  290. }
  291. // unfreeze repo
  292. model.isFrozen = false;
  293. repositories().updateRepositoryModel(model.name, model, false);
  294. results = git.push().setPushAll().setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password)).call();
  295. GitBlitSuite.close(git);
  296. for (PushResult result : results) {
  297. for (RemoteRefUpdate update : result.getRemoteUpdates()) {
  298. assertEquals(Status.OK, update.getStatus());
  299. }
  300. }
  301. }
  302. @Test
  303. public void testPushToNonBareRepository() throws Exception {
  304. CloneCommand clone = Git.cloneRepository();
  305. clone.setURI(MessageFormat.format("{0}/working/jgit", url));
  306. clone.setDirectory(jgit2Folder);
  307. clone.setBare(false);
  308. clone.setCloneAllBranches(true);
  309. clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password));
  310. GitBlitSuite.close(clone.call());
  311. assertTrue(true);
  312. Git git = Git.open(jgit2Folder);
  313. File file = new File(jgit2Folder, "NONBARE");
  314. OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(file, true), Constants.CHARSET);
  315. BufferedWriter w = new BufferedWriter(os);
  316. w.write("// " + new Date().toString() + "\n");
  317. w.close();
  318. git.add().addFilepattern(file.getName()).call();
  319. git.commit().setMessage("test commit followed by push to non-bare repository").call();
  320. Iterable<PushResult> results = git.push().setPushAll().setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password)).call();
  321. GitBlitSuite.close(git);
  322. for (PushResult result : results) {
  323. for (RemoteRefUpdate update : result.getRemoteUpdates()) {
  324. assertEquals(Status.REJECTED_OTHER_REASON, update.getStatus());
  325. }
  326. }
  327. }
  328. @Test
  329. public void testCommitterVerification() throws Exception {
  330. UserModel user = getUser();
  331. testCommitterVerification(user, "joe", null, false);
  332. testCommitterVerification(user, "joe", user.emailAddress, false);
  333. testCommitterVerification(user, user.username, null, false);
  334. testCommitterVerification(user, user.username, user.emailAddress, true);
  335. user.displayName = "James Moger";
  336. testCommitterVerification(user, user.displayName, null, false);
  337. testCommitterVerification(user, user.displayName, "something", false);
  338. testCommitterVerification(user, user.displayName, user.emailAddress, true);
  339. }
  340. private void testCommitterVerification(UserModel user, String displayName, String emailAddress, boolean expectedSuccess) throws Exception {
  341. delete(user);
  342. CredentialsProvider cp = new UsernamePasswordCredentialsProvider(user.username, user.password);
  343. // fork from original to a temporary bare repo
  344. File verification = new File(GitBlitSuite.REPOSITORIES, "refchecks/verify-committer.git");
  345. if (verification.exists()) {
  346. FileUtils.delete(verification, FileUtils.RECURSIVE);
  347. }
  348. CloneCommand clone = Git.cloneRepository();
  349. clone.setURI(MessageFormat.format("{0}/ticgit.git", url));
  350. clone.setDirectory(verification);
  351. clone.setBare(true);
  352. clone.setCloneAllBranches(true);
  353. clone.setCredentialsProvider(cp);
  354. GitBlitSuite.close(clone.call());
  355. // require push permissions and committer verification
  356. RepositoryModel model = repositories().getRepositoryModel("refchecks/verify-committer.git");
  357. model.authorizationControl = AuthorizationControl.NAMED;
  358. model.accessRestriction = AccessRestrictionType.PUSH;
  359. model.verifyCommitter = true;
  360. // grant user push permission
  361. user.setRepositoryPermission(model.name, AccessPermission.PUSH);
  362. gitblit().updateUserModel(user.username, user, true);
  363. repositories().updateRepositoryModel(model.name, model, false);
  364. // clone temp bare repo to working copy
  365. File local = new File(GitBlitSuite.REPOSITORIES, "refchecks/verify-wc");
  366. if (local.exists()) {
  367. FileUtils.delete(local, FileUtils.RECURSIVE);
  368. }
  369. clone = Git.cloneRepository();
  370. clone.setURI(MessageFormat.format("{0}/{1}", url, model.name));
  371. clone.setDirectory(local);
  372. clone.setBare(false);
  373. clone.setCloneAllBranches(true);
  374. clone.setCredentialsProvider(cp);
  375. GitBlitSuite.close(clone.call());
  376. Git git = Git.open(local);
  377. // force an identity which may or may not match the account's identity
  378. git.getRepository().getConfig().setString("user", null, "name", displayName);
  379. git.getRepository().getConfig().setString("user", null, "email", emailAddress);
  380. git.getRepository().getConfig().save();
  381. // commit a file and push it
  382. File file = new File(local, "PUSHCHK");
  383. OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(file, true), Constants.CHARSET);
  384. BufferedWriter w = new BufferedWriter(os);
  385. w.write("// " + new Date().toString() + "\n");
  386. w.close();
  387. git.add().addFilepattern(file.getName()).call();
  388. git.commit().setMessage("push test").call();
  389. Iterable<PushResult> results = git.push().setCredentialsProvider(cp).setRemote("origin").call();
  390. for (PushResult result : results) {
  391. RemoteRefUpdate ref = result.getRemoteUpdate("refs/heads/master");
  392. Status status = ref.getStatus();
  393. if (expectedSuccess) {
  394. assertTrue("Verification failed! User was NOT able to push commit! " + status.name(), Status.OK.equals(status));
  395. } else {
  396. assertTrue("Verification failed! User was able to push commit! " + status.name(), Status.REJECTED_OTHER_REASON.equals(status));
  397. }
  398. }
  399. GitBlitSuite.close(git);
  400. // close serving repository
  401. GitBlitSuite.close(verification);
  402. }
  403. @Test
  404. public void testMergeCommitterVerification() throws Exception {
  405. testMergeCommitterVerification(false);
  406. testMergeCommitterVerification(true);
  407. }
  408. private void testMergeCommitterVerification(boolean expectedSuccess) throws Exception {
  409. UserModel user = getUser();
  410. delete(user);
  411. CredentialsProvider cp = new UsernamePasswordCredentialsProvider(user.username, user.password);
  412. // fork from original to a temporary bare repo
  413. File verification = new File(GitBlitSuite.REPOSITORIES, "refchecks/verify-committer.git");
  414. if (verification.exists()) {
  415. FileUtils.delete(verification, FileUtils.RECURSIVE);
  416. }
  417. CloneCommand clone = Git.cloneRepository();
  418. clone.setURI(MessageFormat.format("{0}/ticgit.git", url));
  419. clone.setDirectory(verification);
  420. clone.setBare(true);
  421. clone.setCloneAllBranches(true);
  422. clone.setCredentialsProvider(cp);
  423. GitBlitSuite.close(clone.call());
  424. // require push permissions and committer verification
  425. RepositoryModel model = repositories().getRepositoryModel("refchecks/verify-committer.git");
  426. model.authorizationControl = AuthorizationControl.NAMED;
  427. model.accessRestriction = AccessRestrictionType.PUSH;
  428. model.verifyCommitter = true;
  429. // grant user push permission
  430. user.setRepositoryPermission(model.name, AccessPermission.PUSH);
  431. gitblit().updateUserModel(user.username, user, true);
  432. repositories().updateRepositoryModel(model.name, model, false);
  433. // clone temp bare repo to working copy
  434. File local = new File(GitBlitSuite.REPOSITORIES, "refchecks/verify-wc");
  435. if (local.exists()) {
  436. FileUtils.delete(local, FileUtils.RECURSIVE);
  437. }
  438. clone = Git.cloneRepository();
  439. clone.setURI(MessageFormat.format("{0}/{1}", url, model.name));
  440. clone.setDirectory(local);
  441. clone.setBare(false);
  442. clone.setCloneAllBranches(true);
  443. clone.setCredentialsProvider(cp);
  444. GitBlitSuite.close(clone.call());
  445. Git git = Git.open(local);
  446. // checkout a mergetest branch
  447. git.checkout().setCreateBranch(true).setName("mergetest").call();
  448. // change identity
  449. git.getRepository().getConfig().setString("user", null, "name", "mergetest");
  450. git.getRepository().getConfig().setString("user", null, "email", "mergetest@merge.com");
  451. git.getRepository().getConfig().save();
  452. // commit a file
  453. File file = new File(local, "MERGECHK2");
  454. OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(file, true), Constants.CHARSET);
  455. BufferedWriter w = new BufferedWriter(os);
  456. w.write("// " + new Date().toString() + "\n");
  457. w.close();
  458. git.add().addFilepattern(file.getName()).call();
  459. RevCommit mergeTip = git.commit().setMessage(file.getName() + " test").call();
  460. // return to master
  461. git.checkout().setName("master").call();
  462. // restore identity
  463. if (expectedSuccess) {
  464. git.getRepository().getConfig().setString("user", null, "name", user.username);
  465. git.getRepository().getConfig().setString("user", null, "email", user.emailAddress);
  466. git.getRepository().getConfig().save();
  467. }
  468. // commit a file
  469. file = new File(local, "MERGECHK1");
  470. os = new OutputStreamWriter(new FileOutputStream(file, true), Constants.CHARSET);
  471. w = new BufferedWriter(os);
  472. w.write("// " + new Date().toString() + "\n");
  473. w.close();
  474. git.add().addFilepattern(file.getName()).call();
  475. git.commit().setMessage(file.getName() + " test").call();
  476. // merge the tip of the mergetest branch into master with --no-ff
  477. MergeResult mergeResult = git.merge().setFastForward(FastForwardMode.NO_FF).include(mergeTip.getId()).call();
  478. assertEquals(MergeResult.MergeStatus.MERGED, mergeResult.getMergeStatus());
  479. // push the merged master to the origin
  480. Iterable<PushResult> results = git.push().setCredentialsProvider(cp).setRemote("origin").call();
  481. for (PushResult result : results) {
  482. RemoteRefUpdate ref = result.getRemoteUpdate("refs/heads/master");
  483. Status status = ref.getStatus();
  484. if (expectedSuccess) {
  485. assertTrue("Verification failed! User was NOT able to push commit! " + status.name(), Status.OK.equals(status));
  486. } else {
  487. assertTrue("Verification failed! User was able to push commit! " + status.name(), Status.REJECTED_OTHER_REASON.equals(status));
  488. }
  489. }
  490. GitBlitSuite.close(git);
  491. // close serving repository
  492. GitBlitSuite.close(verification);
  493. }
  494. @Test
  495. public void testBlockClone() throws Exception {
  496. testRefChange(AccessPermission.VIEW, null, null, null);
  497. }
  498. @Test
  499. public void testBlockPush() throws Exception {
  500. testRefChange(AccessPermission.CLONE, null, null, null);
  501. }
  502. @Test
  503. public void testBlockBranchCreation() throws Exception {
  504. testRefChange(AccessPermission.PUSH, Status.REJECTED_OTHER_REASON, null, null);
  505. }
  506. @Test
  507. public void testBlockBranchDeletion() throws Exception {
  508. testRefChange(AccessPermission.CREATE, Status.OK, Status.REJECTED_OTHER_REASON, null);
  509. }
  510. @Test
  511. public void testBlockBranchRewind() throws Exception {
  512. testRefChange(AccessPermission.DELETE, Status.OK, Status.OK, Status.REJECTED_OTHER_REASON);
  513. }
  514. @Test
  515. public void testBranchRewind() throws Exception {
  516. testRefChange(AccessPermission.REWIND, Status.OK, Status.OK, Status.OK);
  517. }
  518. private void testRefChange(AccessPermission permission, Status expectedCreate, Status expectedDelete, Status expectedRewind) throws Exception {
  519. final String originName = "ticgit.git";
  520. final String forkName = "refchecks/ticgit.git";
  521. final String workingCopy = "refchecks/ticgit-wc";
  522. // lower access restriction on origin repository
  523. RepositoryModel origin = repositories().getRepositoryModel(originName);
  524. origin.accessRestriction = AccessRestrictionType.NONE;
  525. repositories().updateRepositoryModel(origin.name, origin, false);
  526. UserModel user = getUser();
  527. delete(user);
  528. CredentialsProvider cp = new UsernamePasswordCredentialsProvider(user.username, user.password);
  529. // fork from original to a temporary bare repo
  530. File refChecks = new File(GitBlitSuite.REPOSITORIES, forkName);
  531. if (refChecks.exists()) {
  532. FileUtils.delete(refChecks, FileUtils.RECURSIVE);
  533. }
  534. CloneCommand clone = Git.cloneRepository();
  535. clone.setURI(url + "/" + originName);
  536. clone.setDirectory(refChecks);
  537. clone.setBare(true);
  538. clone.setCloneAllBranches(true);
  539. clone.setCredentialsProvider(cp);
  540. GitBlitSuite.close(clone.call());
  541. // elevate repository to clone permission
  542. RepositoryModel model = repositories().getRepositoryModel(forkName);
  543. switch (permission) {
  544. case VIEW:
  545. model.accessRestriction = AccessRestrictionType.CLONE;
  546. break;
  547. case CLONE:
  548. model.accessRestriction = AccessRestrictionType.CLONE;
  549. break;
  550. default:
  551. model.accessRestriction = AccessRestrictionType.PUSH;
  552. }
  553. model.authorizationControl = AuthorizationControl.NAMED;
  554. // grant user specified
  555. user.setRepositoryPermission(model.name, permission);
  556. gitblit().updateUserModel(user.username, user, true);
  557. repositories().updateRepositoryModel(model.name, model, false);
  558. // clone temp bare repo to working copy
  559. File local = new File(GitBlitSuite.REPOSITORIES, workingCopy);
  560. if (local.exists()) {
  561. FileUtils.delete(local, FileUtils.RECURSIVE);
  562. }
  563. clone = Git.cloneRepository();
  564. clone.setURI(MessageFormat.format("{0}/{1}", url, model.name));
  565. clone.setDirectory(local);
  566. clone.setBare(false);
  567. clone.setCloneAllBranches(true);
  568. clone.setCredentialsProvider(cp);
  569. try {
  570. GitBlitSuite.close(clone.call());
  571. } catch (GitAPIException e) {
  572. if (permission.atLeast(AccessPermission.CLONE)) {
  573. throw e;
  574. } else {
  575. // close serving repository
  576. GitBlitSuite.close(refChecks);
  577. // user does not have clone permission
  578. assertTrue(e.getMessage(), e.getMessage().contains("not permitted"));
  579. return;
  580. }
  581. }
  582. Git git = Git.open(local);
  583. // commit a file and push it
  584. File file = new File(local, "PUSHCHK");
  585. OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(file, true), Constants.CHARSET);
  586. BufferedWriter w = new BufferedWriter(os);
  587. w.write("// " + new Date().toString() + "\n");
  588. w.close();
  589. git.add().addFilepattern(file.getName()).call();
  590. git.commit().setMessage("push test").call();
  591. Iterable<PushResult> results = null;
  592. try {
  593. results = git.push().setCredentialsProvider(cp).setRemote("origin").call();
  594. } catch (GitAPIException e) {
  595. if (permission.atLeast(AccessPermission.PUSH)) {
  596. throw e;
  597. } else {
  598. // close serving repository
  599. GitBlitSuite.close(refChecks);
  600. // user does not have push permission
  601. assertTrue(e.getMessage(), e.getMessage().contains("not permitted"));
  602. GitBlitSuite.close(git);
  603. return;
  604. }
  605. }
  606. for (PushResult result : results) {
  607. RemoteRefUpdate ref = result.getRemoteUpdate("refs/heads/master");
  608. Status status = ref.getStatus();
  609. if (permission.atLeast(AccessPermission.PUSH)) {
  610. assertTrue("User failed to push commit?! " + status.name(), Status.OK.equals(status));
  611. } else {
  612. // close serving repository
  613. GitBlitSuite.close(refChecks);
  614. assertTrue("User was able to push commit! " + status.name(), Status.REJECTED_OTHER_REASON.equals(status));
  615. GitBlitSuite.close(git);
  616. // skip delete test
  617. return;
  618. }
  619. }
  620. // create a local branch and push the new branch back to the origin
  621. git.branchCreate().setName("protectme").call();
  622. RefSpec refSpec = new RefSpec("refs/heads/protectme:refs/heads/protectme");
  623. results = git.push().setCredentialsProvider(cp).setRefSpecs(refSpec).setRemote("origin").call();
  624. for (PushResult result : results) {
  625. RemoteRefUpdate ref = result.getRemoteUpdate("refs/heads/protectme");
  626. Status status = ref.getStatus();
  627. if (Status.OK.equals(expectedCreate)) {
  628. assertTrue("User failed to push creation?! " + status.name(), status.equals(expectedCreate));
  629. } else {
  630. // close serving repository
  631. GitBlitSuite.close(refChecks);
  632. assertTrue("User was able to push ref creation! " + status.name(), status.equals(expectedCreate));
  633. GitBlitSuite.close(git);
  634. // skip delete test
  635. return;
  636. }
  637. }
  638. // delete the branch locally
  639. git.branchDelete().setBranchNames("protectme").call();
  640. // push a delete ref command
  641. refSpec = new RefSpec(":refs/heads/protectme");
  642. results = git.push().setCredentialsProvider(cp).setRefSpecs(refSpec).setRemote("origin").call();
  643. for (PushResult result : results) {
  644. RemoteRefUpdate ref = result.getRemoteUpdate("refs/heads/protectme");
  645. Status status = ref.getStatus();
  646. if (Status.OK.equals(expectedDelete)) {
  647. assertTrue("User failed to push ref deletion?! " + status.name(), status.equals(Status.OK));
  648. } else {
  649. // close serving repository
  650. GitBlitSuite.close(refChecks);
  651. assertTrue("User was able to push ref deletion?! " + status.name(), status.equals(expectedDelete));
  652. GitBlitSuite.close(git);
  653. // skip rewind test
  654. return;
  655. }
  656. }
  657. // rewind master by two commits
  658. git.reset().setRef("HEAD~2").setMode(ResetType.HARD).call();
  659. // commit a change on this detached HEAD
  660. file = new File(local, "REWINDCHK");
  661. os = new OutputStreamWriter(new FileOutputStream(file, true), Constants.CHARSET);
  662. w = new BufferedWriter(os);
  663. w.write("// " + new Date().toString() + "\n");
  664. w.close();
  665. git.add().addFilepattern(file.getName()).call();
  666. RevCommit commit = git.commit().setMessage("rewind master and new commit").call();
  667. // Reset master to our new commit now we our local branch tip is no longer
  668. // upstream of the remote branch tip. It is an alternate tip of the branch.
  669. JGitUtils.setBranchRef(git.getRepository(), "refs/heads/master", commit.getName());
  670. // Try pushing our new tip to the origin.
  671. // This requires the server to "rewind" it's master branch and update it
  672. // to point to our alternate tip. This leaves the original master tip
  673. // unreferenced.
  674. results = git.push().setCredentialsProvider(cp).setRemote("origin").setForce(true).call();
  675. for (PushResult result : results) {
  676. RemoteRefUpdate ref = result.getRemoteUpdate("refs/heads/master");
  677. Status status = ref.getStatus();
  678. if (Status.OK.equals(expectedRewind)) {
  679. assertTrue("User failed to rewind master?! " + status.name(), status.equals(expectedRewind));
  680. } else {
  681. assertTrue("User was able to rewind master?! " + status.name(), status.equals(expectedRewind));
  682. }
  683. }
  684. GitBlitSuite.close(git);
  685. // close serving repository
  686. GitBlitSuite.close(refChecks);
  687. delete(user);
  688. }
  689. @Test
  690. public void testCreateOnPush() throws Exception {
  691. testCreateOnPush(false, false);
  692. testCreateOnPush(true, false);
  693. testCreateOnPush(false, true);
  694. }
  695. private void testCreateOnPush(boolean canCreate, boolean canAdmin) throws Exception {
  696. UserModel user = new UserModel("sampleuser");
  697. user.password = user.username;
  698. delete(user);
  699. user.canCreate = canCreate;
  700. user.canAdmin = canAdmin;
  701. gitblit().updateUserModel(user.username, user, true);
  702. CredentialsProvider cp = new UsernamePasswordCredentialsProvider(user.username, user.password);
  703. // fork from original to a temporary bare repo
  704. File tmpFolder = File.createTempFile("gitblit", "").getParentFile();
  705. File createCheck = new File(tmpFolder, "ticgit.git");
  706. if (createCheck.exists()) {
  707. FileUtils.delete(createCheck, FileUtils.RECURSIVE);
  708. }
  709. File personalRepo = new File(GitBlitSuite.REPOSITORIES, MessageFormat.format("~{0}/ticgit.git", user.username));
  710. GitBlitSuite.close(personalRepo);
  711. if (personalRepo.exists()) {
  712. FileUtils.delete(personalRepo, FileUtils.RECURSIVE);
  713. }
  714. File projectRepo = new File(GitBlitSuite.REPOSITORIES, "project/ticgit.git");
  715. GitBlitSuite.close(projectRepo);
  716. if (projectRepo.exists()) {
  717. FileUtils.delete(projectRepo, FileUtils.RECURSIVE);
  718. }
  719. CloneCommand clone = Git.cloneRepository();
  720. clone.setURI(MessageFormat.format("{0}/ticgit.git", url));
  721. clone.setDirectory(createCheck);
  722. clone.setBare(true);
  723. clone.setCloneAllBranches(true);
  724. clone.setCredentialsProvider(cp);
  725. Git git = clone.call();
  726. GitBlitSuite.close(personalRepo);
  727. // add a personal repository remote and a project remote
  728. git.getRepository().getConfig().setString("remote", "user", "url", MessageFormat.format("{0}/~{1}/ticgit.git", url, user.username));
  729. git.getRepository().getConfig().setString("remote", "project", "url", MessageFormat.format("{0}/project/ticgit.git", url));
  730. git.getRepository().getConfig().save();
  731. // push to non-existent user repository
  732. try {
  733. Iterable<PushResult> results = git.push().setRemote("user").setPushAll().setCredentialsProvider(cp).call();
  734. for (PushResult result : results) {
  735. RemoteRefUpdate ref = result.getRemoteUpdate("refs/heads/master");
  736. Status status = ref.getStatus();
  737. assertTrue("User failed to create repository?! " + status.name(), Status.OK.equals(status));
  738. }
  739. assertTrue("User canAdmin:" + user.canAdmin + " canCreate:" + user.canCreate, user.canAdmin || user.canCreate);
  740. // confirm default personal repository permissions
  741. RepositoryModel model = repositories().getRepositoryModel(MessageFormat.format("~{0}/ticgit.git", user.username));
  742. assertEquals("Unexpected owner", user.username, ArrayUtils.toString(model.owners));
  743. assertEquals("Unexpected authorization control", AuthorizationControl.NAMED, model.authorizationControl);
  744. assertEquals("Unexpected access restriction", AccessRestrictionType.VIEW, model.accessRestriction);
  745. } catch (GitAPIException e) {
  746. assertTrue(e.getMessage(), e.getMessage().contains("git-receive-pack not found"));
  747. assertFalse("User canAdmin:" + user.canAdmin + " canCreate:" + user.canCreate, user.canAdmin || user.canCreate);
  748. }
  749. // push to non-existent project repository
  750. try {
  751. Iterable<PushResult> results = git.push().setRemote("project").setPushAll().setCredentialsProvider(cp).call();
  752. GitBlitSuite.close(git);
  753. for (PushResult result : results) {
  754. RemoteRefUpdate ref = result.getRemoteUpdate("refs/heads/master");
  755. Status status = ref.getStatus();
  756. assertTrue("User failed to create repository?! " + status.name(), Status.OK.equals(status));
  757. }
  758. assertTrue("User canAdmin:" + user.canAdmin, user.canAdmin);
  759. // confirm default project repository permissions
  760. RepositoryModel model = repositories().getRepositoryModel("project/ticgit.git");
  761. assertEquals("Unexpected owner", user.username, ArrayUtils.toString(model.owners));
  762. assertEquals("Unexpected authorization control", AuthorizationControl.fromName(settings().getString(Keys.git.defaultAuthorizationControl, "NAMED")), model.authorizationControl);
  763. assertEquals("Unexpected access restriction", AccessRestrictionType.fromName(settings().getString(Keys.git.defaultAccessRestriction, "NONE")), model.accessRestriction);
  764. } catch (GitAPIException e) {
  765. assertTrue(e.getMessage(), e.getMessage().contains("git-receive-pack not found"));
  766. assertFalse("User canAdmin:" + user.canAdmin, user.canAdmin);
  767. }
  768. GitBlitSuite.close(git);
  769. delete(user);
  770. }
  771. @Test
  772. public void testPushLog() throws IOException {
  773. String name = "refchecks/ticgit.git";
  774. File refChecks = new File(GitBlitSuite.REPOSITORIES, name);
  775. Repository repository = new FileRepositoryBuilder().setGitDir(refChecks).build();
  776. List<RefLogEntry> pushes = RefLogUtils.getRefLog(name, repository);
  777. GitBlitSuite.close(repository);
  778. assertTrue("Repository has an empty push log!", pushes.size() > 0);
  779. }
  780. }