fix: 一部のAP Objectがデシリアライズできなくなっていた問題を修正

This commit is contained in:
usbharu 2023-12-03 10:26:48 +09:00
parent faff15f559
commit bfc5c9e110
8 changed files with 139 additions and 9 deletions

View File

@ -3,12 +3,11 @@ package dev.usbharu.hideout.activitypub.domain.model
import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.domain.model.objects.Object
open class Key( open class Key(
type: List<String>,
override val id: String, override val id: String,
val owner: String, val owner: String,
val publicKeyPem: String val publicKeyPem: String
) : Object( ) : Object(
type = add(list = type, type = "Key") type = add(list = emptyList(), type = "Key")
), ),
HasId { HasId {

View File

@ -14,7 +14,7 @@ constructor(
var outbox: String, var outbox: String,
var url: String, var url: String,
private var icon: Image?, private var icon: Image?,
var publicKey: Key?, var publicKey: Key,
var endpoints: Map<String, String> = emptyMap(), var endpoints: Map<String, String> = emptyMap(),
var followers: String?, var followers: String?,
var following: String? var following: String?

View File

@ -61,7 +61,6 @@ class APUserServiceImpl(
url = "$userUrl/icon.png" url = "$userUrl/icon.png"
), ),
publicKey = Key( publicKey = Key(
type = emptyList(),
id = userEntity.keyId, id = userEntity.keyId,
owner = userUrl, owner = userUrl,
publicKeyPem = userEntity.publicKey publicKeyPem = userEntity.publicKey
@ -129,7 +128,6 @@ class APUserServiceImpl(
url = "$id/icon.png" url = "$id/icon.png"
), ),
publicKey = Key( publicKey = Key(
type = emptyList(),
id = userEntity.keyId, id = userEntity.keyId,
owner = id, owner = id,
publicKeyPem = userEntity.publicKey publicKeyPem = userEntity.publicKey

View File

@ -7,6 +7,8 @@ import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import dev.usbharu.hideout.core.infrastructure.httpsignature.HttpRequestMixIn
import dev.usbharu.httpsignature.common.HttpRequest
import dev.usbharu.httpsignature.sign.HttpSignatureSigner import dev.usbharu.httpsignature.sign.HttpSignatureSigner
import dev.usbharu.httpsignature.sign.RsaSha256HttpSignatureSigner import dev.usbharu.httpsignature.sign.RsaSha256HttpSignatureSigner
import org.springframework.beans.factory.annotation.Qualifier import org.springframework.beans.factory.annotation.Qualifier
@ -24,12 +26,13 @@ class ActivityPubConfig {
val objectMapper = jacksonObjectMapper() val objectMapper = jacksonObjectMapper()
.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
.setSerializationInclusion(JsonInclude.Include.NON_EMPTY) .setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
.setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.AS_EMPTY)) .setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP))
.setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY)) .setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP))
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(JsonParser.Feature.ALLOW_COMMENTS, true) .configure(JsonParser.Feature.ALLOW_COMMENTS, true)
.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true)
.configure(JsonParser.Feature.ALLOW_TRAILING_COMMA, true) .configure(JsonParser.Feature.ALLOW_TRAILING_COMMA, true)
.addMixIn(HttpRequest::class.java, HttpRequestMixIn::class.java)
return objectMapper return objectMapper
} }

View File

@ -0,0 +1,33 @@
package dev.usbharu.hideout.core.infrastructure.httpsignature
import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import dev.usbharu.httpsignature.common.HttpHeaders
import dev.usbharu.httpsignature.common.HttpMethod
import dev.usbharu.httpsignature.common.HttpRequest
import java.net.URL
@JsonDeserialize(using = HttpRequestDeserializer::class)
@JsonSubTypes
abstract class HttpRequestMixIn
class HttpRequestDeserializer : JsonDeserializer<HttpRequest>() {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): HttpRequest {
val readTree: JsonNode = p.codec.readTree(p)
return HttpRequest(
URL(readTree["url"].textValue()),
HttpHeaders(emptyMap()),
HttpMethod.valueOf(readTree["method"].textValue())
)
}
}

View File

@ -0,0 +1,24 @@
package dev.usbharu.hideout.activitypub.domain.model
import com.fasterxml.jackson.module.kotlin.readValue
import dev.usbharu.hideout.application.config.ActivityPubConfig
import org.junit.jupiter.api.Test
class KeySerializeTest {
@Test
fun Keyのデシリアライズができる() {
//language=JSON
val trimIndent = """
{
"id": "https://mastodon.social/users/Gargron#main-key",
"owner": "https://mastodon.social/users/Gargron",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvXc4vkECU2/CeuSo1wtn\nFoim94Ne1jBMYxTZ9wm2YTdJq1oiZKif06I2fOqDzY/4q/S9uccrE9Bkajv1dnkO\nVm31QjWlhVpSKynVxEWjVBO5Ienue8gND0xvHIuXf87o61poqjEoepvsQFElA5ym\novljWGSA/jpj7ozygUZhCXtaS2W5AD5tnBQUpcO0lhItYPYTjnmzcc4y2NbJV8hz\n2s2G8qKv8fyimE23gY1XrPJg+cRF+g4PqFXujjlJ7MihD9oqtLGxbu7o1cifTn3x\nBfIdPythWu5b4cujNsB3m3awJjVmx+MHQ9SugkSIYXV0Ina77cTNS0M2PYiH1PFR\nTwIDAQAB\n-----END PUBLIC KEY-----\n"
}
""".trimIndent()
val objectMapper = ActivityPubConfig().objectMapper()
val readValue = objectMapper.readValue<Key>(trimIndent)
}
}

View File

@ -0,0 +1,75 @@
package dev.usbharu.hideout.activitypub.domain.model
import com.fasterxml.jackson.module.kotlin.readValue
import dev.usbharu.hideout.application.config.ActivityPubConfig
import org.junit.jupiter.api.Test
class PersonSerializeTest {
@Test
fun MastodonのPersonのデシリアライズができる() {
val personString = """
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1"
],
"id": "https://mastodon.social/users/Gargron",
"type": "Person",
"following": "https://mastodon.social/users/Gargron/following",
"followers": "https://mastodon.social/users/Gargron/followers",
"inbox": "https://mastodon.social/users/Gargron/inbox",
"outbox": "https://mastodon.social/users/Gargron/outbox",
"featured": "https://mastodon.social/users/Gargron/collections/featured",
"featuredTags": "https://mastodon.social/users/Gargron/collections/tags",
"preferredUsername": "Gargron",
"name": "Eugen Rochko",
"summary": "\u003cp\u003eFounder, CEO and lead developer \u003cspan class=\"h-card\"\u003e\u003ca href=\"https://mastodon.social/@Mastodon\" class=\"u-url mention\"\u003e@\u003cspan\u003eMastodon\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e, Germany.\u003c/p\u003e",
"url": "https://mastodon.social/@Gargron",
"manuallyApprovesFollowers": false,
"discoverable": true,
"published": "2016-03-16T00:00:00Z",
"devices": "https://mastodon.social/users/Gargron/collections/devices",
"alsoKnownAs": [
"https://tooting.ai/users/Gargron"
],
"publicKey": {
"id": "https://mastodon.social/users/Gargron#main-key",
"owner": "https://mastodon.social/users/Gargron",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvXc4vkECU2/CeuSo1wtn\nFoim94Ne1jBMYxTZ9wm2YTdJq1oiZKif06I2fOqDzY/4q/S9uccrE9Bkajv1dnkO\nVm31QjWlhVpSKynVxEWjVBO5Ienue8gND0xvHIuXf87o61poqjEoepvsQFElA5ym\novljWGSA/jpj7ozygUZhCXtaS2W5AD5tnBQUpcO0lhItYPYTjnmzcc4y2NbJV8hz\n2s2G8qKv8fyimE23gY1XrPJg+cRF+g4PqFXujjlJ7MihD9oqtLGxbu7o1cifTn3x\nBfIdPythWu5b4cujNsB3m3awJjVmx+MHQ9SugkSIYXV0Ina77cTNS0M2PYiH1PFR\nTwIDAQAB\n-----END PUBLIC KEY-----\n"
},
"tag": [],
"attachment": [
{
"type": "PropertyValue",
"name": "Patreon",
"value": "\u003ca href=\"https://www.patreon.com/mastodon\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://www.\u003c/span\u003e\u003cspan class=\"\"\u003epatreon.com/mastodon\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e"
},
{
"type": "PropertyValue",
"name": "GitHub",
"value": "\u003ca href=\"https://github.com/Gargron\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003egithub.com/Gargron\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e"
}
],
"endpoints": {
"sharedInbox": "https://mastodon.social/inbox"
},
"icon": {
"type": "Image",
"mediaType": "image/jpeg",
"url": "https://files.mastodon.social/accounts/avatars/000/000/001/original/dc4286ceb8fab734.jpg"
},
"image": {
"type": "Image",
"mediaType": "image/jpeg",
"url": "https://files.mastodon.social/accounts/headers/000/000/001/original/3b91c9965d00888b.jpeg"
}
}
""".trimIndent()
val objectMapper = ActivityPubConfig().objectMapper()
val readValue = objectMapper.readValue<Person>(personString)
}
}

View File

@ -126,7 +126,6 @@ class APNoteServiceImplTest {
url = user.url + "/icon.png" url = user.url + "/icon.png"
), ),
publicKey = Key( publicKey = Key(
type = emptyList(),
id = user.keyId, id = user.keyId,
owner = user.url, owner = user.url,
publicKeyPem = user.publicKey publicKeyPem = user.publicKey
@ -245,7 +244,6 @@ class APNoteServiceImplTest {
url = user.url + "/icon.png" url = user.url + "/icon.png"
), ),
publicKey = Key( publicKey = Key(
type = emptyList(),
id = user.keyId, id = user.keyId,
owner = user.url, owner = user.url,
publicKeyPem = user.publicKey publicKeyPem = user.publicKey