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

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