", + play.twirl.api.HtmlFormat.escape(new String(c)).body, + "", "
/:user/:repo.git/
+ */
+ private val githubRepositoryPattern = """^/[^/]+/[^/]+\.git/.*""".r
+
+ override def init(filterConfig: FilterConfig) = {}
+
+ override def doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain) = {
+ implicit val request = req.asInstanceOf[HttpServletRequest]
+ val response = res.asInstanceOf[HttpServletResponse]
+ val requestPath = request.getRequestURI.substring(request.getContextPath.length)
+ requestPath match {
+ case githubRepositoryPattern() =>
+ response.sendRedirect(baseUrl + "/git" + requestPath)
+
+ case _ =>
+ chain.doFilter(req, res)
+ }
+ }
+
+ override def destroy() = {}
+
+}
diff --git a/src/main/scala/gitbucket/core/servlet/TransactionFilter.scala b/src/main/scala/gitbucket/core/servlet/TransactionFilter.scala
index e3fff33..891f1f0 100644
--- a/src/main/scala/gitbucket/core/servlet/TransactionFilter.scala
+++ b/src/main/scala/gitbucket/core/servlet/TransactionFilter.scala
@@ -53,6 +53,13 @@
config.setJdbcUrl(DatabaseConfig.url)
config.setUsername(DatabaseConfig.user)
config.setPassword(DatabaseConfig.password)
+ config.setAutoCommit(false)
+ DatabaseConfig.connectionTimeout.foreach(config.setConnectionTimeout)
+ DatabaseConfig.idleTimeout.foreach(config.setIdleTimeout)
+ DatabaseConfig.maxLifetime.foreach(config.setMaxLifetime)
+ DatabaseConfig.minimumIdle.foreach(config.setMinimumIdle)
+ DatabaseConfig.maximumPoolSize.foreach(config.setMaximumPoolSize)
+
logger.debug("load database connection pool")
new HikariDataSource(config)
}
diff --git a/src/main/scala/gitbucket/core/ssh/GitCommand.scala b/src/main/scala/gitbucket/core/ssh/GitCommand.scala
index 3abfb22..c7b4277 100644
--- a/src/main/scala/gitbucket/core/ssh/GitCommand.scala
+++ b/src/main/scala/gitbucket/core/ssh/GitCommand.scala
@@ -37,7 +37,7 @@
override def run(): Unit = {
authUser match {
case Some(authUser) =>
- Database() withSession { implicit session =>
+ Database() withTransaction { implicit session =>
try {
runTask(authUser)
callback.onExit(0)
diff --git a/src/main/scala/gitbucket/core/util/Authenticator.scala b/src/main/scala/gitbucket/core/util/Authenticator.scala
index 0035494..b4cfef5 100644
--- a/src/main/scala/gitbucket/core/util/Authenticator.scala
+++ b/src/main/scala/gitbucket/core/util/Authenticator.scala
@@ -1,11 +1,14 @@
package gitbucket.core.util
import gitbucket.core.controller.ControllerBase
-import gitbucket.core.service.{RepositoryService, AccountService}
+import gitbucket.core.service.{AccountService, RepositoryService}
+import gitbucket.core.model.Permission
import RepositoryService.RepositoryInfo
import Implicits._
import ControlUtil._
+import scala.collection.Searching.search
+
/**
* Allows only oneself and administrators.
*/
@@ -40,9 +43,9 @@
context.loginAccount match {
case Some(x) if(x.isAdmin) => action(repository)
case Some(x) if(repository.owner == x.userName) => action(repository)
- case Some(x) if(getGroupMembers(repository.owner).exists { member =>
- member.userName == x.userName && member.isManager == true
- }) => action(repository)
+ // TODO Repository management is allowed for only group managers?
+ case Some(x) if(getGroupMembers(repository.owner).exists { m => m.userName == x.userName && m.isManager == true }) => action(repository)
+ case Some(x) if(getCollaboratorUserNames(paths(0), paths(1), Seq(Permission.ADMIN)).contains(x.userName)) => action(repository)
case _ => Unauthorized()
}
} getOrElse NotFound()
@@ -86,32 +89,9 @@
}
/**
- * Allows only collaborators and administrators.
+ * Allows only guests and signed in users who can access the repository.
*/
-trait CollaboratorsAuthenticator { self: ControllerBase with RepositoryService =>
- protected def collaboratorsOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
- protected def collaboratorsOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
-
- private def authenticate(action: (RepositoryInfo) => Any) = {
- {
- defining(request.paths){ paths =>
- getRepository(paths(0), paths(1)).map { repository =>
- context.loginAccount match {
- case Some(x) if(x.isAdmin) => action(repository)
- case Some(x) if(paths(0) == x.userName) => action(repository)
- case Some(x) if(getCollaborators(paths(0), paths(1)).contains(x.userName)) => action(repository)
- case _ => Unauthorized()
- }
- } getOrElse NotFound()
- }
- }
- }
-}
-
-/**
- * Allows only the repository owner (or manager for group repository) and administrators.
- */
-trait ReferrerAuthenticator { self: ControllerBase with RepositoryService =>
+trait ReferrerAuthenticator { self: ControllerBase with RepositoryService with AccountService =>
protected def referrersOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
protected def referrersOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
@@ -125,7 +105,8 @@
context.loginAccount match {
case Some(x) if(x.isAdmin) => action(repository)
case Some(x) if(paths(0) == x.userName) => action(repository)
- case Some(x) if(getCollaborators(paths(0), paths(1)).contains(x.userName)) => action(repository)
+ case Some(x) if(getGroupMembers(repository.owner).exists(_.userName == x.userName)) => action(repository)
+ case Some(x) if(getCollaboratorUserNames(paths(0), paths(1)).contains(x.userName)) => action(repository)
case _ => Unauthorized()
}
}
@@ -136,9 +117,9 @@
}
/**
- * Allows only signed in users which can access the repository.
+ * Allows only signed in users who have read permission for the repository.
*/
-trait ReadableUsersAuthenticator { self: ControllerBase with RepositoryService =>
+trait ReadableUsersAuthenticator { self: ControllerBase with RepositoryService with AccountService =>
protected def readableUsersOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
protected def readableUsersOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
@@ -150,7 +131,32 @@
case Some(x) if(x.isAdmin) => action(repository)
case Some(x) if(!repository.repository.isPrivate) => action(repository)
case Some(x) if(paths(0) == x.userName) => action(repository)
- case Some(x) if(getCollaborators(paths(0), paths(1)).contains(x.userName)) => action(repository)
+ case Some(x) if(getGroupMembers(repository.owner).exists(_.userName == x.userName)) => action(repository)
+ case Some(x) if(getCollaboratorUserNames(paths(0), paths(1)).contains(x.userName)) => action(repository)
+ case _ => Unauthorized()
+ }
+ } getOrElse NotFound()
+ }
+ }
+ }
+}
+
+/**
+ * Allows only signed in users who have write permission for the repository.
+ */
+trait WritableUsersAuthenticator { self: ControllerBase with RepositoryService with AccountService =>
+ protected def writableUsersOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
+ protected def writableUsersOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
+
+ private def authenticate(action: (RepositoryInfo) => Any) = {
+ {
+ defining(request.paths){ paths =>
+ getRepository(paths(0), paths(1)).map { repository =>
+ context.loginAccount match {
+ case Some(x) if(x.isAdmin) => action(repository)
+ case Some(x) if(paths(0) == x.userName) => action(repository)
+ case Some(x) if(getGroupMembers(repository.owner).exists(_.userName == x.userName)) => action(repository)
+ case Some(x) if(getCollaboratorUserNames(paths(0), paths(1), Seq(Permission.ADMIN, Permission.WRITE)).contains(x.userName)) => action(repository)
case _ => Unauthorized()
}
} getOrElse NotFound()
diff --git a/src/main/scala/gitbucket/core/util/DatabaseConfig.scala b/src/main/scala/gitbucket/core/util/DatabaseConfig.scala
index 9047991..681cebc 100644
--- a/src/main/scala/gitbucket/core/util/DatabaseConfig.scala
+++ b/src/main/scala/gitbucket/core/util/DatabaseConfig.scala
@@ -19,6 +19,11 @@
| url = "jdbc:h2:${DatabaseHome};MVCC=true"
| user = "sa"
| password = "sa"
+ |# connectionTimeout = 30000
+ |# idleTimeout = 600000
+ |# maxLifetime = 1800000
+ |# minimumIdle = 10
+ |# maximumPoolSize = 10
|}
|""".stripMargin, "UTF-8")
}
@@ -30,12 +35,21 @@
def url(directory: Option[String]): String =
dbUrl.replace("${DatabaseHome}", directory.getOrElse(DatabaseHome))
- lazy val url: String = url(None)
- lazy val user: String = config.getString("db.user")
- lazy val password: String = config.getString("db.password")
- lazy val jdbcDriver: String = DatabaseType(url).jdbcDriver
- lazy val slickDriver: BlockingJdbcProfile = DatabaseType(url).slickDriver
- lazy val liquiDriver: AbstractJdbcDatabase = DatabaseType(url).liquiDriver
+ lazy val url : String = url(None)
+ lazy val user : String = config.getString("db.user")
+ lazy val password : String = config.getString("db.password")
+ lazy val jdbcDriver : String = DatabaseType(url).jdbcDriver
+ lazy val slickDriver : BlockingJdbcProfile = DatabaseType(url).slickDriver
+ lazy val liquiDriver : AbstractJdbcDatabase = DatabaseType(url).liquiDriver
+ lazy val connectionTimeout : Option[Long] = getOptionValue("db.connectionTimeout", config.getLong)
+ lazy val idleTimeout : Option[Long] = getOptionValue("db.idleTimeout" , config.getLong)
+ lazy val maxLifetime : Option[Long] = getOptionValue("db.maxLifetime" , config.getLong)
+ lazy val minimumIdle : Option[Int] = getOptionValue("db.minimumIdle" , config.getInt)
+ lazy val maximumPoolSize : Option[Int] = getOptionValue("db.maximumPoolSize" , config.getInt)
+
+ private def getOptionValue[T](path: String, f: String => T): Option[T] = {
+ if(config.hasPath(path)) Some(f(path)) else None
+ }
}
diff --git a/src/main/scala/gitbucket/core/util/JGitUtil.scala b/src/main/scala/gitbucket/core/util/JGitUtil.scala
index 230fa6f..7c5f687 100644
--- a/src/main/scala/gitbucket/core/util/JGitUtil.scala
+++ b/src/main/scala/gitbucket/core/util/JGitUtil.scala
@@ -830,14 +830,16 @@
existIds.toSeq
}
- def processTree(git: Git, id: ObjectId)(f: (String, CanonicalTreeParser) => Unit) = {
+ def processTree[T](git: Git, id: ObjectId)(f: (String, CanonicalTreeParser) => T): Seq[T] = {
using(new RevWalk(git.getRepository)){ revWalk =>
using(new TreeWalk(git.getRepository)){ treeWalk =>
val index = treeWalk.addTree(revWalk.parseTree(id))
treeWalk.setRecursive(true)
+ val result = new collection.mutable.ListBuffer[T]()
while(treeWalk.next){
- f(treeWalk.getPathString, treeWalk.getTree(index, classOf[CanonicalTreeParser]))
+ result += f(treeWalk.getPathString, treeWalk.getTree(index, classOf[CanonicalTreeParser]))
}
+ result.toSeq
}
}
}
diff --git a/src/main/scala/gitbucket/core/util/Notifier.scala b/src/main/scala/gitbucket/core/util/Notifier.scala
index 3270bb0..41d5b6b 100644
--- a/src/main/scala/gitbucket/core/util/Notifier.scala
+++ b/src/main/scala/gitbucket/core/util/Notifier.scala
@@ -23,8 +23,10 @@
(
// individual repository's owner
issue.userName ::
+ // group members of group repository
+ getGroupMembers(issue.userName).map(_.userName) :::
// collaborators
- getCollaborators(issue.userName, issue.repositoryName) :::
+ getCollaboratorUserNames(issue.userName, issue.repositoryName) :::
// participants
issue.openedUserName ::
getComments(issue.userName, issue.repositoryName, issue.issueId).map(_.commentedUserName)
diff --git a/src/main/scala/gitbucket/core/util/StringUtil.scala b/src/main/scala/gitbucket/core/util/StringUtil.scala
index aef8e62..9710b50 100644
--- a/src/main/scala/gitbucket/core/util/StringUtil.scala
+++ b/src/main/scala/gitbucket/core/util/StringUtil.scala
@@ -86,8 +86,9 @@
*@param message the message which may contains issue id
* @return the iterator of issue id
*/
- def extractIssueId(message: String): Iterator[String] =
- "(^|\\W)#(\\d+)(\\W|$)".r.findAllIn(message).matchData.map(_.group(2))
+ def extractIssueId(message: String): Seq[String] =
+ "(^|\\W)#(\\d+)(\\W|$)".r
+ .findAllIn(message).matchData.map(_.group(2)).toSeq.distinct
/**
* Extract close issue id like ```close #issueId ``` from the given message.
@@ -95,8 +96,9 @@
* @param message the message which may contains close command
* @return the iterator of issue id
*/
- def extractCloseId(message: String): Iterator[String] =
- "(?i)(?
getAccountByUserName(m.group(2)).map { _ =>
- s"""${m.group(2)}/${m.group(3)}@${m.group(4).substring(0, 7)}"""
+ s"""${m.group(2)}/${m.group(3)}@${m.group(4).substring(0, 7)}
"""
}
}
@@ -56,7 +56,7 @@
// convert username@SHA to link
.replaceBy( ("(?<=(^|\\W))([a-zA-Z0-9\\-_]+)@([a-f0-9]{40})(?=(\\W|$))").r ) { m =>
getAccountByUserName(m.group(2)).map { _ =>
- s"""${m.group(2)}@${m.group(3).substring(0, 7)}"""
+ s"""${m.group(2)}@${m.group(3).substring(0, 7)}
"""
}
}
@@ -93,6 +93,8 @@
}
// convert commit id to link
- .replaceAll("(?<=(^|[^\\w/@]))([a-f0-9]{40})(?=(\\W|$))", s"""$$2""")
+ .replaceBy("(?<=(^|[^\\w/@]))([a-f0-9]{40})(?=(\\W|$))".r){ m =>
+ Some(s"""${m.group(2).substring(0, 7)}
""")
+ }
}
}
diff --git a/src/main/scala/gitbucket/core/view/Markdown.scala b/src/main/scala/gitbucket/core/view/Markdown.scala
index 49ceeed..6eb3641 100644
--- a/src/main/scala/gitbucket/core/view/Markdown.scala
+++ b/src/main/scala/gitbucket/core/view/Markdown.scala
@@ -147,21 +147,23 @@
}
private def fixUrl(url: String, isImage: Boolean = false): String = {
+ lazy val urlWithRawParam: String = url + (if(isImage && !url.endsWith("?raw=true")) "?raw=true" else "")
+
if(url.startsWith("http://") || url.startsWith("https://") || url.startsWith("/")){
url
} else if(url.startsWith("#")){
("#" + generateAnchorName(url.substring(1)))
} else if(!enableWikiLink){
if(context.currentPath.contains("/blob/")){
- url + (if(isImage) "?raw=true" else "")
+ urlWithRawParam
} else if(context.currentPath.contains("/tree/")){
val paths = context.currentPath.split("/")
val branch = if(paths.length > 3) paths.drop(4).mkString("/") else repository.repository.defaultBranch
- repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/blob/" + branch + "/" + url + (if(isImage) "?raw=true" else "")
+ repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/blob/" + branch + "/" + urlWithRawParam
} else {
val paths = context.currentPath.split("/")
val branch = if(paths.length > 3) paths.last else repository.repository.defaultBranch
- repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/blob/" + branch + "/" + url + (if(isImage) "?raw=true" else "")
+ repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/blob/" + branch + "/" + urlWithRawParam
}
} else {
repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/wiki/_blob/" + url
diff --git a/src/main/twirl/gitbucket/core/account/group.scala.html b/src/main/twirl/gitbucket/core/account/group.scala.html
index 1bda182..797c669 100644
--- a/src/main/twirl/gitbucket/core/account/group.scala.html
+++ b/src/main/twirl/gitbucket/core/account/group.scala.html
@@ -31,7 +31,7 @@