From 27ae9095639bb228a1b7ff86a3ebe4264abf05be Mon Sep 17 00:00:00 2001
From: mschaefers <mschaefers@scoop-gmbh.de>
Date: Thu, 29 Nov 2012 12:33:09 -0500
Subject: [PATCH] feature: when using LdapUserService one can configure Gitblit to fetch all users from ldap that can possibly login. This allows to see newly generated LDAP users instantly in Gitblit. By now an LDAP user had to log in once to appear in GitBlit.

---
 tests/com/gitblit/tests/GitServletTest.java |  677 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 636 insertions(+), 41 deletions(-)

diff --git a/tests/com/gitblit/tests/GitServletTest.java b/tests/com/gitblit/tests/GitServletTest.java
index db8182d..e65c61c 100644
--- a/tests/com/gitblit/tests/GitServletTest.java
+++ b/tests/com/gitblit/tests/GitServletTest.java
@@ -1,95 +1,690 @@
 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.OutputStreamWriter;
 import java.text.MessageFormat;
 import java.util.Date;
-import java.util.concurrent.Executors;
+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.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.GitBlitServer;
+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.RepositoryModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.JGitUtils;
 
 public class GitServletTest {
 
-	File folder = new File(GitBlitSuite.REPOSITORIES, "working/ticgit");
+	static File ticgitFolder = new File(GitBlitSuite.REPOSITORIES, "working/ticgit");
+	
+	static File ticgit2Folder = new File(GitBlitSuite.REPOSITORIES, "working/ticgit2");
 
-	static int port = 8180;
+	static File jgitFolder = new File(GitBlitSuite.REPOSITORIES, "working/jgit");
+	
+	static File jgit2Folder = new File(GitBlitSuite.REPOSITORIES, "working/jgit2");
 
-	static int shutdownPort = 8181;
+	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 {
-		// Start a Gitblit instance
-		Executors.newSingleThreadExecutor().execute(new Runnable() {
-			public void run() {
-				GitBlitServer.main("--httpPort", "" + port, "--httpsPort", "0", "--shutdownPort",
-						"" + shutdownPort, "--repositoriesFolder",
-						"\"" + GitBlitSuite.REPOSITORIES.getAbsolutePath() + "\"", "--userService",
-						"distrib/users.conf");
-			}
-		});
-
-		// Wait a few seconds for it to be running
-		Thread.sleep(2500);
+		started.set(GitBlitSuite.startGitblit());
 	}
 
 	@AfterClass
 	public static void stopGitblit() throws Exception {
-		// Stop Gitblit
-		GitBlitServer.main("--stop", "--shutdownPort", "" + shutdownPort);
-
-		// Wait a few seconds for it to be running
-		Thread.sleep(2500);
+		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 {
-		if (folder.exists()) {
-			FileUtils.delete(folder, FileUtils.RECURSIVE);
-		}
 		CloneCommand clone = Git.cloneRepository();
-		clone.setURI(MessageFormat.format("http://localhost:{0,number,#}/git/ticgit.git", port));
-		clone.setDirectory(folder);
+		clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url));
+		clone.setDirectory(ticgitFolder);
 		clone.setBare(false);
 		clone.setCloneAllBranches(true);
-		clone.call();
+		clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password));
+		GitBlitSuite.close(clone.call());		
+		assertTrue(true);
 	}
 
 	@Test
-	public void testAnonymousCommit() throws Exception {
-		Git git = Git.open(folder);
-		File file = new File(folder, "TODO");
-		OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(file, true));
+	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 {
+		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 {
+		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();
-		git.getRepository().close();
+		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 testBogusLoginClone() throws Exception {
-		File folder = new File(GitBlitSuite.REPOSITORIES, "working/gitblit");
-		if (folder.exists()) {
-			FileUtils.delete(folder, FileUtils.RECURSIVE);
+	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("http://localhost:{0,number,#}/git/gitblit.git", port));
-		clone.setDirectory(folder);
+		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(new UsernamePasswordCredentialsProvider("bogus", "bogus"));
-		clone.call();
+		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);
 	}
 }

--
Gitblit v1.9.1