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

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