(refs #812)Add new extension point to serve Git repository by plug-in

This commit is contained in:
Naoki Takezoe
2015-07-04 17:32:13 +09:00
parent 3d5e4a4225
commit 6f7579f8d9
4 changed files with 139 additions and 63 deletions

View File

@@ -57,6 +57,16 @@ trait Plugin {
*/
def renderers(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(String, Renderer)] = Nil
/**
* Override to add git repository routings.
*/
val repositoryRoutings: Seq[(String, String)] = Nil
/**
* Override to add git repository routings.
*/
def repositoryRoutings(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(String, String)] = Nil
/**
* This method is invoked in initialization of plugin system.
* Register plugin functionality to PluginRegistry.
@@ -74,6 +84,9 @@ trait Plugin {
(renderers ++ renderers(registry, context, settings)).foreach { case (extension, renderer) =>
registry.addRenderer(extension, renderer)
}
(repositoryRoutings ++ repositoryRoutings(registry, context, settings)).foreach { case (urlPath, localPath) =>
registry.addRepositoryRouting(urlPath, localPath)
}
}
/**

View File

@@ -28,6 +28,7 @@ class PluginRegistry {
renderers ++= Seq(
"md" -> MarkdownRenderer, "markdown" -> MarkdownRenderer
)
private val repositoryRoutings = new ListBuffer[(String, String)]
def addPlugin(pluginInfo: PluginInfo): Unit = {
plugins += pluginInfo
@@ -81,6 +82,25 @@ class PluginRegistry {
def renderableExtensions: Seq[String] = renderers.keys.toSeq
def addRepositoryRouting(urlPath: String, localPath: String): Unit = {
repositoryRoutings += ((urlPath, localPath))
}
def getRepositoryRoutings(): Seq[(String, String)] = {
repositoryRoutings.toSeq
}
def getRepositoryRouting(requestURI: String): Option[(String, String)] = {
val path = requestURI.replaceFirst("^/git/", "")
PluginRegistry().getRepositoryRoutings().find {
case (urlPath, localPath) => {
println(urlPath)
path.matches(urlPath + "(/.*)?")
}
}
}
private case class GlobalAction(
method: String,
path: String,

View File

@@ -2,6 +2,7 @@ package gitbucket.core.servlet
import javax.servlet._
import javax.servlet.http._
import gitbucket.core.plugin.PluginRegistry
import gitbucket.core.service.{RepositoryService, AccountService, SystemSettingsService}
import gitbucket.core.util.{ControlUtil, Keys, Implicits}
import org.slf4j.LoggerFactory
@@ -31,46 +32,70 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou
val settings = loadSystemSettings()
try {
defining(request.paths){
case Array(_, repositoryOwner, repositoryName, _*) =>
getRepository(repositoryOwner, repositoryName.replaceFirst("\\.wiki\\.git$|\\.git$", ""), "") match {
case Some(repository) => {
if(!isUpdating && !repository.repository.isPrivate && settings.allowAnonymousAccess){
chain.doFilter(req, wrappedResponse)
} else {
request.getHeader("Authorization") match {
case null => requireAuth(response)
case auth => decodeAuthHeader(auth).split(":", 2) match {
case Array(username, password) => {
authenticate(settings, username, password) match {
case Some(account) => {
if (isUpdating || repository.repository.isPrivate) {
if(hasWritePermission(repository.owner, repository.name, Some(account))){
request.setAttribute(Keys.Request.UserName, account.userName)
chain.doFilter(req, wrappedResponse)
} else {
requireAuth(response)
}
} else {
chain.doFilter(req, wrappedResponse)
}
}
case _ => requireAuth(response)
}
}
case _ => requireAuth(response)
PluginRegistry().getRepositoryRouting(request.getRequestURI).map { case (urlPath, localPath) =>
// served by plug-ins
chain.doFilter(req, wrappedResponse)
}.getOrElse {
// default repositories
defining(request.paths){
case Array(_, repositoryOwner, repositoryName, _*) =>
getRepository(repositoryOwner, repositoryName.replaceFirst("\\.wiki\\.git$|\\.git$", ""), "") match {
case Some(repository) => {
if(!isUpdating && !repository.repository.isPrivate && settings.allowAnonymousAccess){
chain.doFilter(req, wrappedResponse)
} else {
// authentication is success then true, otherwise false
val passed = for {
auth <- Option(request.getHeader("Authorization"))
Array(username, password) = decodeAuthHeader(auth).split(":", 2)
account <- authenticate(settings, username, password)
} yield if(isUpdating || repository.repository.isPrivate){
if(hasWritePermission(repository.owner, repository.name, Some(account))){
request.setAttribute(Keys.Request.UserName, account.userName)
true
} else false
} else true
if(passed.getOrElse(false)){
chain.doFilter(req, wrappedResponse)
} else {
requireAuth(response)
}
// request.getHeader("Authorization") match {
// case null => requireAuth(response)
// case auth => decodeAuthHeader(auth).split(":", 2) match {
// case Array(username, password) => {
// authenticate(settings, username, password) match {
// case Some(account) => {
// if (isUpdating || repository.repository.isPrivate) {
// if(hasWritePermission(repository.owner, repository.name, Some(account))){
// request.setAttribute(Keys.Request.UserName, account.userName)
// chain.doFilter(req, wrappedResponse)
// } else {
// requireAuth(response)
// }
// } else {
// chain.doFilter(req, wrappedResponse)
// }
// }
// case _ => requireAuth(response)
// }
// }
// case _ => requireAuth(response)
// }
// }
}
}
case None => {
logger.debug(s"Repository ${repositoryOwner}/${repositoryName} is not found.")
response.sendError(HttpServletResponse.SC_NOT_FOUND)
}
}
case None => {
logger.debug(s"Repository ${repositoryOwner}/${repositoryName} is not found.")
response.sendError(HttpServletResponse.SC_NOT_FOUND)
}
case _ => {
logger.debug(s"Not enough path arguments: ${request.paths}")
response.sendError(HttpServletResponse.SC_NOT_FOUND)
}
case _ => {
logger.debug(s"Not enough path arguments: ${request.paths}")
response.sendError(HttpServletResponse.SC_NOT_FOUND)
}
}
} catch {
@@ -81,6 +106,9 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou
}
}
private def requireAuth(response: HttpServletResponse): Unit = {
response.setHeader("WWW-Authenticate", "BASIC realm=\"GitBucket\"")
response.sendError(HttpServletResponse.SC_UNAUTHORIZED)

View File

@@ -1,7 +1,10 @@
package gitbucket.core.servlet
import java.io.File
import gitbucket.core.api
import gitbucket.core.model.Session
import gitbucket.core.plugin.PluginRegistry
import gitbucket.core.service.IssuesService.IssueSearchCondition
import gitbucket.core.service.WebHookService._
import gitbucket.core.service._
@@ -18,7 +21,6 @@ import org.eclipse.jgit.transport.resolver._
import org.slf4j.LoggerFactory
import javax.servlet.ServletConfig
import javax.servlet.ServletContext
import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
@@ -35,20 +37,8 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
override def init(config: ServletConfig): Unit = {
setReceivePackFactory(new GitBucketReceivePackFactory())
// TODO are there any other ways...?
super.init(new ServletConfig(){
def getInitParameter(name: String): String = name match {
case "base-path" => Directory.RepositoryHome
case "export-all" => "true"
case name => config.getInitParameter(name)
}
def getInitParameterNames(): java.util.Enumeration[String] = {
config.getInitParameterNames
}
def getServletContext(): ServletContext = config.getServletContext
def getServletName(): String = config.getServletName
})
val root: File = new File(Directory.RepositoryHome)
setRepositoryResolver(new GitBucketRepositoryResolver(new FileResolver[HttpServletRequest](root, true)))
super.init(config)
}
@@ -67,32 +57,57 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
}
}
class GitBucketRepositoryResolver(parent: FileResolver[HttpServletRequest]) extends RepositoryResolver[HttpServletRequest] {
private val resolver = new FileResolver[HttpServletRequest](new File(Directory.GitBucketHome), true)
override def open(req: HttpServletRequest, name: String): Repository = {
// Check routing which are provided by plug-in
val routing: Option[(String, String)] = PluginRegistry().getRepositoryRoutings().find {
case (urlPath, localPath) => name.matches(urlPath)
}
// Rewrite repository path if routing is marched
routing.map { case (urlPath, localPath) =>
val path = urlPath.r.replaceFirstIn(name, localPath)
resolver.open(req, path)
}.getOrElse {
parent.open(req, name)
}
}
}
class GitBucketReceivePackFactory extends ReceivePackFactory[HttpServletRequest] with SystemSettingsService {
private val logger = LoggerFactory.getLogger(classOf[GitBucketReceivePackFactory])
override def create(request: HttpServletRequest, db: Repository): ReceivePack = {
val receivePack = new ReceivePack(db)
val pusher = request.getAttribute(Keys.Request.UserName).asInstanceOf[String]
logger.debug("requestURI: " + request.getRequestURI)
logger.debug("pusher:" + pusher)
if(PluginRegistry().getRepositoryRouting(request.getRequestURI).isEmpty){
val pusher = request.getAttribute(Keys.Request.UserName).asInstanceOf[String]
defining(request.paths){ paths =>
val owner = paths(1)
val repository = paths(2).stripSuffix(".git")
logger.debug("requestURI: " + request.getRequestURI)
logger.debug("pusher:" + pusher)
logger.debug("repository:" + owner + "/" + repository)
defining(request.paths){ paths =>
val owner = paths(1)
val repository = paths(2).stripSuffix(".git")
if(!repository.endsWith(".wiki")){
defining(request) { implicit r =>
val hook = new CommitLogHook(owner, repository, pusher, baseUrl)
receivePack.setPreReceiveHook(hook)
receivePack.setPostReceiveHook(hook)
logger.debug("repository:" + owner + "/" + repository)
if(!repository.endsWith(".wiki")){
defining(request) { implicit r =>
val hook = new CommitLogHook(owner, repository, pusher, baseUrl)
receivePack.setPreReceiveHook(hook)
receivePack.setPostReceiveHook(hook)
}
}
}
receivePack
}
receivePack
}
}