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