diff --git a/src/main/scala/gitbucket/core/servlet/GitLfsTransferServlet.scala b/src/main/scala/gitbucket/core/servlet/GitLfsTransferServlet.scala index e84a53d..426b85f 100644 --- a/src/main/scala/gitbucket/core/servlet/GitLfsTransferServlet.scala +++ b/src/main/scala/gitbucket/core/servlet/GitLfsTransferServlet.scala @@ -4,7 +4,7 @@ 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 @@ 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 @@ 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 @@ } } + 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 diff --git a/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala b/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala index 6114b30..f7d232c 100644 --- a/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala +++ b/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala @@ -74,6 +74,7 @@ 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 @@ 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 @@ 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) )) ) ) diff --git a/src/main/scala/gitbucket/core/util/StringUtil.scala b/src/main/scala/gitbucket/core/util/StringUtil.scala index 76cc389..f255b1d 100644 --- a/src/main/scala/gitbucket/core/util/StringUtil.scala +++ b/src/main/scala/gitbucket/core/util/StringUtil.scala @@ -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 @@ 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")