diff --git a/src/main/resources/update/3_13.sql b/src/main/resources/update/3_13.sql index 8efe1a3..aa7d006 100644 --- a/src/main/resources/update/3_13.sql +++ b/src/main/resources/update/3_13.sql @@ -1 +1,4 @@ -ALTER TABLE WEB_HOOK ADD COLUMN TOKEN VARCHAR(100); \ No newline at end of file +ALTER TABLE WEB_HOOK ADD COLUMN TOKEN VARCHAR(100); +ALTER TABLE WEB_HOOK ADD COLUMN CTYPE VARCHAR(10); + +UPDATE WEB_HOOK SET CTYPE = 'form'; \ No newline at end of file diff --git a/src/main/scala/gitbucket/core/controller/RepositorySettingsController.scala b/src/main/scala/gitbucket/core/controller/RepositorySettingsController.scala index 7867334..ac54435 100644 --- a/src/main/scala/gitbucket/core/controller/RepositorySettingsController.scala +++ b/src/main/scala/gitbucket/core/controller/RepositorySettingsController.scala @@ -15,6 +15,7 @@ import org.eclipse.jgit.api.Git import org.eclipse.jgit.lib.Constants import org.eclipse.jgit.lib.ObjectId +import gitbucket.core.model.WebHookContentType class RepositorySettingsController extends RepositorySettingsControllerBase @@ -49,13 +50,16 @@ )(CollaboratorForm.apply) // for web hook url addition - case class WebHookForm(url: String, events: Set[WebHook.Event], token: Option[String]) + case class WebHookForm(url: String, events: Set[WebHook.Event], ctype: WebHookContentType, token: Option[String]) def webHookForm(update:Boolean) = mapping( "url" -> trim(label("url", text(required, webHook(update)))), "events" -> webhookEvents, + "ctype" -> label("ctype", text()), "token" -> optional(trim(label("token", text(maxlength(100))))) - )(WebHookForm.apply) + )( + (url, events, ctype, token) => WebHookForm(url, events, WebHookContentType.valueOf(ctype), token) + ) // for transfer ownership case class TransferOwnerShipForm(newOwner: String) @@ -199,7 +203,7 @@ * Display the web hook edit page. */ get("/:owner/:repository/settings/hooks/new")(ownerOnly { repository => - val webhook = WebHook(repository.owner, repository.name, "", None) + val webhook = WebHook(repository.owner, repository.name, "", WebHookContentType.FORM, None) html.edithooks(webhook, Set(WebHook.Push), repository, flash.get("info"), true) }) @@ -207,7 +211,7 @@ * Add the web hook URL. */ post("/:owner/:repository/settings/hooks/new", webHookForm(false))(ownerOnly { (form, repository) => - addWebHook(repository.owner, repository.name, form.url, form.events, form.token) + addWebHook(repository.owner, repository.name, form.url, form.events, form.ctype, form.token) flash += "info" -> s"Webhook ${form.url} created" redirect(s"/${repository.owner}/${repository.name}/settings/hooks") }) @@ -237,7 +241,8 @@ val url = params("url") val token = Some(params("token")) - val dummyWebHookInfo = WebHook(repository.owner, repository.name, url, token) + val ctype = WebHookContentType.valueOf(params("ctype")) + val dummyWebHookInfo = WebHook(repository.owner, repository.name, url, ctype, token) val dummyPayload = { val ownerAccount = getAccountByUserName(repository.owner).get val commits = if(repository.commitCount == 0) List.empty else git.log @@ -296,7 +301,7 @@ * Update web hook settings. */ post("/:owner/:repository/settings/hooks/edit", webHookForm(true))(ownerOnly { (form, repository) => - updateWebHook(repository.owner, repository.name, form.url, form.events, form.token) + updateWebHook(repository.owner, repository.name, form.url, form.events, form.ctype, form.token) flash += "info" -> s"webhook ${form.url} updated" redirect(s"/${repository.owner}/${repository.name}/settings/hooks") }) diff --git a/src/main/scala/gitbucket/core/model/WebHook.scala b/src/main/scala/gitbucket/core/model/WebHook.scala index 9be55a8..d87f9cb 100644 --- a/src/main/scala/gitbucket/core/model/WebHook.scala +++ b/src/main/scala/gitbucket/core/model/WebHook.scala @@ -3,21 +3,42 @@ trait WebHookComponent extends TemplateComponent { self: Profile => import profile.simple._ + implicit val whContentTypeColumnType = MappedColumnType.base[WebHookContentType, String](whct => whct.code , code => WebHookContentType.valueOf(code)) + lazy val WebHooks = TableQuery[WebHooks] class WebHooks(tag: Tag) extends Table[WebHook](tag, "WEB_HOOK") with BasicTemplate { val url = column[String]("URL") val token = column[Option[String]]("TOKEN", O.Nullable) - def * = (userName, repositoryName, url, token) <> ((WebHook.apply _).tupled, WebHook.unapply) + val ctype = column[WebHookContentType]("CTYPE", O.NotNull) + def * = (userName, repositoryName, url, ctype, token) <> ((WebHook.apply _).tupled, WebHook.unapply) def byPrimaryKey(owner: String, repository: String, url: String) = byRepository(owner, repository) && (this.url === url.bind) } } +case class WebHookContentType(val code: String, val ctype: String) + +object WebHookContentType { + object JSON extends WebHookContentType("json", "application/json") + + object FORM extends WebHookContentType("form", "application/x-www-form-urlencoded") + + val values: Vector[WebHookContentType] = Vector(JSON, FORM) + + private val map: Map[String, WebHookContentType] = values.map(enum => enum.code -> enum).toMap + + def apply(code: String): WebHookContentType = map(code) + + def valueOf(code: String): WebHookContentType = map(code) + def valueOpt(code: String): Option[WebHookContentType] = map.get(code) +} + case class WebHook( userName: String, repositoryName: String, url: String, + ctype: WebHookContentType, token: Option[String] ) diff --git a/src/main/scala/gitbucket/core/service/WebHookService.scala b/src/main/scala/gitbucket/core/service/WebHookService.scala index 4af5ec3..1ece4b3 100644 --- a/src/main/scala/gitbucket/core/service/WebHookService.scala +++ b/src/main/scala/gitbucket/core/service/WebHookService.scala @@ -1,7 +1,6 @@ package gitbucket.core.service import java.io.ByteArrayInputStream - import fr.brouillard.oss.security.xhub.XHub import fr.brouillard.oss.security.xhub.XHub.{XHubDigest, XHubConverter} import gitbucket.core.api._ @@ -12,7 +11,6 @@ import gitbucket.core.util.JGitUtil.CommitInfo import gitbucket.core.util.RepositoryName import gitbucket.core.service.RepositoryService.RepositoryInfo - import org.apache.http.NameValuePair import org.apache.http.client.entity.UrlEncodedFormEntity import org.apache.http.message.BasicNameValuePair @@ -22,6 +20,8 @@ import scala.concurrent._ import org.apache.http.HttpRequest import org.apache.http.HttpResponse +import gitbucket.core.model.WebHookContentType +import org.apache.http.client.entity.EntityBuilder trait WebHookService { @@ -52,15 +52,15 @@ .map{ case (w,t) => w -> t.event } .list.groupBy(_._1).mapValues(_.map(_._2).toSet).headOption - def addWebHook(owner: String, repository: String, url :String, events: Set[WebHook.Event], token: Option[String])(implicit s: Session): Unit = { - WebHooks insert WebHook(owner, repository, url, token) + def addWebHook(owner: String, repository: String, url :String, events: Set[WebHook.Event], ctype: WebHookContentType, token: Option[String])(implicit s: Session): Unit = { + WebHooks insert WebHook(owner, repository, url, ctype, token) events.toSet.map{ event: WebHook.Event => WebHookEvents insert WebHookEvent(owner, repository, url, event) } } - def updateWebHook(owner: String, repository: String, url :String, events: Set[WebHook.Event], token: Option[String])(implicit s: Session): Unit = { - WebHooks.filter(_.byPrimaryKey(owner, repository, url)).map(w => w.token).update(token) + def updateWebHook(owner: String, repository: String, url :String, events: Set[WebHook.Event], ctype: WebHookContentType, token: Option[String])(implicit s: Session): Unit = { + WebHooks.filter(_.byPrimaryKey(owner, repository, url)).map(w => (w.ctype, w.token)).update((ctype, token)) WebHookEvents.filter(_.byWebHook(owner, repository, url)).delete events.toSet.map{ event: WebHook.Event => WebHookEvents insert WebHookEvent(owner, repository, url, event) @@ -100,19 +100,29 @@ val httpClient = HttpClientBuilder.create.addInterceptorLast(itcp).build logger.debug(s"start web hook invocation for ${webHook.url}") val httpPost = new HttpPost(webHook.url) - httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded") + logger.info(s"Content-Type: ${webHook.ctype.ctype}") + httpPost.addHeader("Content-Type", webHook.ctype.ctype) httpPost.addHeader("X-Github-Event", event.name) httpPost.addHeader("X-Github-Delivery", java.util.UUID.randomUUID().toString) - val params: java.util.List[NameValuePair] = new java.util.ArrayList() - params.add(new BasicNameValuePair("payload", json)) - def postContent = new UrlEncodedFormEntity(params, "UTF-8") - httpPost.setEntity(postContent) - - if (!webHook.token.isEmpty) { - // TODO find a better way and see how to extract content from postContent - val contentAsBytes = URLEncodedUtils.format(params, "UTF-8").getBytes("UTF-8") - httpPost.addHeader("X-Hub-Signature", XHub.generateHeaderXHubToken(XHubConverter.HEXA_LOWERCASE, XHubDigest.SHA1, webHook.token.orNull, contentAsBytes)) + webHook.ctype match { + case WebHookContentType.FORM => { + val params: java.util.List[NameValuePair] = new java.util.ArrayList() + params.add(new BasicNameValuePair("payload", json)) + def postContent = new UrlEncodedFormEntity(params, "UTF-8") + httpPost.setEntity(postContent) + if (!webHook.token.isEmpty) { + // TODO find a better way and see how to extract content from postContent + val contentAsBytes = URLEncodedUtils.format(params, "UTF-8").getBytes("UTF-8") + httpPost.addHeader("X-Hub-Signature", XHub.generateHeaderXHubToken(XHubConverter.HEXA_LOWERCASE, XHubDigest.SHA1, webHook.token.orNull, contentAsBytes)) + } + } + case WebHookContentType.JSON => { + httpPost.setEntity(EntityBuilder.create().setText(json).build()) + if (!webHook.token.isEmpty) { + httpPost.addHeader("X-Hub-Signature", XHub.generateHeaderXHubToken(XHubConverter.HEXA_LOWERCASE, XHubDigest.SHA1, webHook.token.orNull, json.getBytes("UTF-8"))) + } + } } val res = httpClient.execute(httpPost) diff --git a/src/main/twirl/gitbucket/core/settings/edithooks.scala.html b/src/main/twirl/gitbucket/core/settings/edithooks.scala.html index fc4f530..19e447b 100644 --- a/src/main/twirl/gitbucket/core/settings/edithooks.scala.html +++ b/src/main/twirl/gitbucket/core/settings/edithooks.scala.html @@ -6,6 +6,7 @@ @import context._ @import gitbucket.core.view.helpers._ @import gitbucket.core.model.WebHook._ +@import gitbucket.core.model.WebHookContentType @check(name: String, event: Event)={ name="@(name).@event.name" value="on" @if(events(event)){checked} } @@ -31,6 +32,14 @@
+