From bfc5c9e1106bb677c3886c85d00213633e27489a Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 3 Dec 2023 10:26:48 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=B8=80=E9=83=A8=E3=81=AEAP=20Object?= =?UTF-8?q?=E3=81=8C=E3=83=87=E3=82=B7=E3=83=AA=E3=82=A2=E3=83=A9=E3=82=A4?= =?UTF-8?q?=E3=82=BA=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=8F=E3=81=AA=E3=81=A3?= =?UTF-8?q?=E3=81=A6=E3=81=84=E3=81=9F=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/activitypub/domain/model/Key.kt | 3 +- .../activitypub/domain/model/Person.kt | 2 +- .../service/objects/user/APUserService.kt | 2 - .../application/config/ActivityPubConfig.kt | 7 +- .../httpsignature/HttpRequestMixIn.kt | 33 ++++++++ .../domain/model/KeySerializeTest.kt | 24 ++++++ .../domain/model/PersonSerializeTest.kt | 75 +++++++++++++++++++ .../objects/note/APNoteServiceImplTest.kt | 2 - 8 files changed, 139 insertions(+), 9 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt create mode 100644 src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/KeySerializeTest.kt create mode 100644 src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/PersonSerializeTest.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt index 7e22097c..e821601f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt @@ -3,12 +3,11 @@ package dev.usbharu.hideout.activitypub.domain.model import dev.usbharu.hideout.activitypub.domain.model.objects.Object open class Key( - type: List, override val id: String, val owner: String, val publicKeyPem: String ) : Object( - type = add(list = type, type = "Key") + type = add(list = emptyList(), type = "Key") ), HasId { diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt index fe1b2d5f..4791fa68 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt @@ -14,7 +14,7 @@ constructor( var outbox: String, var url: String, private var icon: Image?, - var publicKey: Key?, + var publicKey: Key, var endpoints: Map = emptyMap(), var followers: String?, var following: String? diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt index 143df20b..9556590f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt @@ -61,7 +61,6 @@ class APUserServiceImpl( url = "$userUrl/icon.png" ), publicKey = Key( - type = emptyList(), id = userEntity.keyId, owner = userUrl, publicKeyPem = userEntity.publicKey @@ -129,7 +128,6 @@ class APUserServiceImpl( url = "$id/icon.png" ), publicKey = Key( - type = emptyList(), id = userEntity.keyId, owner = id, publicKeyPem = userEntity.publicKey diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt index d661a30a..c99d07b2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt @@ -7,6 +7,8 @@ import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.ObjectMapper 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.RsaSha256HttpSignatureSigner import org.springframework.beans.factory.annotation.Qualifier @@ -24,12 +26,13 @@ class ActivityPubConfig { val objectMapper = jacksonObjectMapper() .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) - .setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.AS_EMPTY)) - .setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY)) + .setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)) + .setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP)) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .configure(JsonParser.Feature.ALLOW_COMMENTS, true) .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) .configure(JsonParser.Feature.ALLOW_TRAILING_COMMA, true) + .addMixIn(HttpRequest::class.java, HttpRequestMixIn::class.java) return objectMapper } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt new file mode 100644 index 00000000..3686d3c9 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt @@ -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() { + 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()) + ) + } + +} diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/KeySerializeTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/KeySerializeTest.kt new file mode 100644 index 00000000..d6e1be7a --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/KeySerializeTest.kt @@ -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(trimIndent) + + } +} diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/PersonSerializeTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/PersonSerializeTest.kt new file mode 100644 index 00000000..9b344337 --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/PersonSerializeTest.kt @@ -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(personString) + } +} diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt index 5ed6597d..b8713816 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt @@ -126,7 +126,6 @@ class APNoteServiceImplTest { url = user.url + "/icon.png" ), publicKey = Key( - type = emptyList(), id = user.keyId, owner = user.url, publicKeyPem = user.publicKey @@ -245,7 +244,6 @@ class APNoteServiceImplTest { url = user.url + "/icon.png" ), publicKey = Key( - type = emptyList(), id = user.keyId, owner = user.url, publicKeyPem = user.publicKey