diff --git a/src/main/scala/gitbucket/core/plugin/Plugin.scala b/src/main/scala/gitbucket/core/plugin/Plugin.scala index 3624d4e..b9100cf 100644 --- a/src/main/scala/gitbucket/core/plugin/Plugin.scala +++ b/src/main/scala/gitbucket/core/plugin/Plugin.scala @@ -58,6 +58,16 @@ 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 @@ (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) + } } /** diff --git a/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala b/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala index e5aac84..1aacc4b 100644 --- a/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala +++ b/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala @@ -28,6 +28,7 @@ renderers ++= Seq( "md" -> MarkdownRenderer, "markdown" -> MarkdownRenderer ) + private val repositoryRoutings = new ListBuffer[(String, String)] def addPlugin(pluginInfo: PluginInfo): Unit = { plugins += pluginInfo @@ -81,6 +82,25 @@ 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, diff --git a/src/main/scala/gitbucket/core/servlet/BasicAuthenticationFilter.scala b/src/main/scala/gitbucket/core/servlet/BasicAuthenticationFilter.scala index 42acffd..6576ced 100644 --- a/src/main/scala/gitbucket/core/servlet/BasicAuthenticationFilter.scala +++ b/src/main/scala/gitbucket/core/servlet/BasicAuthenticationFilter.scala @@ -2,6 +2,7 @@ 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 @@ 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 @@ } } + + + private def requireAuth(response: HttpServletResponse): Unit = { response.setHeader("WWW-Authenticate", "BASIC realm=\"GitBucket\"") response.sendError(HttpServletResponse.SC_UNAUTHORIZED) diff --git a/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala b/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala index 88b8124..fdb03b3 100644 --- a/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala +++ b/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala @@ -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.slf4j.LoggerFactory import javax.servlet.ServletConfig -import javax.servlet.ServletContext import javax.servlet.http.{HttpServletResponse, HttpServletRequest} @@ -35,20 +37,8 @@ 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 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 } }