test: Inboxのセキュリティテストを追加

This commit is contained in:
usbharu 2023-11-11 14:20:26 +09:00
parent 3a0d91de79
commit ee0ad59d40
8 changed files with 161 additions and 6 deletions

View File

@ -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 {

View File

@ -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<DefaultMockMvcBuilder>(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
}
}

View File

@ -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
)

View File

@ -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<WithHttpSignature> {
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
}
}

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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
}