Add Basic Authentication support for API access

This commit is contained in:
KOUNOIKE Yuusuke
2016-07-24 03:14:57 +09:00
parent 8d35494169
commit e510b1c26b
3 changed files with 38 additions and 24 deletions

View File

@@ -4,14 +4,14 @@ import javax.servlet._
import javax.servlet.http.{HttpServletRequest, HttpServletResponse} import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
import gitbucket.core.model.Account import gitbucket.core.model.Account
import gitbucket.core.service.AccessTokenService import gitbucket.core.service.SystemSettingsService.SystemSettings
import gitbucket.core.util.Keys import gitbucket.core.service.{AccessTokenService, AccountService, SystemSettingsService}
import gitbucket.core.util.{AuthUtil, Keys}
import org.scalatra.servlet.ServletApiImplicits._ import org.scalatra.servlet.ServletApiImplicits._
import org.scalatra._ import org.scalatra._
class AccessTokenAuthenticationFilter extends Filter with AccessTokenService { class AccessTokenAuthenticationFilter extends Filter with AccessTokenService with AccountService with SystemSettingsService {
private val tokenHeaderPrefix = "token " private val tokenHeaderPrefix = "token "
override def init(filterConfig: FilterConfig): Unit = {} override def init(filterConfig: FilterConfig): Unit = {}
@@ -24,7 +24,7 @@ class AccessTokenAuthenticationFilter extends Filter with AccessTokenService {
val response = res.asInstanceOf[HttpServletResponse] val response = res.asInstanceOf[HttpServletResponse]
Option(request.getHeader("Authorization")).map{ Option(request.getHeader("Authorization")).map{
case auth if auth.startsWith("token ") => AccessTokenService.getAccountByAccessToken(auth.substring(6).trim).toRight(Unit) case auth if auth.startsWith("token ") => AccessTokenService.getAccountByAccessToken(auth.substring(6).trim).toRight(Unit)
// TODO Basic Authentication Support case auth if auth.startsWith("Basic ") => doBasicAuth(auth, loadSystemSettings(), request).toRight(Unit)
case _ => Left(Unit) case _ => Left(Unit)
}.orElse{ }.orElse{
Option(request.getSession.getAttribute(Keys.Session.LoginAccount).asInstanceOf[Account]).map(Right(_)) Option(request.getSession.getAttribute(Keys.Session.LoginAccount).asInstanceOf[Account]).map(Right(_))
@@ -40,4 +40,10 @@ class AccessTokenAuthenticationFilter extends Filter with AccessTokenService {
} }
} }
} }
def doBasicAuth(auth: String, settings: SystemSettings, request: HttpServletRequest): Option[Account] = {
implicit val session = request.getAttribute(Keys.Request.DBSession).asInstanceOf[slick.jdbc.JdbcBackend#Session]
val Array(username, password) = AuthUtil.decodeAuthHeader(auth).split(":", 2)
authenticate(settings, username, password)
}
} }

View File

@@ -5,7 +5,7 @@ import javax.servlet.http._
import gitbucket.core.plugin.{GitRepositoryFilter, GitRepositoryRouting, PluginRegistry} import gitbucket.core.plugin.{GitRepositoryFilter, GitRepositoryRouting, PluginRegistry}
import gitbucket.core.service.SystemSettingsService.SystemSettings import gitbucket.core.service.SystemSettingsService.SystemSettings
import gitbucket.core.service.{RepositoryService, AccountService, SystemSettingsService} import gitbucket.core.service.{RepositoryService, AccountService, SystemSettingsService}
import gitbucket.core.util.{Keys, Implicits} import gitbucket.core.util.{Keys, Implicits, AuthUtil}
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import Implicits._ import Implicits._
@@ -43,7 +43,7 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou
} catch { } catch {
case ex: Exception => { case ex: Exception => {
logger.error("error", ex) logger.error("error", ex)
requireAuth(response) AuthUtil.requireAuth(response)
} }
} }
} }
@@ -54,7 +54,7 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou
val account = for { val account = for {
auth <- Option(request.getHeader("Authorization")) auth <- Option(request.getHeader("Authorization"))
Array(username, password) = decodeAuthHeader(auth).split(":", 2) Array(username, password) = AuthUtil.decodeAuthHeader(auth).split(":", 2)
account <- authenticate(settings, username, password) account <- authenticate(settings, username, password)
} yield { } yield {
request.setAttribute(Keys.Request.UserName, account.userName) request.setAttribute(Keys.Request.UserName, account.userName)
@@ -64,7 +64,7 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou
if(filter.filter(request.gitRepositoryPath, account.map(_.userName), settings, isUpdating)){ if(filter.filter(request.gitRepositoryPath, account.map(_.userName), settings, isUpdating)){
chain.doFilter(request, response) chain.doFilter(request, response)
} else { } else {
requireAuth(response) AuthUtil.requireAuth(response)
} }
} }
@@ -81,7 +81,7 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou
} else { } else {
val passed = for { val passed = for {
auth <- Option(request.getHeader("Authorization")) auth <- Option(request.getHeader("Authorization"))
Array(username, password) = decodeAuthHeader(auth).split(":", 2) Array(username, password) = AuthUtil.decodeAuthHeader(auth).split(":", 2)
account <- authenticate(settings, username, password) account <- authenticate(settings, username, password)
} yield if(isUpdating || repository.repository.isPrivate){ } yield if(isUpdating || repository.repository.isPrivate){
if(hasWritePermission(repository.owner, repository.name, Some(account))){ if(hasWritePermission(repository.owner, repository.name, Some(account))){
@@ -93,7 +93,7 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou
if(passed.getOrElse(false)){ if(passed.getOrElse(false)){
chain.doFilter(request, response) chain.doFilter(request, response)
} else { } else {
requireAuth(response) AuthUtil.requireAuth(response)
} }
} }
} }
@@ -108,17 +108,4 @@ 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)
}
private def decodeAuthHeader(header: String): String = {
try {
new String(new sun.misc.BASE64Decoder().decodeBuffer(header.substring(6)))
} catch {
case _: Throwable => ""
}
}
} }

View File

@@ -0,0 +1,21 @@
package gitbucket.core.util
import javax.servlet.http.HttpServletResponse
/**
* Provides HTTP (Basic) Authentication related functions.
*/
object AuthUtil {
def requireAuth(response: HttpServletResponse): Unit = {
response.setHeader("WWW-Authenticate", "BASIC realm=\"GitBucket\"")
response.sendError(HttpServletResponse.SC_UNAUTHORIZED)
}
def decodeAuthHeader(header: String): String = {
try {
new String(new sun.misc.BASE64Decoder().decodeBuffer(header.substring(6)))
} catch {
case _: Throwable => ""
}
}
}