Skip to content

Commit

Permalink
Support GitHub-style (sha256 signature) git hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
sealexan committed Dec 19, 2023
1 parent 150d0cb commit 47ff69b
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 6 deletions.
12 changes: 9 additions & 3 deletions src/main/kotlin/ch/uzh/ifi/access/controller/CourseController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,16 @@ class WebhooksController(
private val logger = KotlinLogging.logger {}

@PostMapping("/courses/{course}/update/gitlab")
fun updateCourse(@PathVariable("course") course: String,
fun hookGitlab(@PathVariable("course") course: String,
@RequestHeader("X-Gitlab-Token") secret: String) {
logger.debug { "webhook triggered for $course"}
courseService.webhookUpdateCourse(course, secret)
logger.debug { "webhook (secret) triggered for $course"}
courseService.webhookUpdateWithSecret(course, secret)
}
@PostMapping("/courses/{course}/update/github")
fun hookGithub(@PathVariable("course") course: String,
@RequestHeader("X-Hub-Signature-256") signature: String) {
logger.debug { "webhook (signature) triggered for $course"}
courseService.webhookUpdateWithSignature(course, signature.substringAfter("sha256="))
}

}
Expand Down
25 changes: 22 additions & 3 deletions src/main/kotlin/ch/uzh/ifi/access/service/CourseService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import java.nio.charset.Charset
import java.nio.file.Files
import java.nio.file.NoSuchFileException
import java.nio.file.Path
import java.security.MessageDigest
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.*
Expand All @@ -43,6 +44,7 @@ import java.util.concurrent.TimeUnit
import java.util.function.Consumer
import java.util.stream.Collectors
import java.util.stream.Stream
import kotlin.math.sign

@Service
class CourseServiceForCaching(
Expand Down Expand Up @@ -554,10 +556,27 @@ fi
}

@Transactional
fun webhookUpdateCourse(courseSlug: String, secret: String): Course? {
fun webhookUpdateWithSecret(courseSlug: String, secret: String?): Course? {
val existingCourse = getCourseBySlug(courseSlug)
if (existingCourse.webhookSecret != null && existingCourse.webhookSecret == secret) {
return updateCourse(courseSlug)
if (existingCourse.webhookSecret != null && secret != null) {
if (existingCourse.webhookSecret == secret) {
return updateCourse(courseSlug)
}
}
logger.debug { "Provided webhook secret does not match secret of course $courseSlug"}
throw ResponseStatusException(HttpStatus.FORBIDDEN)
}

fun webhookUpdateWithSignature(courseSlug: String, signature: String?): Course? {
val existingCourse = getCourseBySlug(courseSlug)
if (existingCourse.webhookSecret != null && signature != null) {
val expected = MessageDigest.getInstance("SHA-256")
.digest(existingCourse.webhookSecret!!.toByteArray())
.fold("") { str, it -> str + "%02x".format(it) }
logger.info{"Expected: ${expected} based on secret ${existingCourse.webhookSecret}, actual: ${signature}"}
if (expected == signature) {
return updateCourse(courseSlug)
}
}
logger.debug { "Provided webhook secret does not match secret of course $courseSlug"}
throw ResponseStatusException(HttpStatus.FORBIDDEN)
Expand Down

0 comments on commit 47ff69b

Please sign in to comment.