Merge pull request #1696 from gitbucket/api_repository_ssh_url

Add ssh_url to web hook request and API response
This commit is contained in:
Naoki Takezoe
2017-09-20 16:18:35 +09:00
committed by GitHub
10 changed files with 62 additions and 51 deletions

View File

@@ -1,6 +1,13 @@
package gitbucket.core.api package gitbucket.core.api
/** /**
* path for api url. if set path '/repos/aa/bb' then, expand 'http://server:post/repos/aa/bb' when converted to json. * Path for API url.
* If set path '/repos/aa/bb' then, expand 'http://server:port/repos/aa/bb' when converted to json.
*/ */
case class ApiPath(path: String) case class ApiPath(path: String)
/**
* Path for git repository via SSH.
* If set path '/aa/bb.git' then, expand 'git@server:port/aa/bb.git' when converted to json.
*/
case class SshPath(path: String)

View File

@@ -24,6 +24,7 @@ case class ApiRepository(
val http_url = ApiPath(s"/git/${full_name}.git") val http_url = ApiPath(s"/git/${full_name}.git")
val clone_url = ApiPath(s"/git/${full_name}.git") val clone_url = ApiPath(s"/git/${full_name}.git")
val html_url = ApiPath(s"/${full_name}") val html_url = ApiPath(s"/${full_name}")
val ssh_url = Some(SshPath(s":${full_name}.git"))
} }
object ApiRepository{ object ApiRepository{
@@ -55,12 +56,13 @@ object ApiRepository{
def forDummyPayload(owner: ApiUser): ApiRepository = def forDummyPayload(owner: ApiUser): ApiRepository =
ApiRepository( ApiRepository(
name="dummy", name = "dummy",
full_name=s"${owner.login}/dummy", full_name = s"${owner.login}/dummy",
description="", description = "",
watchers=0, watchers = 0,
forks=0, forks = 0,
`private`=false, `private` = false,
default_branch="master", default_branch = "master",
owner=owner)(true) owner = owner
)(true)
} }

View File

@@ -10,7 +10,7 @@ import scala.util.Try
object JsonFormat { object JsonFormat {
case class Context(baseUrl: String) case class Context(baseUrl: String, sshUrl: Option[String])
val parserISO = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'") val parserISO = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")
@@ -40,21 +40,24 @@ object JsonFormat {
FieldSerializer[ApiCommits.File]() + FieldSerializer[ApiCommits.File]() +
ApiBranchProtection.enforcementLevelSerializer ApiBranchProtection.enforcementLevelSerializer
def apiPathSerializer(c: Context) = new CustomSerializer[ApiPath](format => def apiPathSerializer(c: Context) = new CustomSerializer[ApiPath](_ => ({
(
{
case JString(s) if s.startsWith(c.baseUrl) => ApiPath(s.substring(c.baseUrl.length)) case JString(s) if s.startsWith(c.baseUrl) => ApiPath(s.substring(c.baseUrl.length))
case JString(s) => throw new MappingException("Can't convert " + s + " to ApiPath") case JString(s) => throw new MappingException("Can't convert " + s + " to ApiPath")
}, }, {
{
case ApiPath(path) => JString(c.baseUrl + path) case ApiPath(path) => JString(c.baseUrl + path)
} }))
)
) def sshPathSerializer(c: Context) = new CustomSerializer[SshPath](_ => ({
case JString(s) if c.sshUrl.exists(sshUrl => s.startsWith(sshUrl)) => SshPath(s.substring(c.sshUrl.get.length))
case JString(s) => throw new MappingException("Can't convert " + s + " to ApiPath")
}, {
case SshPath(path) => c.sshUrl.map { sshUrl => JString(sshUrl + path) } getOrElse JNothing
}))
/** /**
* convert object to json string * convert object to json string
*/ */
def apply(obj: AnyRef)(implicit c: Context): String = Serialization.write(obj)(jsonFormats + apiPathSerializer(c)) def apply(obj: AnyRef)(implicit c: Context): String =
Serialization.write(obj)(jsonFormats + apiPathSerializer(c) + sshPathSerializer(c))
} }

View File

@@ -140,17 +140,16 @@ object SystemSettingsService {
ldapAuthentication: Boolean, ldapAuthentication: Boolean,
ldap: Option[Ldap], ldap: Option[Ldap],
skinName: String){ skinName: String){
def baseUrl(request: HttpServletRequest): String = baseUrl.fold(request.baseUrl)(_.stripSuffix("/"))
def sshAddress:Option[SshAddress] = def baseUrl(request: HttpServletRequest): String = baseUrl.fold {
for { val url = request.getRequestURL.toString
host <- sshHost if ssh val len = url.length - (request.getRequestURI.length - request.getContextPath.length)
url.substring(0, len).stripSuffix("/")
} (_.stripSuffix("/"))
def sshAddress:Option[SshAddress] = sshHost.collect { case host if ssh =>
SshAddress(host, sshPort.getOrElse(DefaultSshPort), "git")
} }
yield SshAddress(
host,
sshPort.getOrElse(DefaultSshPort),
"git"
)
} }
case class Ldap( case class Ldap(

View File

@@ -156,9 +156,13 @@ class GitBucketReceivePackFactory extends ReceivePackFactory[HttpServletRequest]
logger.debug("repository:" + owner + "/" + repository) logger.debug("repository:" + owner + "/" + repository)
val settings = loadSystemSettings()
val baseUrl = settings.baseUrl(request)
val sshUrl = settings.sshAddress.map { x => s"${x.genericUser}@${x.host}:${x.port}" }
if(!repository.endsWith(".wiki")){ if(!repository.endsWith(".wiki")){
defining(request) { implicit r => defining(request) { implicit r =>
val hook = new CommitLogHook(owner, repository, pusher, baseUrl) val hook = new CommitLogHook(owner, repository, pusher, baseUrl, sshUrl)
receivePack.setPreReceiveHook(hook) receivePack.setPreReceiveHook(hook)
receivePack.setPostReceiveHook(hook) receivePack.setPostReceiveHook(hook)
} }
@@ -166,7 +170,7 @@ class GitBucketReceivePackFactory extends ReceivePackFactory[HttpServletRequest]
if(repository.endsWith(".wiki")){ if(repository.endsWith(".wiki")){
defining(request) { implicit r => defining(request) { implicit r =>
receivePack.setPostReceiveHook(new WikiCommitHook(owner, repository.stripSuffix(".wiki"), pusher, baseUrl)) receivePack.setPostReceiveHook(new WikiCommitHook(owner, repository.stripSuffix(".wiki"), pusher, baseUrl, sshUrl))
} }
} }
} }
@@ -178,7 +182,7 @@ class GitBucketReceivePackFactory extends ReceivePackFactory[HttpServletRequest]
import scala.collection.JavaConverters._ import scala.collection.JavaConverters._
class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl: String) class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl: String, sshUrl: Option[String])
extends PostReceiveHook with PreReceiveHook extends PostReceiveHook with PreReceiveHook
with RepositoryService with AccountService with IssuesService with ActivityService with PullRequestService with WebHookService with RepositoryService with AccountService with IssuesService with ActivityService with PullRequestService with WebHookService
with WebHookPullRequestService with CommitsService { with WebHookPullRequestService with CommitsService {
@@ -219,7 +223,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
val pushedIds = scala.collection.mutable.Set[String]() val pushedIds = scala.collection.mutable.Set[String]()
commands.asScala.foreach { command => commands.asScala.foreach { command =>
logger.debug(s"commandType: ${command.getType}, refName: ${command.getRefName}") logger.debug(s"commandType: ${command.getType}, refName: ${command.getRefName}")
implicit val apiContext = api.JsonFormat.Context(baseUrl) implicit val apiContext = api.JsonFormat.Context(baseUrl, sshUrl)
val refName = command.getRefName.split("/") val refName = command.getRefName.split("/")
val branchName = refName.drop(2).mkString("/") val branchName = refName.drop(2).mkString("/")
val commits = if (refName(1) == "tags") { val commits = if (refName(1) == "tags") {
@@ -320,7 +324,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
} }
class WikiCommitHook(owner: String, repository: String, pusher: String, baseUrl: String) class WikiCommitHook(owner: String, repository: String, pusher: String, baseUrl: String, sshUrl: Option[String])
extends PostReceiveHook with WebHookService with AccountService with RepositoryService { extends PostReceiveHook with WebHookService with AccountService with RepositoryService {
private val logger = LoggerFactory.getLogger(classOf[WikiCommitHook]) private val logger = LoggerFactory.getLogger(classOf[WikiCommitHook])
@@ -329,7 +333,7 @@ class WikiCommitHook(owner: String, repository: String, pusher: String, baseUrl:
Database() withTransaction { implicit session => Database() withTransaction { implicit session =>
try { try {
commands.asScala.headOption.foreach { command => commands.asScala.headOption.foreach { command =>
implicit val apiContext = api.JsonFormat.Context(baseUrl) implicit val apiContext = api.JsonFormat.Context(baseUrl, sshUrl)
val refName = command.getRefName.split("/") val refName = command.getRefName.split("/")
val commitIds = if (refName(1) == "tags") { val commitIds = if (refName(1) == "tags") {
None None

View File

@@ -154,7 +154,7 @@ class DefaultGitUploadPack(owner: String, repoName: String) extends DefaultGitCo
} }
} }
class DefaultGitReceivePack(owner: String, repoName: String, baseUrl: String) extends DefaultGitCommand(owner, repoName) class DefaultGitReceivePack(owner: String, repoName: String, baseUrl: String, sshUrl: Option[String]) extends DefaultGitCommand(owner, repoName)
with RepositoryService with AccountService with DeployKeyService { with RepositoryService with AccountService with DeployKeyService {
override protected def runTask(authType: AuthType): Unit = { override protected def runTask(authType: AuthType): Unit = {
@@ -169,7 +169,7 @@ class DefaultGitReceivePack(owner: String, repoName: String, baseUrl: String) ex
val repository = git.getRepository val repository = git.getRepository
val receive = new ReceivePack(repository) val receive = new ReceivePack(repository)
if (!repoName.endsWith(".wiki")) { if (!repoName.endsWith(".wiki")) {
val hook = new CommitLogHook(owner, repoName, userName(authType), baseUrl) val hook = new CommitLogHook(owner, repoName, userName(authType), baseUrl, sshUrl)
receive.setPreReceiveHook(hook) receive.setPreReceiveHook(hook)
receive.setPostReceiveHook(hook) receive.setPostReceiveHook(hook)
} }
@@ -216,7 +216,7 @@ class PluginGitReceivePack(repoName: String, routing: GitRepositoryRouting) exte
} }
class GitCommandFactory(baseUrl: String) extends CommandFactory { class GitCommandFactory(baseUrl: String, sshUrl: Option[String]) extends CommandFactory {
private val logger = LoggerFactory.getLogger(classOf[GitCommandFactory]) private val logger = LoggerFactory.getLogger(classOf[GitCommandFactory])
override def createCommand(command: String): Command = { override def createCommand(command: String): Command = {
@@ -227,7 +227,7 @@ class GitCommandFactory(baseUrl: String) extends CommandFactory {
case SimpleCommandRegex ("upload" , repoName) if(pluginRepository(repoName)) => new PluginGitUploadPack (repoName, routing(repoName)) case SimpleCommandRegex ("upload" , repoName) if(pluginRepository(repoName)) => new PluginGitUploadPack (repoName, routing(repoName))
case SimpleCommandRegex ("receive", repoName) if(pluginRepository(repoName)) => new PluginGitReceivePack(repoName, routing(repoName)) case SimpleCommandRegex ("receive", repoName) if(pluginRepository(repoName)) => new PluginGitReceivePack(repoName, routing(repoName))
case DefaultCommandRegex("upload" , owner, repoName) => new DefaultGitUploadPack (owner, repoName) case DefaultCommandRegex("upload" , owner, repoName) => new DefaultGitUploadPack (owner, repoName)
case DefaultCommandRegex("receive", owner, repoName) => new DefaultGitReceivePack(owner, repoName, baseUrl) case DefaultCommandRegex("receive", owner, repoName) => new DefaultGitReceivePack(owner, repoName, baseUrl, sshUrl)
case _ => new UnknownCommand(command) case _ => new UnknownCommand(command)
} }
} }

View File

@@ -22,7 +22,7 @@ object SshServer {
provider.setOverwriteAllowed(false) provider.setOverwriteAllowed(false)
server.setKeyPairProvider(provider) server.setKeyPairProvider(provider)
server.setPublickeyAuthenticator(new PublicKeyAuthenticator(sshAddress.genericUser)) server.setPublickeyAuthenticator(new PublicKeyAuthenticator(sshAddress.genericUser))
server.setCommandFactory(new GitCommandFactory(baseUrl)) server.setCommandFactory(new GitCommandFactory(baseUrl, Some(s"${sshAddress.genericUser}@${sshAddress.host}:${sshAddress.port}")))
server.setShellFactory(new NoShell(sshAddress)) server.setShellFactory(new NoShell(sshAddress))
} }

View File

@@ -22,7 +22,8 @@ object Implicits {
// Convert to slick session. // Convert to slick session.
implicit def request2Session(implicit request: HttpServletRequest): JdbcBackend#Session = Database.getSession(request) implicit def request2Session(implicit request: HttpServletRequest): JdbcBackend#Session = Database.getSession(request)
implicit def context2ApiJsonFormatContext(implicit context: Context): JsonFormat.Context = JsonFormat.Context(context.baseUrl) implicit def context2ApiJsonFormatContext(implicit context: Context): JsonFormat.Context =
JsonFormat.Context(context.baseUrl, context.settings.sshAddress.map { x => s"${x.genericUser}@${x.host}:${x.port}" })
implicit class RichSeq[A](private val seq: Seq[A]) extends AnyVal { implicit class RichSeq[A](private val seq: Seq[A]) extends AnyVal {
@@ -77,11 +78,6 @@ object Implicits {
def gitRepositoryPath: String = request.getRequestURI.replaceFirst("^" + quote(request.getContextPath) + "/git/", "/") def gitRepositoryPath: String = request.getRequestURI.replaceFirst("^" + quote(request.getContextPath) + "/git/", "/")
def baseUrl:String = {
val url = request.getRequestURL.toString
val len = url.length - (request.getRequestURI.length - request.getContextPath.length)
url.substring(0, len).stripSuffix("/")
}
} }
implicit class RichSession(private val session: HttpSession) extends AnyVal { implicit class RichSession(private val session: HttpSession) extends AnyVal {

View File

@@ -22,7 +22,7 @@ class JsonFormatSpec extends FunSuite {
} }
val sha1 = "6dcb09b5b57875f334f61aebed695e2e4193db5e" val sha1 = "6dcb09b5b57875f334f61aebed695e2e4193db5e"
val repo1Name = RepositoryName("octocat/Hello-World") val repo1Name = RepositoryName("octocat/Hello-World")
implicit val context = JsonFormat.Context("http://gitbucket.exmple.com") implicit val context = JsonFormat.Context("http://gitbucket.exmple.com", None)
val apiUser = ApiUser( val apiUser = ApiUser(
login = "octocat", login = "octocat",

View File

@@ -5,7 +5,7 @@ import org.scalatest.FunSpec
class GitCommandFactorySpec extends FunSpec { class GitCommandFactorySpec extends FunSpec {
val factory = new GitCommandFactory("http://localhost:8080") val factory = new GitCommandFactory("http://localhost:8080", None)
describe("createCommand") { describe("createCommand") {
it("should return GitReceivePack when command is git-receive-pack"){ it("should return GitReceivePack when command is git-receive-pack"){