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 30KB

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