(refs #812)Add GitRepositoryFilter for authentication

This commit is contained in:
Naoki Takezoe
2015-07-05 00:13:34 +09:00
parent 6f7579f8d9
commit cd5e28c0b8
5 changed files with 105 additions and 83 deletions

View File

@@ -0,0 +1,16 @@
package gitbucket.core.plugin
import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
import gitbucket.core.service.SystemSettingsService.SystemSettings
case class GitRepositoryRouting(urlPattern: String, localPath: String, filter: GitRepositoryFilter){
def this(urlPattern: String, localPath: String) = {
this(urlPattern, localPath, new GitRepositoryFilter(){
def filter(request: HttpServletRequest, response: HttpServletResponse, settings: SystemSettings, isUpdating: Boolean): Boolean = true
})
}
}
trait GitRepositoryFilter {
def filter(request: HttpServletRequest, response: HttpServletResponse, settings: SystemSettings, isUpdating: Boolean): Boolean
}

View File

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

View File

@@ -28,7 +28,7 @@ class PluginRegistry {
renderers ++= Seq( renderers ++= Seq(
"md" -> MarkdownRenderer, "markdown" -> MarkdownRenderer "md" -> MarkdownRenderer, "markdown" -> MarkdownRenderer
) )
private val repositoryRoutings = new ListBuffer[(String, String)] private val repositoryRoutings = new ListBuffer[GitRepositoryRouting]
def addPlugin(pluginInfo: PluginInfo): Unit = { def addPlugin(pluginInfo: PluginInfo): Unit = {
plugins += pluginInfo plugins += pluginInfo
@@ -62,7 +62,7 @@ class PluginRegistry {
addController(path, controller) addController(path, controller)
} }
def getControllers(): List[(ControllerBase, String)] = controllers.toList def getControllers(): Seq[(ControllerBase, String)] = controllers.toSeq
def addJavaScript(path: String, script: String): Unit = { def addJavaScript(path: String, script: String): Unit = {
javaScripts += ((path, script)) javaScripts += ((path, script))
@@ -82,20 +82,19 @@ class PluginRegistry {
def renderableExtensions: Seq[String] = renderers.keys.toSeq def renderableExtensions: Seq[String] = renderers.keys.toSeq
def addRepositoryRouting(urlPath: String, localPath: String): Unit = { def addRepositoryRouting(routing: GitRepositoryRouting): Unit = {
repositoryRoutings += ((urlPath, localPath)) repositoryRoutings += routing
} }
def getRepositoryRoutings(): Seq[(String, String)] = { def getRepositoryRoutings(): Seq[GitRepositoryRouting] = {
repositoryRoutings.toSeq repositoryRoutings.toSeq
} }
def getRepositoryRouting(requestURI: String): Option[(String, String)] = { def getRepositoryRouting(requestURI: String): Option[GitRepositoryRouting] = {
val path = requestURI.replaceFirst("^/git/", "") val path = requestURI.replaceFirst("^/git/", "")
PluginRegistry().getRepositoryRoutings().find { PluginRegistry().getRepositoryRoutings().find {
case (urlPath, localPath) => { case GitRepositoryRouting(urlPath, _, _) => {
println(urlPath)
path.matches(urlPath + "(/.*)?") path.matches(urlPath + "(/.*)?")
} }
} }

View File

@@ -2,12 +2,12 @@ package gitbucket.core.servlet
import javax.servlet._ import javax.servlet._
import javax.servlet.http._ import javax.servlet.http._
import gitbucket.core.plugin.PluginRegistry import gitbucket.core.plugin.{GitRepositoryRouting, PluginRegistry}
import gitbucket.core.service.SystemSettingsService.SystemSettings
import gitbucket.core.service.{RepositoryService, AccountService, SystemSettingsService} import gitbucket.core.service.{RepositoryService, AccountService, SystemSettingsService}
import gitbucket.core.util.{ControlUtil, Keys, Implicits} import gitbucket.core.util.{Keys, Implicits}
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import Implicits._ import Implicits._
import ControlUtil._
/** /**
* Provides BASIC Authentication for [[GitRepositoryServlet]]. * Provides BASIC Authentication for [[GitRepositoryServlet]].
@@ -21,7 +21,7 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou
def destroy(): Unit = {} def destroy(): Unit = {}
def doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain): Unit = { def doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain): Unit = {
implicit val request = req.asInstanceOf[HttpServletRequest] val request = req.asInstanceOf[HttpServletRequest]
val response = res.asInstanceOf[HttpServletResponse] val response = res.asInstanceOf[HttpServletResponse]
val wrappedResponse = new HttpServletResponseWrapper(response){ val wrappedResponse = new HttpServletResponseWrapper(response){
@@ -32,71 +32,16 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou
val settings = loadSystemSettings() val settings = loadSystemSettings()
try { try {
PluginRegistry().getRepositoryRouting(request.getRequestURI).map { case (urlPath, localPath) => PluginRegistry().getRepositoryRouting(request.getRequestURI).map { case GitRepositoryRouting(_, _, f) =>
// served by plug-ins // served by plug-ins
chain.doFilter(req, wrappedResponse) if(f.filter(request, wrappedResponse, settings, isUpdating)){
pluginRepository(request, wrappedResponse, chain, settings, isUpdating)
} else {
requireAuth(response)
}
}.getOrElse { }.getOrElse {
// default repositories // default repositories
defining(request.paths){ defaultRepository(request, wrappedResponse, chain, settings, isUpdating)
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 _ => {
logger.debug(s"Not enough path arguments: ${request.paths}")
response.sendError(HttpServletResponse.SC_NOT_FOUND)
}
}
} }
} catch { } catch {
case ex: Exception => { case ex: Exception => {
@@ -106,8 +51,70 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou
} }
} }
private def pluginRepository(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain,
settings: SystemSettings, isUpdating: Boolean): Unit = {
implicit val r = request
if(!isUpdating && settings.allowAnonymousAccess){
chain.doFilter(request, response)
} else {
val passed = for {
auth <- Option(request.getHeader("Authorization"))
Array(username, password) = decodeAuthHeader(auth).split(":", 2)
account <- authenticate(settings, username, password)
} yield {
request.setAttribute(Keys.Request.UserName, account.userName)
true
}
if(passed.getOrElse(false)){
chain.doFilter(request, response)
} else {
requireAuth(response)
}
}
}
private def defaultRepository(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain,
settings: SystemSettings, isUpdating: Boolean): Unit = {
implicit val r = request
request.paths match {
case Array(_, repositoryOwner, repositoryName, _*) =>
getRepository(repositoryOwner, repositoryName.replaceFirst("\\.wiki\\.git$|\\.git$", ""), "") match {
case Some(repository) => {
if(!isUpdating && !repository.repository.isPrivate && settings.allowAnonymousAccess){
chain.doFilter(request, response)
} else {
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(request, response)
} else {
requireAuth(response)
}
}
}
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)
}
}
}
private def requireAuth(response: HttpServletResponse): Unit = { private def requireAuth(response: HttpServletResponse): Unit = {
response.setHeader("WWW-Authenticate", "BASIC realm=\"GitBucket\"") response.setHeader("WWW-Authenticate", "BASIC realm=\"GitBucket\"")

View File

@@ -4,7 +4,7 @@ import java.io.File
import gitbucket.core.api import gitbucket.core.api
import gitbucket.core.model.Session import gitbucket.core.model.Session
import gitbucket.core.plugin.PluginRegistry import gitbucket.core.plugin.{GitRepositoryRouting, PluginRegistry}
import gitbucket.core.service.IssuesService.IssueSearchCondition import gitbucket.core.service.IssuesService.IssueSearchCondition
import gitbucket.core.service.WebHookService._ import gitbucket.core.service.WebHookService._
import gitbucket.core.service._ import gitbucket.core.service._
@@ -63,13 +63,13 @@ class GitBucketRepositoryResolver(parent: FileResolver[HttpServletRequest]) exte
override def open(req: HttpServletRequest, name: String): Repository = { override def open(req: HttpServletRequest, name: String): Repository = {
// Check routing which are provided by plug-in // Check routing which are provided by plug-in
val routing: Option[(String, String)] = PluginRegistry().getRepositoryRoutings().find { val routing = PluginRegistry().getRepositoryRoutings().find {
case (urlPath, localPath) => name.matches(urlPath) case GitRepositoryRouting(urlPattern, _, _) => name.matches(urlPattern)
} }
// Rewrite repository path if routing is marched // Rewrite repository path if routing is marched
routing.map { case (urlPath, localPath) => routing.map { case GitRepositoryRouting(urlPattern, localPath, _) =>
val path = urlPath.r.replaceFirstIn(name, localPath) val path = urlPattern.r.replaceFirstIn(name, localPath)
resolver.open(req, path) resolver.open(req, path)
}.getOrElse { }.getOrElse {
parent.open(req, name) parent.open(req, name)