| package com.gitblit.tests; | |
| import static org.junit.Assert.assertEquals; | |
| import static org.junit.Assert.assertFalse; | |
| import static org.junit.Assert.assertTrue; | |
| import java.io.BufferedWriter; | |
| import java.io.File; | |
| import java.io.FileOutputStream; | |
| import java.io.IOException; | |
| import java.io.OutputStreamWriter; | |
| import java.text.MessageFormat; | |
| import java.util.Date; | |
| import java.util.List; | |
| import java.util.concurrent.atomic.AtomicBoolean; | |
| import org.eclipse.jgit.api.CloneCommand; | |
| import org.eclipse.jgit.api.Git; | |
| import org.eclipse.jgit.api.ResetCommand.ResetType; | |
| import org.eclipse.jgit.api.errors.GitAPIException; | |
| import org.eclipse.jgit.lib.Constants; | |
| import org.eclipse.jgit.revwalk.RevCommit; | |
| import org.eclipse.jgit.storage.file.FileRepository; | |
| import org.eclipse.jgit.transport.CredentialsProvider; | |
| import org.eclipse.jgit.transport.PushResult; | |
| import org.eclipse.jgit.transport.RefSpec; | |
| import org.eclipse.jgit.transport.RemoteRefUpdate; | |
| import org.eclipse.jgit.transport.RemoteRefUpdate.Status; | |
| import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; | |
| import org.eclipse.jgit.util.FileUtils; | |
| import org.junit.AfterClass; | |
| import org.junit.BeforeClass; | |
| import org.junit.Test; | |
| import com.gitblit.Constants.AccessPermission; | |
| import com.gitblit.Constants.AccessRestrictionType; | |
| import com.gitblit.Constants.AuthorizationControl; | |
| import com.gitblit.GitBlit; | |
| import com.gitblit.Keys; | |
| import com.gitblit.models.PushLogEntry; | |
| import com.gitblit.models.RepositoryModel; | |
| import com.gitblit.models.UserModel; | |
| import com.gitblit.utils.JGitUtils; | |
| import com.gitblit.utils.PushLogUtils; | |
| public class GitServletTest { | |
| static File ticgitFolder = new File(GitBlitSuite.REPOSITORIES, "working/ticgit"); | |
| static File ticgit2Folder = new File(GitBlitSuite.REPOSITORIES, "working/ticgit2"); | |
| static File jgitFolder = new File(GitBlitSuite.REPOSITORIES, "working/jgit"); | |
| static File jgit2Folder = new File(GitBlitSuite.REPOSITORIES, "working/jgit2"); | |
| String url = GitBlitSuite.url; | |
| String account = GitBlitSuite.account; | |
| String password = GitBlitSuite.password; | |
| private static final AtomicBoolean started = new AtomicBoolean(false); | |
| @BeforeClass | |
| public static void startGitblit() throws Exception { | |
| started.set(GitBlitSuite.startGitblit()); | |
| } | |
| @AfterClass | |
| public static void stopGitblit() throws Exception { | |
| if (started.get()) { | |
| GitBlitSuite.stopGitblit(); | |
| deleteWorkingFolders(); | |
| } | |
| } | |
| public static void deleteWorkingFolders() throws Exception { | |
| if (ticgitFolder.exists()) { | |
| GitBlitSuite.close(ticgitFolder); | |
| FileUtils.delete(ticgitFolder, FileUtils.RECURSIVE); | |
| } | |
| if (ticgit2Folder.exists()) { | |
| GitBlitSuite.close(ticgit2Folder); | |
| FileUtils.delete(ticgit2Folder, FileUtils.RECURSIVE); | |
| } | |
| if (jgitFolder.exists()) { | |
| GitBlitSuite.close(jgitFolder); | |
| FileUtils.delete(jgitFolder, FileUtils.RECURSIVE); | |
| } | |
| if (jgit2Folder.exists()) { | |
| GitBlitSuite.close(jgit2Folder); | |
| FileUtils.delete(jgit2Folder, FileUtils.RECURSIVE); | |
| } | |
| } | |
| @Test | |
| public void testClone() throws Exception { | |
| GitBlitSuite.close(ticgitFolder); | |
| if (ticgitFolder.exists()) { | |
| FileUtils.delete(ticgitFolder, FileUtils.RECURSIVE | FileUtils.RETRY); | |
| } | |
| CloneCommand clone = Git.cloneRepository(); | |
| clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url)); | |
| clone.setDirectory(ticgitFolder); | |
| clone.setBare(false); | |
| clone.setCloneAllBranches(true); | |
| clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password)); | |
| GitBlitSuite.close(clone.call()); | |
| assertTrue(true); | |
| } | |
| @Test | |
| public void testBogusLoginClone() throws Exception { | |
| // restrict repository access | |
| RepositoryModel model = GitBlit.self().getRepositoryModel("ticgit.git"); | |
| model.accessRestriction = AccessRestrictionType.CLONE; | |
| GitBlit.self().updateRepositoryModel(model.name, model, false); | |
| // delete any existing working folder | |
| boolean cloned = false; | |
| try { | |
| CloneCommand clone = Git.cloneRepository(); | |
| clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url)); | |
| clone.setDirectory(ticgit2Folder); | |
| clone.setBare(false); | |
| clone.setCloneAllBranches(true); | |
| clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider("bogus", "bogus")); | |
| GitBlitSuite.close(clone.call()); | |
| cloned = true; | |
| } catch (Exception e) { | |
| // swallow the exception which we expect | |
| } | |
| // restore anonymous repository access | |
| model.accessRestriction = AccessRestrictionType.NONE; | |
| GitBlit.self().updateRepositoryModel(model.name, model, false); | |
| assertFalse("Bogus login cloned a repository?!", cloned); | |
| } | |
| @Test | |
| public void testUnauthorizedLoginClone() throws Exception { | |
| // restrict repository access | |
| RepositoryModel model = GitBlit.self().getRepositoryModel("ticgit.git"); | |
| model.accessRestriction = AccessRestrictionType.CLONE; | |
| model.authorizationControl = AuthorizationControl.NAMED; | |
| UserModel user = new UserModel("james"); | |
| user.password = "james"; | |
| GitBlit.self().updateUserModel(user.username, user, true); | |
| GitBlit.self().updateRepositoryModel(model.name, model, false); | |
| FileUtils.delete(ticgit2Folder, FileUtils.RECURSIVE); | |
| // delete any existing working folder | |
| boolean cloned = false; | |
| try { | |
| CloneCommand clone = Git.cloneRepository(); | |
| clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url)); | |
| clone.setDirectory(ticgit2Folder); | |
| clone.setBare(false); | |
| clone.setCloneAllBranches(true); | |
| clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(user.username, user.password)); | |
| GitBlitSuite.close(clone.call()); | |
| cloned = true; | |
| } catch (Exception e) { | |
| // swallow the exception which we expect | |
| } | |
| assertFalse("Unauthorized login cloned a repository?!", cloned); | |
| FileUtils.delete(ticgit2Folder, FileUtils.RECURSIVE); | |
| // switch to authenticated | |
| model.authorizationControl = AuthorizationControl.AUTHENTICATED; | |
| GitBlit.self().updateRepositoryModel(model.name, model, false); | |
| // try clone again | |
| cloned = false; | |
| CloneCommand clone = Git.cloneRepository(); | |
| clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url)); | |
| clone.setDirectory(ticgit2Folder); | |
| clone.setBare(false); | |
| clone.setCloneAllBranches(true); | |
| clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(user.username, user.password)); | |
| GitBlitSuite.close(clone.call()); | |
| cloned = true; | |
| assertTrue("Authenticated login could not clone!", cloned); | |
| FileUtils.delete(ticgit2Folder, FileUtils.RECURSIVE); | |
| // restore anonymous repository access | |
| model.accessRestriction = AccessRestrictionType.NONE; | |
| model.authorizationControl = AuthorizationControl.NAMED; | |
| GitBlit.self().updateRepositoryModel(model.name, model, false); | |
| GitBlit.self().deleteUser(user.username); | |
| } | |
| @Test | |
| public void testAnonymousPush() throws Exception { | |
| GitBlitSuite.close(ticgitFolder); | |
| if (ticgitFolder.exists()) { | |
| FileUtils.delete(ticgitFolder, FileUtils.RECURSIVE | FileUtils.RETRY); | |
| } | |
| CloneCommand clone = Git.cloneRepository(); | |
| clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url)); | |
| clone.setDirectory(ticgitFolder); | |
| clone.setBare(false); | |
| clone.setCloneAllBranches(true); | |
| clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password)); | |
| GitBlitSuite.close(clone.call()); | |
| assertTrue(true); | |
| Git git = Git.open(ticgitFolder); | |
| File file = new File(ticgitFolder, "TODO"); | |
| OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(file, true), Constants.CHARSET); | |
| BufferedWriter w = new BufferedWriter(os); | |
| w.write("// hellol䏿–‡ " + new Date().toString() + "\n"); | |
| w.close(); | |
| git.add().addFilepattern(file.getName()).call(); | |
| git.commit().setMessage("test commit").call(); | |
| git.push().setPushAll().call(); | |
| GitBlitSuite.close(git); | |
| } | |
| @Test | |
| public void testSubfolderPush() throws Exception { | |
| GitBlitSuite.close(jgitFolder); | |
| if (jgitFolder.exists()) { | |
| FileUtils.delete(jgitFolder, FileUtils.RECURSIVE | FileUtils.RETRY); | |
| } | |
| CloneCommand clone = Git.cloneRepository(); | |
| clone.setURI(MessageFormat.format("{0}/git/test/jgit.git", url)); | |
| clone.setDirectory(jgitFolder); | |
| clone.setBare(false); | |
| clone.setCloneAllBranches(true); | |
| clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password)); | |
| GitBlitSuite.close(clone.call()); | |
| assertTrue(true); | |
| Git git = Git.open(jgitFolder); | |
| File file = new File(jgitFolder, "TODO"); | |
| OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(file, true), Constants.CHARSET); | |
| BufferedWriter w = new BufferedWriter(os); | |
| w.write("// " + new Date().toString() + "\n"); | |
| w.close(); | |
| git.add().addFilepattern(file.getName()).call(); | |
| git.commit().setMessage("test commit").call(); | |
| git.push().setPushAll().call(); | |
| GitBlitSuite.close(git); | |
| } | |
| @Test | |
| public void testPushToFrozenRepo() throws Exception { | |
| GitBlitSuite.close(jgitFolder); | |
| if (jgitFolder.exists()) { | |
| FileUtils.delete(jgitFolder, FileUtils.RECURSIVE | FileUtils.RETRY); | |
| } | |
| CloneCommand clone = Git.cloneRepository(); | |
| clone.setURI(MessageFormat.format("{0}/git/test/jgit.git", url)); | |
| clone.setDirectory(jgitFolder); | |
| clone.setBare(false); | |
| clone.setCloneAllBranches(true); | |
| clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password)); | |
| GitBlitSuite.close(clone.call()); | |
| assertTrue(true); | |
| // freeze repo | |
| RepositoryModel model = GitBlit.self().getRepositoryModel("test/jgit.git"); | |
| model.isFrozen = true; | |
| GitBlit.self().updateRepositoryModel(model.name, model, false); | |
| Git git = Git.open(jgitFolder); | |
| File file = new File(jgitFolder, "TODO"); | |
| OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(file, true), Constants.CHARSET); | |
| BufferedWriter w = new BufferedWriter(os); | |
| w.write("// " + new Date().toString() + "\n"); | |
| w.close(); | |
| git.add().addFilepattern(file.getName()).call(); | |
| git.commit().setMessage("test commit").call(); | |
| try { | |
| git.push().setPushAll().call(); | |
| assertTrue(false); | |
| } catch (Exception e) { | |
| assertTrue(e.getCause().getMessage().contains("access forbidden")); | |
| } | |
| // unfreeze repo | |
| model.isFrozen = false; | |
| GitBlit.self().updateRepositoryModel(model.name, model, false); | |
| git.push().setPushAll().call(); | |
| GitBlitSuite.close(git); | |
| } | |
| @Test | |
| public void testPushToNonBareRepository() throws Exception { | |
| CloneCommand clone = Git.cloneRepository(); | |
| clone.setURI(MessageFormat.format("{0}/git/working/jgit", url)); | |
| clone.setDirectory(jgit2Folder); | |
| clone.setBare(false); | |
| clone.setCloneAllBranches(true); | |
| clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password)); | |
| GitBlitSuite.close(clone.call()); | |
| assertTrue(true); | |
| Git git = Git.open(jgit2Folder); | |
| File file = new File(jgit2Folder, "NONBARE"); | |
| OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(file, true), Constants.CHARSET); | |
| BufferedWriter w = new BufferedWriter(os); | |
| w.write("// " + new Date().toString() + "\n"); | |
| w.close(); | |
| git.add().addFilepattern(file.getName()).call(); | |
| git.commit().setMessage("test commit followed by push to non-bare repository").call(); | |
| try { | |
| git.push().setPushAll().call(); | |
| assertTrue(false); | |
| } catch (Exception e) { | |
| assertTrue(e.getCause().getMessage().contains("git-receive-pack not permitted")); | |
| } | |
| GitBlitSuite.close(git); | |
| } | |
| @Test | |
| public void testCommitterVerification() throws Exception { | |
| UserModel user = new UserModel("james"); | |
| user.password = "james"; | |
| // account only uses account name to verify | |
| testCommitterVerification(user, user.username, null, true); | |
| // committer email address is ignored because account does not specify email | |
| testCommitterVerification(user, user.username, "something", true); | |
| // completely different committer | |
| testCommitterVerification(user, "joe", null, false); | |
| // test display name verification | |
| user.displayName = "James Moger"; | |
| testCommitterVerification(user, user.displayName, null, true); | |
| testCommitterVerification(user, user.displayName, "something", true); | |
| testCommitterVerification(user, "joe", null, false); | |
| // test email address verification | |
| user.emailAddress = "something"; | |
| testCommitterVerification(user, user.displayName, null, false); | |
| testCommitterVerification(user, user.displayName, "somethingelse", false); | |
| testCommitterVerification(user, user.displayName, user.emailAddress, true); | |
| // use same email address but with different committer | |
| testCommitterVerification(user, "joe", "somethingelse", false); | |
| } | |
| private void testCommitterVerification(UserModel user, String displayName, String emailAddress, boolean expectedSuccess) throws Exception { | |
| if (GitBlit.self().getUserModel(user.username) != null) { | |
| GitBlit.self().deleteUser(user.username); | |
| } | |
| CredentialsProvider cp = new UsernamePasswordCredentialsProvider(user.username, user.password); | |
| // fork from original to a temporary bare repo | |
| File verification = new File(GitBlitSuite.REPOSITORIES, "refchecks/verify-committer.git"); | |
| if (verification.exists()) { | |
| FileUtils.delete(verification, FileUtils.RECURSIVE); | |
| } | |
| CloneCommand clone = Git.cloneRepository(); | |
| clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url)); | |
| clone.setDirectory(verification); | |
| clone.setBare(true); | |
| clone.setCloneAllBranches(true); | |
| clone.setCredentialsProvider(cp); | |
| GitBlitSuite.close(clone.call()); | |
| // require push permissions and committer verification | |
| RepositoryModel model = GitBlit.self().getRepositoryModel("refchecks/verify-committer.git"); | |
| model.authorizationControl = AuthorizationControl.NAMED; | |
| model.accessRestriction = AccessRestrictionType.PUSH; | |
| model.verifyCommitter = true; | |
| // grant user push permission | |
| user.setRepositoryPermission(model.name, AccessPermission.PUSH); | |
| GitBlit.self().updateUserModel(user.username, user, true); | |
| GitBlit.self().updateRepositoryModel(model.name, model, false); | |
| // clone temp bare repo to working copy | |
| File local = new File(GitBlitSuite.REPOSITORIES, "refchecks/verify-wc"); | |
| if (local.exists()) { | |
| FileUtils.delete(local, FileUtils.RECURSIVE); | |
| } | |
| clone = Git.cloneRepository(); | |
| clone.setURI(MessageFormat.format("{0}/git/{1}", url, model.name)); | |
| clone.setDirectory(local); | |
| clone.setBare(false); | |
| clone.setCloneAllBranches(true); | |
| clone.setCredentialsProvider(cp); | |
| GitBlitSuite.close(clone.call()); | |
| Git git = Git.open(local); | |
| // force an identity which may or may not match the account's identity | |
| git.getRepository().getConfig().setString("user", null, "name", displayName); | |
| git.getRepository().getConfig().setString("user", null, "email", emailAddress); | |
| git.getRepository().getConfig().save(); | |
| // commit a file and push it | |
| File file = new File(local, "PUSHCHK"); | |
| OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(file, true), Constants.CHARSET); | |
| BufferedWriter w = new BufferedWriter(os); | |
| w.write("// " + new Date().toString() + "\n"); | |
| w.close(); | |
| git.add().addFilepattern(file.getName()).call(); | |
| git.commit().setMessage("push test").call(); | |
| Iterable<PushResult> results = git.push().setCredentialsProvider(cp).setRemote("origin").call(); | |
| for (PushResult result : results) { | |
| RemoteRefUpdate ref = result.getRemoteUpdate("refs/heads/master"); | |
| Status status = ref.getStatus(); | |
| if (expectedSuccess) { | |
| assertTrue("Verification failed! User was NOT able to push commit! " + status.name(), Status.OK.equals(status)); | |
| } else { | |
| assertTrue("Verification failed! User was able to push commit! " + status.name(), Status.REJECTED_OTHER_REASON.equals(status)); | |
| } | |
| } | |
| GitBlitSuite.close(git); | |
| // close serving repository | |
| GitBlitSuite.close(verification); | |
| } | |
| @Test | |
| public void testBlockClone() throws Exception { | |
| testRefChange(AccessPermission.VIEW, null, null, null); | |
| } | |
| @Test | |
| public void testBlockPush() throws Exception { | |
| testRefChange(AccessPermission.CLONE, null, null, null); | |
| } | |
| @Test | |
| public void testBlockBranchCreation() throws Exception { | |
| testRefChange(AccessPermission.PUSH, Status.REJECTED_OTHER_REASON, null, null); | |
| } | |
| @Test | |
| public void testBlockBranchDeletion() throws Exception { | |
| testRefChange(AccessPermission.CREATE, Status.OK, Status.REJECTED_OTHER_REASON, null); | |
| } | |
| @Test | |
| public void testBlockBranchRewind() throws Exception { | |
| testRefChange(AccessPermission.DELETE, Status.OK, Status.OK, Status.REJECTED_OTHER_REASON); | |
| } | |
| @Test | |
| public void testBranchRewind() throws Exception { | |
| testRefChange(AccessPermission.REWIND, Status.OK, Status.OK, Status.OK); | |
| } | |
| private void testRefChange(AccessPermission permission, Status expectedCreate, Status expectedDelete, Status expectedRewind) throws Exception { | |
| UserModel user = new UserModel("james"); | |
| user.password = "james"; | |
| if (GitBlit.self().getUserModel(user.username) != null) { | |
| GitBlit.self().deleteUser(user.username); | |
| } | |
| CredentialsProvider cp = new UsernamePasswordCredentialsProvider(user.username, user.password); | |
| // fork from original to a temporary bare repo | |
| File refChecks = new File(GitBlitSuite.REPOSITORIES, "refchecks/ticgit.git"); | |
| if (refChecks.exists()) { | |
| FileUtils.delete(refChecks, FileUtils.RECURSIVE); | |
| } | |
| CloneCommand clone = Git.cloneRepository(); | |
| clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url)); | |
| clone.setDirectory(refChecks); | |
| clone.setBare(true); | |
| clone.setCloneAllBranches(true); | |
| clone.setCredentialsProvider(cp); | |
| GitBlitSuite.close(clone.call()); | |
| // elevate repository to clone permission | |
| RepositoryModel model = GitBlit.self().getRepositoryModel("refchecks/ticgit.git"); | |
| switch (permission) { | |
| case VIEW: | |
| model.accessRestriction = AccessRestrictionType.CLONE; | |
| break; | |
| case CLONE: | |
| model.accessRestriction = AccessRestrictionType.CLONE; | |
| break; | |
| default: | |
| model.accessRestriction = AccessRestrictionType.PUSH; | |
| } | |
| model.authorizationControl = AuthorizationControl.NAMED; | |
| // grant user specified | |
| user.setRepositoryPermission(model.name, permission); | |
| GitBlit.self().updateUserModel(user.username, user, true); | |
| GitBlit.self().updateRepositoryModel(model.name, model, false); | |
| // clone temp bare repo to working copy | |
| File local = new File(GitBlitSuite.REPOSITORIES, "refchecks/ticgit-wc"); | |
| if (local.exists()) { | |
| FileUtils.delete(local, FileUtils.RECURSIVE); | |
| } | |
| clone = Git.cloneRepository(); | |
| clone.setURI(MessageFormat.format("{0}/git/{1}", url, model.name)); | |
| clone.setDirectory(local); | |
| clone.setBare(false); | |
| clone.setCloneAllBranches(true); | |
| clone.setCredentialsProvider(cp); | |
| try { | |
| GitBlitSuite.close(clone.call()); | |
| } catch (GitAPIException e) { | |
| if (permission.atLeast(AccessPermission.CLONE)) { | |
| throw e; | |
| } else { | |
| // close serving repository | |
| GitBlitSuite.close(refChecks); | |
| // user does not have clone permission | |
| assertTrue(e.getMessage(), e.getMessage().contains("not permitted")); | |
| return; | |
| } | |
| } | |
| Git git = Git.open(local); | |
| // commit a file and push it | |
| File file = new File(local, "PUSHCHK"); | |
| OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(file, true), Constants.CHARSET); | |
| BufferedWriter w = new BufferedWriter(os); | |
| w.write("// " + new Date().toString() + "\n"); | |
| w.close(); | |
| git.add().addFilepattern(file.getName()).call(); | |
| git.commit().setMessage("push test").call(); | |
| Iterable<PushResult> results = null; | |
| try { | |
| results = git.push().setCredentialsProvider(cp).setRemote("origin").call(); | |
| } catch (GitAPIException e) { | |
| if (permission.atLeast(AccessPermission.PUSH)) { | |
| throw e; | |
| } else { | |
| // close serving repository | |
| GitBlitSuite.close(refChecks); | |
| // user does not have push permission | |
| assertTrue(e.getMessage(), e.getMessage().contains("not permitted")); | |
| GitBlitSuite.close(git); | |
| return; | |
| } | |
| } | |
| for (PushResult result : results) { | |
| RemoteRefUpdate ref = result.getRemoteUpdate("refs/heads/master"); | |
| Status status = ref.getStatus(); | |
| if (permission.atLeast(AccessPermission.PUSH)) { | |
| assertTrue("User failed to push commit?! " + status.name(), Status.OK.equals(status)); | |
| } else { | |
| // close serving repository | |
| GitBlitSuite.close(refChecks); | |
| assertTrue("User was able to push commit! " + status.name(), Status.REJECTED_OTHER_REASON.equals(status)); | |
| GitBlitSuite.close(git); | |
| // skip delete test | |
| return; | |
| } | |
| } | |
| // create a local branch and push the new branch back to the origin | |
| git.branchCreate().setName("protectme").call(); | |
| RefSpec refSpec = new RefSpec("refs/heads/protectme:refs/heads/protectme"); | |
| results = git.push().setCredentialsProvider(cp).setRefSpecs(refSpec).setRemote("origin").call(); | |
| for (PushResult result : results) { | |
| RemoteRefUpdate ref = result.getRemoteUpdate("refs/heads/protectme"); | |
| Status status = ref.getStatus(); | |
| if (Status.OK.equals(expectedCreate)) { | |
| assertTrue("User failed to push creation?! " + status.name(), status.equals(expectedCreate)); | |
| } else { | |
| // close serving repository | |
| GitBlitSuite.close(refChecks); | |
| assertTrue("User was able to push ref creation! " + status.name(), status.equals(expectedCreate)); | |
| GitBlitSuite.close(git); | |
| // skip delete test | |
| return; | |
| } | |
| } | |
| // delete the branch locally | |
| git.branchDelete().setBranchNames("protectme").call(); | |
| // push a delete ref command | |
| refSpec = new RefSpec(":refs/heads/protectme"); | |
| results = git.push().setCredentialsProvider(cp).setRefSpecs(refSpec).setRemote("origin").call(); | |
| for (PushResult result : results) { | |
| RemoteRefUpdate ref = result.getRemoteUpdate("refs/heads/protectme"); | |
| Status status = ref.getStatus(); | |
| if (Status.OK.equals(expectedDelete)) { | |
| assertTrue("User failed to push ref deletion?! " + status.name(), status.equals(Status.OK)); | |
| } else { | |
| // close serving repository | |
| GitBlitSuite.close(refChecks); | |
| assertTrue("User was able to push ref deletion?! " + status.name(), status.equals(expectedDelete)); | |
| GitBlitSuite.close(git); | |
| // skip rewind test | |
| return; | |
| } | |
| } | |
| // rewind master by two commits | |
| git.reset().setRef("HEAD~2").setMode(ResetType.HARD).call(); | |
| // commit a change on this detached HEAD | |
| file = new File(local, "REWINDCHK"); | |
| os = new OutputStreamWriter(new FileOutputStream(file, true), Constants.CHARSET); | |
| w = new BufferedWriter(os); | |
| w.write("// " + new Date().toString() + "\n"); | |
| w.close(); | |
| git.add().addFilepattern(file.getName()).call(); | |
| RevCommit commit = git.commit().setMessage("rewind master and new commit").call(); | |
| // Reset master to our new commit now we our local branch tip is no longer | |
| // upstream of the remote branch tip. It is an alternate tip of the branch. | |
| JGitUtils.setBranchRef(git.getRepository(), "refs/heads/master", commit.getName()); | |
| // Try pushing our new tip to the origin. | |
| // This requires the server to "rewind" it's master branch and update it | |
| // to point to our alternate tip. This leaves the original master tip | |
| // unreferenced. | |
| results = git.push().setCredentialsProvider(cp).setRemote("origin").setForce(true).call(); | |
| for (PushResult result : results) { | |
| RemoteRefUpdate ref = result.getRemoteUpdate("refs/heads/master"); | |
| Status status = ref.getStatus(); | |
| if (Status.OK.equals(expectedRewind)) { | |
| assertTrue("User failed to rewind master?! " + status.name(), status.equals(expectedRewind)); | |
| } else { | |
| assertTrue("User was able to rewind master?! " + status.name(), status.equals(expectedRewind)); | |
| } | |
| } | |
| GitBlitSuite.close(git); | |
| // close serving repository | |
| GitBlitSuite.close(refChecks); | |
| GitBlit.self().deleteUser(user.username); | |
| } | |
| @Test | |
| public void testCreateOnPush() throws Exception { | |
| testCreateOnPush(false, false); | |
| testCreateOnPush(true, false); | |
| testCreateOnPush(false, true); | |
| } | |
| private void testCreateOnPush(boolean canCreate, boolean canAdmin) throws Exception { | |
| UserModel user = new UserModel("sampleuser"); | |
| user.password = user.username; | |
| if (GitBlit.self().getUserModel(user.username) != null) { | |
| GitBlit.self().deleteUser(user.username); | |
| } | |
| user.canCreate = canCreate; | |
| user.canAdmin = canAdmin; | |
| GitBlit.self().updateUserModel(user.username, user, true); | |
| CredentialsProvider cp = new UsernamePasswordCredentialsProvider(user.username, user.password); | |
| // fork from original to a temporary bare repo | |
| File tmpFolder = File.createTempFile("gitblit", "").getParentFile(); | |
| File createCheck = new File(tmpFolder, "ticgit.git"); | |
| if (createCheck.exists()) { | |
| FileUtils.delete(createCheck, FileUtils.RECURSIVE); | |
| } | |
| File personalRepo = new File(GitBlitSuite.REPOSITORIES, MessageFormat.format("~{0}/ticgit.git", user.username)); | |
| GitBlitSuite.close(personalRepo); | |
| if (personalRepo.exists()) { | |
| FileUtils.delete(personalRepo, FileUtils.RECURSIVE); | |
| } | |
| File projectRepo = new File(GitBlitSuite.REPOSITORIES, "project/ticgit.git"); | |
| GitBlitSuite.close(projectRepo); | |
| if (projectRepo.exists()) { | |
| FileUtils.delete(projectRepo, FileUtils.RECURSIVE); | |
| } | |
| CloneCommand clone = Git.cloneRepository(); | |
| clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url)); | |
| clone.setDirectory(createCheck); | |
| clone.setBare(true); | |
| clone.setCloneAllBranches(true); | |
| clone.setCredentialsProvider(cp); | |
| Git git = clone.call(); | |
| GitBlitSuite.close(personalRepo); | |
| // add a personal repository remote and a project remote | |
| git.getRepository().getConfig().setString("remote", "user", "url", MessageFormat.format("{0}/git/~{1}/ticgit.git", url, user.username)); | |
| git.getRepository().getConfig().setString("remote", "project", "url", MessageFormat.format("{0}/git/project/ticgit.git", url)); | |
| git.getRepository().getConfig().save(); | |
| // push to non-existent user repository | |
| try { | |
| Iterable<PushResult> results = git.push().setRemote("user").setPushAll().setCredentialsProvider(cp).call(); | |
| for (PushResult result : results) { | |
| RemoteRefUpdate ref = result.getRemoteUpdate("refs/heads/master"); | |
| Status status = ref.getStatus(); | |
| assertTrue("User failed to create repository?! " + status.name(), Status.OK.equals(status)); | |
| } | |
| assertTrue("User canAdmin:" + user.canAdmin + " canCreate:" + user.canCreate, user.canAdmin || user.canCreate); | |
| // confirm default personal repository permissions | |
| RepositoryModel model = GitBlit.self().getRepositoryModel(MessageFormat.format("~{0}/ticgit.git", user.username)); | |
| assertEquals("Unexpected owner", user.username, model.owner); | |
| assertEquals("Unexpected authorization control", AuthorizationControl.NAMED, model.authorizationControl); | |
| assertEquals("Unexpected access restriction", AccessRestrictionType.VIEW, model.accessRestriction); | |
| } catch (GitAPIException e) { | |
| assertTrue(e.getMessage(), e.getMessage().contains("git-receive-pack not found")); | |
| assertFalse("User canAdmin:" + user.canAdmin + " canCreate:" + user.canCreate, user.canAdmin || user.canCreate); | |
| } | |
| // push to non-existent project repository | |
| try { | |
| Iterable<PushResult> results = git.push().setRemote("project").setPushAll().setCredentialsProvider(cp).call(); | |
| GitBlitSuite.close(git); | |
| for (PushResult result : results) { | |
| RemoteRefUpdate ref = result.getRemoteUpdate("refs/heads/master"); | |
| Status status = ref.getStatus(); | |
| assertTrue("User failed to create repository?! " + status.name(), Status.OK.equals(status)); | |
| } | |
| assertTrue("User canAdmin:" + user.canAdmin, user.canAdmin); | |
| // confirm default project repository permissions | |
| RepositoryModel model = GitBlit.self().getRepositoryModel("project/ticgit.git"); | |
| assertEquals("Unexpected owner", user.username, model.owner); | |
| assertEquals("Unexpected authorization control", AuthorizationControl.fromName(GitBlit.getString(Keys.git.defaultAuthorizationControl, "NAMED")), model.authorizationControl); | |
| assertEquals("Unexpected access restriction", AccessRestrictionType.fromName(GitBlit.getString(Keys.git.defaultAccessRestriction, "NONE")), model.accessRestriction); | |
| } catch (GitAPIException e) { | |
| assertTrue(e.getMessage(), e.getMessage().contains("git-receive-pack not found")); | |
| assertFalse("User canAdmin:" + user.canAdmin, user.canAdmin); | |
| } | |
| GitBlitSuite.close(git); | |
| GitBlit.self().deleteUser(user.username); | |
| } | |
| @Test | |
| public void testPushLog() throws IOException { | |
| String name = "refchecks/ticgit.git"; | |
| File refChecks = new File(GitBlitSuite.REPOSITORIES, name); | |
| FileRepository repository = new FileRepository(refChecks); | |
| List<PushLogEntry> pushes = PushLogUtils.getPushLog(name, repository); | |
| GitBlitSuite.close(repository); | |
| assertTrue("Repository has an empty push log!", pushes.size() > 0); | |
| } | |
| } |