From 5d0d680d3c7cf10d51d2a903f5a2a8f56832baf4 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 11 Nov 2023 14:20:26 +0900 Subject: [PATCH] =?UTF-8?q?test:=20Inbox=E3=81=AE=E3=82=BB=E3=82=AD?= =?UTF-8?q?=E3=83=A5=E3=83=AA=E3=83=86=E3=82=A3=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 2 + .../kotlin/activitypub/inbox/InboxTest.kt | 91 +++++++++++++++++++ src/intTest/kotlin/util/WithHttpSignature.kt | 17 ++++ ...WithHttpSignatureSecurityContextFactory.kt | 39 ++++++++ src/intTest/resources/logback.xml | 10 ++ .../activitypub/domain/model/JsonLd.kt | 2 - .../domain/model/objects/Object.kt | 1 - .../application/config/SecurityConfig.kt | 5 +- 8 files changed, 161 insertions(+), 6 deletions(-) create mode 100644 src/intTest/kotlin/activitypub/inbox/InboxTest.kt create mode 100644 src/intTest/kotlin/util/WithHttpSignature.kt create mode 100644 src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt create mode 100644 src/intTest/resources/logback.xml diff --git a/build.gradle.kts b/build.gradle.kts index 7b52ac31..9e63dc58 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -202,6 +202,8 @@ dependencies { 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 { diff --git a/src/intTest/kotlin/activitypub/inbox/InboxTest.kt b/src/intTest/kotlin/activitypub/inbox/InboxTest.kt new file mode 100644 index 00000000..e58aedf3 --- /dev/null +++ b/src/intTest/kotlin/activitypub/inbox/InboxTest.kt @@ -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.WithHttpSignature + +@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(springSecurity()) + .build() + } + + @Test + @WithAnonymousUser + fun `匿名でinboxにPOSTしたら401`() { + mockMvc + .post("/inbox") { + content = "{}" + contentType = MediaType.APPLICATION_JSON + } + .andExpect { status { isUnauthorized() } } + } + + @Test + @WithHttpSignature + 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 + @WithHttpSignature + 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 + } +} diff --git a/src/intTest/kotlin/util/WithHttpSignature.kt b/src/intTest/kotlin/util/WithHttpSignature.kt new file mode 100644 index 00000000..49e5ead4 --- /dev/null +++ b/src/intTest/kotlin/util/WithHttpSignature.kt @@ -0,0 +1,17 @@ +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 +) diff --git a/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt b/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt new file mode 100644 index 00000000..3ca3751c --- /dev/null +++ b/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt @@ -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 WithHttpSignatureSecurityContextFactory : WithSecurityContextFactory { + + private val securityContextStrategy = SecurityContextHolder.getContextHolderStrategy() + + override fun createSecurityContext(annotation: WithHttpSignature): SecurityContext { + val httpSignatureUser = HttpSignatureUser( + "user", + "example.com", + 12345, + true, + true, + mutableListOf() + ) + val preAuthenticatedAuthenticationToken = PreAuthenticatedAuthenticationToken( + "user", HttpRequest( + URL("https://example.com/inbox"), + HttpHeaders(mapOf()), HttpMethod.GET + ) + ) + preAuthenticatedAuthenticationToken.details = httpSignatureUser + preAuthenticatedAuthenticationToken.isAuthenticated = true + val emptyContext = securityContextStrategy.createEmptyContext() + emptyContext.authentication = preAuthenticatedAuthenticationToken + return emptyContext + } + +} diff --git a/src/intTest/resources/logback.xml b/src/intTest/resources/logback.xml new file mode 100644 index 00000000..54cfd39a --- /dev/null +++ b/src/intTest/resources/logback.xml @@ -0,0 +1,10 @@ + + + + %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{x-request-id}] %logger{36} - %msg%n + + + + + + diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLd.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLd.kt index da4e2def..19b88de3 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLd.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLd.kt @@ -48,7 +48,6 @@ open class JsonLd { class ContextDeserializer : JsonDeserializer() { - override fun deserialize( p0: com.fasterxml.jackson.core.JsonParser?, p1: com.fasterxml.jackson.databind.DeserializationContext? @@ -72,7 +71,6 @@ class ContextSerializer : JsonSerializer>() { } override fun serialize(value: List?, gen: JsonGenerator?, serializers: SerializerProvider) { - if (value.isNullOrEmpty()) { serializers.defaultSerializeNull(gen) return diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/Object.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/Object.kt index f3befd0f..62c32928 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/Object.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/Object.kt @@ -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 diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index 2ba41dba..7ca3dfbc 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -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,7 +82,7 @@ class SecurityConfig { HttpSignatureFilter::class.java ) .authorizeHttpRequests { - it.anyRequest().permitAll() + it.anyRequest().authenticated() } .csrf { it.disable() @@ -110,7 +110,6 @@ class SecurityConfig { AuthenticationEntryPointFailureHandler(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)) authenticationEntryPointFailureHandler.setRethrowAuthenticationServiceException(false) httpSignatureFilter.setAuthenticationFailureHandler(authenticationEntryPointFailureHandler) - return httpSignatureFilter }