mirror of https://github.com/usbharu/Hideout.git
Merge pull request #299 from usbharu/feature/register-account
アカウント作成の改善
This commit is contained in:
commit
b225e48cb8
|
@ -12,6 +12,7 @@ hideout:
|
||||||
debug:
|
debug:
|
||||||
trace-query-exception: true
|
trace-query-exception: true
|
||||||
trace-query-call: true
|
trace-query-call: true
|
||||||
|
private: false
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
flyway:
|
flyway:
|
||||||
|
|
|
@ -113,6 +113,7 @@ class AccountApiTest {
|
||||||
param("email", "test@example.com")
|
param("email", "test@example.com")
|
||||||
param("agreement", "true")
|
param("agreement", "true")
|
||||||
param("locale", "")
|
param("locale", "")
|
||||||
|
with(jwt())
|
||||||
with(csrf())
|
with(csrf())
|
||||||
}
|
}
|
||||||
.asyncDispatch()
|
.asyncDispatch()
|
||||||
|
@ -129,6 +130,7 @@ class AccountApiTest {
|
||||||
contentType = MediaType.APPLICATION_FORM_URLENCODED
|
contentType = MediaType.APPLICATION_FORM_URLENCODED
|
||||||
param("username", "api-test-user-2")
|
param("username", "api-test-user-2")
|
||||||
param("password", "very-secure-password")
|
param("password", "very-secure-password")
|
||||||
|
with(jwt())
|
||||||
with(csrf())
|
with(csrf())
|
||||||
}
|
}
|
||||||
.asyncDispatch()
|
.asyncDispatch()
|
||||||
|
@ -145,6 +147,7 @@ class AccountApiTest {
|
||||||
contentType = MediaType.APPLICATION_FORM_URLENCODED
|
contentType = MediaType.APPLICATION_FORM_URLENCODED
|
||||||
param("password", "api-test-user-3")
|
param("password", "api-test-user-3")
|
||||||
with(csrf())
|
with(csrf())
|
||||||
|
with(jwt())
|
||||||
}
|
}
|
||||||
.andDo { print() }
|
.andDo { print() }
|
||||||
.andExpect { status { isUnprocessableEntity() } }
|
.andExpect { status { isUnprocessableEntity() } }
|
||||||
|
@ -158,6 +161,7 @@ class AccountApiTest {
|
||||||
contentType = MediaType.APPLICATION_FORM_URLENCODED
|
contentType = MediaType.APPLICATION_FORM_URLENCODED
|
||||||
param("username", "api-test-user-4")
|
param("username", "api-test-user-4")
|
||||||
with(csrf())
|
with(csrf())
|
||||||
|
with(jwt())
|
||||||
}
|
}
|
||||||
.andExpect { status { isUnprocessableEntity() } }
|
.andExpect { status { isUnprocessableEntity() } }
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ hideout:
|
||||||
public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB"
|
public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB"
|
||||||
storage:
|
storage:
|
||||||
type: local
|
type: local
|
||||||
|
private: false
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
flyway:
|
flyway:
|
||||||
|
|
|
@ -43,9 +43,9 @@ class NoteQueryServiceImpl(private val postRepository: PostRepository, private v
|
||||||
.selectAll().where { Posts.id eq id }
|
.selectAll().where { Posts.id eq id }
|
||||||
.let {
|
.let {
|
||||||
(it.toNote() ?: return null) to (
|
(it.toNote() ?: return null) to (
|
||||||
postQueryMapper.map(it)
|
postQueryMapper.map(it)
|
||||||
.singleOrNull() ?: return null
|
.singleOrNull() ?: return null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,9 +57,9 @@ class NoteQueryServiceImpl(private val postRepository: PostRepository, private v
|
||||||
.selectAll().where { Posts.apId eq apId }
|
.selectAll().where { Posts.apId eq apId }
|
||||||
.let {
|
.let {
|
||||||
(it.toNote() ?: return null) to (
|
(it.toNote() ?: return null) to (
|
||||||
postQueryMapper.map(it)
|
postQueryMapper.map(it)
|
||||||
.singleOrNull() ?: return null
|
.singleOrNull() ?: return null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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?
|
||||||
|
)
|
|
@ -218,7 +218,7 @@ class SecurityConfig {
|
||||||
authorize(GET, "/users/*", permitAll)
|
authorize(GET, "/users/*", permitAll)
|
||||||
authorize(GET, "/users/*/posts/*", 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, "/files/*", permitAll)
|
||||||
authorize(GET, "/users/*/icon.jpg", permitAll)
|
authorize(GET, "/users/*/icon.jpg", permitAll)
|
||||||
authorize(GET, "/users/*/header.jpg", permitAll)
|
authorize(GET, "/users/*/header.jpg", permitAll)
|
||||||
|
|
|
@ -44,7 +44,8 @@ class SpringConfig {
|
||||||
|
|
||||||
@ConfigurationProperties("hideout")
|
@ConfigurationProperties("hideout")
|
||||||
data class ApplicationConfig(
|
data class ApplicationConfig(
|
||||||
val url: URL
|
val url: URL,
|
||||||
|
val private:Boolean = true
|
||||||
)
|
)
|
||||||
|
|
||||||
@ConfigurationProperties("hideout.storage.s3")
|
@ConfigurationProperties("hideout.storage.s3")
|
||||||
|
|
|
@ -16,12 +16,40 @@
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.interfaces.api.auth
|
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.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.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
class AuthController {
|
class AuthController(
|
||||||
|
private val authApiService: AuthApiService,
|
||||||
|
private val captchaConfig: CaptchaConfig,
|
||||||
|
private val applicationConfig: ApplicationConfig
|
||||||
|
) {
|
||||||
@GetMapping("/auth/sign_up")
|
@GetMapping("/auth/sign_up")
|
||||||
@Suppress("FunctionOnlyReturningConstant")
|
fun signUp(model: Model): String {
|
||||||
fun signUp(): String = "sign_up"
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package dev.usbharu.hideout.core.interfaces.api.auth
|
||||||
|
|
||||||
|
data class SignUpForm(
|
||||||
|
val username: String,
|
||||||
|
val password: String,
|
||||||
|
val recaptchaResponse: String
|
||||||
|
)
|
|
@ -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
|
||||||
|
}
|
|
@ -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<RecaptchaResult>(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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
)
|
|
@ -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
|
||||||
|
)
|
|
@ -59,6 +59,10 @@ class UserServiceImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun createLocalUser(user: UserCreateDto): Actor {
|
override suspend fun createLocalUser(user: UserCreateDto): Actor {
|
||||||
|
if (applicationConfig.private) {
|
||||||
|
throw IllegalStateException("Instance is a private mode.")
|
||||||
|
}
|
||||||
|
|
||||||
val nextId = actorRepository.nextId()
|
val nextId = actorRepository.nextId()
|
||||||
val hashedPassword = userAuthService.hash(user.password)
|
val hashedPassword = userAuthService.hash(user.password)
|
||||||
val keyPair = userAuthService.generateKeyPair()
|
val keyPair = userAuthService.generateKeyPair()
|
||||||
|
|
|
@ -41,7 +41,7 @@ class MastodonApiSecurityConfig {
|
||||||
authorizeHttpRequests {
|
authorizeHttpRequests {
|
||||||
authorize(POST, "/api/v1/apps", permitAll)
|
authorize(POST, "/api/v1/apps", permitAll)
|
||||||
authorize(GET, "/api/v1/instance/**", 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/verify_credentials", rf.hasScope("read:accounts"))
|
||||||
authorize(GET, "/api/v1/accounts/relationships", rf.hasScope("read:follows"))
|
authorize(GET, "/api/v1/accounts/relationships", rf.hasScope("read:follows"))
|
||||||
|
|
|
@ -10,6 +10,7 @@ hideout:
|
||||||
key-id: a
|
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=="
|
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"
|
public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB"
|
||||||
|
private: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,23 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>SignUp</title>
|
<title>SignUp</title>
|
||||||
|
<script th:src="https://www.google.com/recaptcha/api.js?render=${siteKey}"></script>
|
||||||
|
<script th:inline="javascript">
|
||||||
|
grecaptcha.ready(function () {
|
||||||
|
grecaptcha.execute( /*[[${siteKey}]]*/ '', {action: 'homepage'}).then(function (token) {
|
||||||
|
var recaptchaResponse = document.getElementById('recaptchaResponse');
|
||||||
|
recaptchaResponse.value = token;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<form method='post' th:action="@{/api/v1/accounts}">
|
<form method='post' th:action="@{/dev/usbharu/hideout/core/service/auth/sign_up}"
|
||||||
|
th:disabled="${applicationConfig.private}">
|
||||||
<input name='username' type='text' value=''>
|
<input name='username' type='text' value=''>
|
||||||
<input name='password' type='password'>
|
<input name='password' type='password'>
|
||||||
|
<input type="hidden" name="recaptchaResponse" id="recaptchaResponse">
|
||||||
<input type="submit">
|
<input type="submit">
|
||||||
</form>
|
</form>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -28,6 +28,7 @@ import jakarta.validation.Validation
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.assertThrows
|
||||||
import org.mockito.ArgumentMatchers.anyString
|
import org.mockito.ArgumentMatchers.anyString
|
||||||
import org.mockito.kotlin.*
|
import org.mockito.kotlin.*
|
||||||
import utils.TestApplicationConfig.testApplicationConfig
|
import utils.TestApplicationConfig.testApplicationConfig
|
||||||
|
@ -60,7 +61,7 @@ class ActorServiceTest {
|
||||||
actorRepository = actorRepository,
|
actorRepository = actorRepository,
|
||||||
userAuthService = userAuthService,
|
userAuthService = userAuthService,
|
||||||
actorBuilder = actorBuilder,
|
actorBuilder = actorBuilder,
|
||||||
applicationConfig = testApplicationConfig,
|
applicationConfig = testApplicationConfig.copy(private = false),
|
||||||
instanceService = mock(),
|
instanceService = mock(),
|
||||||
userDetailRepository = mock(),
|
userDetailRepository = mock(),
|
||||||
deletedActorRepository = mock(),
|
deletedActorRepository = mock(),
|
||||||
|
@ -87,6 +88,39 @@ class ActorServiceTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `createLocalUser applicationconfig privateがtrueのときアカウントを作成できない`() = runTest {
|
||||||
|
|
||||||
|
val actorRepository = mock<ActorRepository> {
|
||||||
|
onBlocking { nextId() } doReturn 110001L
|
||||||
|
}
|
||||||
|
val generateKeyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair()
|
||||||
|
val userAuthService = mock<UserAuthService> {
|
||||||
|
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<IllegalStateException> {
|
||||||
|
userService.createLocalUser(UserCreateDto("test", "testUser", "XXXXXXXXXXXXX", "test"))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `createRemoteUser リモートユーザーを作成できる`() = runTest {
|
fun `createRemoteUser リモートユーザーを作成できる`() = runTest {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue