mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-15 09:55:49 +01:00
(refs #812)Add GitRepositoryFilter for authentication
This commit is contained in:
@@ -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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 + "(/.*)?")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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\"")
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user