mirror of https://github.com/usbharu/Hideout.git
feat: APRequestServiceを追加
This commit is contained in:
parent
468b318daa
commit
1f633b0d25
|
@ -7,6 +7,8 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
|||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.*
|
||||
|
||||
@Configuration
|
||||
class ActivityPubConfig {
|
||||
|
@ -20,4 +22,8 @@ class ActivityPubConfig {
|
|||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
return objectMapper
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Qualifier("http")
|
||||
fun dateTimeFormatter(): DateTimeFormatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package dev.usbharu.hideout.service.ap
|
||||
|
||||
import dev.usbharu.hideout.domain.model.ap.Object
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||
|
||||
interface APRequestService {
|
||||
suspend fun <R : Object> apGet(url: String, signer: User? = null, responseClass: Class<R>): R
|
||||
suspend fun <T : Object, R : Object> apPost(
|
||||
url: String,
|
||||
body: T? = null,
|
||||
signer: User? = null,
|
||||
responseClass: Class<R>
|
||||
): R
|
||||
}
|
||||
|
||||
suspend inline fun <reified R : Object> APRequestService.apGet(url: String, signer: User? = null): R =
|
||||
apGet(url, signer, R::class.java)
|
||||
|
||||
suspend inline fun <T : Object, reified R : Object> APRequestService.apPost(
|
||||
url: String,
|
||||
body: T? = null,
|
||||
signer: User? = null
|
||||
): R = apPost(url, body, signer, R::class.java)
|
|
@ -0,0 +1,121 @@
|
|||
package dev.usbharu.hideout.service.ap
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import dev.usbharu.hideout.domain.model.ap.Object
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||
import dev.usbharu.hideout.service.signature.HttpSignatureSigner
|
||||
import dev.usbharu.hideout.service.signature.Key
|
||||
import dev.usbharu.hideout.util.Base64Util
|
||||
import dev.usbharu.hideout.util.HttpUtil.Activity
|
||||
import dev.usbharu.hideout.util.RsaUtil
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.http.*
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.stereotype.Service
|
||||
import java.net.URL
|
||||
import java.security.MessageDigest
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
@Service
|
||||
class APRequestServiceImpl(
|
||||
private val httpClient: HttpClient,
|
||||
@Qualifier("activitypub") private val objectMapper: ObjectMapper,
|
||||
private val httpSignatureSigner: HttpSignatureSigner,
|
||||
@Qualifier("http") private val dateTimeFormatter: DateTimeFormatter,
|
||||
) : APRequestService {
|
||||
|
||||
override suspend fun <R : Object> apGet(url: String, signer: User?, responseClass: Class<R>): R {
|
||||
|
||||
val date = dateTimeFormatter.format(LocalDateTime.now(ZoneId.of("GMT")))
|
||||
val u = URL(url)
|
||||
if (signer?.privateKey == null) {
|
||||
val bodyAsText = httpClient.get(url) {
|
||||
header("Accept", ContentType.Application.Activity)
|
||||
header("Date", date)
|
||||
}.bodyAsText()
|
||||
return objectMapper.readValue(bodyAsText, responseClass)
|
||||
}
|
||||
|
||||
val headers = headers {
|
||||
append("Accept", ContentType.Application.Activity)
|
||||
append("Date", date)
|
||||
append("Host", u.host)
|
||||
}
|
||||
|
||||
val sign = httpSignatureSigner.sign(
|
||||
url, HttpMethod.Get, headers, "", Key(
|
||||
keyId = "${signer.url}#pubkey",
|
||||
privateKey = RsaUtil.decodeRsaPrivateKey(signer.privateKey),
|
||||
publicKey = RsaUtil.decodeRsaPublicKey(signer.publicKey)
|
||||
), listOf("(request-target)", "date", "host", "accept")
|
||||
)
|
||||
|
||||
val bodyAsText = httpClient.get(url) {
|
||||
headers {
|
||||
headers {
|
||||
appendAll(sign.headers)
|
||||
remove("Host")
|
||||
}
|
||||
}
|
||||
}.bodyAsText()
|
||||
return objectMapper.readValue(bodyAsText, responseClass)
|
||||
}
|
||||
|
||||
override suspend fun <T : Object, R : Object> apPost(
|
||||
url: String,
|
||||
body: T?,
|
||||
signer: User?,
|
||||
responseClass: Class<R>
|
||||
): R {
|
||||
|
||||
val requestBody = objectMapper.writeValueAsString(body)
|
||||
|
||||
val sha256 = MessageDigest.getInstance("SHA-256")
|
||||
|
||||
val digest = Base64Util.encode(sha256.digest(requestBody.toByteArray()))
|
||||
|
||||
val date = dateTimeFormatter.format(LocalDateTime.now(ZoneId.of("GMT")))
|
||||
val u = URL(url)
|
||||
if (signer?.privateKey == null) {
|
||||
val bodyAsText = httpClient.post(url) {
|
||||
header("Accept", ContentType.Application.Activity)
|
||||
header("ContentType", ContentType.Application.Activity)
|
||||
header("Date", date)
|
||||
header("Digest", digest)
|
||||
setBody(requestBody)
|
||||
}.bodyAsText()
|
||||
return objectMapper.readValue(bodyAsText, responseClass)
|
||||
}
|
||||
|
||||
val headers = headers {
|
||||
append("Accept", ContentType.Application.Activity)
|
||||
append("ContentType", ContentType.Application.Activity)
|
||||
append("Date", date)
|
||||
append("Host", u.host)
|
||||
append("Digest", digest)
|
||||
}
|
||||
|
||||
val sign = httpSignatureSigner.sign(
|
||||
url, HttpMethod.Get, headers, "", Key(
|
||||
keyId = "${signer.url}#pubkey",
|
||||
privateKey = RsaUtil.decodeRsaPrivateKey(signer.privateKey),
|
||||
publicKey = RsaUtil.decodeRsaPublicKey(signer.publicKey)
|
||||
), listOf("(request-target)", "date", "host", "digest")
|
||||
)
|
||||
|
||||
val bodyAsText = httpClient.post(url) {
|
||||
headers {
|
||||
headers {
|
||||
appendAll(sign.headers)
|
||||
remove("Host")
|
||||
}
|
||||
}
|
||||
setBody(requestBody)
|
||||
}.bodyAsText()
|
||||
return objectMapper.readValue(bodyAsText, responseClass)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue