(refs #1101)Authentication for Transfer API by one-time token

This commit is contained in:
Naoki Takezoe
2017-01-04 16:12:44 +09:00
parent 2297ef0bec
commit d460185317
3 changed files with 41 additions and 5 deletions

View File

@@ -4,7 +4,7 @@ import java.io.{File, FileInputStream, FileOutputStream}
import java.text.MessageFormat
import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse}
import gitbucket.core.util.{Directory, FileUtil}
import gitbucket.core.util.{Directory, FileUtil, StringUtil}
import org.apache.commons.io.{FileUtils, IOUtils}
import org.json4s.jackson.Serialization._
import org.apache.http.HttpStatus
@@ -22,7 +22,7 @@ class GitLfsTransferServlet extends HttpServlet {
override protected def doGet(req: HttpServletRequest, res: HttpServletResponse): Unit = {
for {
oid <- getObjectId(req, res)
oid <- getObjectId(req, res) if checkToken(req, oid)
} yield {
val file = new File(FileUtil.getLfsFilePath(oid))
if(file.exists()){
@@ -42,7 +42,7 @@ class GitLfsTransferServlet extends HttpServlet {
override protected def doPut(req: HttpServletRequest, res: HttpServletResponse): Unit = {
for {
oid <- getObjectId(req, res)
oid <- getObjectId(req, res) if checkToken(req, oid)
} yield {
val file = new File(FileUtil.getLfsFilePath(oid))
FileUtils.forceMkdir(file.getParentFile)
@@ -53,6 +53,16 @@ class GitLfsTransferServlet extends HttpServlet {
}
}
private def checkToken(req: HttpServletRequest, oid: String): Boolean = {
val token = req.getHeader("Authorization")
if(token != null){
val Array(expireAt, targetOid) = StringUtil.decodeBlowfish(token).split(" ")
oid == targetOid && expireAt.toLong > System.currentTimeMillis
} else {
false
}
}
private def getObjectId(req: HttpServletRequest, rsp: HttpServletResponse): Option[String] = {
val info: String = req.getPathInfo
val length: Int = 1 + LongObjectIdStringLength

View File

@@ -74,6 +74,7 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
throw new IllegalStateException("lfs.server_url is not configured.")
case Some(baseUrl) =>
val timeout = System.currentTimeMillis + (60000 * 10) // 10 min.
val batchResponse = batchRequest.operation match {
case "upload" =>
GitLfs.BatchUploadResponse("basic", batchRequest.objects.map { requestObject =>
@@ -81,7 +82,8 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
GitLfs.Actions(
upload = Some(GitLfs.Action(
href = baseUrl + "/git-lfs/" + requestObject.oid,
expires_at = new Date(System.currentTimeMillis + 60000L)
header = Map("Authorization" -> StringUtil.encodeBlowfish(timeout + " " + requestObject.oid)),
expires_at = new Date(timeout)
))
)
)
@@ -92,7 +94,8 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
GitLfs.Actions(
download = Some(GitLfs.Action(
href = baseUrl + "/git-lfs/" + requestObject.oid,
expires_at = new Date(System.currentTimeMillis + 60000L)
header = Map("Authorization" -> StringUtil.encodeBlowfish(timeout + " " + requestObject.oid)),
expires_at = new Date(timeout)
))
)
)

View File

@@ -1,14 +1,23 @@
package gitbucket.core.util
import java.net.{URLDecoder, URLEncoder}
import org.mozilla.universalchardet.UniversalDetector
import ControlUtil._
import org.apache.commons.io.input.BOMInputStream
import org.apache.commons.io.IOUtils
import org.apache.commons.codec.binary.{Base64, StringUtils}
import scala.util.control.Exception._
object StringUtil {
private lazy val BlowfishKey = {
// last 4 numbers in current timestamp
val time = System.currentTimeMillis.toString
time.substring(time.length - 4)
}
def sha1(value: String): String =
defining(java.security.MessageDigest.getInstance("SHA-1")){ md =>
md.update(value.getBytes)
@@ -21,6 +30,20 @@ object StringUtil {
md.digest.map(b => "%02x".format(b)).mkString
}
def encodeBlowfish(value: String): String = {
val spec = new javax.crypto.spec.SecretKeySpec(BlowfishKey.getBytes(), "Blowfish")
val cipher = javax.crypto.Cipher.getInstance("Blowfish")
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, spec)
new String(Base64.encodeBase64(cipher.doFinal(value.getBytes("UTF-8"))), "UTF-8")
}
def decodeBlowfish(value: String): String = {
val spec = new javax.crypto.spec.SecretKeySpec(BlowfishKey.getBytes(), "Blowfish")
val cipher = javax.crypto.Cipher.getInstance("Blowfish")
cipher.init(javax.crypto.Cipher.DECRYPT_MODE, spec)
new String(cipher.doFinal(Base64.decodeBase64(value)), "UTF-8")
}
def urlEncode(value: String): String = URLEncoder.encode(value, "UTF-8").replace("+", "%20")
def urlDecode(value: String): String = URLDecoder.decode(value, "UTF-8")