mirror of https://github.com/usbharu/Hideout.git
feat: ローカルユーザーを作成できるように
This commit is contained in:
parent
9c271b8cc8
commit
cf48ae651b
|
@ -28,6 +28,7 @@ import dev.usbharu.hideout.core.domain.service.userdetail.UserDetailDomainServic
|
||||||
import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService
|
import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService
|
||||||
import dev.usbharu.hideout.core.infrastructure.factory.ActorFactoryImpl
|
import dev.usbharu.hideout.core.infrastructure.factory.ActorFactoryImpl
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
|
import java.net.URI
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class RegisterLocalActorApplicationService(
|
class RegisterLocalActorApplicationService(
|
||||||
|
@ -41,8 +42,8 @@ class RegisterLocalActorApplicationService(
|
||||||
private val userDetailRepository: UserDetailRepository,
|
private val userDetailRepository: UserDetailRepository,
|
||||||
private val idGenerateService: IdGenerateService,
|
private val idGenerateService: IdGenerateService,
|
||||||
) {
|
) {
|
||||||
suspend fun register(registerLocalActor: RegisterLocalActor) {
|
suspend fun register(registerLocalActor: RegisterLocalActor): URI {
|
||||||
transaction.transaction {
|
return transaction.transaction {
|
||||||
if (actorDomainService.usernameAlreadyUse(registerLocalActor.name)) {
|
if (actorDomainService.usernameAlreadyUse(registerLocalActor.name)) {
|
||||||
// todo 適切な例外を考える
|
// todo 適切な例外を考える
|
||||||
throw Exception("Username already exists")
|
throw Exception("Username already exists")
|
||||||
|
@ -61,6 +62,7 @@ class RegisterLocalActorApplicationService(
|
||||||
password = userDetailDomainService.hashPassword(registerLocalActor.password),
|
password = userDetailDomainService.hashPassword(registerLocalActor.password),
|
||||||
)
|
)
|
||||||
userDetailRepository.save(userDetail)
|
userDetailRepository.save(userDetail)
|
||||||
|
actor.url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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.application.application
|
||||||
|
|
||||||
|
import java.net.URI
|
||||||
|
|
||||||
|
data class RegisterApplication(
|
||||||
|
val name: String,
|
||||||
|
val redirectUris: Set<URI>,
|
||||||
|
val useRefreshToken: Boolean,
|
||||||
|
val scopes: Set<String>,
|
||||||
|
)
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* 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.application.application
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
|
import dev.usbharu.hideout.core.domain.model.application.Application
|
||||||
|
import dev.usbharu.hideout.core.domain.model.application.ApplicationId
|
||||||
|
import dev.usbharu.hideout.core.domain.model.application.ApplicationName
|
||||||
|
import dev.usbharu.hideout.core.domain.model.application.ApplicationRepository
|
||||||
|
import dev.usbharu.hideout.core.domain.service.userdetail.PasswordEncoder
|
||||||
|
import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService
|
||||||
|
import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.SecureTokenGenerator
|
||||||
|
import org.springframework.security.oauth2.core.AuthorizationGrantType
|
||||||
|
import org.springframework.security.oauth2.core.ClientAuthenticationMethod
|
||||||
|
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient
|
||||||
|
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository
|
||||||
|
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings
|
||||||
|
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import java.time.Duration
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class RegisterApplicationApplicationService(
|
||||||
|
private val idGenerateService: IdGenerateService,
|
||||||
|
private val passwordEncoder: PasswordEncoder,
|
||||||
|
private val secureTokenGenerator: SecureTokenGenerator,
|
||||||
|
private val registeredClientRepository: RegisteredClientRepository,
|
||||||
|
private val transaction: Transaction,
|
||||||
|
private val applicationRepository: ApplicationRepository,
|
||||||
|
) {
|
||||||
|
suspend fun register(registerApplication: RegisterApplication): RegisteredApplication {
|
||||||
|
|
||||||
|
return transaction.transaction {
|
||||||
|
|
||||||
|
val id = idGenerateService.generateId()
|
||||||
|
val clientSecret = secureTokenGenerator.generate()
|
||||||
|
val registeredClient = RegisteredClient
|
||||||
|
.withId(id.toString())
|
||||||
|
.clientId(id.toString())
|
||||||
|
.clientSecret(passwordEncoder.encode(clientSecret))
|
||||||
|
.clientName(registerApplication.name)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
|
||||||
|
.apply {
|
||||||
|
if (registerApplication.useRefreshToken) {
|
||||||
|
authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
|
||||||
|
tokenSettings(
|
||||||
|
TokenSettings
|
||||||
|
.builder()
|
||||||
|
.accessTokenTimeToLive(Duration.ofSeconds(31536000000))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.redirectUris { set ->
|
||||||
|
set.addAll(registerApplication.redirectUris.map { it.toString() })
|
||||||
|
}
|
||||||
|
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
|
||||||
|
.scopes { it.addAll(registerApplication.scopes) }
|
||||||
|
.build()
|
||||||
|
registeredClientRepository.save(registeredClient)
|
||||||
|
|
||||||
|
val application = Application(ApplicationId(id), ApplicationName(registerApplication.name))
|
||||||
|
|
||||||
|
applicationRepository.save(application)
|
||||||
|
RegisteredApplication(
|
||||||
|
id = id,
|
||||||
|
name = registerApplication.name,
|
||||||
|
clientSecret = clientSecret,
|
||||||
|
clientId = id,
|
||||||
|
redirectUris = registerApplication.redirectUris
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* 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.application.application
|
||||||
|
|
||||||
|
import java.net.URI
|
||||||
|
|
||||||
|
data class RegisteredApplication(
|
||||||
|
val id: Long,
|
||||||
|
val name: String,
|
||||||
|
val redirectUris: Set<URI>,
|
||||||
|
val clientSecret: String,
|
||||||
|
val clientId: Long,
|
||||||
|
)
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* 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.application.instance
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
|
import dev.usbharu.hideout.core.config.ApplicationConfig
|
||||||
|
import dev.usbharu.hideout.core.domain.model.instance.*
|
||||||
|
import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService
|
||||||
|
import org.springframework.boot.context.event.ApplicationReadyEvent
|
||||||
|
import org.springframework.boot.info.BuildProperties
|
||||||
|
import org.springframework.context.event.EventListener
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class InitLocalInstanceApplicationService(
|
||||||
|
private val applicationConfig: ApplicationConfig,
|
||||||
|
private val instanceRepository: InstanceRepository,
|
||||||
|
private val idGenerateService: IdGenerateService,
|
||||||
|
private val buildProperties: BuildProperties,
|
||||||
|
private val transaction: Transaction,
|
||||||
|
) {
|
||||||
|
@EventListener(ApplicationReadyEvent::class)
|
||||||
|
suspend fun init() = transaction.transaction {
|
||||||
|
val findByUrl = instanceRepository.findByUrl(applicationConfig.url.toURI())
|
||||||
|
|
||||||
|
if (findByUrl == null) {
|
||||||
|
val instance = Instance(
|
||||||
|
InstanceId(idGenerateService.generateId()),
|
||||||
|
InstanceName(applicationConfig.url.host),
|
||||||
|
InstanceDescription(""),
|
||||||
|
applicationConfig.url.toURI(),
|
||||||
|
applicationConfig.url.toURI(),
|
||||||
|
null,
|
||||||
|
InstanceSoftware("hideout"),
|
||||||
|
InstanceVersion(buildProperties.version),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
InstanceModerationNote(""),
|
||||||
|
Instant.now(),
|
||||||
|
)
|
||||||
|
instanceRepository.save(instance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,13 +18,124 @@ package dev.usbharu.hideout.core.config
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.core.annotation.Order
|
||||||
|
import org.springframework.http.HttpMethod.GET
|
||||||
|
import org.springframework.http.HttpMethod.POST
|
||||||
|
import org.springframework.jdbc.core.JdbcOperations
|
||||||
|
import org.springframework.security.access.hierarchicalroles.RoleHierarchy
|
||||||
|
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||||
|
import org.springframework.security.config.annotation.web.invoke
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder
|
import org.springframework.security.crypto.password.PasswordEncoder
|
||||||
|
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository
|
||||||
|
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository
|
||||||
|
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration
|
||||||
|
import org.springframework.security.web.SecurityFilterChain
|
||||||
|
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@EnableWebSecurity(debug = false)
|
||||||
class SecurityConfig {
|
class SecurityConfig {
|
||||||
@Bean
|
@Bean
|
||||||
fun passwordEncoder(): PasswordEncoder {
|
fun passwordEncoder(): PasswordEncoder {
|
||||||
return BCryptPasswordEncoder()
|
return BCryptPasswordEncoder()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(1)
|
||||||
|
fun oauth2Provider(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http)
|
||||||
|
http {
|
||||||
|
exceptionHandling {
|
||||||
|
authenticationEntryPoint = LoginUrlAuthenticationEntryPoint("/login")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(3)
|
||||||
|
fun httpSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
http {
|
||||||
|
authorizeHttpRequests {
|
||||||
|
authorize("/error", permitAll)
|
||||||
|
authorize("/login", permitAll)
|
||||||
|
authorize(GET, "/.well-known/**", permitAll)
|
||||||
|
authorize(GET, "/nodeinfo/2.0", permitAll)
|
||||||
|
|
||||||
|
authorize(GET, "/auth/sign_up", hasRole("ANONYMOUS"))
|
||||||
|
authorize(POST, "/auth/sign_up", permitAll)
|
||||||
|
|
||||||
|
authorize(anyRequest, authenticated)
|
||||||
|
}
|
||||||
|
formLogin {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun registeredClientRepository(jdbcOperations: JdbcOperations): RegisteredClientRepository {
|
||||||
|
return JdbcRegisteredClientRepository(jdbcOperations)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun roleHierarchy(): RoleHierarchy {
|
||||||
|
val roleHierarchyImpl = RoleHierarchyImpl.fromHierarchy(
|
||||||
|
"""
|
||||||
|
SCOPE_read > SCOPE_read:accounts
|
||||||
|
SCOPE_read > SCOPE_read:accounts
|
||||||
|
SCOPE_read > SCOPE_read:blocks
|
||||||
|
SCOPE_read > SCOPE_read:bookmarks
|
||||||
|
SCOPE_read > SCOPE_read:favourites
|
||||||
|
SCOPE_read > SCOPE_read:filters
|
||||||
|
SCOPE_read > SCOPE_read:follows
|
||||||
|
SCOPE_read > SCOPE_read:lists
|
||||||
|
SCOPE_read > SCOPE_read:mutes
|
||||||
|
SCOPE_read > SCOPE_read:notifications
|
||||||
|
SCOPE_read > SCOPE_read:search
|
||||||
|
SCOPE_read > SCOPE_read:statuses
|
||||||
|
SCOPE_write > SCOPE_write:accounts
|
||||||
|
SCOPE_write > SCOPE_write:blocks
|
||||||
|
SCOPE_write > SCOPE_write:bookmarks
|
||||||
|
SCOPE_write > SCOPE_write:conversations
|
||||||
|
SCOPE_write > SCOPE_write:favourites
|
||||||
|
SCOPE_write > SCOPE_write:filters
|
||||||
|
SCOPE_write > SCOPE_write:follows
|
||||||
|
SCOPE_write > SCOPE_write:lists
|
||||||
|
SCOPE_write > SCOPE_write:media
|
||||||
|
SCOPE_write > SCOPE_write:mutes
|
||||||
|
SCOPE_write > SCOPE_write:notifications
|
||||||
|
SCOPE_write > SCOPE_write:reports
|
||||||
|
SCOPE_write > SCOPE_write:statuses
|
||||||
|
SCOPE_follow > SCOPE_write:blocks
|
||||||
|
SCOPE_follow > SCOPE_write:follows
|
||||||
|
SCOPE_follow > SCOPE_write:mutes
|
||||||
|
SCOPE_follow > SCOPE_read:blocks
|
||||||
|
SCOPE_follow > SCOPE_read:follows
|
||||||
|
SCOPE_follow > SCOPE_read:mutes
|
||||||
|
SCOPE_admin > SCOPE_admin:read
|
||||||
|
SCOPE_admin > SCOPE_admin:write
|
||||||
|
SCOPE_admin:read > SCOPE_admin:read:accounts
|
||||||
|
SCOPE_admin:read > SCOPE_admin:read:reports
|
||||||
|
SCOPE_admin:read > SCOPE_admin:read:domain_allows
|
||||||
|
SCOPE_admin:read > SCOPE_admin:read:domain_blocks
|
||||||
|
SCOPE_admin:read > SCOPE_admin:read:ip_blocks
|
||||||
|
SCOPE_admin:read > SCOPE_admin:read:email_domain_blocks
|
||||||
|
SCOPE_admin:read > SCOPE_admin:read:canonical_email_blocks
|
||||||
|
SCOPE_admin:write > SCOPE_admin:write:accounts
|
||||||
|
SCOPE_admin:write > SCOPE_admin:write:reports
|
||||||
|
SCOPE_admin:write > SCOPE_admin:write:domain_allows
|
||||||
|
SCOPE_admin:write > SCOPE_admin:write:domain_blocks
|
||||||
|
SCOPE_admin:write > SCOPE_admin:write:ip_blocks
|
||||||
|
SCOPE_admin:write > SCOPE_admin:write:email_domain_blocks
|
||||||
|
SCOPE_admin:write > SCOPE_admin:write:canonical_email_blocks
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
return roleHierarchyImpl
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* 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.config
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.generate.JsonOrFormModelMethodProcessor
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.http.converter.HttpMessageConverter
|
||||||
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class MvcConfigurer(private val jsonOrFormModelMethodProcessor: JsonOrFormModelMethodProcessor) : WebMvcConfigurer {
|
||||||
|
override fun addArgumentResolvers(resolvers: MutableList<HandlerMethodArgumentResolver>) {
|
||||||
|
resolvers.add(jsonOrFormModelMethodProcessor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class JsonOrFormModelMethodProcessorConfig {
|
||||||
|
@Bean
|
||||||
|
fun jsonOrFormModelMethodProcessor(converter: List<HttpMessageConverter<*>>): JsonOrFormModelMethodProcessor {
|
||||||
|
return JsonOrFormModelMethodProcessor(
|
||||||
|
ServletModelAttributeMethodProcessor(true),
|
||||||
|
RequestResponseBodyMethodProcessor(
|
||||||
|
converter
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* 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.domain.model.application
|
||||||
|
|
||||||
|
class Application(
|
||||||
|
val applicationId: ApplicationId,
|
||||||
|
val name: ApplicationName,
|
||||||
|
)
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* 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.domain.model.application
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class ApplicationId(val id: Long)
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* 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.domain.model.application
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class ApplicationName(val name: String)
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* 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.domain.model.application
|
||||||
|
|
||||||
|
interface ApplicationRepository {
|
||||||
|
suspend fun save(application: Application): Application
|
||||||
|
suspend fun delete(application: Application)
|
||||||
|
}
|
|
@ -29,7 +29,7 @@ class LocalActorDomainServiceImpl(
|
||||||
private val applicationConfig: ApplicationConfig,
|
private val applicationConfig: ApplicationConfig,
|
||||||
) : LocalActorDomainService {
|
) : LocalActorDomainService {
|
||||||
override suspend fun usernameAlreadyUse(name: String): Boolean =
|
override suspend fun usernameAlreadyUse(name: String): Boolean =
|
||||||
actorRepository.findByNameAndDomain(name, applicationConfig.url.host) == null
|
actorRepository.findByNameAndDomain(name, applicationConfig.url.host) != null
|
||||||
|
|
||||||
override suspend fun generateKeyPair(): Pair<ActorPublicKey, ActorPrivateKey> {
|
override suspend fun generateKeyPair(): Pair<ActorPublicKey, ActorPrivateKey> {
|
||||||
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
||||||
|
|
|
@ -135,10 +135,10 @@ object Actors : Table("actors") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object ActorsAlsoKnownAs : Table("actor_alsoknwonas") {
|
object ActorsAlsoKnownAs : Table("actor_alsoknownas") {
|
||||||
val actorId =
|
val actorId =
|
||||||
long("actor_id").references(Actors.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE)
|
long("actor_id").references(Actors.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE)
|
||||||
val alsoKnownAs = long("alsoKnownAs").references(Actors.id, ReferenceOption.CASCADE, ReferenceOption.CASCADE)
|
val alsoKnownAs = long("also_known_as").references(Actors.id, ReferenceOption.CASCADE, ReferenceOption.CASCADE)
|
||||||
|
|
||||||
override val primaryKey: PrimaryKey = PrimaryKey(actorId, alsoKnownAs)
|
override val primaryKey: PrimaryKey = PrimaryKey(actorId, alsoKnownAs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* 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.infrastructure.exposedrepository
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.domain.model.application.Application
|
||||||
|
import dev.usbharu.hideout.core.domain.model.application.ApplicationRepository
|
||||||
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
|
import org.jetbrains.exposed.sql.Table
|
||||||
|
import org.jetbrains.exposed.sql.deleteWhere
|
||||||
|
import org.jetbrains.exposed.sql.upsert
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.stereotype.Repository
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
class ExposedApplicationRepository : ApplicationRepository, AbstractRepository() {
|
||||||
|
override suspend fun save(application: Application) = query {
|
||||||
|
Applications.upsert {
|
||||||
|
it[id] = application.applicationId.id
|
||||||
|
it[name] = application.name.name
|
||||||
|
}
|
||||||
|
application
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun delete(application: Application): Unit = query {
|
||||||
|
Applications.deleteWhere { id eq application.applicationId.id }
|
||||||
|
}
|
||||||
|
|
||||||
|
override val logger: Logger
|
||||||
|
get() = Companion.logger
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val logger = LoggerFactory.getLogger(ExposedApplicationRepository::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
object Applications : Table("applications") {
|
||||||
|
val id = long("id")
|
||||||
|
val name = varchar("name", 500)
|
||||||
|
override val primaryKey: PrimaryKey = PrimaryKey(id)
|
||||||
|
}
|
|
@ -45,7 +45,7 @@ class ActorFactoryImpl(
|
||||||
description = ActorDescription(""),
|
description = ActorDescription(""),
|
||||||
inbox = URI.create("$userUrl/inbox"),
|
inbox = URI.create("$userUrl/inbox"),
|
||||||
outbox = URI.create("$userUrl/outbox"),
|
outbox = URI.create("$userUrl/outbox"),
|
||||||
url = applicationConfig.url.toURI(),
|
url = URI.create(userUrl),
|
||||||
publicKey = keyPair.first,
|
publicKey = keyPair.first,
|
||||||
privateKey = keyPair.second,
|
privateKey = keyPair.second,
|
||||||
createdAt = Instant.now(),
|
createdAt = Instant.now(),
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* 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.infrastructure.springframework.oauth2
|
||||||
|
|
||||||
|
import org.springframework.security.core.GrantedAuthority
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails
|
||||||
|
|
||||||
|
class HideoutUserDetails(
|
||||||
|
private val authorities: MutableList<out GrantedAuthority>,
|
||||||
|
private val password: String,
|
||||||
|
private val username: String,
|
||||||
|
val userDetailsId: Long,
|
||||||
|
) : UserDetails {
|
||||||
|
override fun getAuthorities(): MutableCollection<out GrantedAuthority> {
|
||||||
|
return authorities
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPassword(): String {
|
||||||
|
return password
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getUsername(): String {
|
||||||
|
return username
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* 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.infrastructure.springframework.oauth2
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
|
import dev.usbharu.hideout.core.config.ApplicationConfig
|
||||||
|
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
|
||||||
|
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class UserDetailsServiceImpl(
|
||||||
|
private val actorRepository: ActorRepository,
|
||||||
|
private val userDetailRepository: UserDetailRepository,
|
||||||
|
private val applicationConfig: ApplicationConfig,
|
||||||
|
private val transaction: Transaction,
|
||||||
|
) : UserDetailsService {
|
||||||
|
override fun loadUserByUsername(username: String?): UserDetails = runBlocking {
|
||||||
|
if (username == null) {
|
||||||
|
throw UsernameNotFoundException("Username not found")
|
||||||
|
}
|
||||||
|
transaction.transaction {
|
||||||
|
val actor = actorRepository.findByNameAndDomain(username, applicationConfig.url.host)
|
||||||
|
?: throw UsernameNotFoundException("$username not found")
|
||||||
|
val userDetail = userDetailRepository.findByActorId(actor.id.id)
|
||||||
|
?: throw UsernameNotFoundException("${actor.id.id} not found")
|
||||||
|
HideoutUserDetails(
|
||||||
|
authorities = mutableListOf(),
|
||||||
|
password = userDetail.password.password,
|
||||||
|
actor.name.name,
|
||||||
|
userDetailsId = userDetail.id.id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,16 +16,27 @@
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.interfaces.api.auth
|
package dev.usbharu.hideout.core.interfaces.api.auth
|
||||||
|
|
||||||
import org.springframework.ui.Model
|
import dev.usbharu.hideout.core.application.actor.RegisterLocalActor
|
||||||
|
import dev.usbharu.hideout.core.application.actor.RegisterLocalActorApplicationService
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import org.springframework.stereotype.Controller
|
||||||
import org.springframework.validation.annotation.Validated
|
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.ModelAttribute
|
||||||
import org.springframework.web.bind.annotation.PostMapping
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
|
|
||||||
interface AuthController {
|
@Controller
|
||||||
|
class AuthController(private val registerLocalActorApplicationService: RegisterLocalActorApplicationService) {
|
||||||
@GetMapping("/auth/sign_up")
|
@GetMapping("/auth/sign_up")
|
||||||
fun signUp(model: Model): String
|
fun signUp(): String {
|
||||||
|
return "sign_up"
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/auth/sign_up")
|
@PostMapping("/auth/sign_up")
|
||||||
suspend fun signUp(@Validated @ModelAttribute signUpForm: SignUpForm): String
|
suspend fun signUp(@Validated @ModelAttribute signUpForm: SignUpForm, request: HttpServletRequest): String {
|
||||||
|
val registerLocalActor = RegisterLocalActor(signUpForm.username, signUpForm.password)
|
||||||
|
val uri = registerLocalActorApplicationService.register(registerLocalActor)
|
||||||
|
request.login(signUpForm.username, signUpForm.password)
|
||||||
|
return "redirect:$uri"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,5 +3,5 @@ package dev.usbharu.hideout.core.interfaces.api.auth
|
||||||
data class SignUpForm(
|
data class SignUpForm(
|
||||||
val username: String,
|
val username: String,
|
||||||
val password: String,
|
val password: String,
|
||||||
val recaptchaResponse: String
|
// val recaptchaResponse: String
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
create table if not exists filters
|
|
||||||
(
|
|
||||||
id bigint primary key not null,
|
|
||||||
user_id bigint not null,
|
|
||||||
name varchar(255) not null,
|
|
||||||
context varchar(500) not null,
|
|
||||||
action varchar(255) not null,
|
|
||||||
constraint fk_filters_user_id__id foreign key (user_id) references actors (id) on delete cascade on update cascade
|
|
||||||
);
|
|
||||||
|
|
||||||
create table if not exists filter_keywords
|
|
||||||
(
|
|
||||||
id bigint primary key not null,
|
|
||||||
filter_id bigint not null,
|
|
||||||
keyword varchar(1000) not null,
|
|
||||||
mode varchar(100) not null,
|
|
||||||
constraint fk_filter_keywords_filter_id__id foreign key (filter_id) references filters (id) on delete cascade on update cascade
|
|
||||||
);
|
|
|
@ -55,17 +55,27 @@ create table if not exists actors
|
||||||
suspend boolean not null,
|
suspend boolean not null,
|
||||||
move_to bigint null default null,
|
move_to bigint null default null,
|
||||||
emojis varchar(3000) not null default '',
|
emojis varchar(3000) not null default '',
|
||||||
|
deleted boolean not null default false,
|
||||||
unique ("name", "domain"),
|
unique ("name", "domain"),
|
||||||
constraint fk_actors_instance__id foreign key ("instance") references instance (id) on delete restrict on update restrict,
|
constraint fk_actors_instance__id foreign key ("instance") references instance (id) on delete restrict on update restrict,
|
||||||
constraint fk_actors_actors__move_to foreign key ("move_to") references actors (id) on delete restrict on update restrict
|
constraint fk_actors_actors__move_to foreign key ("move_to") references actors (id) on delete restrict on update restrict
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create table if not exists actor_alsoknownas
|
||||||
|
(
|
||||||
|
actor_id bigint not null,
|
||||||
|
also_known_as bigint not null,
|
||||||
|
constraint fk_actor_alsoknownas_actors__actor_id foreign key ("actor_id") references actors (id) on delete cascade on update cascade,
|
||||||
|
constraint fk_actor_alsoknownas_actors__also_known_as foreign key ("also_known_as") references actors (id) on delete cascade on update cascade
|
||||||
|
);
|
||||||
|
|
||||||
create table if not exists user_details
|
create table if not exists user_details
|
||||||
(
|
(
|
||||||
id bigserial primary key,
|
id bigserial primary key,
|
||||||
actor_id bigint not null unique,
|
actor_id bigint not null unique,
|
||||||
password varchar(255) not null,
|
password varchar(255) not null,
|
||||||
auto_accept_followee_follow_request boolean not null,
|
auto_accept_followee_follow_request boolean not null,
|
||||||
|
last_migration timestamp null default null,
|
||||||
constraint fk_user_details_actor_id__id foreign key (actor_id) references actors (id) on delete restrict on update restrict
|
constraint fk_user_details_actor_id__id foreign key (actor_id) references actors (id) on delete restrict on update restrict
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -81,14 +91,6 @@ create table if not exists media
|
||||||
mime_type varchar(255) not null,
|
mime_type varchar(255) not null,
|
||||||
description varchar(4000) null
|
description varchar(4000) null
|
||||||
);
|
);
|
||||||
create table if not exists meta_info
|
|
||||||
(
|
|
||||||
id bigint primary key,
|
|
||||||
version varchar(1000) not null,
|
|
||||||
kid varchar(1000) not null,
|
|
||||||
jwt_private_key varchar(100000) not null,
|
|
||||||
jwt_public_key varchar(100000) not null
|
|
||||||
);
|
|
||||||
create table if not exists posts
|
create table if not exists posts
|
||||||
(
|
(
|
||||||
id bigint primary key,
|
id bigint primary key,
|
||||||
|
@ -134,100 +136,6 @@ alter table posts_emojis
|
||||||
alter table posts_emojis
|
alter table posts_emojis
|
||||||
add constraint fk_posts_emojis_emoji_id__id foreign key (emoji_id) references emojis (id) on delete cascade on update cascade;
|
add constraint fk_posts_emojis_emoji_id__id foreign key (emoji_id) references emojis (id) on delete cascade on update cascade;
|
||||||
|
|
||||||
create table if not exists reactions
|
|
||||||
(
|
|
||||||
id bigint primary key,
|
|
||||||
unicode_emoji varchar(255) null default null,
|
|
||||||
custom_emoji_id bigint null default null,
|
|
||||||
post_id bigint not null,
|
|
||||||
actor_id bigint not null,
|
|
||||||
unique (post_id, actor_id)
|
|
||||||
);
|
|
||||||
alter table reactions
|
|
||||||
add constraint fk_reactions_post_id__id foreign key (post_id) references posts (id) on delete restrict on update restrict;
|
|
||||||
alter table reactions
|
|
||||||
add constraint fk_reactions_actor_id__id foreign key (actor_id) references actors (id) on delete restrict on update restrict;
|
|
||||||
alter table reactions
|
|
||||||
add constraint fk_reactions_custom_emoji_id__id foreign key (custom_emoji_id) references emojis (id) on delete cascade on update cascade;
|
|
||||||
|
|
||||||
create table if not exists timelines
|
|
||||||
(
|
|
||||||
id bigint primary key,
|
|
||||||
user_id bigint not null,
|
|
||||||
timeline_id bigint not null,
|
|
||||||
post_id bigint not null,
|
|
||||||
post_actor_id bigint not null,
|
|
||||||
created_at bigint not null,
|
|
||||||
reply_id bigint null,
|
|
||||||
repost_id bigint null,
|
|
||||||
visibility int not null,
|
|
||||||
"sensitive" boolean not null,
|
|
||||||
is_local boolean not null,
|
|
||||||
is_pure_repost boolean not null,
|
|
||||||
media_ids varchar(255) not null,
|
|
||||||
emoji_ids varchar(255) not null
|
|
||||||
);
|
|
||||||
|
|
||||||
create table if not exists application_authorization
|
|
||||||
(
|
|
||||||
id varchar(255) primary key,
|
|
||||||
registered_client_id varchar(255) not null,
|
|
||||||
principal_name varchar(255) not null,
|
|
||||||
authorization_grant_type varchar(255) not null,
|
|
||||||
authorized_scopes varchar(1000) default null null,
|
|
||||||
"attributes" varchar(4000) default null null,
|
|
||||||
"state" varchar(500) default null null,
|
|
||||||
authorization_code_value varchar(4000) default null null,
|
|
||||||
authorization_code_issued_at timestamp default null null,
|
|
||||||
authorization_code_expires_at timestamp default null null,
|
|
||||||
authorization_code_metadata varchar(2000) default null null,
|
|
||||||
access_token_value varchar(4000) default null null,
|
|
||||||
access_token_issued_at timestamp default null null,
|
|
||||||
access_token_expires_at timestamp default null null,
|
|
||||||
access_token_metadata varchar(2000) default null null,
|
|
||||||
access_token_type varchar(255) default null null,
|
|
||||||
access_token_scopes varchar(1000) default null null,
|
|
||||||
refresh_token_value varchar(4000) default null null,
|
|
||||||
refresh_token_issued_at timestamp default null null,
|
|
||||||
refresh_token_expires_at timestamp default null null,
|
|
||||||
refresh_token_metadata varchar(2000) default null null,
|
|
||||||
oidc_id_token_value varchar(4000) default null null,
|
|
||||||
oidc_id_token_issued_at timestamp default null null,
|
|
||||||
oidc_id_token_expires_at timestamp default null null,
|
|
||||||
oidc_id_token_metadata varchar(2000) default null null,
|
|
||||||
oidc_id_token_claims varchar(2000) default null null,
|
|
||||||
user_code_value varchar(4000) default null null,
|
|
||||||
user_code_issued_at timestamp default null null,
|
|
||||||
user_code_expires_at timestamp default null null,
|
|
||||||
user_code_metadata varchar(2000) default null null,
|
|
||||||
device_code_value varchar(4000) default null null,
|
|
||||||
device_code_issued_at timestamp default null null,
|
|
||||||
device_code_expires_at timestamp default null null,
|
|
||||||
device_code_metadata varchar(2000) default null null
|
|
||||||
);
|
|
||||||
create table if not exists oauth2_authorization_consent
|
|
||||||
(
|
|
||||||
registered_client_id varchar(100),
|
|
||||||
principal_name varchar(200),
|
|
||||||
authorities varchar(1000) not null,
|
|
||||||
constraint pk_oauth2_authorization_consent primary key (registered_client_id, principal_name)
|
|
||||||
);
|
|
||||||
create table if not exists registered_client
|
|
||||||
(
|
|
||||||
id varchar(100) primary key,
|
|
||||||
client_id varchar(100) not null,
|
|
||||||
client_id_issued_at timestamp default current_timestamp not null,
|
|
||||||
client_secret varchar(200) default null null,
|
|
||||||
client_secret_expires_at timestamp default null null,
|
|
||||||
client_name varchar(200) not null,
|
|
||||||
client_authentication_methods varchar(1000) not null,
|
|
||||||
authorization_grant_types varchar(1000) not null,
|
|
||||||
redirect_uris varchar(1000) default null null,
|
|
||||||
post_logout_redirect_uris varchar(1000) default null null,
|
|
||||||
scopes varchar(1000) not null,
|
|
||||||
client_settings varchar(2000) not null,
|
|
||||||
token_settings varchar(2000) not null
|
|
||||||
);
|
|
||||||
|
|
||||||
create table if not exists relationships
|
create table if not exists relationships
|
||||||
(
|
(
|
||||||
|
@ -254,40 +162,26 @@ insert into actors (id, name, domain, screen_name, description, inbox, outbox, u
|
||||||
values (0, '', '', '', '', '', '', '', '', null, current_timestamp, '', null, null, 0, true, null, null, 0, null,
|
values (0, '', '', '', '', '', '', '', '', null, current_timestamp, '', null, null, 0, true, null, null, 0, null,
|
||||||
current_timestamp, false, null, '');
|
current_timestamp, false, null, '');
|
||||||
|
|
||||||
create table if not exists deleted_actors
|
create table if not exists applications
|
||||||
(
|
(
|
||||||
id bigint primary key,
|
id bigint primary key,
|
||||||
"name" varchar(300) not null,
|
name varchar(500) not null
|
||||||
domain varchar(255) not null,
|
|
||||||
public_key varchar(10000) not null,
|
|
||||||
deleted_at timestamp not null,
|
|
||||||
unique ("name", domain)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
create table if not exists notifications
|
create table if not exists oauth2_registered_client
|
||||||
(
|
(
|
||||||
id bigint primary key,
|
id varchar(100) NOT NULL,
|
||||||
type varchar(100) not null,
|
client_id varchar(100) NOT NULL,
|
||||||
user_id bigint not null,
|
client_id_issued_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
source_actor_id bigint null,
|
client_secret varchar(200) DEFAULT NULL,
|
||||||
post_id bigint null,
|
client_secret_expires_at timestamp DEFAULT NULL,
|
||||||
text varchar(3000) null,
|
client_name varchar(200) NOT NULL,
|
||||||
reaction_id bigint null,
|
client_authentication_methods varchar(1000) NOT NULL,
|
||||||
created_at timestamp not null,
|
authorization_grant_types varchar(1000) NOT NULL,
|
||||||
constraint fk_notifications_user_id__id foreign key (user_id) references actors (id) on delete cascade on update cascade,
|
redirect_uris varchar(1000) DEFAULT NULL,
|
||||||
constraint fk_notifications_source_actor__id foreign key (source_actor_id) references actors (id) on delete cascade on update cascade,
|
post_logout_redirect_uris varchar(1000) DEFAULT NULL,
|
||||||
constraint fk_notifications_post_id__id foreign key (post_id) references posts (id) on delete cascade on update cascade,
|
scopes varchar(1000) NOT NULL,
|
||||||
constraint fk_notifications_reaction_id__id foreign key (reaction_id) references reactions (id) on delete cascade on update cascade
|
client_settings varchar(2000) NOT NULL,
|
||||||
|
token_settings varchar(2000) NOT NULL,
|
||||||
|
PRIMARY KEY (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
create table if not exists mastodon_notifications
|
|
||||||
(
|
|
||||||
id bigint primary key,
|
|
||||||
user_id bigint not null,
|
|
||||||
type varchar(100) not null,
|
|
||||||
created_at timestamp not null,
|
|
||||||
account_id bigint not null,
|
|
||||||
status_id bigint null,
|
|
||||||
report_id bigint null,
|
|
||||||
relationship_serverance_event_id bigint null
|
|
||||||
)
|
|
|
@ -6,9 +6,10 @@
|
||||||
</Console>
|
</Console>
|
||||||
</Appenders>
|
</Appenders>
|
||||||
<Loggers>
|
<Loggers>
|
||||||
<Root level="INFO">
|
<Root level="DEBUG">
|
||||||
<AppenderRef ref="Console"/>
|
<AppenderRef ref="Console"/>
|
||||||
</Root>
|
</Root>
|
||||||
<Logger name="dev.usbharu.owl.broker.service.QueuedTaskAssignerImpl" level="TRACE"/>
|
<Logger name="dev.usbharu.owl.broker.service.QueuedTaskAssignerImpl" level="TRACE"/>
|
||||||
|
<Logger name="org.mongodb.driver.cluster" level="WARN"/>
|
||||||
</Loggers>
|
</Loggers>
|
||||||
</Configuration>
|
</Configuration>
|
|
@ -3,23 +3,12 @@
|
||||||
<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="@{/dev/usbharu/hideout/core/service/auth/sign_up}"
|
<form method='post' th:action="@{/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>
|
||||||
|
|
|
@ -21,8 +21,6 @@ import org.springframework.context.annotation.Bean
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
import org.springframework.core.annotation.Order
|
import org.springframework.core.annotation.Order
|
||||||
import org.springframework.http.HttpMethod.*
|
import org.springframework.http.HttpMethod.*
|
||||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy
|
|
||||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl
|
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
import org.springframework.security.config.annotation.web.invoke
|
import org.springframework.security.config.annotation.web.invoke
|
||||||
import org.springframework.security.web.SecurityFilterChain
|
import org.springframework.security.web.SecurityFilterChain
|
||||||
|
@ -30,7 +28,7 @@ import org.springframework.security.web.SecurityFilterChain
|
||||||
@Configuration
|
@Configuration
|
||||||
class MastodonSecurityConfig {
|
class MastodonSecurityConfig {
|
||||||
@Bean
|
@Bean
|
||||||
@Order(4)
|
@Order(2)
|
||||||
@Suppress("LongMethod")
|
@Suppress("LongMethod")
|
||||||
fun mastodonApiSecurityFilterChain(
|
fun mastodonApiSecurityFilterChain(
|
||||||
http: HttpSecurity,
|
http: HttpSecurity,
|
||||||
|
@ -111,63 +109,4 @@ class MastodonSecurityConfig {
|
||||||
|
|
||||||
return http.build()
|
return http.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
fun roleHierarchy(): RoleHierarchy {
|
|
||||||
val roleHierarchyImpl = RoleHierarchyImpl()
|
|
||||||
|
|
||||||
roleHierarchyImpl.setHierarchy(
|
|
||||||
"""
|
|
||||||
SCOPE_read > SCOPE_read:accounts
|
|
||||||
SCOPE_read > SCOPE_read:accounts
|
|
||||||
SCOPE_read > SCOPE_read:blocks
|
|
||||||
SCOPE_read > SCOPE_read:bookmarks
|
|
||||||
SCOPE_read > SCOPE_read:favourites
|
|
||||||
SCOPE_read > SCOPE_read:filters
|
|
||||||
SCOPE_read > SCOPE_read:follows
|
|
||||||
SCOPE_read > SCOPE_read:lists
|
|
||||||
SCOPE_read > SCOPE_read:mutes
|
|
||||||
SCOPE_read > SCOPE_read:notifications
|
|
||||||
SCOPE_read > SCOPE_read:search
|
|
||||||
SCOPE_read > SCOPE_read:statuses
|
|
||||||
SCOPE_write > SCOPE_write:accounts
|
|
||||||
SCOPE_write > SCOPE_write:blocks
|
|
||||||
SCOPE_write > SCOPE_write:bookmarks
|
|
||||||
SCOPE_write > SCOPE_write:conversations
|
|
||||||
SCOPE_write > SCOPE_write:favourites
|
|
||||||
SCOPE_write > SCOPE_write:filters
|
|
||||||
SCOPE_write > SCOPE_write:follows
|
|
||||||
SCOPE_write > SCOPE_write:lists
|
|
||||||
SCOPE_write > SCOPE_write:media
|
|
||||||
SCOPE_write > SCOPE_write:mutes
|
|
||||||
SCOPE_write > SCOPE_write:notifications
|
|
||||||
SCOPE_write > SCOPE_write:reports
|
|
||||||
SCOPE_write > SCOPE_write:statuses
|
|
||||||
SCOPE_follow > SCOPE_write:blocks
|
|
||||||
SCOPE_follow > SCOPE_write:follows
|
|
||||||
SCOPE_follow > SCOPE_write:mutes
|
|
||||||
SCOPE_follow > SCOPE_read:blocks
|
|
||||||
SCOPE_follow > SCOPE_read:follows
|
|
||||||
SCOPE_follow > SCOPE_read:mutes
|
|
||||||
SCOPE_admin > SCOPE_admin:read
|
|
||||||
SCOPE_admin > SCOPE_admin:write
|
|
||||||
SCOPE_admin:read > SCOPE_admin:read:accounts
|
|
||||||
SCOPE_admin:read > SCOPE_admin:read:reports
|
|
||||||
SCOPE_admin:read > SCOPE_admin:read:domain_allows
|
|
||||||
SCOPE_admin:read > SCOPE_admin:read:domain_blocks
|
|
||||||
SCOPE_admin:read > SCOPE_admin:read:ip_blocks
|
|
||||||
SCOPE_admin:read > SCOPE_admin:read:email_domain_blocks
|
|
||||||
SCOPE_admin:read > SCOPE_admin:read:canonical_email_blocks
|
|
||||||
SCOPE_admin:write > SCOPE_admin:write:accounts
|
|
||||||
SCOPE_admin:write > SCOPE_admin:write:reports
|
|
||||||
SCOPE_admin:write > SCOPE_admin:write:domain_allows
|
|
||||||
SCOPE_admin:write > SCOPE_admin:write:domain_blocks
|
|
||||||
SCOPE_admin:write > SCOPE_admin:write:ip_blocks
|
|
||||||
SCOPE_admin:write > SCOPE_admin:write:email_domain_blocks
|
|
||||||
SCOPE_admin:write > SCOPE_admin:write:canonical_email_blocks
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
|
|
||||||
return roleHierarchyImpl
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -16,15 +16,35 @@
|
||||||
|
|
||||||
package dev.usbharu.hideout.mastodon.interfaces.api
|
package dev.usbharu.hideout.mastodon.interfaces.api
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.application.application.RegisterApplication
|
||||||
|
import dev.usbharu.hideout.core.application.application.RegisterApplicationApplicationService
|
||||||
import dev.usbharu.hideout.mastodon.interfaces.api.generated.AppApi
|
import dev.usbharu.hideout.mastodon.interfaces.api.generated.AppApi
|
||||||
import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Application
|
import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Application
|
||||||
import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.AppsRequest
|
import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.AppsRequest
|
||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
import org.springframework.stereotype.Controller
|
import org.springframework.stereotype.Controller
|
||||||
|
import java.net.URI
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
class SpringAppApi : AppApi {
|
class SpringAppApi(private val registerApplicationApplicationService: RegisterApplicationApplicationService) : AppApi {
|
||||||
override suspend fun apiV1AppsPost(appsRequest: AppsRequest): ResponseEntity<Application> {
|
override suspend fun apiV1AppsPost(appsRequest: AppsRequest): ResponseEntity<Application> {
|
||||||
return super.apiV1AppsPost(appsRequest)
|
|
||||||
|
val registerApplication = RegisterApplication(
|
||||||
|
appsRequest.clientName,
|
||||||
|
setOf(URI.create(appsRequest.redirectUris)),
|
||||||
|
false,
|
||||||
|
appsRequest.scopes?.split(" ").orEmpty().toSet()
|
||||||
|
)
|
||||||
|
val registeredApplication = registerApplicationApplicationService.register(registerApplication)
|
||||||
|
return ResponseEntity.ok(
|
||||||
|
Application(
|
||||||
|
registeredApplication.name,
|
||||||
|
"invalid-vapid-key",
|
||||||
|
null,
|
||||||
|
registeredApplication.clientId.toString(),
|
||||||
|
registeredApplication.clientSecret,
|
||||||
|
appsRequest.redirectUris
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue