feat: ActivityPub関係のルーティングを整備

This commit is contained in:
usbharu 2023-04-05 21:47:29 +09:00
parent 2e7cecdd13
commit f02ca9e5b0
10 changed files with 83 additions and 22 deletions

View File

@ -0,0 +1,8 @@
package dev.usbharu.hideout.exception
class HttpSignatureVerifyException : IllegalArgumentException {
constructor() : super()
constructor(s: String?) : super(s)
constructor(message: String?, cause: Throwable?) : super(message, cause)
constructor(cause: Throwable?) : super(cause)
}

View File

@ -1,4 +1,4 @@
package dev.usbharu.hideout.exception.ap package dev.usbharu.hideout.exception
class JsonParseException : IllegalArgumentException { class JsonParseException : IllegalArgumentException {
constructor() : super() constructor() : super()

View File

@ -1,15 +1,19 @@
package dev.usbharu.hideout.plugins package dev.usbharu.hideout.plugins
import dev.usbharu.hideout.routing.activitypub.inbox
import dev.usbharu.hideout.routing.activitypub.outbox
import dev.usbharu.hideout.routing.activitypub.usersAP
import dev.usbharu.hideout.service.activitypub.ActivityPubService
import dev.usbharu.hideout.service.signature.HttpSignatureVerifyService
import io.ktor.server.routing.* import io.ktor.server.routing.*
import io.ktor.server.response.*
import io.ktor.server.plugins.autohead.* import io.ktor.server.plugins.autohead.*
import io.ktor.server.application.* import io.ktor.server.application.*
fun Application.configureRouting() { fun Application.configureRouting(httpSignatureVerifyService: HttpSignatureVerifyService,activityPubService: ActivityPubService) {
install(AutoHeadResponse) install(AutoHeadResponse)
routing { routing {
get("/") { inbox(httpSignatureVerifyService,activityPubService)
call.respondText("Hello World!") outbox()
} usersAP(activityPubService)
} }
} }

View File

@ -1,24 +1,35 @@
package dev.usbharu.hideout.routing.activitypub package dev.usbharu.hideout.routing.activitypub
import dev.usbharu.hideout.exception.HttpSignatureVerifyException
import dev.usbharu.hideout.service.signature.HttpSignatureVerifyService
import io.ktor.http.*
import io.ktor.server.application.* import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.* import io.ktor.server.routing.*
fun Routing.inbox(){ fun Routing.inbox(httpSignatureVerifyService: HttpSignatureVerifyService,activityPubService: dev.usbharu.hideout.service.activitypub.ActivityPubService){
route("/inbox") { route("/inbox") {
get { get {
call.respond(HttpStatusCode.MethodNotAllowed)
} }
post { post {
if (httpSignatureVerifyService.verify(call.request.headers).not()) {
throw HttpSignatureVerifyException()
}
val json = call.receiveText()
val activityTypes = activityPubService.parseActivity(json)
activityPubService.processActivity(json,activityTypes)
call.respond(HttpStatusCode.NotImplemented)
} }
} }
route("/users/{name}/inbox"){ route("/users/{name}/inbox"){
get { get {
call.respond(HttpStatusCode.NotImplemented)
} }
post { post {
call.respond(HttpStatusCode.MethodNotAllowed)
} }
} }

View File

@ -1,24 +1,26 @@
package dev.usbharu.hideout.routing.activitypub package dev.usbharu.hideout.routing.activitypub
import io.ktor.http.*
import io.ktor.server.application.* import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.* import io.ktor.server.routing.*
fun Routing.outbox() { fun Routing.outbox() {
route("/outbox") { route("/outbox") {
get { get {
call.respond(HttpStatusCode.NotImplemented)
} }
post { post {
call.respond(HttpStatusCode.NotImplemented)
} }
} }
route("/users/{name}/outbox"){ route("/users/{name}/outbox"){
get { get {
call.respond(HttpStatusCode.NotImplemented)
} }
post { post {
call.respond(HttpStatusCode.NotImplemented)
} }
} }

View File

@ -1,14 +1,20 @@
package dev.usbharu.hideout.routing.activitypub package dev.usbharu.hideout.routing.activitypub
import dev.usbharu.hideout.service.activitypub.ActivityPubService
import dev.usbharu.hideout.util.HttpUtil.Activity import dev.usbharu.hideout.util.HttpUtil.Activity
import io.ktor.http.* import io.ktor.http.*
import io.ktor.server.application.* import io.ktor.server.application.*
import io.ktor.server.request.* import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.* import io.ktor.server.routing.*
fun Routing.users(){ fun Routing.usersAP(activityPubService: ActivityPubService){
route("/users/{name}"){ route("/users/{name}"){
createChild(ContentTypeRouteSelector(ContentType.Application.Activity)).handle { createChild(ContentTypeRouteSelector(ContentType.Application.Activity)).handle {
val json = call.receiveText()
val activityTypes = activityPubService.parseActivity(json)
activityPubService.processActivity(json,activityTypes)
call.respond(HttpStatusCode.NotImplemented)
} }
} }
} }

View File

@ -1,7 +1,7 @@
package dev.usbharu.hideout.service.activitypub package dev.usbharu.hideout.service.activitypub
interface ActivityPubService { interface ActivityPubService {
fun parseActivity(json:String): List<ActivityType> fun parseActivity(json:String): ActivityType
fun processActivity(json:String, type: ActivityType) fun processActivity(json:String, type: ActivityType)
} }

View File

@ -2,10 +2,10 @@ package dev.usbharu.hideout.service.activitypub
import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.JsonNode
import dev.usbharu.hideout.config.Config import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.exception.ap.JsonParseException import dev.usbharu.hideout.exception.JsonParseException
class ActivityPubServiceImpl : ActivityPubService { class ActivityPubServiceImpl : ActivityPubService {
override fun parseActivity(json: String): List<ActivityType> { override fun parseActivity(json: String): ActivityType {
val readTree = Config.configData.objectMapper.readTree(json) val readTree = Config.configData.objectMapper.readTree(json)
if (readTree.isObject.not()) { if (readTree.isObject.not()) {
throw JsonParseException("Json is not object.") throw JsonParseException("Json is not object.")
@ -13,10 +13,10 @@ class ActivityPubServiceImpl : ActivityPubService {
val type = readTree["type"] val type = readTree["type"]
if (type.isArray) { if (type.isArray) {
return type.mapNotNull { jsonNode: JsonNode -> return type.mapNotNull { jsonNode: JsonNode ->
ActivityType.values().filter { it.name.equals(jsonNode.toPrettyString(), true) } ActivityType.values().firstOrNull { it.name.equals(jsonNode.asText(), true) }
}.flatten() }.first()
} }
return ActivityType.values().filter { it.name.equals(type.toPrettyString(), true) } return ActivityType.values().first { it.name.equals(type.asText(), true) }
} }
override fun processActivity(json: String, type: ActivityType) { override fun processActivity(json: String, type: ActivityType) {

View File

@ -0,0 +1,7 @@
package dev.usbharu.hideout.service.signature
import io.ktor.http.*
interface HttpSignatureVerifyService {
fun verify(headers:Headers):Boolean
}

View File

@ -0,0 +1,23 @@
package dev.usbharu.hideout.service.signature
import dev.usbharu.hideout.plugins.KtorKeyMap
import dev.usbharu.hideout.service.IUserAuthService
import io.ktor.http.*
import tech.barbero.http.message.signing.HttpMessage
import tech.barbero.http.message.signing.SignatureHeaderVerifier
class HttpSignatureVerifyServiceImpl(private val userAuthService: IUserAuthService) : HttpSignatureVerifyService {
override fun verify(headers: Headers): Boolean {
val build = SignatureHeaderVerifier.builder().keyMap(KtorKeyMap(userAuthService)).build()
return build.verify(object : HttpMessage {
override fun headerValues(name: String?): MutableList<String> {
return name?.let { headers.getAll(it) }?.toMutableList() ?: mutableListOf()
}
override fun addHeader(name: String?, value: String?) {
TODO()
}
})
}
}