From e510b1c26b2d5f7302e7f785aa7cc0c53c4222d4 Mon Sep 17 00:00:00 2001 From: KOUNOIKE Yuusuke Date: Sun, 24 Jul 2016 03:14:57 +0900 Subject: [PATCH] Add Basic Authentication support for API access --- .../AccessTokenAuthenticationFilter.scala | 16 ++++++++---- .../servlet/BasicAuthenticationFilter.scala | 25 +++++-------------- .../scala/gitbucket/core/util/AuthUtil.scala | 21 ++++++++++++++++ 3 files changed, 38 insertions(+), 24 deletions(-) create mode 100644 src/main/scala/gitbucket/core/util/AuthUtil.scala diff --git a/src/main/scala/gitbucket/core/servlet/AccessTokenAuthenticationFilter.scala b/src/main/scala/gitbucket/core/servlet/AccessTokenAuthenticationFilter.scala index a3d019b1b..ab294ed70 100644 --- a/src/main/scala/gitbucket/core/servlet/AccessTokenAuthenticationFilter.scala +++ b/src/main/scala/gitbucket/core/servlet/AccessTokenAuthenticationFilter.scala @@ -4,14 +4,14 @@ import javax.servlet._ import javax.servlet.http.{HttpServletRequest, HttpServletResponse} import gitbucket.core.model.Account -import gitbucket.core.service.AccessTokenService -import gitbucket.core.util.Keys - +import gitbucket.core.service.SystemSettingsService.SystemSettings +import gitbucket.core.service.{AccessTokenService, AccountService, SystemSettingsService} +import gitbucket.core.util.{AuthUtil, Keys} import org.scalatra.servlet.ServletApiImplicits._ import org.scalatra._ -class AccessTokenAuthenticationFilter extends Filter with AccessTokenService { +class AccessTokenAuthenticationFilter extends Filter with AccessTokenService with AccountService with SystemSettingsService { private val tokenHeaderPrefix = "token " override def init(filterConfig: FilterConfig): Unit = {} @@ -24,7 +24,7 @@ class AccessTokenAuthenticationFilter extends Filter with AccessTokenService { val response = res.asInstanceOf[HttpServletResponse] Option(request.getHeader("Authorization")).map{ 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) }.orElse{ 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) + } } diff --git a/src/main/scala/gitbucket/core/servlet/BasicAuthenticationFilter.scala b/src/main/scala/gitbucket/core/servlet/BasicAuthenticationFilter.scala index 696f5f16a..cca1a645b 100644 --- a/src/main/scala/gitbucket/core/servlet/BasicAuthenticationFilter.scala +++ b/src/main/scala/gitbucket/core/servlet/BasicAuthenticationFilter.scala @@ -5,7 +5,7 @@ import javax.servlet.http._ import gitbucket.core.plugin.{GitRepositoryFilter, GitRepositoryRouting, PluginRegistry} import gitbucket.core.service.SystemSettingsService.SystemSettings 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 Implicits._ @@ -43,7 +43,7 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou } catch { case ex: Exception => { logger.error("error", ex) - requireAuth(response) + AuthUtil.requireAuth(response) } } } @@ -54,7 +54,7 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou val account = for { 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) } yield { 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)){ chain.doFilter(request, response) } else { - requireAuth(response) + AuthUtil.requireAuth(response) } } @@ -81,7 +81,7 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou } else { val passed = for { 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) } yield if(isUpdating || repository.repository.isPrivate){ if(hasWritePermission(repository.owner, repository.name, Some(account))){ @@ -93,7 +93,7 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou if(passed.getOrElse(false)){ chain.doFilter(request, response) } 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 => "" - } - } } \ No newline at end of file diff --git a/src/main/scala/gitbucket/core/util/AuthUtil.scala b/src/main/scala/gitbucket/core/util/AuthUtil.scala new file mode 100644 index 000000000..7a83c1387 --- /dev/null +++ b/src/main/scala/gitbucket/core/util/AuthUtil.scala @@ -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 => "" + } + } +} \ No newline at end of file