diff --git a/hideout-core/src/e2eTest/resources/application.yml b/hideout-core/src/e2eTest/resources/application.yml index 73e011d0..778035ba 100644 --- a/hideout-core/src/e2eTest/resources/application.yml +++ b/hideout-core/src/e2eTest/resources/application.yml @@ -12,6 +12,7 @@ hideout: debug: trace-query-exception: true trace-query-call: true + private: false spring: flyway: diff --git a/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiTest.kt b/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiTest.kt index c936764d..6b482f85 100644 --- a/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiTest.kt +++ b/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiTest.kt @@ -113,6 +113,7 @@ class AccountApiTest { param("email", "test@example.com") param("agreement", "true") param("locale", "") + with(jwt()) with(csrf()) } .asyncDispatch() @@ -129,6 +130,7 @@ class AccountApiTest { contentType = MediaType.APPLICATION_FORM_URLENCODED param("username", "api-test-user-2") param("password", "very-secure-password") + with(jwt()) with(csrf()) } .asyncDispatch() @@ -145,6 +147,7 @@ class AccountApiTest { contentType = MediaType.APPLICATION_FORM_URLENCODED param("password", "api-test-user-3") with(csrf()) + with(jwt()) } .andDo { print() } .andExpect { status { isUnprocessableEntity() } } @@ -158,6 +161,7 @@ class AccountApiTest { contentType = MediaType.APPLICATION_FORM_URLENCODED param("username", "api-test-user-4") with(csrf()) + with(jwt()) } .andExpect { status { isUnprocessableEntity() } } } diff --git a/hideout-core/src/intTest/resources/application.yml b/hideout-core/src/intTest/resources/application.yml index 6f788e6d..57ab70fa 100644 --- a/hideout-core/src/intTest/resources/application.yml +++ b/hideout-core/src/intTest/resources/application.yml @@ -12,6 +12,7 @@ hideout: public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB" storage: type: local + private: false spring: flyway: diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt index b69475c7..85ee76ae 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt @@ -43,9 +43,9 @@ class NoteQueryServiceImpl(private val postRepository: PostRepository, private v .selectAll().where { Posts.id eq id } .let { (it.toNote() ?: return null) to ( - postQueryMapper.map(it) - .singleOrNull() ?: return null - ) + postQueryMapper.map(it) + .singleOrNull() ?: return null + ) } } @@ -57,9 +57,9 @@ class NoteQueryServiceImpl(private val postRepository: PostRepository, private v .selectAll().where { Posts.apId eq apId } .let { (it.toNote() ?: return null) to ( - postQueryMapper.map(it) - .singleOrNull() ?: return null - ) + postQueryMapper.map(it) + .singleOrNull() ?: return null + ) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/CaptchaConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/CaptchaConfig.kt new file mode 100644 index 00000000..ac8237f6 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/CaptchaConfig.kt @@ -0,0 +1,9 @@ +package dev.usbharu.hideout.application.config + +import org.springframework.boot.context.properties.ConfigurationProperties + +@ConfigurationProperties("hideout.security") +data class CaptchaConfig( + val reCaptchaSiteKey: String?, + val reCaptchaSecretKey: String? +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index 889133e7..22037168 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -218,7 +218,7 @@ class SecurityConfig { authorize(GET, "/users/*", permitAll) authorize(GET, "/users/*/posts/*", permitAll) - authorize("/auth/sign_up", hasRole("ANONYMOUS")) + authorize("/dev/usbharu/hideout/core/service/auth/sign_up", hasRole("ANONYMOUS")) authorize(GET, "/files/*", permitAll) authorize(GET, "/users/*/icon.jpg", permitAll) authorize(GET, "/users/*/header.jpg", permitAll) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt index 38c78e30..b2178286 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt @@ -44,7 +44,8 @@ class SpringConfig { @ConfigurationProperties("hideout") data class ApplicationConfig( - val url: URL + val url: URL, + val private:Boolean = true ) @ConfigurationProperties("hideout.storage.s3") diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt index 7b582c77..bbda1b84 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt @@ -16,12 +16,40 @@ package dev.usbharu.hideout.core.interfaces.api.auth +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.application.config.CaptchaConfig +import dev.usbharu.hideout.core.service.auth.AuthApiService +import dev.usbharu.hideout.core.service.auth.RegisterAccountDto import org.springframework.stereotype.Controller +import org.springframework.ui.Model +import org.springframework.validation.annotation.Validated import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.ModelAttribute +import org.springframework.web.bind.annotation.PostMapping @Controller -class AuthController { +class AuthController( + private val authApiService: AuthApiService, + private val captchaConfig: CaptchaConfig, + private val applicationConfig: ApplicationConfig +) { @GetMapping("/auth/sign_up") - @Suppress("FunctionOnlyReturningConstant") - fun signUp(): String = "sign_up" + fun signUp(model: Model): String { + model.addAttribute("siteKey", captchaConfig.reCaptchaSiteKey) + model.addAttribute("applicationConfig", applicationConfig) + return "sign_up" + } + + @PostMapping("/auth/sign_up") + suspend fun signUp(@Validated @ModelAttribute signUpForm: SignUpForm, model: Model): String { + val registerAccount = authApiService.registerAccount( + RegisterAccountDto( + signUpForm.username, + signUpForm.password, + signUpForm.recaptchaResponse + ) + ) + + return "redirect:" + registerAccount.url + } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/SignUpForm.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/SignUpForm.kt new file mode 100644 index 00000000..5c032c5a --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/SignUpForm.kt @@ -0,0 +1,7 @@ +package dev.usbharu.hideout.core.interfaces.api.auth + +data class SignUpForm( + val username: String, + val password: String, + val recaptchaResponse: String +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiService.kt new file mode 100644 index 00000000..e4ca5a32 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiService.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.service.auth + +import dev.usbharu.hideout.core.domain.model.actor.Actor + +interface AuthApiService { + suspend fun registerAccount(registerAccountDto: RegisterAccountDto): Actor +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiServiceImpl.kt new file mode 100644 index 00000000..bfdd81d1 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiServiceImpl.kt @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.service.auth + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import dev.usbharu.hideout.application.config.CaptchaConfig +import dev.usbharu.hideout.core.domain.model.actor.Actor +import dev.usbharu.hideout.core.service.user.UserCreateDto +import dev.usbharu.hideout.core.service.user.UserService +import io.ktor.client.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class AuthApiServiceImpl( + private val httpClient: HttpClient, + private val captchaConfig: CaptchaConfig, + private val objectMapper: ObjectMapper, + private val userService: UserService +) : + AuthApiService { + override suspend fun registerAccount(registerAccountDto: RegisterAccountDto): Actor { + if (captchaConfig.reCaptchaSecretKey != null && captchaConfig.reCaptchaSiteKey != null) { + val get = + httpClient.get("https://www.google.com/recaptcha/api/siteverify?secret=" + captchaConfig.reCaptchaSecretKey + "&response=" + registerAccountDto.recaptchaResponse) + val recaptchaResult = objectMapper.readValue(get.bodyAsText()) + logger.debug("reCAPTCHA: {}", recaptchaResult) + require(recaptchaResult.success) + require(!(recaptchaResult.score < 0.5)) + } + + + val createLocalUser = userService.createLocalUser( + UserCreateDto( + registerAccountDto.username, + registerAccountDto.username, + "", + registerAccountDto.password + ) + ) + + return createLocalUser + } + + companion object { + private val logger = LoggerFactory.getLogger(AuthApiServiceImpl::class.java) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/RecaptchaResult.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/RecaptchaResult.kt new file mode 100644 index 00000000..956fbcd9 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/RecaptchaResult.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.service.auth + +data class RecaptchaResult( + val success: Boolean, + val challenge_ts: String, + val hostname: String, + val score: Float, + val action: String +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/RegisterAccountDto.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/RegisterAccountDto.kt new file mode 100644 index 00000000..fec3ced4 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/RegisterAccountDto.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.service.auth + +data class RegisterAccountDto( + val username:String, + val password:String, + val recaptchaResponse:String +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt index 294656ca..2149f343 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt @@ -59,6 +59,10 @@ class UserServiceImpl( } override suspend fun createLocalUser(user: UserCreateDto): Actor { + if (applicationConfig.private) { + throw IllegalStateException("Instance is a private mode.") + } + val nextId = actorRepository.nextId() val hashedPassword = userAuthService.hash(user.password) val keyPair = userAuthService.generateKeyPair() diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/config/MastodonApiSecurityConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/config/MastodonApiSecurityConfig.kt index 39ec70fb..c4bce048 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/config/MastodonApiSecurityConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/config/MastodonApiSecurityConfig.kt @@ -41,7 +41,7 @@ class MastodonApiSecurityConfig { authorizeHttpRequests { authorize(POST, "/api/v1/apps", permitAll) authorize(GET, "/api/v1/instance/**", permitAll) - authorize(POST, "/api/v1/accounts", permitAll) + authorize(POST, "/api/v1/accounts", authenticated) authorize(GET, "/api/v1/accounts/verify_credentials", rf.hasScope("read:accounts")) authorize(GET, "/api/v1/accounts/relationships", rf.hasScope("read:follows")) diff --git a/hideout-core/src/main/resources/application.yml b/hideout-core/src/main/resources/application.yml index ae863531..52bc6eba 100644 --- a/hideout-core/src/main/resources/application.yml +++ b/hideout-core/src/main/resources/application.yml @@ -10,6 +10,7 @@ hideout: 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" + private: true diff --git a/hideout-core/src/main/resources/templates/sign_up.html b/hideout-core/src/main/resources/templates/sign_up.html index 079fdd7c..3408bb8a 100644 --- a/hideout-core/src/main/resources/templates/sign_up.html +++ b/hideout-core/src/main/resources/templates/sign_up.html @@ -3,12 +3,23 @@ SignUp + + -
+ +
diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt index 846c0d9e..7a22a57b 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt @@ -28,6 +28,7 @@ import jakarta.validation.Validation import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows import org.mockito.ArgumentMatchers.anyString import org.mockito.kotlin.* import utils.TestApplicationConfig.testApplicationConfig @@ -60,7 +61,7 @@ class ActorServiceTest { actorRepository = actorRepository, userAuthService = userAuthService, actorBuilder = actorBuilder, - applicationConfig = testApplicationConfig, + applicationConfig = testApplicationConfig.copy(private = false), instanceService = mock(), userDetailRepository = mock(), deletedActorRepository = mock(), @@ -87,6 +88,39 @@ class ActorServiceTest { } } + @Test + fun `createLocalUser applicationconfig privateがtrueのときアカウントを作成できない`() = runTest { + + val actorRepository = mock { + onBlocking { nextId() } doReturn 110001L + } + val generateKeyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair() + val userAuthService = mock { + onBlocking { hash(anyString()) } doReturn "hashedPassword" + onBlocking { generateKeyPair() } doReturn generateKeyPair + } + val userService = + UserServiceImpl( + actorRepository = actorRepository, + userAuthService = userAuthService, + actorBuilder = actorBuilder, + applicationConfig = testApplicationConfig.copy(private = true), + instanceService = mock(), + userDetailRepository = mock(), + deletedActorRepository = mock(), + reactionRepository = mock(), + relationshipRepository = mock(), + postService = mock(), + apSendDeleteService = mock(), + postRepository = mock() + ) + + assertThrows { + userService.createLocalUser(UserCreateDto("test", "testUser", "XXXXXXXXXXXXX", "test")) + } + + } + @Test fun `createRemoteUser リモートユーザーを作成できる`() = runTest {