@@ -1,20 +1,21 @@ | |||
/temp | |||
/lib | |||
/ext | |||
/build | |||
/site | |||
/git | |||
/build.properties | |||
/federation.properties | |||
/mailtest.properties | |||
/test-users.conf | |||
/.settings/ | |||
/src/main/java/reference.properties | |||
/src/main/java/WEB-INF/reference.properties | |||
/bin/ | |||
/build-demo.xml | |||
*.directory | |||
/.gradle | |||
/pom.xml | |||
/x509test | |||
/temp | |||
/lib | |||
/ext | |||
/build | |||
/site | |||
/git | |||
/build.properties | |||
/federation.properties | |||
/mailtest.properties | |||
/test-users.conf | |||
/.settings/ | |||
/src/main/java/reference.properties | |||
/src/main/java/WEB-INF/reference.properties | |||
/bin/ | |||
/build-demo.xml | |||
*.directory | |||
/.gradle | |||
/pom.xml | |||
/x509test | |||
/data | |||
/*.conf |
@@ -24,6 +24,7 @@ r17: { | |||
- Use standard ServletRequestWrapper instead of custom wrapper (issue 224) | |||
additions: | |||
- Added Git Daemon serving | |||
- Option to automatically tag branch tips on each push with an incremental revision number | |||
- Implemented multiple repository owners | |||
- Optional periodic LDAP user and team pre-fetching & synchronization | |||
@@ -62,6 +63,8 @@ r17: { | |||
- JGit 2.3.1.201302201838-r | |||
settings: | |||
- { name: 'git.daemonBindInterface', defaultValue: 'localhost' } | |||
- { name: 'git.daemonPort', defaultValue: 9418 } | |||
- { name: 'git.defaultIncrementalPushTagPrefix', defaultValue: 'r' } | |||
} | |||
@@ -76,6 +76,23 @@ git.searchExclusions = | |||
# SINCE 1.1.0 | |||
git.submoduleUrlPatterns = .*?://github.com/(.*) | |||
# Specify the interface for Git Daemon to bind it's service. | |||
# You may specify an ip or an empty value to bind to all interfaces. | |||
# Specifying localhost will result in Gitblit ONLY listening to requests to | |||
# localhost. | |||
# | |||
# SINCE 1.3.0 | |||
# RESTART REQUIRED | |||
git.daemonBindInterface = localhost | |||
# port for serving the Git Daemon service. <= 0 disables this service. | |||
# On Unix/Linux systems, ports < 1024 require root permissions. | |||
# Recommended value: 9418 | |||
# | |||
# SINCE 1.3.0 | |||
# RESTART REQUIRED | |||
git.daemonPort = 9418 | |||
# Allow push/pull over http/https with JGit servlet. | |||
# If you do NOT want to allow Git clients to clone/push to Gitblit set this | |||
# to false. You might want to do this if you are only using ssh:// or git://. |
@@ -47,7 +47,7 @@ | |||
* Wicket Filter ignorePaths parameter --> | |||
<servlet> | |||
<servlet-name>GitServlet</servlet-name> | |||
<servlet-class>com.gitblit.GitServlet</servlet-class> | |||
<servlet-class>com.gitblit.git.GitServlet</servlet-class> | |||
</servlet> | |||
<servlet-mapping> | |||
<servlet-name>GitServlet</servlet-name> |
@@ -90,6 +90,7 @@ import com.gitblit.Constants.RegistrantType; | |||
import com.gitblit.fanout.FanoutNioService; | |||
import com.gitblit.fanout.FanoutService; | |||
import com.gitblit.fanout.FanoutSocketService; | |||
import com.gitblit.git.GitDaemon; | |||
import com.gitblit.models.FederationModel; | |||
import com.gitblit.models.FederationProposal; | |||
import com.gitblit.models.FederationSet; | |||
@@ -190,6 +191,8 @@ public class GitBlit implements ServletContextListener { | |||
private FanoutService fanoutService; | |||
private GitDaemon gitDaemon; | |||
public GitBlit() { | |||
if (gitblit == null) { | |||
// set the static singleton reference | |||
@@ -633,15 +636,18 @@ public class GitBlit implements ServletContextListener { | |||
// try to authenticate by servlet container principal | |||
Principal principal = httpRequest.getUserPrincipal(); | |||
if (principal != null) { | |||
UserModel user = getUserModel(principal.getName()); | |||
if (user != null) { | |||
flagWicketSession(AuthenticationType.CONTAINER); | |||
logger.debug(MessageFormat.format("{0} authenticated by servlet container principal from {1}", | |||
user.username, httpRequest.getRemoteAddr())); | |||
return user; | |||
} else { | |||
logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted servlet container authentication from {1}", | |||
principal.getName(), httpRequest.getRemoteAddr())); | |||
String username = principal.getName(); | |||
if (StringUtils.isEmpty(username)) { | |||
UserModel user = getUserModel(username); | |||
if (user != null) { | |||
flagWicketSession(AuthenticationType.CONTAINER); | |||
logger.debug(MessageFormat.format("{0} authenticated by servlet container principal from {1}", | |||
user.username, httpRequest.getRemoteAddr())); | |||
return user; | |||
} else { | |||
logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted servlet container authentication from {1}", | |||
principal.getName(), httpRequest.getRemoteAddr())); | |||
} | |||
} | |||
} | |||
@@ -1376,7 +1382,7 @@ public class GitBlit implements ServletContextListener { | |||
FileBasedConfig config = (FileBasedConfig) getRepositoryConfig(r); | |||
if (config.isOutdated()) { | |||
// reload model | |||
logger.info(MessageFormat.format("Config for \"{0}\" has changed. Reloading model and updating cache.", repositoryName)); | |||
logger.debug(MessageFormat.format("Config for \"{0}\" has changed. Reloading model and updating cache.", repositoryName)); | |||
model = loadRepositoryModel(model.name); | |||
removeFromCachedRepositoryList(model.name); | |||
addToCachedRepositoryList(model); | |||
@@ -3115,18 +3121,34 @@ public class GitBlit implements ServletContextListener { | |||
projectConfigs = new FileBasedConfig(getFileOrFolder(Keys.web.projectsFile, "${baseFolder}/projects.conf"), FS.detect()); | |||
getProjectConfigs(); | |||
// schedule mail engine | |||
configureMailExecutor(); | |||
configureLuceneIndexing(); | |||
configureGarbageCollector(); | |||
if (startFederation) { | |||
configureFederation(); | |||
} | |||
configureJGit(); | |||
configureFanout(); | |||
configureGitDaemon(); | |||
ContainerUtils.CVE_2007_0450.test(); | |||
} | |||
protected void configureMailExecutor() { | |||
if (mailExecutor.isReady()) { | |||
logger.info("Mail executor is scheduled to process the message queue every 2 minutes."); | |||
scheduledExecutor.scheduleAtFixedRate(mailExecutor, 1, 2, TimeUnit.MINUTES); | |||
} else { | |||
logger.warn("Mail server is not properly configured. Mail services disabled."); | |||
} | |||
// schedule lucene engine | |||
enableLuceneIndexing(); | |||
} | |||
protected void configureLuceneIndexing() { | |||
scheduledExecutor.scheduleAtFixedRate(luceneExecutor, 1, 2, TimeUnit.MINUTES); | |||
logger.info("Lucene executor is scheduled to process indexed branches every 2 minutes."); | |||
} | |||
protected void configureGarbageCollector() { | |||
// schedule gc engine | |||
if (gcExecutor.isReady()) { | |||
logger.info("GC executor is scheduled to scan repositories every 24 hours."); | |||
@@ -3150,21 +3172,19 @@ public class GitBlit implements ServletContextListener { | |||
logger.info(MessageFormat.format("Next scheculed GC scan is in {0}", when)); | |||
scheduledExecutor.scheduleAtFixedRate(gcExecutor, delay, 60*24, TimeUnit.MINUTES); | |||
} | |||
if (startFederation) { | |||
configureFederation(); | |||
} | |||
} | |||
protected void configureJGit() { | |||
// Configure JGit | |||
WindowCacheConfig cfg = new WindowCacheConfig(); | |||
cfg.setPackedGitWindowSize(settings.getFilesize(Keys.git.packedGitWindowSize, cfg.getPackedGitWindowSize())); | |||
cfg.setPackedGitLimit(settings.getFilesize(Keys.git.packedGitLimit, cfg.getPackedGitLimit())); | |||
cfg.setDeltaBaseCacheLimit(settings.getFilesize(Keys.git.deltaBaseCacheLimit, cfg.getDeltaBaseCacheLimit())); | |||
cfg.setPackedGitOpenFiles(settings.getFilesize(Keys.git.packedGitOpenFiles, cfg.getPackedGitOpenFiles())); | |||
cfg.setStreamFileThreshold(settings.getFilesize(Keys.git.streamFileThreshold, cfg.getStreamFileThreshold())); | |||
cfg.setPackedGitMMAP(settings.getBoolean(Keys.git.packedGitMmap, cfg.isPackedGitMMAP())); | |||
try { | |||
WindowCache.reconfigure(cfg); | |||
logger.debug(MessageFormat.format("{0} = {1,number,0}", Keys.git.packedGitWindowSize, cfg.getPackedGitWindowSize())); | |||
@@ -3176,16 +3196,16 @@ public class GitBlit implements ServletContextListener { | |||
} catch (IllegalArgumentException e) { | |||
logger.error("Failed to configure JGit parameters!", e); | |||
} | |||
ContainerUtils.CVE_2007_0450.test(); | |||
} | |||
protected void configureFanout() { | |||
// startup Fanout PubSub service | |||
if (settings.getInteger(Keys.fanout.port, 0) > 0) { | |||
String bindInterface = settings.getString(Keys.fanout.bindInterface, null); | |||
int port = settings.getInteger(Keys.fanout.port, FanoutService.DEFAULT_PORT); | |||
boolean useNio = settings.getBoolean(Keys.fanout.useNio, true); | |||
int limit = settings.getInteger(Keys.fanout.connectionLimit, 0); | |||
if (useNio) { | |||
if (StringUtils.isEmpty(bindInterface)) { | |||
fanoutService = new FanoutNioService(port); | |||
@@ -3199,16 +3219,25 @@ public class GitBlit implements ServletContextListener { | |||
fanoutService = new FanoutSocketService(bindInterface, port); | |||
} | |||
} | |||
fanoutService.setConcurrentConnectionLimit(limit); | |||
fanoutService.setAllowAllChannelAnnouncements(false); | |||
fanoutService.start(); | |||
} | |||
} | |||
protected void enableLuceneIndexing() { | |||
scheduledExecutor.scheduleAtFixedRate(luceneExecutor, 1, 2, TimeUnit.MINUTES); | |||
logger.info("Lucene executor is scheduled to process indexed branches every 2 minutes."); | |||
protected void configureGitDaemon() { | |||
String bindInterface = settings.getString(Keys.git.daemonBindInterface, "localhost"); | |||
int port = settings.getInteger(Keys.git.daemonPort, GitDaemon.DEFAULT_PORT); | |||
if (port > 0) { | |||
try { | |||
gitDaemon = new GitDaemon(bindInterface, port, getRepositoriesFolder()); | |||
gitDaemon.start(); | |||
logger.info(MessageFormat.format("Git daemon is listening on {0}:{1,number,0}", bindInterface, port)); | |||
} catch (IOException e) { | |||
logger.error(MessageFormat.format("Failed to start Git daemon on {0}:{1,number.0}", bindInterface, port), e); | |||
} | |||
} | |||
} | |||
protected final Logger getLogger() { | |||
@@ -3319,6 +3348,9 @@ public class GitBlit implements ServletContextListener { | |||
if (fanoutService != null) { | |||
fanoutService.stop(); | |||
} | |||
if (gitDaemon != null) { | |||
gitDaemon.stop(); | |||
} | |||
} | |||
/** |
@@ -342,6 +342,7 @@ public class GitBlitServer { | |||
// Override settings from the command-line | |||
settings.overrideSetting(Keys.realm.userService, params.userService); | |||
settings.overrideSetting(Keys.git.repositoriesFolder, params.repositoriesFolder); | |||
settings.overrideSetting(Keys.git.daemonPort, params.gitPort); | |||
// Start up an in-memory LDAP server, if configured | |||
try { | |||
@@ -601,6 +602,9 @@ public class GitBlitServer { | |||
@Parameter(names = "--ajpPort", description = "AJP port to serve. (port <= 0 will disable this connector)") | |||
public Integer ajpPort = FILESETTINGS.getInteger(Keys.server.ajpPort, 0); | |||
@Parameter(names = "--gitPort", description = "Git Daemon port to serve. (port <= 0 will disable this connector)") | |||
public Integer gitPort = FILESETTINGS.getInteger(Keys.git.daemonPort, 9418); | |||
@Parameter(names = "--alias", description = "Alias of SSL certificate in keystore for serving https.") | |||
public String alias = FILESETTINGS.getString(Keys.server.certificateAlias, ""); | |||
@@ -112,15 +112,8 @@ public class GitFilter extends AccessRestrictionFilter { | |||
*/ | |||
@Override | |||
protected boolean isActionAllowed(RepositoryModel repository, String action) { | |||
if (!StringUtils.isEmpty(action)) { | |||
if (action.equals(gitReceivePack)) { | |||
// Push request | |||
if (!repository.isBare) { | |||
logger.warn("Gitblit does not allow pushes to repositories with a working copy"); | |||
return false; | |||
} | |||
} | |||
} | |||
// the log here has been moved into ReceiveHook to provide clients with | |||
// error messages | |||
return true; | |||
} | |||
@@ -1,445 +0,0 @@ | |||
/* | |||
* Copyright 2011 gitblit.com. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.gitblit; | |||
import groovy.lang.Binding; | |||
import groovy.util.GroovyScriptEngine; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.text.MessageFormat; | |||
import java.util.Collection; | |||
import java.util.Enumeration; | |||
import java.util.LinkedHashSet; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import javax.servlet.ServletConfig; | |||
import javax.servlet.ServletContext; | |||
import javax.servlet.ServletException; | |||
import javax.servlet.http.HttpServletRequest; | |||
import org.eclipse.jgit.http.server.resolver.DefaultReceivePackFactory; | |||
import org.eclipse.jgit.http.server.resolver.DefaultUploadPackFactory; | |||
import org.eclipse.jgit.lib.PersonIdent; | |||
import org.eclipse.jgit.lib.Ref; | |||
import org.eclipse.jgit.lib.Repository; | |||
import org.eclipse.jgit.revwalk.RevCommit; | |||
import org.eclipse.jgit.transport.PostReceiveHook; | |||
import org.eclipse.jgit.transport.PreReceiveHook; | |||
import org.eclipse.jgit.transport.ReceiveCommand; | |||
import org.eclipse.jgit.transport.ReceiveCommand.Result; | |||
import org.eclipse.jgit.transport.ReceivePack; | |||
import org.eclipse.jgit.transport.RefFilter; | |||
import org.eclipse.jgit.transport.UploadPack; | |||
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; | |||
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import com.gitblit.Constants.AccessRestrictionType; | |||
import com.gitblit.client.Translation; | |||
import com.gitblit.models.RepositoryModel; | |||
import com.gitblit.models.UserModel; | |||
import com.gitblit.utils.ClientLogger; | |||
import com.gitblit.utils.HttpUtils; | |||
import com.gitblit.utils.IssueUtils; | |||
import com.gitblit.utils.JGitUtils; | |||
import com.gitblit.utils.PushLogUtils; | |||
import com.gitblit.utils.StringUtils; | |||
/** | |||
* The GitServlet exists to force configuration of the JGit GitServlet based on | |||
* the Gitblit settings from either gitblit.properties or from context | |||
* parameters in the web.xml file. | |||
* | |||
* It also implements and registers the Groovy hook mechanism. | |||
* | |||
* Access to this servlet is protected by the GitFilter. | |||
* | |||
* @author James Moger | |||
* | |||
*/ | |||
public class GitServlet extends org.eclipse.jgit.http.server.GitServlet { | |||
private static final long serialVersionUID = 1L; | |||
private GroovyScriptEngine gse; | |||
private File groovyDir; | |||
@Override | |||
public void init(ServletConfig config) throws ServletException { | |||
groovyDir = GitBlit.getGroovyScriptsFolder(); | |||
try { | |||
// set Grape root | |||
File grapeRoot = GitBlit.getFileOrFolder(Keys.groovy.grapeFolder, "${baseFolder}/groovy/grape").getAbsoluteFile(); | |||
grapeRoot.mkdirs(); | |||
System.setProperty("grape.root", grapeRoot.getAbsolutePath()); | |||
gse = new GroovyScriptEngine(groovyDir.getAbsolutePath()); | |||
} catch (IOException e) { | |||
throw new ServletException("Failed to instantiate Groovy Script Engine!", e); | |||
} | |||
// set the Gitblit receive hook | |||
setReceivePackFactory(new DefaultReceivePackFactory() { | |||
@Override | |||
public ReceivePack create(HttpServletRequest req, Repository db) | |||
throws ServiceNotEnabledException, ServiceNotAuthorizedException { | |||
// determine repository name from request | |||
String repositoryName = req.getPathInfo().substring(1); | |||
repositoryName = GitFilter.getRepositoryName(repositoryName); | |||
GitblitReceiveHook hook = new GitblitReceiveHook(); | |||
hook.repositoryName = repositoryName; | |||
hook.gitblitUrl = HttpUtils.getGitblitURL(req); | |||
ReceivePack rp = super.create(req, db); | |||
rp.setPreReceiveHook(hook); | |||
rp.setPostReceiveHook(hook); | |||
// determine pushing user | |||
PersonIdent person = rp.getRefLogIdent(); | |||
UserModel user = GitBlit.self().getUserModel(person.getName()); | |||
if (user == null) { | |||
// anonymous push, create a temporary usermodel | |||
user = new UserModel(person.getName()); | |||
} | |||
// enforce advanced ref permissions | |||
RepositoryModel repository = GitBlit.self().getRepositoryModel(repositoryName); | |||
rp.setAllowCreates(user.canCreateRef(repository)); | |||
rp.setAllowDeletes(user.canDeleteRef(repository)); | |||
rp.setAllowNonFastForwards(user.canRewindRef(repository)); | |||
if (repository.isFrozen) { | |||
throw new ServiceNotEnabledException(); | |||
} | |||
return rp; | |||
} | |||
}); | |||
// override the default upload pack to exclude gitblit refs | |||
setUploadPackFactory(new DefaultUploadPackFactory() { | |||
@Override | |||
public UploadPack create(final HttpServletRequest req, final Repository db) | |||
throws ServiceNotEnabledException, ServiceNotAuthorizedException { | |||
UploadPack up = super.create(req, db); | |||
RefFilter refFilter = new RefFilter() { | |||
@Override | |||
public Map<String, Ref> filter(Map<String, Ref> refs) { | |||
// admin accounts can access all refs | |||
UserModel user = GitBlit.self().authenticate(req); | |||
if (user == null) { | |||
user = UserModel.ANONYMOUS; | |||
} | |||
if (user.canAdmin()) { | |||
return refs; | |||
} | |||
// normal users can not clone gitblit refs | |||
refs.remove(IssueUtils.GB_ISSUES); | |||
refs.remove(PushLogUtils.GB_PUSHES); | |||
return refs; | |||
} | |||
}; | |||
up.setRefFilter(refFilter); | |||
return up; | |||
} | |||
}); | |||
super.init(new GitblitServletConfig(config)); | |||
} | |||
/** | |||
* Transitional wrapper class to configure the JGit 1.2 GitFilter. This | |||
* GitServlet will probably be replaced by a GitFilter so that Gitblit can | |||
* serve Git repositories on the root URL and not a /git sub-url. | |||
* | |||
* @author James Moger | |||
* | |||
*/ | |||
private class GitblitServletConfig implements ServletConfig { | |||
final ServletConfig config; | |||
GitblitServletConfig(ServletConfig config) { | |||
this.config = config; | |||
} | |||
@Override | |||
public String getServletName() { | |||
return config.getServletName(); | |||
} | |||
@Override | |||
public ServletContext getServletContext() { | |||
return config.getServletContext(); | |||
} | |||
@Override | |||
public String getInitParameter(String name) { | |||
if (name.equals("base-path")) { | |||
return GitBlit.getRepositoriesFolder().getAbsolutePath(); | |||
} else if (name.equals("export-all")) { | |||
return "1"; | |||
} | |||
return config.getInitParameter(name); | |||
} | |||
@Override | |||
public Enumeration<String> getInitParameterNames() { | |||
return config.getInitParameterNames(); | |||
} | |||
} | |||
/** | |||
* The Gitblit receive hook allows for special processing on push events. | |||
* That might include rejecting writes to specific branches or executing a | |||
* script. | |||
* | |||
* @author James Moger | |||
* | |||
*/ | |||
private class GitblitReceiveHook implements PreReceiveHook, PostReceiveHook { | |||
protected final Logger logger = LoggerFactory.getLogger(GitblitReceiveHook.class); | |||
protected String repositoryName; | |||
protected String gitblitUrl; | |||
/** | |||
* Instrumentation point where the incoming push event has been parsed, | |||
* validated, objects created BUT refs have not been updated. You might | |||
* use this to enforce a branch-write permissions model. | |||
*/ | |||
@Override | |||
public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) { | |||
RepositoryModel repository = GitBlit.self().getRepositoryModel(repositoryName); | |||
UserModel user = getUserModel(rp); | |||
if (repository.accessRestriction.atLeast(AccessRestrictionType.PUSH) && repository.verifyCommitter) { | |||
if (StringUtils.isEmpty(user.emailAddress)) { | |||
// emit warning if user does not have an email address | |||
logger.warn(MessageFormat.format("Consider setting an email address for {0} ({1}) to improve committer verification.", user.getDisplayName(), user.username)); | |||
} | |||
// Optionally enforce that the committer of the left parent chain | |||
// match the account being used to push the commits. | |||
// | |||
// This requires all merge commits are executed with the "--no-ff" | |||
// option to force a merge commit even if fast-forward is possible. | |||
// This ensures that the chain of left parents has the commit | |||
// identity of the merging user. | |||
for (ReceiveCommand cmd : commands) { | |||
try { | |||
List<RevCommit> commits = JGitUtils.getRevLog(rp.getRepository(), cmd.getOldId().name(), cmd.getNewId().name()); | |||
for (RevCommit commit : commits) { | |||
PersonIdent committer = commit.getCommitterIdent(); | |||
if (!user.is(committer.getName(), committer.getEmailAddress())) { | |||
String reason; | |||
if (StringUtils.isEmpty(user.emailAddress)) { | |||
// account does not have en email address | |||
reason = MessageFormat.format("{0} by {1} <{2}> was not committed by {3} ({4})", commit.getId().name(), committer.getName(), StringUtils.isEmpty(committer.getEmailAddress()) ? "?":committer.getEmailAddress(), user.getDisplayName(), user.username); | |||
} else { | |||
// account has an email address | |||
reason = MessageFormat.format("{0} by {1} <{2}> was not committed by {3} ({4}) <{5}>", commit.getId().name(), committer.getName(), StringUtils.isEmpty(committer.getEmailAddress()) ? "?":committer.getEmailAddress(), user.getDisplayName(), user.username, user.emailAddress); | |||
} | |||
cmd.setResult(Result.REJECTED_OTHER_REASON, reason); | |||
break; | |||
} | |||
} | |||
} catch (Exception e) { | |||
logger.error("Failed to verify commits were made by pushing user", e); | |||
} | |||
} | |||
} | |||
Set<String> scripts = new LinkedHashSet<String>(); | |||
scripts.addAll(GitBlit.self().getPreReceiveScriptsInherited(repository)); | |||
scripts.addAll(repository.preReceiveScripts); | |||
runGroovy(repository, user, commands, rp, scripts); | |||
for (ReceiveCommand cmd : commands) { | |||
if (!Result.NOT_ATTEMPTED.equals(cmd.getResult())) { | |||
logger.warn(MessageFormat.format("{0} {1} because \"{2}\"", cmd.getNewId() | |||
.getName(), cmd.getResult(), cmd.getMessage())); | |||
} | |||
} | |||
} | |||
/** | |||
* Instrumentation point where the incoming push has been applied to the | |||
* repository. This is the point where we would trigger a Jenkins build | |||
* or send an email. | |||
*/ | |||
@Override | |||
public void onPostReceive(ReceivePack rp, Collection<ReceiveCommand> commands) { | |||
if (commands.size() == 0) { | |||
logger.info("skipping post-receive hooks, no refs created, updated, or removed"); | |||
return; | |||
} | |||
UserModel user = getUserModel(rp); | |||
RepositoryModel repository = GitBlit.self().getRepositoryModel(repositoryName); | |||
if (repository.useIncrementalPushTags) { | |||
// tag each pushed branch tip | |||
String emailAddress = user.emailAddress == null ? rp.getRefLogIdent().getEmailAddress() : user.emailAddress; | |||
PersonIdent userIdent = new PersonIdent(user.getDisplayName(), emailAddress); | |||
for (ReceiveCommand cmd : commands) { | |||
if (!cmd.getRefName().startsWith("refs/heads/")) { | |||
// only tag branch ref changes | |||
continue; | |||
} | |||
if (!ReceiveCommand.Type.DELETE.equals(cmd.getType()) | |||
&& ReceiveCommand.Result.OK.equals(cmd.getResult())) { | |||
String objectId = cmd.getNewId().getName(); | |||
String branch = cmd.getRefName().substring("refs/heads/".length()); | |||
// get translation based on the server's locale setting | |||
String template = Translation.get("gb.incrementalPushTagMessage"); | |||
String msg = MessageFormat.format(template, branch); | |||
String prefix; | |||
if (StringUtils.isEmpty(repository.incrementalPushTagPrefix)) { | |||
prefix = GitBlit.getString(Keys.git.defaultIncrementalPushTagPrefix, "r"); | |||
} else { | |||
prefix = repository.incrementalPushTagPrefix; | |||
} | |||
JGitUtils.createIncrementalRevisionTag( | |||
rp.getRepository(), | |||
objectId, | |||
userIdent, | |||
prefix, | |||
"0", | |||
msg); | |||
} | |||
} | |||
} | |||
// log ref changes | |||
for (ReceiveCommand cmd : commands) { | |||
if (Result.OK.equals(cmd.getResult())) { | |||
// add some logging for important ref changes | |||
switch (cmd.getType()) { | |||
case DELETE: | |||
logger.info(MessageFormat.format("{0} DELETED {1} in {2} ({3})", user.username, cmd.getRefName(), repository.name, cmd.getOldId().name())); | |||
break; | |||
case CREATE: | |||
logger.info(MessageFormat.format("{0} CREATED {1} in {2}", user.username, cmd.getRefName(), repository.name)); | |||
break; | |||
case UPDATE: | |||
logger.info(MessageFormat.format("{0} UPDATED {1} in {2} (from {3} to {4})", user.username, cmd.getRefName(), repository.name, cmd.getOldId().name(), cmd.getNewId().name())); | |||
break; | |||
case UPDATE_NONFASTFORWARD: | |||
logger.info(MessageFormat.format("{0} UPDATED NON-FAST-FORWARD {1} in {2} (from {3} to {4})", user.username, cmd.getRefName(), repository.name, cmd.getOldId().name(), cmd.getNewId().name())); | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
} | |||
// update push log | |||
try { | |||
PushLogUtils.updatePushLog(user, rp.getRepository(), commands); | |||
logger.info(MessageFormat.format("{0} push log updated", repository.name)); | |||
} catch (Exception e) { | |||
logger.error(MessageFormat.format("Failed to update {0} pushlog", repository.name), e); | |||
} | |||
// run Groovy hook scripts | |||
Set<String> scripts = new LinkedHashSet<String>(); | |||
scripts.addAll(GitBlit.self().getPostReceiveScriptsInherited(repository)); | |||
scripts.addAll(repository.postReceiveScripts); | |||
runGroovy(repository, user, commands, rp, scripts); | |||
} | |||
/** | |||
* Returns the UserModel for the user pushing the changes. | |||
* | |||
* @param rp | |||
* @return a UserModel | |||
*/ | |||
protected UserModel getUserModel(ReceivePack rp) { | |||
PersonIdent person = rp.getRefLogIdent(); | |||
UserModel user = GitBlit.self().getUserModel(person.getName()); | |||
if (user == null) { | |||
// anonymous push, create a temporary usermodel | |||
user = new UserModel(person.getName()); | |||
user.isAuthenticated = false; | |||
} | |||
return user; | |||
} | |||
/** | |||
* Runs the specified Groovy hook scripts. | |||
* | |||
* @param repository | |||
* @param user | |||
* @param commands | |||
* @param scripts | |||
*/ | |||
protected void runGroovy(RepositoryModel repository, UserModel user, | |||
Collection<ReceiveCommand> commands, ReceivePack rp, Set<String> scripts) { | |||
if (scripts == null || scripts.size() == 0) { | |||
// no Groovy scripts to execute | |||
return; | |||
} | |||
Binding binding = new Binding(); | |||
binding.setVariable("gitblit", GitBlit.self()); | |||
binding.setVariable("repository", repository); | |||
binding.setVariable("receivePack", rp); | |||
binding.setVariable("user", user); | |||
binding.setVariable("commands", commands); | |||
binding.setVariable("url", gitblitUrl); | |||
binding.setVariable("logger", logger); | |||
binding.setVariable("clientLogger", new ClientLogger(rp)); | |||
for (String script : scripts) { | |||
if (StringUtils.isEmpty(script)) { | |||
continue; | |||
} | |||
// allow script to be specified without .groovy extension | |||
// this is easier to read in the settings | |||
File file = new File(groovyDir, script); | |||
if (!file.exists() && !script.toLowerCase().endsWith(".groovy")) { | |||
file = new File(groovyDir, script + ".groovy"); | |||
if (file.exists()) { | |||
script = file.getName(); | |||
} | |||
} | |||
try { | |||
Object result = gse.run(script, binding); | |||
if (result instanceof Boolean) { | |||
if (!((Boolean) result)) { | |||
logger.error(MessageFormat.format( | |||
"Groovy script {0} has failed! Hook scripts aborted.", script)); | |||
break; | |||
} | |||
} | |||
} catch (Exception e) { | |||
logger.error( | |||
MessageFormat.format("Failed to execute Groovy script {0}", script), e); | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -287,6 +287,16 @@ public abstract class IStoredSettings { | |||
overrides.put(key, value); | |||
} | |||
/** | |||
* Override the specified key with the specified value. | |||
* | |||
* @param key | |||
* @param value | |||
*/ | |||
public void overrideSetting(String key, int value) { | |||
overrides.put(key, "" + value); | |||
} | |||
/** | |||
* Updates the values for the specified keys and persists the entire | |||
* configuration file. |
@@ -0,0 +1,64 @@ | |||
/* | |||
* Copyright 2013 gitblit.com. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.gitblit.git; | |||
import java.io.File; | |||
import java.net.InetSocketAddress; | |||
import org.eclipse.jgit.transport.Daemon; | |||
import org.eclipse.jgit.transport.DaemonClient; | |||
import com.gitblit.utils.StringUtils; | |||
/** | |||
* Gitblit's Git Daemon ignores any and all per-repository daemon settings | |||
* and integrates into Gitblit's security model. | |||
* | |||
* @author James Moger | |||
* | |||
*/ | |||
public class GitDaemon extends Daemon { | |||
/** | |||
* Construct the Gitblit Git daemon. | |||
* | |||
* @param bindInterface | |||
* the ip address of the interface to bind | |||
* @param port | |||
* the port to serve on | |||
* @param folder | |||
* the folder to serve from | |||
*/ | |||
public GitDaemon(String bindInterface, int port, File folder) { | |||
super(StringUtils.isEmpty(bindInterface) ? new InetSocketAddress(port) : new InetSocketAddress(bindInterface, port)); | |||
// set the repository resolver and pack factories | |||
setRepositoryResolver(new RepositoryResolver<DaemonClient>(folder)); | |||
setUploadPackFactory(new GitblitUploadPackFactory<DaemonClient>()); | |||
setReceivePackFactory(new GitblitReceivePackFactory<DaemonClient>()); | |||
// configure the git daemon to ignore the per-repository settings, | |||
// daemon.uploadpack and daemon.receivepack | |||
getService("git-upload-pack").setOverridable(false); | |||
getService("git-receive-pack").setOverridable(false); | |||
// enable both the upload and receive services and let the resolver, | |||
// pack factories, and receive hook handle security | |||
getService("git-upload-pack").setEnabled(true); | |||
getService("git-receive-pack").setEnabled(true); | |||
} | |||
} |
@@ -0,0 +1,42 @@ | |||
/* | |||
* Copyright 2011 gitblit.com. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.gitblit.git; | |||
import javax.servlet.ServletConfig; | |||
import javax.servlet.ServletException; | |||
import javax.servlet.http.HttpServletRequest; | |||
import com.gitblit.GitBlit; | |||
/** | |||
* The GitServlet provides http/https access to Git repositories. | |||
* Access to this servlet is protected by the GitFilter. | |||
* | |||
* @author James Moger | |||
* | |||
*/ | |||
public class GitServlet extends org.eclipse.jgit.http.server.GitServlet { | |||
private static final long serialVersionUID = 1L; | |||
@Override | |||
public void init(ServletConfig config) throws ServletException { | |||
setRepositoryResolver(new RepositoryResolver<HttpServletRequest>(GitBlit.getRepositoriesFolder())); | |||
setUploadPackFactory(new GitblitUploadPackFactory<HttpServletRequest>()); | |||
setReceivePackFactory(new GitblitReceivePackFactory<HttpServletRequest>()); | |||
super.init(config); | |||
} | |||
} |
@@ -0,0 +1,106 @@ | |||
/* | |||
* Copyright 2011 gitblit.com. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.gitblit.git; | |||
import javax.servlet.http.HttpServletRequest; | |||
import org.eclipse.jgit.lib.PersonIdent; | |||
import org.eclipse.jgit.lib.Repository; | |||
import org.eclipse.jgit.transport.DaemonClient; | |||
import org.eclipse.jgit.transport.ReceivePack; | |||
import org.eclipse.jgit.transport.resolver.ReceivePackFactory; | |||
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; | |||
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import com.gitblit.GitBlit; | |||
import com.gitblit.models.RepositoryModel; | |||
import com.gitblit.models.UserModel; | |||
import com.gitblit.utils.HttpUtils; | |||
/** | |||
* The receive pack factory creates a receive pack which accepts pushes from | |||
* clients. | |||
* | |||
* @author James Moger | |||
* | |||
* @param <X> the connection type | |||
*/ | |||
public class GitblitReceivePackFactory<X> implements ReceivePackFactory<X> { | |||
protected final Logger logger = LoggerFactory.getLogger(GitblitReceivePackFactory.class); | |||
@Override | |||
public ReceivePack create(X req, Repository db) | |||
throws ServiceNotEnabledException, ServiceNotAuthorizedException { | |||
final ReceivePack rp = new ReceivePack(db); | |||
UserModel user = UserModel.ANONYMOUS; | |||
String origin = ""; | |||
String gitblitUrl = ""; | |||
int timeout = 0; | |||
// XXX extract the repository name from the config | |||
// the name is injected by GitRepositoryResolver | |||
String repositoryName = db.getConfig().getString("gitblit", null, "repositoryName"); | |||
if (req instanceof HttpServletRequest) { | |||
// http/https request may or may not be authenticated | |||
HttpServletRequest request = (HttpServletRequest) req; | |||
origin = request.getRemoteHost(); | |||
gitblitUrl = HttpUtils.getGitblitURL(request); | |||
// determine pushing user | |||
String username = request.getRemoteUser(); | |||
if (username != null && !"".equals(username)) { | |||
user = GitBlit.self().getUserModel(username); | |||
if (user == null) { | |||
// anonymous push, create a temporary usermodel | |||
user = new UserModel(username); | |||
} | |||
} | |||
} else if (req instanceof DaemonClient) { | |||
// git daemon request is alway anonymous | |||
DaemonClient client = (DaemonClient) req; | |||
origin = client.getRemoteAddress().getHostAddress(); | |||
// set timeout from Git daemon | |||
timeout = client.getDaemon().getTimeout(); | |||
} | |||
// set pushing user identity for reflog | |||
rp.setRefLogIdent(new PersonIdent(user.username, user.username + "@" + origin)); | |||
rp.setTimeout(timeout); | |||
// set advanced ref permissions | |||
RepositoryModel repository = GitBlit.self().getRepositoryModel(repositoryName); | |||
rp.setAllowCreates(user.canCreateRef(repository)); | |||
rp.setAllowDeletes(user.canDeleteRef(repository)); | |||
rp.setAllowNonFastForwards(user.canRewindRef(repository)); | |||
// setup the receive hook | |||
ReceiveHook hook = new ReceiveHook(); | |||
hook.user = user; | |||
hook.repository = repository; | |||
hook.gitblitUrl = gitblitUrl; | |||
rp.setPreReceiveHook(hook); | |||
rp.setPostReceiveHook(hook); | |||
return rp; | |||
} | |||
} |
@@ -0,0 +1,99 @@ | |||
/* | |||
* Copyright 2011 gitblit.com. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.gitblit.git; | |||
import java.util.Map; | |||
import javax.servlet.http.HttpServletRequest; | |||
import org.eclipse.jgit.lib.Ref; | |||
import org.eclipse.jgit.lib.Repository; | |||
import org.eclipse.jgit.transport.DaemonClient; | |||
import org.eclipse.jgit.transport.RefFilter; | |||
import org.eclipse.jgit.transport.UploadPack; | |||
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; | |||
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; | |||
import org.eclipse.jgit.transport.resolver.UploadPackFactory; | |||
import com.gitblit.GitBlit; | |||
import com.gitblit.models.UserModel; | |||
import com.gitblit.utils.IssueUtils; | |||
import com.gitblit.utils.PushLogUtils; | |||
/** | |||
* The upload pack factory creates an upload pack which controls what refs are | |||
* advertised to cloning/pulling clients. | |||
* | |||
* @author James Moger | |||
* | |||
* @param <X> the connection type | |||
*/ | |||
public class GitblitUploadPackFactory<X> implements UploadPackFactory<X> { | |||
@Override | |||
public UploadPack create(X req, Repository db) | |||
throws ServiceNotEnabledException, ServiceNotAuthorizedException { | |||
UserModel user = UserModel.ANONYMOUS; | |||
int timeout = 0; | |||
if (req instanceof HttpServletRequest) { | |||
// http/https request may or may not be authenticated | |||
user = GitBlit.self().authenticate((HttpServletRequest) req); | |||
if (user == null) { | |||
user = UserModel.ANONYMOUS; | |||
} | |||
} else if (req instanceof DaemonClient) { | |||
// git daemon request is always anonymous | |||
DaemonClient client = (DaemonClient) req; | |||
// set timeout from Git daemon | |||
timeout = client.getDaemon().getTimeout(); | |||
} | |||
RefFilter refFilter = new UserRefFilter(user); | |||
UploadPack up = new UploadPack(db); | |||
up.setRefFilter(refFilter); | |||
up.setTimeout(timeout); | |||
return up; | |||
} | |||
/** | |||
* Restricts advertisement of certain refs based on the permission of the | |||
* requesting user. | |||
*/ | |||
public static class UserRefFilter implements RefFilter { | |||
final UserModel user; | |||
public UserRefFilter(UserModel user) { | |||
this.user = user; | |||
} | |||
@Override | |||
public Map<String, Ref> filter(Map<String, Ref> refs) { | |||
if (user.canAdmin()) { | |||
// admins can see all refs | |||
return refs; | |||
} | |||
// normal users can not clone gitblit refs | |||
refs.remove(IssueUtils.GB_ISSUES); | |||
refs.remove(PushLogUtils.GB_PUSHES); | |||
return refs; | |||
} | |||
} | |||
} |
@@ -0,0 +1,321 @@ | |||
/* | |||
* Copyright 2011 gitblit.com. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.gitblit.git; | |||
import groovy.lang.Binding; | |||
import groovy.util.GroovyScriptEngine; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.text.MessageFormat; | |||
import java.util.Collection; | |||
import java.util.LinkedHashSet; | |||
import java.util.List; | |||
import java.util.Set; | |||
import org.eclipse.jgit.lib.PersonIdent; | |||
import org.eclipse.jgit.revwalk.RevCommit; | |||
import org.eclipse.jgit.transport.PostReceiveHook; | |||
import org.eclipse.jgit.transport.PreReceiveHook; | |||
import org.eclipse.jgit.transport.ReceiveCommand; | |||
import org.eclipse.jgit.transport.ReceiveCommand.Result; | |||
import org.eclipse.jgit.transport.ReceivePack; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import com.gitblit.Constants.AccessRestrictionType; | |||
import com.gitblit.GitBlit; | |||
import com.gitblit.Keys; | |||
import com.gitblit.client.Translation; | |||
import com.gitblit.models.RepositoryModel; | |||
import com.gitblit.models.UserModel; | |||
import com.gitblit.utils.ClientLogger; | |||
import com.gitblit.utils.JGitUtils; | |||
import com.gitblit.utils.PushLogUtils; | |||
import com.gitblit.utils.StringUtils; | |||
/** | |||
* The Gitblit receive hook allows for special processing on push events. | |||
* That might include rejecting writes to specific branches or executing a | |||
* script. | |||
* | |||
* @author James Moger | |||
* | |||
*/ | |||
public class ReceiveHook implements PreReceiveHook, PostReceiveHook { | |||
protected final Logger logger = LoggerFactory.getLogger(ReceiveHook.class); | |||
protected UserModel user; | |||
protected RepositoryModel repository; | |||
protected String gitblitUrl; | |||
private GroovyScriptEngine gse; | |||
private File groovyDir; | |||
public ReceiveHook() { | |||
groovyDir = GitBlit.getGroovyScriptsFolder(); | |||
try { | |||
// set Grape root | |||
File grapeRoot = GitBlit.getFileOrFolder(Keys.groovy.grapeFolder, "${baseFolder}/groovy/grape").getAbsoluteFile(); | |||
grapeRoot.mkdirs(); | |||
System.setProperty("grape.root", grapeRoot.getAbsolutePath()); | |||
gse = new GroovyScriptEngine(groovyDir.getAbsolutePath()); | |||
} catch (IOException e) { | |||
//throw new ServletException("Failed to instantiate Groovy Script Engine!", e); | |||
} | |||
} | |||
/** | |||
* Instrumentation point where the incoming push event has been parsed, | |||
* validated, objects created BUT refs have not been updated. You might | |||
* use this to enforce a branch-write permissions model. | |||
*/ | |||
@Override | |||
public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) { | |||
if (repository.isFrozen) { | |||
// repository is frozen/readonly | |||
String reason = MessageFormat.format("Gitblit does not allow pushes to \"{0}\" because it is frozen!", repository.name); | |||
logger.warn(reason); | |||
for (ReceiveCommand cmd : commands) { | |||
cmd.setResult(Result.REJECTED_OTHER_REASON, reason); | |||
} | |||
return; | |||
} | |||
if (!repository.isBare) { | |||
// repository has a working copy | |||
String reason = MessageFormat.format("Gitblit does not allow pushes to \"{0}\" because it has a working copy!", repository.name); | |||
logger.warn(reason); | |||
for (ReceiveCommand cmd : commands) { | |||
cmd.setResult(Result.REJECTED_OTHER_REASON, reason); | |||
} | |||
return; | |||
} | |||
if (!user.canPush(repository)) { | |||
// user does not have push permissions | |||
String reason = MessageFormat.format("User \"{0}\" does not have push permissions for \"{1}\"!", user.username, repository.name); | |||
logger.warn(reason); | |||
for (ReceiveCommand cmd : commands) { | |||
cmd.setResult(Result.REJECTED_OTHER_REASON, reason); | |||
} | |||
return; | |||
} | |||
if (repository.accessRestriction.atLeast(AccessRestrictionType.PUSH) && repository.verifyCommitter) { | |||
// enforce committer verification | |||
if (StringUtils.isEmpty(user.emailAddress)) { | |||
// emit warning if user does not have an email address | |||
logger.warn(MessageFormat.format("Consider setting an email address for {0} ({1}) to improve committer verification.", user.getDisplayName(), user.username)); | |||
} | |||
// Optionally enforce that the committer of the left parent chain | |||
// match the account being used to push the commits. | |||
// | |||
// This requires all merge commits are executed with the "--no-ff" | |||
// option to force a merge commit even if fast-forward is possible. | |||
// This ensures that the chain of left parents has the commit | |||
// identity of the merging user. | |||
boolean allRejected = false; | |||
for (ReceiveCommand cmd : commands) { | |||
try { | |||
List<RevCommit> commits = JGitUtils.getRevLog(rp.getRepository(), cmd.getOldId().name(), cmd.getNewId().name()); | |||
for (RevCommit commit : commits) { | |||
PersonIdent committer = commit.getCommitterIdent(); | |||
if (!user.is(committer.getName(), committer.getEmailAddress())) { | |||
String reason; | |||
if (StringUtils.isEmpty(user.emailAddress)) { | |||
// account does not have en email address | |||
reason = MessageFormat.format("{0} by {1} <{2}> was not committed by {3} ({4})", commit.getId().name(), committer.getName(), StringUtils.isEmpty(committer.getEmailAddress()) ? "?":committer.getEmailAddress(), user.getDisplayName(), user.username); | |||
} else { | |||
// account has an email address | |||
reason = MessageFormat.format("{0} by {1} <{2}> was not committed by {3} ({4}) <{5}>", commit.getId().name(), committer.getName(), StringUtils.isEmpty(committer.getEmailAddress()) ? "?":committer.getEmailAddress(), user.getDisplayName(), user.username, user.emailAddress); | |||
} | |||
logger.warn(reason); | |||
cmd.setResult(Result.REJECTED_OTHER_REASON, reason); | |||
allRejected &= true; | |||
break; | |||
} else { | |||
allRejected = false; | |||
} | |||
} | |||
} catch (Exception e) { | |||
logger.error("Failed to verify commits were made by pushing user", e); | |||
} | |||
} | |||
if (allRejected) { | |||
// all ref updates rejected, abort | |||
return; | |||
} | |||
} | |||
Set<String> scripts = new LinkedHashSet<String>(); | |||
scripts.addAll(GitBlit.self().getPreReceiveScriptsInherited(repository)); | |||
scripts.addAll(repository.preReceiveScripts); | |||
runGroovy(repository, user, commands, rp, scripts); | |||
for (ReceiveCommand cmd : commands) { | |||
if (!Result.NOT_ATTEMPTED.equals(cmd.getResult())) { | |||
logger.warn(MessageFormat.format("{0} {1} because \"{2}\"", cmd.getNewId() | |||
.getName(), cmd.getResult(), cmd.getMessage())); | |||
} | |||
} | |||
} | |||
/** | |||
* Instrumentation point where the incoming push has been applied to the | |||
* repository. This is the point where we would trigger a Jenkins build | |||
* or send an email. | |||
*/ | |||
@Override | |||
public void onPostReceive(ReceivePack rp, Collection<ReceiveCommand> commands) { | |||
if (commands.size() == 0) { | |||
logger.debug("skipping post-receive hooks, no refs created, updated, or removed"); | |||
return; | |||
} | |||
// log ref changes | |||
for (ReceiveCommand cmd : commands) { | |||
if (Result.OK.equals(cmd.getResult())) { | |||
// add some logging for important ref changes | |||
switch (cmd.getType()) { | |||
case DELETE: | |||
logger.info(MessageFormat.format("{0} DELETED {1} in {2} ({3})", user.username, cmd.getRefName(), repository.name, cmd.getOldId().name())); | |||
break; | |||
case CREATE: | |||
logger.info(MessageFormat.format("{0} CREATED {1} in {2}", user.username, cmd.getRefName(), repository.name)); | |||
break; | |||
case UPDATE: | |||
logger.info(MessageFormat.format("{0} UPDATED {1} in {2} (from {3} to {4})", user.username, cmd.getRefName(), repository.name, cmd.getOldId().name(), cmd.getNewId().name())); | |||
break; | |||
case UPDATE_NONFASTFORWARD: | |||
logger.info(MessageFormat.format("{0} UPDATED NON-FAST-FORWARD {1} in {2} (from {3} to {4})", user.username, cmd.getRefName(), repository.name, cmd.getOldId().name(), cmd.getNewId().name())); | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
} | |||
if (repository.useIncrementalPushTags) { | |||
// tag each pushed branch tip | |||
String emailAddress = user.emailAddress == null ? rp.getRefLogIdent().getEmailAddress() : user.emailAddress; | |||
PersonIdent userIdent = new PersonIdent(user.getDisplayName(), emailAddress); | |||
for (ReceiveCommand cmd : commands) { | |||
if (!cmd.getRefName().startsWith("refs/heads/")) { | |||
// only tag branch ref changes | |||
continue; | |||
} | |||
if (!ReceiveCommand.Type.DELETE.equals(cmd.getType()) | |||
&& ReceiveCommand.Result.OK.equals(cmd.getResult())) { | |||
String objectId = cmd.getNewId().getName(); | |||
String branch = cmd.getRefName().substring("refs/heads/".length()); | |||
// get translation based on the server's locale setting | |||
String template = Translation.get("gb.incrementalPushTagMessage"); | |||
String msg = MessageFormat.format(template, branch); | |||
String prefix; | |||
if (StringUtils.isEmpty(repository.incrementalPushTagPrefix)) { | |||
prefix = GitBlit.getString(Keys.git.defaultIncrementalPushTagPrefix, "r"); | |||
} else { | |||
prefix = repository.incrementalPushTagPrefix; | |||
} | |||
JGitUtils.createIncrementalRevisionTag( | |||
rp.getRepository(), | |||
objectId, | |||
userIdent, | |||
prefix, | |||
"0", | |||
msg); | |||
} | |||
} | |||
} | |||
// update push log | |||
try { | |||
PushLogUtils.updatePushLog(user, rp.getRepository(), commands); | |||
logger.debug(MessageFormat.format("{0} push log updated", repository.name)); | |||
} catch (Exception e) { | |||
logger.error(MessageFormat.format("Failed to update {0} pushlog", repository.name), e); | |||
} | |||
// run Groovy hook scripts | |||
Set<String> scripts = new LinkedHashSet<String>(); | |||
scripts.addAll(GitBlit.self().getPostReceiveScriptsInherited(repository)); | |||
scripts.addAll(repository.postReceiveScripts); | |||
runGroovy(repository, user, commands, rp, scripts); | |||
} | |||
/** | |||
* Runs the specified Groovy hook scripts. | |||
* | |||
* @param repository | |||
* @param user | |||
* @param commands | |||
* @param scripts | |||
*/ | |||
protected void runGroovy(RepositoryModel repository, UserModel user, | |||
Collection<ReceiveCommand> commands, ReceivePack rp, Set<String> scripts) { | |||
if (scripts == null || scripts.size() == 0) { | |||
// no Groovy scripts to execute | |||
return; | |||
} | |||
Binding binding = new Binding(); | |||
binding.setVariable("gitblit", GitBlit.self()); | |||
binding.setVariable("repository", repository); | |||
binding.setVariable("receivePack", rp); | |||
binding.setVariable("user", user); | |||
binding.setVariable("commands", commands); | |||
binding.setVariable("url", gitblitUrl); | |||
binding.setVariable("logger", logger); | |||
binding.setVariable("clientLogger", new ClientLogger(rp)); | |||
for (String script : scripts) { | |||
if (StringUtils.isEmpty(script)) { | |||
continue; | |||
} | |||
// allow script to be specified without .groovy extension | |||
// this is easier to read in the settings | |||
File file = new File(groovyDir, script); | |||
if (!file.exists() && !script.toLowerCase().endsWith(".groovy")) { | |||
file = new File(groovyDir, script + ".groovy"); | |||
if (file.exists()) { | |||
script = file.getName(); | |||
} | |||
} | |||
try { | |||
Object result = gse.run(script, binding); | |||
if (result instanceof Boolean) { | |||
if (!((Boolean) result)) { | |||
logger.error(MessageFormat.format( | |||
"Groovy script {0} has failed! Hook scripts aborted.", script)); | |||
break; | |||
} | |||
} | |||
} catch (Exception e) { | |||
logger.error( | |||
MessageFormat.format("Failed to execute Groovy script {0}", script), e); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,105 @@ | |||
/* | |||
* Copyright 2013 gitblit.com. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.gitblit.git; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.text.MessageFormat; | |||
import javax.servlet.http.HttpServletRequest; | |||
import org.eclipse.jgit.errors.RepositoryNotFoundException; | |||
import org.eclipse.jgit.lib.Repository; | |||
import org.eclipse.jgit.transport.DaemonClient; | |||
import org.eclipse.jgit.transport.resolver.FileResolver; | |||
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import com.gitblit.GitBlit; | |||
import com.gitblit.models.RepositoryModel; | |||
import com.gitblit.models.UserModel; | |||
/** | |||
* Resolves repositories and grants export access. | |||
* | |||
* @author James Moger | |||
* | |||
*/ | |||
public class RepositoryResolver<X> extends FileResolver<X> { | |||
private final Logger logger = LoggerFactory.getLogger(RepositoryResolver.class); | |||
public RepositoryResolver(File repositoriesFolder) { | |||
super(repositoriesFolder, true); | |||
} | |||
/** | |||
* Open the repository and inject the repository name into the settings. | |||
*/ | |||
@Override | |||
public Repository open(final X req, final String name) | |||
throws RepositoryNotFoundException, ServiceNotEnabledException { | |||
Repository repo = super.open(req, name); | |||
// XXX Set repository name for the pack factories | |||
// We do this because the JGit API does not have a consistent way to | |||
// retrieve the repository name from the pack factories or the hooks. | |||
repo.getConfig().setString("gitblit", null, "repositoryName", name); | |||
return repo; | |||
} | |||
/** | |||
* Check if this repository can be served by the requested client connection. | |||
*/ | |||
@Override | |||
protected boolean isExportOk(X req, String repositoryName, Repository db) throws IOException { | |||
RepositoryModel model = GitBlit.self().getRepositoryModel(repositoryName); | |||
String scheme = null; | |||
UserModel user = null; | |||
String origin = null; | |||
if (req instanceof DaemonClient) { | |||
// git daemon request | |||
// this is an anonymous/unauthenticated protocol | |||
DaemonClient client = (DaemonClient) req; | |||
scheme = "git"; | |||
origin = client.getRemoteAddress().toString(); | |||
user = UserModel.ANONYMOUS; | |||
} else if (req instanceof HttpServletRequest) { | |||
// http/https request | |||
HttpServletRequest httpRequest = (HttpServletRequest) req; | |||
scheme = httpRequest.getScheme(); | |||
origin = httpRequest.getRemoteAddr(); | |||
user = GitBlit.self().authenticate(httpRequest); | |||
if (user == null) { | |||
user = UserModel.ANONYMOUS; | |||
} | |||
} | |||
if (user.canClone(model)) { | |||
// user can access this git repo | |||
logger.debug(MessageFormat.format("{0}:// access of {1} by {2} from {3} PERMITTED", | |||
scheme, repositoryName, user.username, origin)); | |||
return true; | |||
} | |||
// user can not access this git repo | |||
logger.warn(MessageFormat.format("{0}:// access of {1} by {2} from {3} DENIED", | |||
scheme, repositoryName, user.username, origin)); | |||
return false; | |||
} | |||
} |
@@ -1,5 +1,6 @@ | |||
## Standard Features (GO/WAR) | |||
- JGit SmartHTTP servlet | |||
- JGit http/https SmartHTTP servlet | |||
- JGit git protocol daemon | |||
- Browser and git client authentication | |||
- Four *per-repository* access restriction configurations with a Read-Only control flag | |||
- ![anonymous](blank.png) *Anonymous View, Clone & Push* | |||
@@ -72,7 +73,7 @@ | |||
- Built-in AJP connector for Apache httpd | |||
## Limitations | |||
- HTTP/HTTPS are the only supported Git protocols | |||
- HTTP/HTTPS/GIT are the only supported Git protocols | |||
- Built-in access controls are not path-based, they are repository-based. | |||
[jgit]: http://eclipse.org/jgit "Eclipse JGit Site" |
@@ -58,7 +58,7 @@ import com.gitblit.utils.JGitUtils; | |||
ObjectCacheTest.class, PermissionsTest.class, UserServiceTest.class, LdapUserServiceTest.class, | |||
MarkdownUtilsTest.class, JGitUtilsTest.class, SyndicationUtilsTest.class, | |||
DiffUtilsTest.class, MetricUtilsTest.class, TicgitUtilsTest.class, X509UtilsTest.class, | |||
GitBlitTest.class, FederationTests.class, RpcTests.class, GitServletTest.class, | |||
GitBlitTest.class, FederationTests.class, RpcTests.class, GitServletTest.class, GitDaemonTest.class, | |||
GroovyScriptTest.class, LuceneExecutorTest.class, IssuesTest.class, RepositoryModelTest.class, | |||
FanoutServiceTest.class }) | |||
public class GitBlitSuite { | |||
@@ -66,9 +66,12 @@ public class GitBlitSuite { | |||
public static final File REPOSITORIES = new File("data/git"); | |||
static int port = 8280; | |||
static int gitPort = 8300; | |||
static int shutdownPort = 8281; | |||
public static String url = "http://localhost:" + port; | |||
public static String gitServletUrl = "http://localhost:" + port + "/git"; | |||
public static String gitDaemonUrl = "git://localhost:" + gitPort; | |||
public static String account = "admin"; | |||
public static String password = "admin"; | |||
@@ -115,7 +118,7 @@ public class GitBlitSuite { | |||
Executors.newSingleThreadExecutor().execute(new Runnable() { | |||
public void run() { | |||
GitBlitServer.main("--httpPort", "" + port, "--httpsPort", "0", "--shutdownPort", | |||
"" + shutdownPort, "--repositoriesFolder", | |||
"" + shutdownPort, "--gitPort", "" + gitPort, "--repositoriesFolder", | |||
"\"" + GitBlitSuite.REPOSITORIES.getAbsolutePath() + "\"", "--userService", | |||
"test-users.conf", "--settings", "test-gitblit.properties", | |||
"--baseFolder", "data"); | |||
@@ -166,7 +169,11 @@ public class GitBlitSuite { | |||
private static void cloneOrFetch(String name, String fromUrl) throws Exception { | |||
System.out.print("Fetching " + name + "... "); | |||
JGitUtils.cloneRepository(REPOSITORIES, name, fromUrl); | |||
try { | |||
JGitUtils.cloneRepository(REPOSITORIES, name, fromUrl); | |||
} catch (Throwable t) { | |||
System.out.println("Error: " + t.getMessage()); | |||
} | |||
System.out.println("done."); | |||
} | |||
@@ -0,0 +1,312 @@ | |||
/* | |||
* Copyright 2013 gitblit.com. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.gitblit.tests; | |||
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.atomic.AtomicBoolean; | |||
import org.eclipse.jgit.api.CloneCommand; | |||
import org.eclipse.jgit.api.Git; | |||
import org.eclipse.jgit.lib.Constants; | |||
import org.eclipse.jgit.transport.PushResult; | |||
import org.eclipse.jgit.transport.RemoteRefUpdate; | |||
import org.eclipse.jgit.transport.RemoteRefUpdate.Status; | |||
import org.eclipse.jgit.util.FileUtils; | |||
import org.junit.AfterClass; | |||
import org.junit.Assert; | |||
import org.junit.BeforeClass; | |||
import org.junit.Test; | |||
import com.gitblit.Constants.AccessRestrictionType; | |||
import com.gitblit.Constants.AuthorizationControl; | |||
import com.gitblit.GitBlit; | |||
import com.gitblit.models.RepositoryModel; | |||
public class GitDaemonTest extends Assert { | |||
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.gitDaemonUrl; | |||
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 testAnonymousClone() throws Exception { | |||
GitBlitSuite.close(ticgitFolder); | |||
if (ticgitFolder.exists()) { | |||
FileUtils.delete(ticgitFolder, FileUtils.RECURSIVE | FileUtils.RETRY); | |||
} | |||
// set push restriction | |||
RepositoryModel model = GitBlit.self().getRepositoryModel("ticgit.git"); | |||
model.accessRestriction = AccessRestrictionType.PUSH; | |||
model.authorizationControl = AuthorizationControl.NAMED; | |||
GitBlit.self().updateRepositoryModel(model.name, model, false); | |||
CloneCommand clone = Git.cloneRepository(); | |||
clone.setURI(MessageFormat.format("{0}/ticgit.git", url)); | |||
clone.setDirectory(ticgitFolder); | |||
clone.setBare(false); | |||
clone.setCloneAllBranches(true); | |||
GitBlitSuite.close(clone.call()); | |||
assertTrue(true); | |||
// restore anonymous repository access | |||
model.accessRestriction = AccessRestrictionType.NONE; | |||
model.authorizationControl = AuthorizationControl.NAMED; | |||
GitBlit.self().updateRepositoryModel(model.name, model, false); | |||
} | |||
@Test | |||
public void testCloneRestrictedRepo() throws Exception { | |||
GitBlitSuite.close(ticgit2Folder); | |||
if (ticgit2Folder.exists()) { | |||
FileUtils.delete(ticgit2Folder, FileUtils.RECURSIVE); | |||
} | |||
// restrict repository access | |||
RepositoryModel model = GitBlit.self().getRepositoryModel("ticgit.git"); | |||
model.accessRestriction = AccessRestrictionType.CLONE; | |||
model.authorizationControl = AuthorizationControl.NAMED; | |||
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}/ticgit.git", url)); | |||
clone.setDirectory(ticgit2Folder); | |||
clone.setBare(false); | |||
clone.setCloneAllBranches(true); | |||
GitBlitSuite.close(clone.call()); | |||
cloned = true; | |||
} catch (Exception e) { | |||
// swallow the exception which we expect | |||
} | |||
assertFalse("Anonymous was able to clone the repository?!", 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); | |||
} | |||
@Test | |||
public void testAnonymousPush() throws Exception { | |||
GitBlitSuite.close(ticgitFolder); | |||
if (ticgitFolder.exists()) { | |||
FileUtils.delete(ticgitFolder, FileUtils.RECURSIVE | FileUtils.RETRY); | |||
} | |||
// restore anonymous repository access | |||
RepositoryModel model = GitBlit.self().getRepositoryModel("ticgit.git"); | |||
model.accessRestriction = AccessRestrictionType.NONE; | |||
model.authorizationControl = AuthorizationControl.NAMED; | |||
GitBlit.self().updateRepositoryModel(model.name, model, false); | |||
CloneCommand clone = Git.cloneRepository(); | |||
clone.setURI(MessageFormat.format("{0}/ticgit.git", url)); | |||
clone.setDirectory(ticgitFolder); | |||
clone.setBare(false); | |||
clone.setCloneAllBranches(true); | |||
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(); | |||
Iterable<PushResult> results = git.push().setPushAll().call(); | |||
GitBlitSuite.close(git); | |||
for (PushResult result : results) { | |||
for (RemoteRefUpdate update : result.getRemoteUpdates()) { | |||
assertEquals(Status.OK, update.getStatus()); | |||
} | |||
} | |||
} | |||
@Test | |||
public void testPushRestrictedRepo() throws Exception { | |||
GitBlitSuite.close(ticgitFolder); | |||
if (ticgitFolder.exists()) { | |||
FileUtils.delete(ticgitFolder, FileUtils.RECURSIVE | FileUtils.RETRY); | |||
} | |||
// restore anonymous repository access | |||
RepositoryModel model = GitBlit.self().getRepositoryModel("ticgit.git"); | |||
model.accessRestriction = AccessRestrictionType.PUSH; | |||
model.authorizationControl = AuthorizationControl.NAMED; | |||
GitBlit.self().updateRepositoryModel(model.name, model, false); | |||
CloneCommand clone = Git.cloneRepository(); | |||
clone.setURI(MessageFormat.format("{0}/ticgit.git", url)); | |||
clone.setDirectory(ticgitFolder); | |||
clone.setBare(false); | |||
clone.setCloneAllBranches(true); | |||
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(); | |||
Iterable<PushResult> results = git.push().setPushAll().call(); | |||
GitBlitSuite.close(git); | |||
for (PushResult result : results) { | |||
for (RemoteRefUpdate update : result.getRemoteUpdates()) { | |||
assertEquals(Status.REJECTED_OTHER_REASON, update.getStatus()); | |||
} | |||
} | |||
} | |||
@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}/test/jgit.git", url)); | |||
clone.setDirectory(jgitFolder); | |||
clone.setBare(false); | |||
clone.setCloneAllBranches(true); | |||
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(); | |||
Iterable<PushResult> results = git.push().call(); | |||
for (PushResult result : results) { | |||
for (RemoteRefUpdate update : result.getRemoteUpdates()) { | |||
assertEquals(Status.REJECTED_OTHER_REASON, update.getStatus()); | |||
} | |||
} | |||
// unfreeze repo | |||
model.isFrozen = false; | |||
GitBlit.self().updateRepositoryModel(model.name, model, false); | |||
results = git.push().setPushAll().call(); | |||
GitBlitSuite.close(git); | |||
for (PushResult result : results) { | |||
for (RemoteRefUpdate update : result.getRemoteUpdates()) { | |||
assertEquals(Status.OK, update.getStatus()); | |||
} | |||
} | |||
} | |||
@Test | |||
public void testPushToNonBareRepository() throws Exception { | |||
GitBlitSuite.close(jgit2Folder); | |||
if (jgit2Folder.exists()) { | |||
FileUtils.delete(jgit2Folder, FileUtils.RECURSIVE | FileUtils.RETRY); | |||
} | |||
CloneCommand clone = Git.cloneRepository(); | |||
clone.setURI(MessageFormat.format("{0}/working/jgit", url)); | |||
clone.setDirectory(jgit2Folder); | |||
clone.setBare(false); | |||
clone.setCloneAllBranches(true); | |||
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(); | |||
Iterable<PushResult> results = git.push().setPushAll().call(); | |||
GitBlitSuite.close(git); | |||
for (PushResult result : results) { | |||
for (RemoteRefUpdate update : result.getRemoteUpdates()) { | |||
assertEquals(Status.REJECTED_OTHER_REASON, update.getStatus()); | |||
} | |||
} | |||
} | |||
} |
@@ -54,7 +54,7 @@ public class GitServletTest { | |||
static File jgit2Folder = new File(GitBlitSuite.REPOSITORIES, "working/jgit2"); | |||
String url = GitBlitSuite.url; | |||
String url = GitBlitSuite.gitServletUrl; | |||
String account = GitBlitSuite.account; | |||
String password = GitBlitSuite.password; | |||
@@ -100,7 +100,7 @@ public class GitServletTest { | |||
} | |||
CloneCommand clone = Git.cloneRepository(); | |||
clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url)); | |||
clone.setURI(MessageFormat.format("{0}/ticgit.git", url)); | |||
clone.setDirectory(ticgitFolder); | |||
clone.setBare(false); | |||
clone.setCloneAllBranches(true); | |||
@@ -120,7 +120,7 @@ public class GitServletTest { | |||
boolean cloned = false; | |||
try { | |||
CloneCommand clone = Git.cloneRepository(); | |||
clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url)); | |||
clone.setURI(MessageFormat.format("{0}/ticgit.git", url)); | |||
clone.setDirectory(ticgit2Folder); | |||
clone.setBare(false); | |||
clone.setCloneAllBranches(true); | |||
@@ -155,7 +155,7 @@ public class GitServletTest { | |||
boolean cloned = false; | |||
try { | |||
CloneCommand clone = Git.cloneRepository(); | |||
clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url)); | |||
clone.setURI(MessageFormat.format("{0}/ticgit.git", url)); | |||
clone.setDirectory(ticgit2Folder); | |||
clone.setBare(false); | |||
clone.setCloneAllBranches(true); | |||
@@ -177,7 +177,7 @@ public class GitServletTest { | |||
// try clone again | |||
cloned = false; | |||
CloneCommand clone = Git.cloneRepository(); | |||
clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url)); | |||
clone.setURI(MessageFormat.format("{0}/ticgit.git", url)); | |||
clone.setDirectory(ticgit2Folder); | |||
clone.setBare(false); | |||
clone.setCloneAllBranches(true); | |||
@@ -203,8 +203,12 @@ public class GitServletTest { | |||
FileUtils.delete(ticgitFolder, FileUtils.RECURSIVE | FileUtils.RETRY); | |||
} | |||
RepositoryModel model = GitBlit.self().getRepositoryModel("ticgit.git"); | |||
model.accessRestriction = AccessRestrictionType.NONE; | |||
GitBlit.self().updateRepositoryModel(model.name, model, false); | |||
CloneCommand clone = Git.cloneRepository(); | |||
clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url)); | |||
clone.setURI(MessageFormat.format("{0}/ticgit.git", url)); | |||
clone.setDirectory(ticgitFolder); | |||
clone.setBare(false); | |||
clone.setCloneAllBranches(true); | |||
@@ -220,8 +224,13 @@ public class GitServletTest { | |||
w.close(); | |||
git.add().addFilepattern(file.getName()).call(); | |||
git.commit().setMessage("test commit").call(); | |||
git.push().setPushAll().call(); | |||
Iterable<PushResult> results = git.push().setPushAll().call(); | |||
GitBlitSuite.close(git); | |||
for (PushResult result : results) { | |||
for (RemoteRefUpdate update : result.getRemoteUpdates()) { | |||
assertEquals(Status.OK, update.getStatus()); | |||
} | |||
} | |||
} | |||
@Test | |||
@@ -232,7 +241,7 @@ public class GitServletTest { | |||
} | |||
CloneCommand clone = Git.cloneRepository(); | |||
clone.setURI(MessageFormat.format("{0}/git/test/jgit.git", url)); | |||
clone.setURI(MessageFormat.format("{0}/test/jgit.git", url)); | |||
clone.setDirectory(jgitFolder); | |||
clone.setBare(false); | |||
clone.setCloneAllBranches(true); | |||
@@ -248,8 +257,13 @@ public class GitServletTest { | |||
w.close(); | |||
git.add().addFilepattern(file.getName()).call(); | |||
git.commit().setMessage("test commit").call(); | |||
git.push().setPushAll().setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password)).call(); | |||
Iterable<PushResult> results = git.push().setPushAll().setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password)).call(); | |||
GitBlitSuite.close(git); | |||
for (PushResult result : results) { | |||
for (RemoteRefUpdate update : result.getRemoteUpdates()) { | |||
assertEquals(Status.OK, update.getStatus()); | |||
} | |||
} | |||
} | |||
@Test | |||
@@ -260,7 +274,7 @@ public class GitServletTest { | |||
} | |||
CloneCommand clone = Git.cloneRepository(); | |||
clone.setURI(MessageFormat.format("{0}/git/test/jgit.git", url)); | |||
clone.setURI(MessageFormat.format("{0}/test/jgit.git", url)); | |||
clone.setDirectory(jgitFolder); | |||
clone.setBare(false); | |||
clone.setCloneAllBranches(true); | |||
@@ -282,25 +296,30 @@ public class GitServletTest { | |||
git.add().addFilepattern(file.getName()).call(); | |||
git.commit().setMessage("test commit").call(); | |||
try { | |||
git.push().setPushAll().setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password)).call(); | |||
assertTrue(false); | |||
} catch (Exception e) { | |||
assertTrue(e.getCause().getMessage().contains("access forbidden")); | |||
Iterable<PushResult> results = git.push().setPushAll().setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password)).call(); | |||
for (PushResult result : results) { | |||
for (RemoteRefUpdate update : result.getRemoteUpdates()) { | |||
assertEquals(Status.REJECTED_OTHER_REASON, update.getStatus()); | |||
} | |||
} | |||
// unfreeze repo | |||
model.isFrozen = false; | |||
GitBlit.self().updateRepositoryModel(model.name, model, false); | |||
git.push().setPushAll().setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password)).call(); | |||
results = git.push().setPushAll().setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password)).call(); | |||
GitBlitSuite.close(git); | |||
for (PushResult result : results) { | |||
for (RemoteRefUpdate update : result.getRemoteUpdates()) { | |||
assertEquals(Status.OK, update.getStatus()); | |||
} | |||
} | |||
} | |||
@Test | |||
public void testPushToNonBareRepository() throws Exception { | |||
CloneCommand clone = Git.cloneRepository(); | |||
clone.setURI(MessageFormat.format("{0}/git/working/jgit", url)); | |||
clone.setURI(MessageFormat.format("{0}/working/jgit", url)); | |||
clone.setDirectory(jgit2Folder); | |||
clone.setBare(false); | |||
clone.setCloneAllBranches(true); | |||
@@ -316,13 +335,13 @@ public class GitServletTest { | |||
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().setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password)).call(); | |||
assertTrue(false); | |||
} catch (Exception e) { | |||
assertTrue(e.getCause().getMessage().contains("git-receive-pack not permitted")); | |||
} | |||
Iterable<PushResult> results = git.push().setPushAll().setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password)).call(); | |||
GitBlitSuite.close(git); | |||
for (PushResult result : results) { | |||
for (RemoteRefUpdate update : result.getRemoteUpdates()) { | |||
assertEquals(Status.REJECTED_OTHER_REASON, update.getStatus()); | |||
} | |||
} | |||
} | |||
@Test | |||
@@ -367,7 +386,7 @@ public class GitServletTest { | |||
FileUtils.delete(verification, FileUtils.RECURSIVE); | |||
} | |||
CloneCommand clone = Git.cloneRepository(); | |||
clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url)); | |||
clone.setURI(MessageFormat.format("{0}/ticgit.git", url)); | |||
clone.setDirectory(verification); | |||
clone.setBare(true); | |||
clone.setCloneAllBranches(true); | |||
@@ -392,7 +411,7 @@ public class GitServletTest { | |||
FileUtils.delete(local, FileUtils.RECURSIVE); | |||
} | |||
clone = Git.cloneRepository(); | |||
clone.setURI(MessageFormat.format("{0}/git/{1}", url, model.name)); | |||
clone.setURI(MessageFormat.format("{0}/{1}", url, model.name)); | |||
clone.setDirectory(local); | |||
clone.setBare(false); | |||
clone.setCloneAllBranches(true); | |||
@@ -478,7 +497,7 @@ public class GitServletTest { | |||
FileUtils.delete(refChecks, FileUtils.RECURSIVE); | |||
} | |||
CloneCommand clone = Git.cloneRepository(); | |||
clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url)); | |||
clone.setURI(MessageFormat.format("{0}/ticgit.git", url)); | |||
clone.setDirectory(refChecks); | |||
clone.setBare(true); | |||
clone.setCloneAllBranches(true); | |||
@@ -511,7 +530,7 @@ public class GitServletTest { | |||
FileUtils.delete(local, FileUtils.RECURSIVE); | |||
} | |||
clone = Git.cloneRepository(); | |||
clone.setURI(MessageFormat.format("{0}/git/{1}", url, model.name)); | |||
clone.setURI(MessageFormat.format("{0}/{1}", url, model.name)); | |||
clone.setDirectory(local); | |||
clone.setBare(false); | |||
clone.setCloneAllBranches(true); | |||
@@ -698,7 +717,7 @@ public class GitServletTest { | |||
} | |||
CloneCommand clone = Git.cloneRepository(); | |||
clone.setURI(MessageFormat.format("{0}/git/ticgit.git", url)); | |||
clone.setURI(MessageFormat.format("{0}/ticgit.git", url)); | |||
clone.setDirectory(createCheck); | |||
clone.setBare(true); | |||
clone.setCloneAllBranches(true); | |||
@@ -708,8 +727,8 @@ public class GitServletTest { | |||
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().setString("remote", "user", "url", MessageFormat.format("{0}/~{1}/ticgit.git", url, user.username)); | |||
git.getRepository().getConfig().setString("remote", "project", "url", MessageFormat.format("{0}/project/ticgit.git", url)); | |||
git.getRepository().getConfig().save(); | |||
// push to non-existent user repository |
@@ -13,7 +13,7 @@ public class GitBlit4UITests extends GitBlit { | |||
} | |||
@Override | |||
protected void enableLuceneIndexing() { | |||
protected void configureLuceneIndexing() { | |||
if (luceneIndexingEnabled) { | |||
getScheduledExecutor().scheduleAtFixedRate(getLuceneExecutor(), 1, | |||
2, TimeUnit.MINUTES); |