mirror of https://github.com/usbharu/Hideout.git
feat: ユーザーページをacceptヘッダーがactivityの状態でアクセスするとpersonを返すように
This commit is contained in:
parent
2a1824edbf
commit
e77c28c6c2
|
@ -8,6 +8,8 @@ import dev.usbharu.hideout.repository.UserRepository
|
||||||
import dev.usbharu.hideout.service.IUserAuthService
|
import dev.usbharu.hideout.service.IUserAuthService
|
||||||
import dev.usbharu.hideout.service.activitypub.ActivityPubService
|
import dev.usbharu.hideout.service.activitypub.ActivityPubService
|
||||||
import dev.usbharu.hideout.service.activitypub.ActivityPubServiceImpl
|
import dev.usbharu.hideout.service.activitypub.ActivityPubServiceImpl
|
||||||
|
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
|
||||||
|
import dev.usbharu.hideout.service.activitypub.ActivityPubUserServiceImpl
|
||||||
import dev.usbharu.hideout.service.impl.UserAuthService
|
import dev.usbharu.hideout.service.impl.UserAuthService
|
||||||
import dev.usbharu.hideout.service.impl.UserService
|
import dev.usbharu.hideout.service.impl.UserService
|
||||||
import dev.usbharu.hideout.service.signature.HttpSignatureVerifyService
|
import dev.usbharu.hideout.service.signature.HttpSignatureVerifyService
|
||||||
|
@ -42,6 +44,7 @@ fun Application.module() {
|
||||||
single<HttpSignatureVerifyService> { HttpSignatureVerifyServiceImpl(get()) }
|
single<HttpSignatureVerifyService> { HttpSignatureVerifyServiceImpl(get()) }
|
||||||
single<ActivityPubService> { ActivityPubServiceImpl() }
|
single<ActivityPubService> { ActivityPubServiceImpl() }
|
||||||
single<UserService> { UserService(get()) }
|
single<UserService> { UserService(get()) }
|
||||||
|
single<ActivityPubUserService> { ActivityPubUserServiceImpl(get(),get()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
configureKoin(module)
|
configureKoin(module)
|
||||||
|
@ -52,6 +55,7 @@ fun Application.module() {
|
||||||
configureRouting(
|
configureRouting(
|
||||||
inject<HttpSignatureVerifyService>().value,
|
inject<HttpSignatureVerifyService>().value,
|
||||||
inject<ActivityPubService>().value,
|
inject<ActivityPubService>().value,
|
||||||
inject<UserService>().value
|
inject<UserService>().value,
|
||||||
|
inject<ActivityPubUserService>().value
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,4 +13,21 @@ open class Image : Object {
|
||||||
this.url = url
|
this.url = url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other !is Image) return false
|
||||||
|
if (!super.equals(other)) return false
|
||||||
|
|
||||||
|
if (mediaType != other.mediaType) return false
|
||||||
|
return url == other.url
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = super.hashCode()
|
||||||
|
result = 31 * result + (mediaType?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (url?.hashCode() ?: 0)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,19 @@ open class JsonLd {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected constructor()
|
protected constructor()
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other !is JsonLd) return false
|
||||||
|
|
||||||
|
return context == other.context
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return context.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ContextDeserializer : JsonDeserializer<String>() {
|
public class ContextDeserializer : JsonDeserializer<String>() {
|
||||||
|
|
|
@ -17,5 +17,23 @@ open class Key : Object{
|
||||||
this.publicKeyPem = publicKeyPem
|
this.publicKeyPem = publicKeyPem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other !is Key) return false
|
||||||
|
if (!super.equals(other)) return false
|
||||||
|
|
||||||
|
if (id != other.id) return false
|
||||||
|
if (owner != other.owner) return false
|
||||||
|
return publicKeyPem == other.publicKeyPem
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = super.hashCode()
|
||||||
|
result = 31 * result + (id?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (owner?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (publicKeyPem?.hashCode() ?: 0)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,22 @@ open class Object : JsonLd {
|
||||||
return toMutableList.distinct()
|
return toMutableList.distinct()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other !is Object) return false
|
||||||
|
|
||||||
|
if (type != other.type) return false
|
||||||
|
return name == other.name
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = type.hashCode()
|
||||||
|
result = 31 * result + (name?.hashCode() ?: 0)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TypeSerializer : JsonSerializer<List<String>>() {
|
public class TypeSerializer : JsonSerializer<List<String>>() {
|
||||||
|
|
|
@ -32,4 +32,31 @@ open class Person : Object {
|
||||||
this.publicKey = publicKey
|
this.publicKey = publicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other !is Person) return false
|
||||||
|
|
||||||
|
if (id != other.id) return false
|
||||||
|
if (preferredUsername != other.preferredUsername) return false
|
||||||
|
if (summary != other.summary) return false
|
||||||
|
if (inbox != other.inbox) return false
|
||||||
|
if (outbox != other.outbox) return false
|
||||||
|
if (url != other.url) return false
|
||||||
|
if (icon != other.icon) return false
|
||||||
|
return publicKey == other.publicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = id?.hashCode() ?: 0
|
||||||
|
result = 31 * result + (preferredUsername?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (summary?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (inbox?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (outbox?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (url?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (icon?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (publicKey?.hashCode() ?: 0)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import dev.usbharu.hideout.routing.activitypub.outbox
|
||||||
import dev.usbharu.hideout.routing.activitypub.usersAP
|
import dev.usbharu.hideout.routing.activitypub.usersAP
|
||||||
import dev.usbharu.hideout.routing.wellknown.webfinger
|
import dev.usbharu.hideout.routing.wellknown.webfinger
|
||||||
import dev.usbharu.hideout.service.activitypub.ActivityPubService
|
import dev.usbharu.hideout.service.activitypub.ActivityPubService
|
||||||
|
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
|
||||||
import dev.usbharu.hideout.service.impl.UserService
|
import dev.usbharu.hideout.service.impl.UserService
|
||||||
import dev.usbharu.hideout.service.signature.HttpSignatureVerifyService
|
import dev.usbharu.hideout.service.signature.HttpSignatureVerifyService
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
|
@ -14,13 +15,14 @@ import io.ktor.server.routing.*
|
||||||
fun Application.configureRouting(
|
fun Application.configureRouting(
|
||||||
httpSignatureVerifyService: HttpSignatureVerifyService,
|
httpSignatureVerifyService: HttpSignatureVerifyService,
|
||||||
activityPubService: ActivityPubService,
|
activityPubService: ActivityPubService,
|
||||||
userService:UserService
|
userService:UserService,
|
||||||
|
activityPubUserService: ActivityPubUserService
|
||||||
) {
|
) {
|
||||||
install(AutoHeadResponse)
|
install(AutoHeadResponse)
|
||||||
routing {
|
routing {
|
||||||
inbox(httpSignatureVerifyService, activityPubService)
|
inbox(httpSignatureVerifyService, activityPubService)
|
||||||
outbox()
|
outbox()
|
||||||
usersAP(activityPubService)
|
usersAP(activityPubUserService)
|
||||||
webfinger(userService)
|
webfinger(userService)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
package dev.usbharu.hideout.plugins
|
package dev.usbharu.hideout.plugins
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSetter
|
||||||
|
import com.fasterxml.jackson.annotation.Nulls
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||||
|
import dev.usbharu.hideout.util.HttpUtil.Activity
|
||||||
|
import io.ktor.http.*
|
||||||
import io.ktor.serialization.jackson.*
|
import io.ktor.serialization.jackson.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.plugins.contentnegotiation.*
|
import io.ktor.server.plugins.contentnegotiation.*
|
||||||
|
@ -8,6 +14,11 @@ import io.ktor.server.routing.*
|
||||||
|
|
||||||
fun Application.configureSerialization() {
|
fun Application.configureSerialization() {
|
||||||
install(ContentNegotiation) {
|
install(ContentNegotiation) {
|
||||||
jackson()
|
jackson {
|
||||||
|
enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
|
||||||
|
setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
|
||||||
|
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||||
|
configOverride(List::class.java).setSetterInfo(JsonSetter.Value.forContentNulls(Nulls.AS_EMPTY))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package dev.usbharu.hideout.routing.activitypub
|
package dev.usbharu.hideout.routing.activitypub
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.config.Config
|
||||||
|
import dev.usbharu.hideout.exception.ParameterNotExistException
|
||||||
import dev.usbharu.hideout.service.activitypub.ActivityPubService
|
import dev.usbharu.hideout.service.activitypub.ActivityPubService
|
||||||
|
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
|
||||||
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.*
|
||||||
|
@ -8,11 +11,13 @@ import io.ktor.server.request.*
|
||||||
import io.ktor.server.response.*
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
|
|
||||||
fun Routing.usersAP(activityPubService: ActivityPubService){
|
fun Routing.usersAP(activityPubUserService:ActivityPubUserService){
|
||||||
route("/users/{name}"){
|
route("/users/{name}"){
|
||||||
createChild(ContentTypeRouteSelector(ContentType.Application.Activity)).handle {
|
createChild(ContentTypeRouteSelector(ContentType.Application.Activity)).handle {
|
||||||
|
val name = call.parameters["name"] ?: throw ParameterNotExistException("Parameter(name='name') does not exist.")
|
||||||
call.respond(HttpStatusCode.NotImplemented)
|
val person = activityPubUserService.getPersonByName(name)
|
||||||
|
call.response.header("Content-Type", ContentType.Application.Activity.toString())
|
||||||
|
call.respond(HttpStatusCode.OK,Config.configData.objectMapper.writeValueAsString(person))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package dev.usbharu.hideout.service.activitypub
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.ap.Person
|
||||||
|
|
||||||
|
interface ActivityPubUserService {
|
||||||
|
suspend fun getPersonByName(name:String):Person
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package dev.usbharu.hideout.service.activitypub
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.ap.Image
|
||||||
|
import dev.usbharu.hideout.ap.Key
|
||||||
|
import dev.usbharu.hideout.ap.Person
|
||||||
|
import dev.usbharu.hideout.config.Config
|
||||||
|
import dev.usbharu.hideout.service.IUserAuthService
|
||||||
|
import dev.usbharu.hideout.service.impl.UserService
|
||||||
|
|
||||||
|
class ActivityPubUserServiceImpl(private val userService: UserService, private val userAuthService: IUserAuthService) :
|
||||||
|
ActivityPubUserService {
|
||||||
|
override suspend fun getPersonByName(name: String): Person {
|
||||||
|
val userEntity = userService.findByName(name)
|
||||||
|
val userAuthEntity = userAuthService.findByUserId(userEntity.id)
|
||||||
|
val userUrl = "${Config.configData.url}/users$name"
|
||||||
|
return Person(
|
||||||
|
type = emptyList(),
|
||||||
|
name = userEntity.name,
|
||||||
|
id = userUrl,
|
||||||
|
preferredUsername = name,
|
||||||
|
summary = userEntity.description,
|
||||||
|
inbox = "$userUrl/inbox",
|
||||||
|
outbox = "$userUrl/outbox",
|
||||||
|
url = userUrl,
|
||||||
|
icon = Image(
|
||||||
|
type = emptyList(),
|
||||||
|
name = "$userUrl/icon.png",
|
||||||
|
mediaType = "image/png",
|
||||||
|
url = "$userUrl/icon.png"
|
||||||
|
),
|
||||||
|
publicKey = Key(
|
||||||
|
type = emptyList(),
|
||||||
|
name = "Public Key",
|
||||||
|
id = "$userUrl#pubkey",
|
||||||
|
owner = userUrl,
|
||||||
|
publicKeyPem = userAuthEntity.publicKey
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,30 @@
|
||||||
package dev.usbharu.hideout.routing.activitypub
|
package dev.usbharu.hideout.routing.activitypub
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSetter
|
||||||
|
import com.fasterxml.jackson.annotation.Nulls
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||||
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
|
import dev.usbharu.hideout.ap.Image
|
||||||
|
import dev.usbharu.hideout.ap.Key
|
||||||
|
import dev.usbharu.hideout.ap.Person
|
||||||
|
import dev.usbharu.hideout.config.Config
|
||||||
import dev.usbharu.hideout.domain.model.ActivityPubResponse
|
import dev.usbharu.hideout.domain.model.ActivityPubResponse
|
||||||
import dev.usbharu.hideout.domain.model.User
|
import dev.usbharu.hideout.domain.model.User
|
||||||
import dev.usbharu.hideout.domain.model.UserEntity
|
import dev.usbharu.hideout.domain.model.UserEntity
|
||||||
import dev.usbharu.hideout.plugins.configureRouting
|
import dev.usbharu.hideout.plugins.configureRouting
|
||||||
|
import dev.usbharu.hideout.plugins.configureSerialization
|
||||||
import dev.usbharu.hideout.repository.IUserRepository
|
import dev.usbharu.hideout.repository.IUserRepository
|
||||||
import dev.usbharu.hideout.service.activitypub.ActivityPubService
|
import dev.usbharu.hideout.service.activitypub.ActivityPubService
|
||||||
|
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
|
||||||
import dev.usbharu.hideout.service.activitypub.ActivityType
|
import dev.usbharu.hideout.service.activitypub.ActivityType
|
||||||
import dev.usbharu.hideout.service.impl.UserService
|
import dev.usbharu.hideout.service.impl.UserService
|
||||||
import dev.usbharu.hideout.service.signature.HttpSignatureVerifyService
|
import dev.usbharu.hideout.service.signature.HttpSignatureVerifyService
|
||||||
import dev.usbharu.hideout.util.HttpUtil.Activity
|
import dev.usbharu.hideout.util.HttpUtil.Activity
|
||||||
|
import io.ktor.client.call.*
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
|
import io.ktor.client.statement.*
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.server.config.*
|
import io.ktor.server.config.*
|
||||||
import io.ktor.server.testing.*
|
import io.ktor.server.testing.*
|
||||||
|
@ -25,7 +39,32 @@ class UsersAPTest {
|
||||||
environment {
|
environment {
|
||||||
config = ApplicationConfig("empty.conf")
|
config = ApplicationConfig("empty.conf")
|
||||||
}
|
}
|
||||||
|
val person = Person(
|
||||||
|
type = emptyList(),
|
||||||
|
name = "test",
|
||||||
|
id = "http://example.com/users/test",
|
||||||
|
preferredUsername = "test",
|
||||||
|
summary = "test user",
|
||||||
|
inbox = "http://example.com/users/test/inbox",
|
||||||
|
outbox = "http://example.com/users/test/outbox",
|
||||||
|
url = "http://example.com/users/test",
|
||||||
|
icon = Image(
|
||||||
|
type = emptyList(),
|
||||||
|
name = "http://example.com/users/test/icon.png",
|
||||||
|
mediaType = "image/png",
|
||||||
|
url = "http://example.com/users/test/icon.png"
|
||||||
|
),
|
||||||
|
publicKey = Key(
|
||||||
|
type = emptyList(),
|
||||||
|
name = "Public Key",
|
||||||
|
id = "http://example.com/users/test#pubkey",
|
||||||
|
owner = "https://example.com/users/test",
|
||||||
|
publicKeyPem = "-----BEGIN PUBLIC KEY-----\n\n-----END PUBLIC KEY-----"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
person.context = listOf("https://www.w3.org/ns/activitystreams")
|
||||||
application {
|
application {
|
||||||
|
configureSerialization()
|
||||||
configureRouting(object : HttpSignatureVerifyService {
|
configureRouting(object : HttpSignatureVerifyService {
|
||||||
override fun verify(headers: Headers): Boolean {
|
override fun verify(headers: Headers): Boolean {
|
||||||
return true
|
return true
|
||||||
|
@ -78,12 +117,26 @@ class UsersAPTest {
|
||||||
override suspend fun findFollowersById(id: Long): List<UserEntity> {
|
override suspend fun findFollowersById(id: Long): List<UserEntity> {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}))
|
}), object : ActivityPubUserService {
|
||||||
|
override suspend fun getPersonByName(name: String): Person {
|
||||||
|
return person
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
}
|
}
|
||||||
client.get("/users/test") {
|
client.get("/users/test") {
|
||||||
accept(ContentType.Application.Activity)
|
accept(ContentType.Application.Activity)
|
||||||
}.let {
|
}.let {
|
||||||
assertEquals(HttpStatusCode.NotImplemented, it.status)
|
val objectMapper = jacksonObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
|
||||||
|
.setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
|
||||||
|
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||||
|
objectMapper.configOverride(List::class.java).setSetterInfo(JsonSetter.Value.forValueNulls(
|
||||||
|
Nulls.AS_EMPTY))
|
||||||
|
val actual = it.bodyAsText()
|
||||||
|
val readValue = objectMapper.readValue<Person>(actual)
|
||||||
|
println(objectMapper.writeValueAsString(person))
|
||||||
|
println(objectMapper.writeValueAsString(readValue))
|
||||||
|
assertEquals(person, readValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue