mirror of https://github.com/usbharu/Hideout.git
Merge pull request #141 from usbharu/feature/integration-test
Feature/integration test
This commit is contained in:
commit
f73b6532f1
|
@ -27,6 +27,37 @@ apply {
|
|||
group = "dev.usbharu"
|
||||
version = "0.0.1"
|
||||
|
||||
sourceSets {
|
||||
create("intTest") {
|
||||
compileClasspath += sourceSets.main.get().output
|
||||
runtimeClasspath += sourceSets.main.get().output
|
||||
}
|
||||
}
|
||||
|
||||
val intTestImplementation by configurations.getting {
|
||||
extendsFrom(configurations.implementation.get())
|
||||
}
|
||||
val intTestRuntimeOnly by configurations.getting {
|
||||
extendsFrom(configurations.runtimeOnly.get())
|
||||
}
|
||||
|
||||
val integrationTest = task<Test>("integrationTest") {
|
||||
description = "Runs integration tests."
|
||||
group = "verification"
|
||||
|
||||
testClassesDirs = sourceSets["intTest"].output.classesDirs
|
||||
classpath = sourceSets["intTest"].runtimeClasspath
|
||||
shouldRunAfter("test")
|
||||
|
||||
useJUnitPlatform()
|
||||
|
||||
testLogging {
|
||||
events("passed")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.check { dependsOn(integrationTest) }
|
||||
|
||||
tasks.withType<Test> {
|
||||
useJUnitPlatform()
|
||||
val cpus = Runtime.getRuntime().availableProcessors()
|
||||
|
@ -132,9 +163,7 @@ dependencies {
|
|||
compileOnly("io.swagger.core.v3:swagger-annotations:2.2.6")
|
||||
implementation("io.swagger.core.v3:swagger-models:2.2.6")
|
||||
implementation("org.jetbrains.exposed:exposed-java-time:$exposed_version")
|
||||
testImplementation("org.springframework.boot:spring-boot-test-autoconfigure")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
testImplementation("org.springframework.security:spring-security-test")
|
||||
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
||||
implementation("org.springframework.security:spring-security-oauth2-jose")
|
||||
|
@ -162,7 +191,6 @@ dependencies {
|
|||
implementation("io.ktor:ktor-client-content-negotiation:$ktor_version")
|
||||
testImplementation("io.ktor:ktor-client-mock:$ktor_version")
|
||||
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.8.1")
|
||||
testImplementation("org.mockito.kotlin:mockito-kotlin:4.1.0")
|
||||
testImplementation("org.mockito:mockito-inline:5.2.0")
|
||||
testImplementation("nl.jqno.equalsverifier:equalsverifier:3.15.3")
|
||||
|
@ -172,6 +200,10 @@ dependencies {
|
|||
implementation("org.drewcarlson:kjob-mongo:0.6.0")
|
||||
|
||||
detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.1")
|
||||
|
||||
intTestImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
intTestImplementation("org.springframework.security:spring-security-test")
|
||||
|
||||
}
|
||||
|
||||
detekt {
|
||||
|
@ -220,7 +252,7 @@ project.gradle.taskGraph.whenReady {
|
|||
|
||||
kover {
|
||||
|
||||
excludeSourceSets {
|
||||
excludeSourceSets {
|
||||
names("aot")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
package activitypub.inbox
|
||||
|
||||
import dev.usbharu.hideout.SpringApplication
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.boot.test.context.TestConfiguration
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.security.test.context.support.WithAnonymousUser
|
||||
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import org.springframework.test.web.servlet.post
|
||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import org.springframework.web.context.WebApplicationContext
|
||||
import util.TestTransaction
|
||||
import util.WithMockHttpSignature
|
||||
|
||||
@SpringBootTest(classes = [SpringApplication::class])
|
||||
@AutoConfigureMockMvc
|
||||
@Transactional
|
||||
class InboxTest {
|
||||
|
||||
@Autowired
|
||||
private lateinit var context: WebApplicationContext
|
||||
|
||||
private lateinit var mockMvc: MockMvc
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
mockMvc = MockMvcBuilders.webAppContextSetup(context)
|
||||
.apply<DefaultMockMvcBuilder>(springSecurity())
|
||||
.build()
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithAnonymousUser
|
||||
fun `匿名でinboxにPOSTしたら401`() {
|
||||
mockMvc
|
||||
.post("/inbox") {
|
||||
content = "{}"
|
||||
contentType = MediaType.APPLICATION_JSON
|
||||
}
|
||||
.andExpect { status { isUnauthorized() } }
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockHttpSignature
|
||||
fun 有効なHttpSignatureでPOSTしたら202() {
|
||||
mockMvc
|
||||
.post("/inbox") {
|
||||
content = "{}"
|
||||
contentType = MediaType.APPLICATION_JSON
|
||||
}
|
||||
.asyncDispatch()
|
||||
.andExpect { status { isAccepted() } }
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithAnonymousUser
|
||||
fun `匿名でuser-inboxにPOSTしたら401`() {
|
||||
mockMvc
|
||||
.post("/users/hoge/inbox") {
|
||||
content = "{}"
|
||||
contentType = MediaType.APPLICATION_JSON
|
||||
}
|
||||
.andExpect { status { isUnauthorized() } }
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockHttpSignature
|
||||
fun 有効なHttpSignaturesでPOSTしたら202() {
|
||||
mockMvc
|
||||
.post("/users/hoge/inbox") {
|
||||
content = "{}"
|
||||
contentType = MediaType.APPLICATION_JSON
|
||||
}
|
||||
.asyncDispatch()
|
||||
.andExpect { status { isAccepted() } }
|
||||
}
|
||||
|
||||
@TestConfiguration
|
||||
class Configuration {
|
||||
@Bean
|
||||
fun testTransaction() = TestTransaction
|
||||
}
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
package activitypub.note
|
||||
|
||||
import dev.usbharu.hideout.SpringApplication
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.security.test.context.support.WithAnonymousUser
|
||||
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity
|
||||
import org.springframework.test.context.jdbc.Sql
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import org.springframework.test.web.servlet.get
|
||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import org.springframework.web.context.WebApplicationContext
|
||||
import util.WithHttpSignature
|
||||
import util.WithMockHttpSignature
|
||||
|
||||
@SpringBootTest(classes = [SpringApplication::class])
|
||||
@AutoConfigureMockMvc
|
||||
@Transactional
|
||||
class NoteTest {
|
||||
private lateinit var mockMvc: MockMvc
|
||||
|
||||
@Autowired
|
||||
private lateinit var context: WebApplicationContext
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
mockMvc = MockMvcBuilders.webAppContextSetup(context).apply<DefaultMockMvcBuilder>(springSecurity()).build()
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithAnonymousUser
|
||||
@Sql("/sql/note/匿名でpublic投稿を取得できる.sql")
|
||||
fun `匿名でpublic投稿を取得できる`() {
|
||||
mockMvc
|
||||
.get("/users/test-user/posts/1234") {
|
||||
accept(MediaType("application", "activity+json"))
|
||||
}
|
||||
.asyncDispatch()
|
||||
.andDo { print() }
|
||||
.andExpect { status { isOk() } }
|
||||
.andExpect { content { contentType("application/activity+json") } }
|
||||
.andExpect { jsonPath("\$.type") { value("Note") } }
|
||||
.andExpect { jsonPath("\$.to") { value("https://www.w3.org/ns/activitystreams#Public") } }
|
||||
.andExpect { jsonPath("\$.cc") { value("https://www.w3.org/ns/activitystreams#Public") } }
|
||||
}
|
||||
|
||||
@Test
|
||||
@Sql("/sql/note/匿名でunlisted投稿を取得できる.sql")
|
||||
@WithAnonymousUser
|
||||
fun 匿名でunlisted投稿を取得できる() {
|
||||
mockMvc
|
||||
.get("/users/test-user2/posts/1235") {
|
||||
accept(MediaType("application", "activity+json"))
|
||||
}
|
||||
.asyncDispatch()
|
||||
.andDo { print() }
|
||||
.andExpect { status { isOk() } }
|
||||
.andExpect { content { contentType("application/activity+json") } }
|
||||
.andExpect { jsonPath("\$.type") { value("Note") } }
|
||||
.andExpect { jsonPath("\$.to") { value("https://example.com/users/test-user2/followers") } }
|
||||
.andExpect { jsonPath("\$.cc") { value("https://www.w3.org/ns/activitystreams#Public") } }
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
@WithAnonymousUser
|
||||
@Sql("/sql/note/匿名でfollowers投稿を取得しようとすると404.sql")
|
||||
fun 匿名でfollowers投稿を取得しようとすると404() {
|
||||
mockMvc
|
||||
.get("/users/test-user2/posts/1236") {
|
||||
accept(MediaType("application", "activity+json"))
|
||||
}
|
||||
.asyncDispatch()
|
||||
.andExpect { status { isNotFound() } }
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithAnonymousUser
|
||||
fun 匿名でdirect投稿を取得しようとすると404() {
|
||||
mockMvc
|
||||
.get("/users/test-user2/posts/1236") {
|
||||
accept(MediaType("application", "activity+json"))
|
||||
}
|
||||
.asyncDispatch()
|
||||
.andExpect { status { isNotFound() } }
|
||||
}
|
||||
|
||||
@Test
|
||||
@Sql("/sql/note/httpSignature認証でフォロワーがpublic投稿を取得できる.sql")
|
||||
@WithHttpSignature(keyId = "https://follower.example.com/users/test-user5#pubkey")
|
||||
fun HttpSignature認証でフォロワーがpublic投稿を取得できる() {
|
||||
mockMvc
|
||||
.get("/users/test-user4/posts/1237") {
|
||||
accept(MediaType("application", "activity+json"))
|
||||
}
|
||||
.asyncDispatch()
|
||||
.andDo { print() }
|
||||
.andExpect { status { isOk() } }
|
||||
.andExpect { content { contentType("application/activity+json") } }
|
||||
.andExpect { jsonPath("\$.type") { value("Note") } }
|
||||
.andExpect { jsonPath("\$.to") { value("https://www.w3.org/ns/activitystreams#Public") } }
|
||||
.andExpect { jsonPath("\$.cc") { value("https://www.w3.org/ns/activitystreams#Public") } }
|
||||
}
|
||||
|
||||
@Test
|
||||
@Sql("/sql/note/httpSignature認証でフォロワーがunlisted投稿を取得できる.sql")
|
||||
@WithHttpSignature(keyId = "https://follower.example.com/users/test-user7#pubkey")
|
||||
fun httpSignature認証でフォロワーがunlisted投稿を取得できる() {
|
||||
mockMvc
|
||||
.get("/users/test-user6/posts/1238") {
|
||||
accept(MediaType("application", "activity+json"))
|
||||
}
|
||||
.asyncDispatch()
|
||||
.andDo { print() }
|
||||
.andExpect { status { isOk() } }
|
||||
.andExpect { content { contentType("application/activity+json") } }
|
||||
.andExpect { jsonPath("\$.type") { value("Note") } }
|
||||
.andExpect { jsonPath("\$.to") { value("https://example.com/users/test-user6/followers") } }
|
||||
.andExpect { jsonPath("\$.cc") { value("https://www.w3.org/ns/activitystreams#Public") } }
|
||||
}
|
||||
|
||||
@Test
|
||||
@Sql("/sql/note/httpSignature認証でフォロワーがfollowers投稿を取得できる.sql")
|
||||
@WithHttpSignature(keyId = "https://follower.example.com/users/test-user9#pubkey")
|
||||
fun httpSignature認証でフォロワーがfollowers投稿を取得できる() {
|
||||
mockMvc
|
||||
.get("/users/test-user8/posts/1239") {
|
||||
accept(MediaType("application", "activity+json"))
|
||||
}
|
||||
.asyncDispatch()
|
||||
.andDo { print() }
|
||||
.andExpect { status { isOk() } }
|
||||
.andExpect { content { contentType("application/activity+json") } }
|
||||
.andExpect { jsonPath("\$.type") { value("Note") } }
|
||||
.andExpect { jsonPath("\$.to") { value("https://example.com/users/test-user8/followers") } }
|
||||
.andExpect { jsonPath("\$.cc") { value("https://example.com/users/test-user8/followers") } }
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Sql("/sql/note/リプライになっている投稿はinReplyToが存在する.sql")
|
||||
@WithMockHttpSignature
|
||||
fun リプライになっている投稿はinReplyToが存在する() {
|
||||
mockMvc
|
||||
.get("/users/test-user10/posts/1241") {
|
||||
accept(MediaType("application", "activity+json"))
|
||||
}
|
||||
.asyncDispatch()
|
||||
.andDo { print() }
|
||||
.andExpect { status { isOk() } }
|
||||
.andExpect { content { contentType("application/activity+json") } }
|
||||
.andExpect { jsonPath("\$.type") { value("Note") } }
|
||||
.andExpect { jsonPath("\$.inReplyTo") { value("https://example.com/users/test-user10/posts/1240") } }
|
||||
}
|
||||
|
||||
@Test
|
||||
@Sql("/sql/note/メディア付き投稿はattachmentにDocumentとして画像が存在する.sql")
|
||||
@WithMockHttpSignature
|
||||
fun メディア付き投稿はattachmentにDocumentとして画像が存在する() {
|
||||
mockMvc
|
||||
.get("/users/test-user10/posts/1242") {
|
||||
accept(MediaType("application", "activity+json"))
|
||||
}
|
||||
.asyncDispatch()
|
||||
.andDo { print() }
|
||||
.andExpect { status { isOk() } }
|
||||
.andExpect { content { contentType("application/activity+json") } }
|
||||
.andExpect { jsonPath("\$.type") { value("Note") } }
|
||||
.andExpect { jsonPath("\$.attachment") { isArray() } }
|
||||
.andExpect { jsonPath("\$.attachment[0].type") { value("Document") } }
|
||||
.andExpect { jsonPath("\$.attachment[0].url") { value("https://example.com/media/test-media.png") } }
|
||||
.andExpect { jsonPath("\$.attachment[1].type") { value("Document") } }
|
||||
.andExpect { jsonPath("\$.attachment[1].url") { value("https://example.com/media/test-media2.png") } }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package activitypub.webfinger
|
||||
|
||||
import dev.usbharu.hideout.SpringApplication
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.boot.test.context.TestConfiguration
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.test.context.jdbc.Sql
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import org.springframework.test.web.servlet.get
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import util.TestTransaction
|
||||
import java.net.URL
|
||||
|
||||
@SpringBootTest(classes = [SpringApplication::class])
|
||||
@AutoConfigureMockMvc
|
||||
@Transactional
|
||||
class WebFingerTest {
|
||||
@Autowired
|
||||
private lateinit var mockMvc: MockMvc
|
||||
|
||||
@Test
|
||||
@Sql("/sql/test-user.sql")
|
||||
fun `webfinger 存在するユーザーを取得`() {
|
||||
mockMvc
|
||||
.get("/.well-known/webfinger?resource=acct:test-user@example.com")
|
||||
.andExpect { status { isOk() } }
|
||||
.andExpect { header { string("Content-Type", "application/json") } }
|
||||
.andExpect {
|
||||
jsonPath("\$.subject") {
|
||||
value("acct:test-user@example.com")
|
||||
}
|
||||
}
|
||||
.andExpect {
|
||||
jsonPath("\$.links[0].rel") {
|
||||
value("self")
|
||||
}
|
||||
}
|
||||
.andExpect {
|
||||
jsonPath("\$.links[0].href") { value("https://example.com/users/test-user") }
|
||||
}
|
||||
.andExpect {
|
||||
jsonPath("\$.links[0].type") {
|
||||
value("application/activity+json")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `webfinger 存在しないユーザーに404`() {
|
||||
mockMvc
|
||||
.get("/.well-known/webfinger?resource=acct:test-user@example.com")
|
||||
.andExpect { status { isNotFound() } }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `webfinger 不正なリクエストは400`() {
|
||||
mockMvc
|
||||
.get("/.well-known/webfinger?res=acct:test")
|
||||
.andExpect { status { isBadRequest() } }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `webfinger acctのパースが出来なくても400`() {
|
||||
mockMvc
|
||||
.get("/.well-known/webfinger?resource=acct:@a@b@c@d")
|
||||
.andExpect { status { isBadRequest() } }
|
||||
}
|
||||
|
||||
@TestConfiguration
|
||||
class Configuration {
|
||||
@Bean
|
||||
fun url(): URL {
|
||||
return URL("https://example.com")
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun testTransaction(): Transaction = TestTransaction
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package util
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
|
||||
@SpringBootTest
|
||||
abstract class SpringApplicationTestBase
|
|
@ -0,0 +1,9 @@
|
|||
package util
|
||||
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
|
||||
object TestTransaction : Transaction {
|
||||
override suspend fun <T> transaction(block: suspend () -> T): T = block()
|
||||
|
||||
override suspend fun <T> transaction(transactionLevel: Int, block: suspend () -> T): T = block()
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package util
|
||||
|
||||
import org.springframework.core.annotation.AliasFor
|
||||
import org.springframework.security.test.context.support.TestExecutionEvent
|
||||
import org.springframework.security.test.context.support.WithSecurityContext
|
||||
import java.lang.annotation.Inherited
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Inherited
|
||||
@MustBeDocumented
|
||||
@WithSecurityContext(factory = WithHttpSignatureSecurityContextFactory::class)
|
||||
annotation class WithHttpSignature(
|
||||
@get:AliasFor(
|
||||
annotation = WithSecurityContext::class
|
||||
) val setupBefore: TestExecutionEvent = TestExecutionEvent.TEST_METHOD,
|
||||
val keyId: String = "https://example.com/users/test-user#pubkey",
|
||||
val url: String = "https://example.com/inbox",
|
||||
val method: String = "GET"
|
||||
)
|
|
@ -0,0 +1,48 @@
|
|||
package util
|
||||
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUser
|
||||
import dev.usbharu.hideout.core.query.UserQueryService
|
||||
import dev.usbharu.httpsignature.common.HttpHeaders
|
||||
import dev.usbharu.httpsignature.common.HttpMethod
|
||||
import dev.usbharu.httpsignature.common.HttpRequest
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.springframework.security.core.context.SecurityContext
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.security.test.context.support.WithSecurityContextFactory
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
|
||||
import java.net.URL
|
||||
|
||||
class WithHttpSignatureSecurityContextFactory(
|
||||
private val userQueryService: UserQueryService,
|
||||
private val transaction: Transaction
|
||||
) : WithSecurityContextFactory<WithHttpSignature> {
|
||||
|
||||
private val securityContextStrategy = SecurityContextHolder.getContextHolderStrategy()
|
||||
|
||||
override fun createSecurityContext(annotation: WithHttpSignature): SecurityContext = runBlocking {
|
||||
val preAuthenticatedAuthenticationToken = PreAuthenticatedAuthenticationToken(
|
||||
annotation.keyId, HttpRequest(
|
||||
URL("https://example.com/inbox"),
|
||||
HttpHeaders(mapOf()), HttpMethod.GET
|
||||
)
|
||||
)
|
||||
val httpSignatureUser = transaction.transaction {
|
||||
val findByKeyId = userQueryService.findByKeyId(annotation.keyId)
|
||||
HttpSignatureUser(
|
||||
findByKeyId.name,
|
||||
findByKeyId.domain,
|
||||
findByKeyId.id,
|
||||
true,
|
||||
true,
|
||||
mutableListOf()
|
||||
)
|
||||
}
|
||||
preAuthenticatedAuthenticationToken.details = httpSignatureUser
|
||||
preAuthenticatedAuthenticationToken.isAuthenticated = true
|
||||
val emptyContext = securityContextStrategy.createEmptyContext()
|
||||
emptyContext.authentication = preAuthenticatedAuthenticationToken
|
||||
return@runBlocking emptyContext
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package util
|
||||
|
||||
import org.springframework.core.annotation.AliasFor
|
||||
import org.springframework.security.test.context.support.TestExecutionEvent
|
||||
import org.springframework.security.test.context.support.WithSecurityContext
|
||||
import java.lang.annotation.Inherited
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Inherited
|
||||
@MustBeDocumented
|
||||
@WithSecurityContext(factory = WithMockHttpSignatureSecurityContextFactory::class)
|
||||
annotation class WithMockHttpSignature(
|
||||
@get:AliasFor(
|
||||
annotation = WithSecurityContext::class
|
||||
) val setupBefore: TestExecutionEvent = TestExecutionEvent.TEST_METHOD,
|
||||
val username: String = "test-user",
|
||||
val domain: String = "example.com",
|
||||
val keyId: String = "https://example.com/users/test-user#pubkey",
|
||||
val id: Long = 1234L,
|
||||
val url: String = "https://example.com/inbox",
|
||||
val method: String = "GET"
|
||||
)
|
|
@ -0,0 +1,39 @@
|
|||
package util
|
||||
|
||||
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUser
|
||||
import dev.usbharu.httpsignature.common.HttpHeaders
|
||||
import dev.usbharu.httpsignature.common.HttpMethod
|
||||
import dev.usbharu.httpsignature.common.HttpRequest
|
||||
import org.springframework.security.core.context.SecurityContext
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.security.test.context.support.WithSecurityContextFactory
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
|
||||
import java.net.URL
|
||||
|
||||
class WithMockHttpSignatureSecurityContextFactory :
|
||||
WithSecurityContextFactory<WithMockHttpSignature> {
|
||||
|
||||
private val securityContextStrategy = SecurityContextHolder.getContextHolderStrategy()
|
||||
|
||||
override fun createSecurityContext(annotation: WithMockHttpSignature): SecurityContext {
|
||||
val preAuthenticatedAuthenticationToken = PreAuthenticatedAuthenticationToken(
|
||||
annotation.keyId, HttpRequest(
|
||||
URL(annotation.url),
|
||||
HttpHeaders(mapOf()), HttpMethod.valueOf(annotation.method.uppercase())
|
||||
)
|
||||
)
|
||||
val httpSignatureUser = HttpSignatureUser(
|
||||
annotation.username,
|
||||
annotation.domain,
|
||||
annotation.id,
|
||||
true,
|
||||
true,
|
||||
mutableListOf()
|
||||
)
|
||||
preAuthenticatedAuthenticationToken.details = httpSignatureUser
|
||||
preAuthenticatedAuthenticationToken.isAuthenticated = true
|
||||
val emptyContext = securityContextStrategy.createEmptyContext()
|
||||
emptyContext.authentication = preAuthenticatedAuthenticationToken
|
||||
return emptyContext
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
hideout:
|
||||
url: "https://localhost:8080"
|
||||
use-mongodb: true
|
||||
security:
|
||||
jwt:
|
||||
generate: true
|
||||
key-id: a
|
||||
private-key: "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKjMzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvuNMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZqgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulgp2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlRZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwiVuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskVlaAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83HmQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwYdgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cwta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQDM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2TN0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPvt8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDUAhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISLDY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnKxt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEAmNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfzet6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhrVBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicDTQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cncdn/RsYEONbwQSjIfMPkvxF+8HQ=="
|
||||
public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB"
|
||||
storage:
|
||||
use-s3: true
|
||||
endpoint: "http://localhost:8082/test-hideout"
|
||||
public-url: "http://localhost:8082/test-hideout"
|
||||
bucket: "test-hideout"
|
||||
region: "auto"
|
||||
access-key: ""
|
||||
secret-key: ""
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
driver-class-name: org.h2.Driver
|
||||
url: "jdbc:h2:mem:test;MODE=POSTGRESQL;DB_CLOSE_DELAY=-1"
|
||||
username: ""
|
||||
password:
|
||||
data:
|
||||
mongodb:
|
||||
auto-index-creation: true
|
||||
host: localhost
|
||||
port: 27017
|
||||
database: hideout
|
||||
h2:
|
||||
console:
|
||||
enabled: true
|
||||
|
||||
exposed:
|
||||
generate-ddl: true
|
||||
excluded-packages: dev.usbharu.hideout.core.infrastructure.kjobexposed
|
||||
server:
|
||||
port: 8080
|
|
@ -0,0 +1,10 @@
|
|||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{x-request-id}] %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<root level="TRACE">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
</configuration>
|
|
@ -0,0 +1,29 @@
|
|||
insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY,
|
||||
CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS)
|
||||
VALUES (8, 'test-user8', 'example.com', 'Im test-user8.', 'THis account is test-user8.',
|
||||
'5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8',
|
||||
'https://example.com/users/test-user8/inbox',
|
||||
'https://example.com/users/test-user8/outbox', 'https://example.com/users/test-user8',
|
||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||
'https://example.com/users/test-user8#pubkey', 'https://example.com/users/test-user8/following',
|
||||
'https://example.com/users/test-user8/followers');
|
||||
|
||||
insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY,
|
||||
CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS)
|
||||
VALUES (9, 'test-user9', 'follower.example.com', 'Im test-user9.', 'THis account is test-user9.',
|
||||
null,
|
||||
'https://follower.example.com/users/test-user9/inbox',
|
||||
'https://follower.example.com/users/test-user9/outbox', 'https://follower.example.com/users/test-user9',
|
||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||
null, 12345678,
|
||||
'https://follower.example.com/users/test-user9#pubkey',
|
||||
'https://follower.example.com/users/test-user9/following',
|
||||
'https://follower.example.com/users/test-user9/followers');
|
||||
|
||||
insert into USERS_FOLLOWERS (USER_ID, FOLLOWER_ID)
|
||||
VALUES (8, 9);
|
||||
|
||||
insert into POSTS (ID, "userId", OVERVIEW, TEXT, "createdAt", VISIBILITY, URL, "repostId", "replyId", SENSITIVE, AP_ID)
|
||||
VALUES (1239, 8, null, 'test post', 12345680, 2, 'https://example.com/users/test-user8/posts/1239', null, null, false,
|
||||
'https://example.com/users/test-user8/posts/1239');
|
|
@ -0,0 +1,29 @@
|
|||
insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY,
|
||||
CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS)
|
||||
VALUES (4, 'test-user4', 'example.com', 'Im test user4.', 'THis account is test user4.',
|
||||
'5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8',
|
||||
'https://example.com/users/test-user4/inbox',
|
||||
'https://example.com/users/test-user4/outbox', 'https://example.com/users/test-user4',
|
||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||
'https://example.com/users/test-user4#pubkey', 'https://example.com/users/test-user4/following',
|
||||
'https://example.com/users/test-user4/followers');
|
||||
|
||||
insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY,
|
||||
CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS)
|
||||
VALUES (5, 'test-user5', 'follower.example.com', 'Im test user5.', 'THis account is test user5.',
|
||||
null,
|
||||
'https://follower.example.com/users/test-user5/inbox',
|
||||
'https://follower.example.com/users/test-user5/outbox', 'https://follower.example.com/users/test-user5',
|
||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||
null, 12345678,
|
||||
'https://follower.example.com/users/test-user5#pubkey',
|
||||
'https://follower.example.com/users/test-user5/following',
|
||||
'https://follower.example.com/users/test-user5/followers');
|
||||
|
||||
insert into USERS_FOLLOWERS (USER_ID, FOLLOWER_ID)
|
||||
VALUES (4, 5);
|
||||
|
||||
insert into POSTS (ID, "userId", OVERVIEW, TEXT, "createdAt", VISIBILITY, URL, "repostId", "replyId", SENSITIVE, AP_ID)
|
||||
VALUES (1237, 4, null, 'test post', 12345680, 0, 'https://example.com/users/test-user4/posts/1237', null, null, false,
|
||||
'https://example.com/users/test-user4/posts/1237');
|
|
@ -0,0 +1,29 @@
|
|||
insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY,
|
||||
CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS)
|
||||
VALUES (6, 'test-user6', 'example.com', 'Im test-user6.', 'THis account is test-user6.',
|
||||
'5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8',
|
||||
'https://example.com/users/test-user6/inbox',
|
||||
'https://example.com/users/test-user6/outbox', 'https://example.com/users/test-user6',
|
||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||
'https://example.com/users/test-user6#pubkey', 'https://example.com/users/test-user6/following',
|
||||
'https://example.com/users/test-user6/followers');
|
||||
|
||||
insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY,
|
||||
CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS)
|
||||
VALUES (7, 'test-user7', 'follower.example.com', 'Im test-user7.', 'THis account is test-user7.',
|
||||
null,
|
||||
'https://follower.example.com/users/test-user7/inbox',
|
||||
'https://follower.example.com/users/test-user7/outbox', 'https://follower.example.com/users/test-user7',
|
||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||
null, 12345678,
|
||||
'https://follower.example.com/users/test-user7#pubkey',
|
||||
'https://follower.example.com/users/test-user7/following',
|
||||
'https://follower.example.com/users/test-user7/followers');
|
||||
|
||||
insert into USERS_FOLLOWERS (USER_ID, FOLLOWER_ID)
|
||||
VALUES (6, 7);
|
||||
|
||||
insert into POSTS (ID, "userId", OVERVIEW, TEXT, "createdAt", VISIBILITY, URL, "repostId", "replyId", SENSITIVE, AP_ID)
|
||||
VALUES (1238, 6, null, 'test post', 12345680, 1, 'https://example.com/users/test-user6/posts/1238', null, null, false,
|
||||
'https://example.com/users/test-user6/posts/1238');
|
|
@ -0,0 +1,22 @@
|
|||
insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY,
|
||||
CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS)
|
||||
VALUES (11, 'test-user11', 'example.com', 'Im test-user11.', 'THis account is test-user11.',
|
||||
'5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8',
|
||||
'https://example.com/users/test-user11/inbox',
|
||||
'https://example.com/users/test-user11/outbox', 'https://example.com/users/test-user11',
|
||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||
'https://example.com/users/test-user11#pubkey', 'https://example.com/users/test-user11/following',
|
||||
'https://example.com/users/test-user11/followers');
|
||||
|
||||
insert into POSTS (ID, "userId", OVERVIEW, TEXT, "createdAt", VISIBILITY, URL, "repostId", "replyId", SENSITIVE, AP_ID)
|
||||
VALUES (1242, 11, null, 'test post', 12345680, 0, 'https://example.com/users/test-user11/posts/1242', null, null, false,
|
||||
'https://example.com/users/test-user11/posts/1242');
|
||||
|
||||
insert into MEDIA (ID, NAME, URL, REMOTE_URL, THUMBNAIL_URL, TYPE, BLURHASH)
|
||||
VALUES (1, 'test-media', 'https://example.com/media/test-media.png', null, null, 0, null),
|
||||
(2, 'test-media2', 'https://example.com/media/test-media2.png', null, null, 0, null);
|
||||
|
||||
insert into POSTSMEDIA(POST_ID, MEDIA_ID)
|
||||
VALUES (1242, 1),
|
||||
(1242, 2);
|
|
@ -0,0 +1,16 @@
|
|||
insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY,
|
||||
CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS)
|
||||
VALUES (10, 'test-user10', 'example.com', 'Im test-user10.', 'THis account is test-user10.',
|
||||
'5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8',
|
||||
'https://example.com/users/test-user10/inbox',
|
||||
'https://example.com/users/test-user10/outbox', 'https://example.com/users/test-user10',
|
||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||
'https://example.com/users/test-user10#pubkey', 'https://example.com/users/test-user10/following',
|
||||
'https://example.com/users/test-user10/followers');
|
||||
|
||||
insert into POSTS (ID, "userId", OVERVIEW, TEXT, "createdAt", VISIBILITY, URL, "repostId", "replyId", SENSITIVE, AP_ID)
|
||||
VALUES (1240, 10, null, 'test post', 12345680, 0, 'https://example.com/users/test-user10/posts/1240', null, null, false,
|
||||
'https://example.com/users/test-user10/posts/1240'),
|
||||
(1241, 10, null, 'test post', 12345680, 0, 'https://example.com/users/test-user10/posts/1241', null, 1240, false,
|
||||
'https://example.com/users/test-user10/posts/1241');
|
|
@ -0,0 +1,14 @@
|
|||
insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY,
|
||||
CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS)
|
||||
VALUES (3, 'test-user3', 'example.com', 'Im test user3.', 'THis account is test user3.',
|
||||
'5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8',
|
||||
'https://example.com/users/test-user3/inbox',
|
||||
'https://example.com/users/test-user3/outbox', 'https://example.com/users/test-user3',
|
||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||
'https://example.com/users/test-user3#pubkey', 'https://example.com/users/test-user3/following',
|
||||
'https://example.com/users/test-user3/followers');
|
||||
|
||||
insert into POSTS (ID, "userId", OVERVIEW, TEXT, "createdAt", VISIBILITY, URL, "repostId", "replyId", SENSITIVE, AP_ID)
|
||||
VALUES (1236, 3, null, 'test post', 12345680, 2, 'https://example.com/users/test-user3/posts/1236', null, null, false,
|
||||
'https://example.com/users/test-user3/posts/1236')
|
|
@ -0,0 +1,13 @@
|
|||
insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY,
|
||||
CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS)
|
||||
VALUES (1, 'test-user', 'example.com', 'Im test user.', 'THis account is test user.',
|
||||
'5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8', 'https://example.com/users/test-user/inbox',
|
||||
'https://example.com/users/test-user/outbox', 'https://example.com/users/test-user',
|
||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||
'https://example.com/users/test-user#pubkey', 'https://example.com/users/test-user/following',
|
||||
'https://example.com/users/test-users/followers');
|
||||
|
||||
insert into POSTS (ID, "userId", OVERVIEW, TEXT, "createdAt", VISIBILITY, URL, "repostId", "replyId", SENSITIVE, AP_ID)
|
||||
VALUES (1234, 1, null, 'test post', 12345680, 0, 'https://example.com/users/test-user/posts/1234', null, null, false,
|
||||
'https://example.com/users/test-user/posts/1234')
|
|
@ -0,0 +1,14 @@
|
|||
insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY,
|
||||
CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS)
|
||||
VALUES (2, 'test-user2', 'example.com', 'Im test user2.', 'THis account is test user2.',
|
||||
'5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8',
|
||||
'https://example.com/users/test-user2/inbox',
|
||||
'https://example.com/users/test-user2/outbox', 'https://example.com/users/test-user2',
|
||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||
'https://example.com/users/test-user2#pubkey', 'https://example.com/users/test-user2/following',
|
||||
'https://example.com/users/test-user2/followers');
|
||||
|
||||
insert into POSTS (ID, "userId", OVERVIEW, TEXT, "createdAt", VISIBILITY, URL, "repostId", "replyId", SENSITIVE, AP_ID)
|
||||
VALUES (1235, 2, null, 'test post', 12345680, 1, 'https://example.com/users/test-user2/posts/1235', null, null, false,
|
||||
'https://example.com/users/test-user2/posts/1235')
|
|
@ -0,0 +1,9 @@
|
|||
insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY,
|
||||
CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS)
|
||||
VALUES (1, 'test-user', 'example.com', 'Im test user.', 'THis account is test user.',
|
||||
'5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8', 'https://example.com/users/test-user/inbox',
|
||||
'https://example.com/users/test-user/outbox', 'https://example.com/users/test-user',
|
||||
'-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
|
||||
'-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678,
|
||||
'https://example.com/users/test-user#pubkey', 'https://example.com/users/test-user/following',
|
||||
'https://example.com/users/test-users/followers');
|
|
@ -48,7 +48,6 @@ open class JsonLd {
|
|||
|
||||
class ContextDeserializer : JsonDeserializer<String>() {
|
||||
|
||||
|
||||
override fun deserialize(
|
||||
p0: com.fasterxml.jackson.core.JsonParser?,
|
||||
p1: com.fasterxml.jackson.databind.DeserializationContext?
|
||||
|
@ -72,7 +71,6 @@ class ContextSerializer : JsonSerializer<List<String>>() {
|
|||
}
|
||||
|
||||
override fun serialize(value: List<String>?, gen: JsonGenerator?, serializers: SerializerProvider) {
|
||||
|
||||
if (value.isNullOrEmpty()) {
|
||||
serializers.defaultSerializeNull(gen)
|
||||
return
|
||||
|
|
|
@ -24,7 +24,6 @@ open class Object : JsonLd {
|
|||
this.id = id
|
||||
}
|
||||
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is Object) return false
|
||||
|
|
|
@ -14,6 +14,7 @@ import dev.usbharu.hideout.util.singleOr
|
|||
import org.jetbrains.exposed.sql.Query
|
||||
import org.jetbrains.exposed.sql.ResultRow
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Repository
|
||||
import java.time.Instant
|
||||
|
||||
|
@ -47,7 +48,12 @@ class NoteQueryServiceImpl(private val postRepository: PostRepository, private v
|
|||
private suspend fun ResultRow.toNote(mediaList: List<dev.usbharu.hideout.core.domain.model.media.Media>): Note {
|
||||
val replyId = this[Posts.replyId]
|
||||
val replyTo = if (replyId != null) {
|
||||
postRepository.findById(replyId).url
|
||||
try {
|
||||
postRepository.findById(replyId).url
|
||||
} catch (e: FailedToGetResourcesException) {
|
||||
logger.warn("Failed to get replyId: $replyId", e)
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
@ -86,4 +92,8 @@ class NoteQueryServiceImpl(private val postRepository: PostRepository, private v
|
|||
Visibility.DIRECT -> TODO()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(NoteQueryServiceImpl::class.java)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,10 @@ package dev.usbharu.hideout.activitypub.service.objects.note
|
|||
import dev.usbharu.hideout.activitypub.domain.model.Note
|
||||
import dev.usbharu.hideout.activitypub.query.NoteQueryService
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
|
||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||
import dev.usbharu.hideout.core.query.FollowerQueryService
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
|
@ -14,7 +16,12 @@ class NoteApApiServiceImpl(
|
|||
private val transaction: Transaction
|
||||
) : NoteApApiService {
|
||||
override suspend fun getNote(postId: Long, userId: Long?): Note? = transaction.transaction {
|
||||
val findById = noteQueryService.findById(postId)
|
||||
val findById = try {
|
||||
noteQueryService.findById(postId)
|
||||
} catch (e: FailedToGetResourcesException) {
|
||||
logger.warn("Note not found.", e)
|
||||
return@transaction null
|
||||
}
|
||||
when (findById.second.visibility) {
|
||||
Visibility.PUBLIC, Visibility.UNLISTED -> {
|
||||
return@transaction findById.first
|
||||
|
@ -34,4 +41,8 @@ class NoteApApiServiceImpl(
|
|||
Visibility.DIRECT -> return@transaction null
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(NoteApApiServiceImpl::class.java)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ import java.security.interfaces.RSAPrivateKey
|
|||
import java.security.interfaces.RSAPublicKey
|
||||
import java.util.*
|
||||
|
||||
@EnableWebSecurity(debug = false)
|
||||
@EnableWebSecurity(debug = true)
|
||||
@Configuration
|
||||
@Suppress("FunctionMaxLength", "TooManyFunctions")
|
||||
class SecurityConfig {
|
||||
|
@ -82,6 +82,7 @@ class SecurityConfig {
|
|||
HttpSignatureFilter::class.java
|
||||
)
|
||||
.authorizeHttpRequests {
|
||||
it.requestMatchers("/inbox", "/outbox", "/users/*/inbox", "/users/*/outbox").authenticated()
|
||||
it.anyRequest().permitAll()
|
||||
}
|
||||
.csrf {
|
||||
|
@ -110,7 +111,6 @@ class SecurityConfig {
|
|||
AuthenticationEntryPointFailureHandler(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
|
||||
authenticationEntryPointFailureHandler.setRethrowAuthenticationServiceException(false)
|
||||
httpSignatureFilter.setAuthenticationFailureHandler(authenticationEntryPointFailureHandler)
|
||||
|
||||
return httpSignatureFilter
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import dev.usbharu.hideout.application.service.id.IdGenerateService
|
|||
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
|
||||
import dev.usbharu.hideout.core.domain.model.post.Post
|
||||
import dev.usbharu.hideout.core.domain.model.post.PostRepository
|
||||
import dev.usbharu.hideout.util.singleOr
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.springframework.stereotype.Repository
|
||||
|
@ -67,11 +68,11 @@ class PostRepositoryImpl(
|
|||
}
|
||||
|
||||
override suspend fun findById(id: Long): Post =
|
||||
Posts.innerJoin(PostsMedia, onColumn = { Posts.id }, otherColumn = { postId })
|
||||
Posts.leftJoin(PostsMedia)
|
||||
.select { Posts.id eq id }
|
||||
.let(postQueryMapper::map)
|
||||
.singleOrNull()
|
||||
?: throw FailedToGetResourcesException("id: $id was not found.")
|
||||
.singleOr { FailedToGetResourcesException("id: $id was not found.", it) }
|
||||
|
||||
|
||||
override suspend fun delete(id: Long) {
|
||||
Posts.deleteWhere { Posts.id eq id }
|
||||
|
|
|
@ -16,17 +16,10 @@ import org.mockito.Mock
|
|||
import org.mockito.junit.jupiter.MockitoExtension
|
||||
import org.mockito.kotlin.*
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.anonymous
|
||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication
|
||||
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
|
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import org.springframework.test.web.servlet.get
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders
|
||||
import org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder
|
||||
import java.net.URL
|
||||
|
||||
@ExtendWith(MockitoExtension::class)
|
||||
|
@ -44,21 +37,21 @@ class NoteApControllerImplTest {
|
|||
fun setUp() {
|
||||
|
||||
mockMvc = MockMvcBuilders.standaloneSetup(noteApControllerImpl)
|
||||
.apply<StandaloneMockMvcBuilder>(
|
||||
springSecurity(
|
||||
FilterChainProxy(
|
||||
DefaultSecurityFilterChain(
|
||||
AnyRequestMatcher.INSTANCE
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
// .apply<StandaloneMockMvcBuilder>(
|
||||
// springSecurity(
|
||||
// FilterChainProxy(
|
||||
// DefaultSecurityFilterChain(
|
||||
// AnyRequestMatcher.INSTANCE
|
||||
// )
|
||||
// )
|
||||
// )
|
||||
// )
|
||||
.build()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `postAP 匿名で取得できる`() = runTest {
|
||||
|
||||
SecurityContextHolder.clearContext()
|
||||
val note = Note(
|
||||
name = "Note",
|
||||
id = "https://example.com/users/hoge/posts/1234",
|
||||
|
@ -74,7 +67,7 @@ class NoteApControllerImplTest {
|
|||
|
||||
mockMvc
|
||||
.get("/users/hoge/posts/1234") {
|
||||
with(anonymous())
|
||||
// with(anonymous())
|
||||
}
|
||||
.asyncDispatch()
|
||||
.andExpect { status { isOk() } }
|
||||
|
@ -83,11 +76,12 @@ class NoteApControllerImplTest {
|
|||
|
||||
@Test
|
||||
fun `postAP 存在しない場合は404`() = runTest {
|
||||
SecurityContextHolder.clearContext()
|
||||
whenever(noteApApiService.getNote(eq(123), isNull())).doReturn(null)
|
||||
|
||||
mockMvc
|
||||
.get("/users/hoge/posts/123") {
|
||||
with(anonymous())
|
||||
// with(anonymous())
|
||||
}
|
||||
.asyncDispatch()
|
||||
.andExpect { status { isNotFound() } }
|
||||
|
@ -117,11 +111,11 @@ class NoteApControllerImplTest {
|
|||
SecurityContextHolder.getContext().authentication = preAuthenticatedAuthenticationToken
|
||||
|
||||
mockMvc.get("/users/hoge/posts/1234") {
|
||||
with(
|
||||
authentication(
|
||||
preAuthenticatedAuthenticationToken
|
||||
)
|
||||
)
|
||||
// with(
|
||||
// authentication(
|
||||
// preAuthenticatedAuthenticationToken
|
||||
// )
|
||||
// )
|
||||
}.asyncDispatch()
|
||||
.andExpect { status { isOk() } }
|
||||
.andExpect { content { json(objectMapper.writeValueAsString(note)) } }
|
||||
|
|
Loading…
Reference in New Issue