Merge pull request #516 from usbharu/auth2

認可処理とテストを追加
This commit is contained in:
usbharu 2024-08-10 00:34:30 +09:00 committed by GitHub
commit e9f263bab1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
90 changed files with 2668 additions and 512 deletions

View File

@ -0,0 +1,5 @@
package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.core.domain.model.actor.ActorId
data class DeleteLocalActor(val actorId: ActorId)

View File

@ -1,37 +0,0 @@
/*
* 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.actor
import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import org.springframework.stereotype.Service
@Service
class DeleteLocalActorApplicationService(
private val transaction: Transaction,
private val actorRepository: ActorRepository,
) {
suspend fun delete(actorId: Long, executor: ActorId) {
transaction.transaction {
val id = ActorId(actorId)
val findById = actorRepository.findById(id)!!
findById.delete()
actorRepository.delete(findById)
}
}
}

View File

@ -16,6 +16,7 @@
package dev.usbharu.hideout.core.application.actor package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.core.application.exception.InternalServerException
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.AbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
@ -36,8 +37,9 @@ class GetUserDetailApplicationService(
AbstractApplicationService<GetUserDetail, UserDetail>(transaction, Companion.logger) { AbstractApplicationService<GetUserDetail, UserDetail>(transaction, Companion.logger) {
override suspend fun internalExecute(command: GetUserDetail, principal: Principal): UserDetail { override suspend fun internalExecute(command: GetUserDetail, principal: Principal): UserDetail {
val userDetail = userDetailRepository.findById(UserDetailId(command.id)) val userDetail = userDetailRepository.findById(UserDetailId(command.id))
?: throw IllegalArgumentException("actor does not exist") ?: throw IllegalArgumentException("User ${command.id} does not exist")
val actor = actorRepository.findById(userDetail.actorId)!! val actor = actorRepository.findById(userDetail.actorId)
?: throw InternalServerException("Actor ${userDetail.actorId} not found")
val emojis = customEmojiRepository.findByIds(actor.emojis.map { it.emojiId }) val emojis = customEmojiRepository.findByIds(actor.emojis.map { it.emojiId })

View File

@ -0,0 +1,3 @@
package dev.usbharu.hideout.core.application.actor
data class MigrationLocalActor(val from: Long, val to: Long)

View File

@ -16,39 +16,59 @@
package dev.usbharu.hideout.core.application.actor package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.core.application.exception.InternalServerException
import dev.usbharu.hideout.core.application.exception.PermissionDeniedException
import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.domain.service.actor.local.AccountMigrationCheck.* import dev.usbharu.hideout.core.domain.service.actor.local.AccountMigrationCheck.*
import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorMigrationCheckDomainService import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorMigrationCheckDomainService
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@Service @Service
class MigrationLocalActorApplicationService( class MigrationLocalActorApplicationService(
private val transaction: Transaction,
private val actorRepository: ActorRepository, private val actorRepository: ActorRepository,
private val localActorMigrationCheckDomainService: LocalActorMigrationCheckDomainService, private val localActorMigrationCheckDomainService: LocalActorMigrationCheckDomainService,
) { transaction: Transaction,
suspend fun migration(from: Long, to: Long, executor: ActorId) { private val userDetailRepository: UserDetailRepository,
transaction.transaction<Unit> { ) : LocalUserAbstractApplicationService<MigrationLocalActor, Unit>(transaction, logger) {
val fromActorId = ActorId(from)
val toActorId = ActorId(to)
val fromActor = actorRepository.findById(fromActorId)!! override suspend fun internalExecute(command: MigrationLocalActor, principal: FromApi) {
val toActor = actorRepository.findById(toActorId)!! if (command.from != principal.actorId.id) {
throw PermissionDeniedException()
}
val canAccountMigration = localActorMigrationCheckDomainService.canAccountMigration(fromActor, toActor) val userDetail = userDetailRepository.findById(principal.userDetailId)
when (canAccountMigration) { ?: throw InternalServerException("User detail ${principal.userDetailId} not found.")
is AlreadyMoved -> TODO()
is CanAccountMigration -> { val fromActorId = ActorId(command.from)
val toActorId = ActorId(command.to)
val fromActor =
actorRepository.findById(fromActorId) ?: throw IllegalArgumentException("Actor ${command.from} not found.")
val toActor =
actorRepository.findById(toActorId) ?: throw IllegalArgumentException("Actor ${command.to} not found.")
val canAccountMigration =
localActorMigrationCheckDomainService.canAccountMigration(userDetail, fromActor, toActor)
if (canAccountMigration.canMigration) {
fromActor.moveTo = toActorId fromActor.moveTo = toActorId
actorRepository.save(fromActor) actorRepository.save(fromActor)
} else when (canAccountMigration) {
is AlreadyMoved -> throw IllegalArgumentException(canAccountMigration.message)
is CanAccountMigration -> throw InternalServerException()
is CircularReferences -> throw IllegalArgumentException(canAccountMigration.message)
is SelfReferences -> throw IllegalArgumentException("Self references are not supported")
is AlsoKnownAsNotFound -> throw IllegalArgumentException(canAccountMigration.message)
is MigrationCoolDown -> throw IllegalArgumentException(canAccountMigration.message)
}
} }
is CircularReferences -> TODO() companion object {
is SelfReferences -> TODO() private val logger = LoggerFactory.getLogger(MigrationLocalActorApplicationService::class.java)
is AlsoKnownAsNotFound -> TODO()
}
}
} }
} }

View File

@ -16,6 +16,7 @@
package dev.usbharu.hideout.core.application.actor package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.core.application.exception.InternalServerException
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.AbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.config.ApplicationConfig import dev.usbharu.hideout.core.config.ApplicationConfig
@ -48,10 +49,10 @@ class RegisterLocalActorApplicationService(
override suspend fun internalExecute(command: RegisterLocalActor, principal: Principal): URI { override suspend fun internalExecute(command: RegisterLocalActor, principal: Principal): URI {
if (actorDomainService.usernameAlreadyUse(command.name)) { if (actorDomainService.usernameAlreadyUse(command.name)) {
// todo 適切な例外を考える throw IllegalArgumentException("Username already exists")
throw Exception("Username already exists")
} }
val instance = instanceRepository.findByUrl(applicationConfig.url.toURI())!! val instance = instanceRepository.findByUrl(applicationConfig.url.toURI())
?: throw InternalServerException("Local instance not found.")
val actor = actorFactoryImpl.createLocal( val actor = actorFactoryImpl.createLocal(
command.name, command.name,
@ -72,6 +73,6 @@ class RegisterLocalActorApplicationService(
} }
companion object { companion object {
val logger = LoggerFactory.getLogger(RegisterLocalActorApplicationService::class.java) private val logger = LoggerFactory.getLogger(RegisterLocalActorApplicationService::class.java)
} }
} }

View File

@ -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.application.actor
import dev.usbharu.hideout.core.application.exception.InternalServerException
import dev.usbharu.hideout.core.application.exception.PermissionDeniedException
import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
@Service
class StartDeleteLocalActorApplicationService(
transaction: Transaction,
private val actorRepository: ActorRepository,
) : LocalUserAbstractApplicationService<DeleteLocalActor, Unit>(transaction, logger) {
override suspend fun internalExecute(command: DeleteLocalActor, principal: FromApi) {
if (command.actorId != principal.actorId) {
throw PermissionDeniedException()
}
val findById = actorRepository.findById(command.actorId)
?: throw InternalServerException("Actor ${command.actorId} Not found")
findById.delete()
actorRepository.save(findById)
}
companion object {
private val logger = LoggerFactory.getLogger(StartDeleteLocalActorApplicationService::class.java)
}
}

View File

@ -30,8 +30,8 @@ class SuspendLocalActorApplicationService(
transaction.transaction { transaction.transaction {
val id = ActorId(actorId) val id = ActorId(actorId)
val findById = actorRepository.findById(id)!! val actor = actorRepository.findById(id)!!
findById.suspend = true actor.suspend = true
} }
} }
} }

View File

@ -16,14 +16,17 @@
package dev.usbharu.hideout.core.application.application package dev.usbharu.hideout.core.application.application
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction 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.Application
import dev.usbharu.hideout.core.domain.model.application.ApplicationId 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.ApplicationName
import dev.usbharu.hideout.core.domain.model.application.ApplicationRepository import dev.usbharu.hideout.core.domain.model.application.ApplicationRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Principal
import dev.usbharu.hideout.core.domain.service.userdetail.PasswordEncoder import dev.usbharu.hideout.core.domain.service.userdetail.PasswordEncoder
import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService
import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.SecureTokenGenerator import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.SecureTokenGenerator
import org.slf4j.LoggerFactory
import org.springframework.security.oauth2.core.AuthorizationGrantType import org.springframework.security.oauth2.core.AuthorizationGrantType
import org.springframework.security.oauth2.core.ClientAuthenticationMethod import org.springframework.security.oauth2.core.ClientAuthenticationMethod
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient import org.springframework.security.oauth2.server.authorization.client.RegisteredClient
@ -39,25 +42,25 @@ class RegisterApplicationApplicationService(
private val passwordEncoder: PasswordEncoder, private val passwordEncoder: PasswordEncoder,
private val secureTokenGenerator: SecureTokenGenerator, private val secureTokenGenerator: SecureTokenGenerator,
private val registeredClientRepository: RegisteredClientRepository, private val registeredClientRepository: RegisteredClientRepository,
private val transaction: Transaction, transaction: Transaction,
private val applicationRepository: ApplicationRepository, private val applicationRepository: ApplicationRepository,
) { ) : AbstractApplicationService<RegisterApplication, RegisteredApplication>(transaction, logger) {
suspend fun register(registerApplication: RegisterApplication): RegisteredApplication {
return transaction.transaction { override suspend fun internalExecute(command: RegisterApplication, principal: Principal): RegisteredApplication {
val id = idGenerateService.generateId() val id = idGenerateService.generateId()
val clientSecret = secureTokenGenerator.generate() val clientSecret = secureTokenGenerator.generate()
val registeredClient = RegisteredClient val registeredClient = RegisteredClient
.withId(id.toString()) .withId(id.toString())
.clientId(id.toString()) .clientId(id.toString())
.clientSecret(passwordEncoder.encode(clientSecret)) .clientSecret(passwordEncoder.encode(clientSecret))
.clientName(registerApplication.name) .clientName(command.name)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST) .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT) .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.apply { .apply {
if (registerApplication.useRefreshToken) { if (command.useRefreshToken) {
authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
} else { } else {
tokenSettings( tokenSettings(
@ -69,23 +72,27 @@ class RegisterApplicationApplicationService(
} }
} }
.redirectUris { set -> .redirectUris { set ->
set.addAll(registerApplication.redirectUris.map { it.toString() }) set.addAll(command.redirectUris.map { it.toString() })
} }
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.scopes { it.addAll(registerApplication.scopes) } .scopes { it.addAll(command.scopes) }
.build() .build()
registeredClientRepository.save(registeredClient) registeredClientRepository.save(registeredClient)
val application = Application(ApplicationId(id), ApplicationName(registerApplication.name)) val application = Application(ApplicationId(id), ApplicationName(command.name))
applicationRepository.save(application) applicationRepository.save(application)
RegisteredApplication( return RegisteredApplication(
id = id, id = id,
name = registerApplication.name, name = command.name,
clientSecret = clientSecret, clientSecret = clientSecret,
clientId = id.toString(), clientId = id.toString(),
redirectUris = registerApplication.redirectUris redirectUris = command.redirectUris
) )
} }
companion object {
private val logger = LoggerFactory.getLogger(RegisterApplicationApplicationService::class.java)
} }
} }

View File

@ -0,0 +1,14 @@
package dev.usbharu.hideout.core.application.exception
class InternalServerException : RuntimeException {
constructor() : super()
constructor(message: String?) : super(message)
constructor(message: String?, cause: Throwable?) : super(message, cause)
constructor(cause: Throwable?) : super(cause)
constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super(
message,
cause,
enableSuppression,
writableStackTrace
)
}

View File

@ -0,0 +1,14 @@
package dev.usbharu.hideout.core.application.exception
class PermissionDeniedException : RuntimeException {
constructor() : super()
constructor(message: String?) : super(message)
constructor(message: String?, cause: Throwable?) : super(message, cause)
constructor(cause: Throwable?) : super(cause)
constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super(
message,
cause,
enableSuppression,
writableStackTrace
)
}

View File

@ -18,12 +18,10 @@ package dev.usbharu.hideout.core.application.filter
import dev.usbharu.hideout.core.domain.model.filter.FilterAction import dev.usbharu.hideout.core.domain.model.filter.FilterAction
import dev.usbharu.hideout.core.domain.model.filter.FilterContext import dev.usbharu.hideout.core.domain.model.filter.FilterContext
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
data class RegisterFilter( data class RegisterFilter(
val filterName: String, val filterName: String,
val filterContext: Set<FilterContext>, val filterContext: Set<FilterContext>,
val filterAction: FilterAction, val filterAction: FilterAction,
val filterKeywords: Set<RegisterFilterKeyword>, val filterKeywords: Set<RegisterFilterKeyword>,
val userDetailId: UserDetailId
) )

View File

@ -16,22 +16,27 @@
package dev.usbharu.hideout.core.application.filter package dev.usbharu.hideout.core.application.filter
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.exception.PermissionDeniedException
import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.filter.FilterId import dev.usbharu.hideout.core.domain.model.filter.FilterId
import dev.usbharu.hideout.core.domain.model.filter.FilterRepository import dev.usbharu.hideout.core.domain.model.filter.FilterRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@Service @Service
class UserDeleteFilterApplicationService(private val filterRepository: FilterRepository, transaction: Transaction) : class UserDeleteFilterApplicationService(private val filterRepository: FilterRepository, transaction: Transaction) :
AbstractApplicationService<DeleteFilter, Unit>( LocalUserAbstractApplicationService<DeleteFilter, Unit>(
transaction, transaction,
logger logger
) { ) {
override suspend fun internalExecute(command: DeleteFilter, principal: Principal) { override suspend fun internalExecute(command: DeleteFilter, principal: FromApi) {
val filter = filterRepository.findByFilterId(FilterId(command.filterId)) ?: throw Exception("not found") val filter =
filterRepository.findByFilterId(FilterId(command.filterId)) ?: throw IllegalArgumentException("not found")
if (filter.userDetailId != principal.userDetailId) {
throw PermissionDeniedException()
}
filterRepository.delete(filter) filterRepository.delete(filter)
} }

View File

@ -16,23 +16,27 @@
package dev.usbharu.hideout.core.application.filter package dev.usbharu.hideout.core.application.filter
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.exception.PermissionDeniedException
import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.filter.FilterId import dev.usbharu.hideout.core.domain.model.filter.FilterId
import dev.usbharu.hideout.core.domain.model.filter.FilterRepository import dev.usbharu.hideout.core.domain.model.filter.FilterRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@Service @Service
class UserGetFilterApplicationService(private val filterRepository: FilterRepository, transaction: Transaction) : class UserGetFilterApplicationService(private val filterRepository: FilterRepository, transaction: Transaction) :
AbstractApplicationService<GetFilter, Filter>( LocalUserAbstractApplicationService<GetFilter, Filter>(
transaction, transaction,
logger logger
) { ) {
override suspend fun internalExecute(command: GetFilter, principal: Principal): Filter { override suspend fun internalExecute(command: GetFilter, principal: FromApi): Filter {
val filter = filterRepository.findByFilterId(FilterId(command.filterId)) ?: throw Exception("Not Found") val filter =
filterRepository.findByFilterId(FilterId(command.filterId)) ?: throw IllegalArgumentException("Not Found")
if (filter.userDetailId != principal.userDetailId) {
throw PermissionDeniedException()
}
return Filter.of(filter) return Filter.of(filter)
} }

View File

@ -16,11 +16,11 @@
package dev.usbharu.hideout.core.application.filter package dev.usbharu.hideout.core.application.filter
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.filter.* import dev.usbharu.hideout.core.domain.model.filter.*
import dev.usbharu.hideout.core.domain.model.filter.FilterKeyword import dev.usbharu.hideout.core.domain.model.filter.FilterKeyword
import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@ -31,16 +31,16 @@ class UserRegisterFilterApplicationService(
private val filterRepository: FilterRepository, private val filterRepository: FilterRepository,
transaction: Transaction, transaction: Transaction,
) : ) :
AbstractApplicationService<RegisterFilter, Filter>( LocalUserAbstractApplicationService<RegisterFilter, Filter>(
transaction, transaction,
logger logger
) { ) {
override suspend fun internalExecute(command: RegisterFilter, principal: Principal): Filter { override suspend fun internalExecute(command: RegisterFilter, principal: FromApi): Filter {
val filter = dev.usbharu.hideout.core.domain.model.filter.Filter.create( val filter = dev.usbharu.hideout.core.domain.model.filter.Filter.create(
id = FilterId(idGenerateService.generateId()), id = FilterId(idGenerateService.generateId()),
userDetailId = command.userDetailId, userDetailId = principal.userDetailId,
name = FilterName(command.filterName), name = FilterName(command.filterName),
filterContext = command.filterContext, filterContext = command.filterContext,
filterAction = command.filterAction, filterAction = command.filterAction,

View File

@ -16,10 +16,10 @@
package dev.usbharu.hideout.core.application.media package dev.usbharu.hideout.core.application.media
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.media.* import dev.usbharu.hideout.core.domain.model.media.*
import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService
import dev.usbharu.hideout.core.external.media.MediaProcessor import dev.usbharu.hideout.core.external.media.MediaProcessor
import dev.usbharu.hideout.core.external.mediastore.MediaStore import dev.usbharu.hideout.core.external.mediastore.MediaStore
@ -35,11 +35,11 @@ class UploadMediaApplicationService(
private val mediaRepository: MediaRepository, private val mediaRepository: MediaRepository,
private val idGenerateService: IdGenerateService, private val idGenerateService: IdGenerateService,
transaction: Transaction transaction: Transaction
) : AbstractApplicationService<UploadMedia, Media>( ) : LocalUserAbstractApplicationService<UploadMedia, Media>(
transaction, transaction,
logger logger
) { ) {
override suspend fun internalExecute(command: UploadMedia, principal: Principal): Media { override suspend fun internalExecute(command: UploadMedia, principal: FromApi): Media {
val process = mediaProcessor.process(command.path, command.name, null) val process = mediaProcessor.process(command.path, command.name, null)
val id = idGenerateService.generateId() val id = idGenerateService.generateId()
val thumbnailUri = if (process.thumbnailPath != null) { val thumbnailUri = if (process.thumbnailPath != null) {

View File

@ -0,0 +1,3 @@
package dev.usbharu.hideout.core.application.post
data class DeleteLocalPost(val postId: Long)

View File

@ -16,24 +16,33 @@
package dev.usbharu.hideout.core.application.post package dev.usbharu.hideout.core.application.post
import dev.usbharu.hideout.core.application.exception.PermissionDeniedException
import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.post.PostId import dev.usbharu.hideout.core.domain.model.post.PostId
import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.post.PostRepository
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@Service @Service
class DeleteLocalPostApplicationService( class DeleteLocalPostApplicationService(
private val postRepository: PostRepository, private val postRepository: PostRepository,
private val userDetailRepository: UserDetailRepository, private val actorRepository: ActorRepository, transaction: Transaction,
private val actorRepository: ActorRepository, ) : LocalUserAbstractApplicationService<DeleteLocalPost, Unit>(transaction, logger) {
) {
suspend fun delete(postId: Long, userDetailId: Long) { override suspend fun internalExecute(command: DeleteLocalPost, principal: FromApi) {
val findById = postRepository.findById(PostId(postId))!! val findById = postRepository.findById(PostId(command.postId))!!
val user = userDetailRepository.findById(UserDetailId(userDetailId))!! if (findById.actorId != principal.actorId) {
val actor = actorRepository.findById(user.actorId)!! throw PermissionDeniedException()
}
val actor = actorRepository.findById(principal.actorId)!!
findById.delete(actor) findById.delete(actor)
postRepository.save(findById) postRepository.save(findById)
} }
companion object {
private val logger = LoggerFactory.getLogger(DeleteLocalPostApplicationService::class.java)
}
} }

View File

@ -16,21 +16,29 @@
package dev.usbharu.hideout.core.application.post package dev.usbharu.hideout.core.application.post
import dev.usbharu.hideout.core.application.exception.PermissionDeniedException
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.AbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.post.PostId import dev.usbharu.hideout.core.domain.model.post.PostId
import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.post.PostRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.model.support.principal.Principal
import dev.usbharu.hideout.core.domain.service.post.IPostReadAccessControl
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@Service @Service
class GetPostApplicationService(private val postRepository: PostRepository, transaction: Transaction) : class GetPostApplicationService(
private val postRepository: PostRepository,
private val iPostReadAccessControl: IPostReadAccessControl,
transaction: Transaction
) :
AbstractApplicationService<GetPost, Post>(transaction, logger) { AbstractApplicationService<GetPost, Post>(transaction, logger) {
override suspend fun internalExecute(command: GetPost, principal: Principal): Post { override suspend fun internalExecute(command: GetPost, principal: Principal): Post {
val post = postRepository.findById(PostId(command.postId)) ?: throw Exception("Post not found") val post = postRepository.findById(PostId(command.postId)) ?: throw IllegalArgumentException("Post not found")
if (iPostReadAccessControl.isAllow(post, principal).not()) {
throw PermissionDeniedException()
}
return Post.of(post) return Post.of(post)
} }

View File

@ -19,7 +19,6 @@ package dev.usbharu.hideout.core.application.post
import dev.usbharu.hideout.core.domain.model.post.Visibility import dev.usbharu.hideout.core.domain.model.post.Visibility
data class RegisterLocalPost( data class RegisterLocalPost(
val userDetailId: Long,
val content: String, val content: String,
val overview: String?, val overview: String?,
val visibility: Visibility, val visibility: Visibility,

View File

@ -16,16 +16,15 @@
package dev.usbharu.hideout.core.application.post package dev.usbharu.hideout.core.application.post
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.exception.InternalServerException
import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.media.MediaId import dev.usbharu.hideout.core.domain.model.media.MediaId
import dev.usbharu.hideout.core.domain.model.post.PostId import dev.usbharu.hideout.core.domain.model.post.PostId
import dev.usbharu.hideout.core.domain.model.post.PostOverview import dev.usbharu.hideout.core.domain.model.post.PostOverview
import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.post.PostRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.infrastructure.factory.PostFactoryImpl import dev.usbharu.hideout.core.infrastructure.factory.PostFactoryImpl
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -36,17 +35,13 @@ class RegisterLocalPostApplicationService(
private val postFactory: PostFactoryImpl, private val postFactory: PostFactoryImpl,
private val actorRepository: ActorRepository, private val actorRepository: ActorRepository,
private val postRepository: PostRepository, private val postRepository: PostRepository,
private val userDetailRepository: UserDetailRepository,
transaction: Transaction, transaction: Transaction,
) : AbstractApplicationService<RegisterLocalPost, Long>(transaction, Companion.logger) { ) : LocalUserAbstractApplicationService<RegisterLocalPost, Long>(transaction, Companion.logger) {
override suspend fun internalExecute(command: RegisterLocalPost, principal: Principal): Long { override suspend fun internalExecute(command: RegisterLocalPost, principal: FromApi): Long {
val actorId = ( val actorId = principal.actorId
userDetailRepository.findById(UserDetailId(command.userDetailId))
?: throw IllegalStateException("actor not found")
).actorId
val actor = actorRepository.findById(actorId)!! val actor = actorRepository.findById(actorId) ?: throw InternalServerException("Actor $actorId not found.")
val post = postFactory.createLocal( val post = postFactory.createLocal(
actor = actor, actor = actor,

View File

@ -16,13 +16,10 @@
package dev.usbharu.hideout.core.application.post package dev.usbharu.hideout.core.application.post
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
data class UpdateLocalNote( data class UpdateLocalNote(
val postId: Long, val postId: Long,
val overview: String?, val overview: String?,
val content: String, val content: String,
val sensitive: Boolean, val sensitive: Boolean,
val mediaIds: List<Long>, val mediaIds: List<Long>
val userDetailId: UserDetailId
) )

View File

@ -16,14 +16,16 @@
package dev.usbharu.hideout.core.application.post package dev.usbharu.hideout.core.application.post
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.exception.InternalServerException
import dev.usbharu.hideout.core.application.exception.PermissionDeniedException
import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.media.MediaId import dev.usbharu.hideout.core.domain.model.media.MediaId
import dev.usbharu.hideout.core.domain.model.post.PostId import dev.usbharu.hideout.core.domain.model.post.PostId
import dev.usbharu.hideout.core.domain.model.post.PostOverview import dev.usbharu.hideout.core.domain.model.post.PostOverview
import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.post.PostRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.infrastructure.factory.PostContentFactoryImpl import dev.usbharu.hideout.core.infrastructure.factory.PostContentFactoryImpl
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -36,13 +38,19 @@ class UpdateLocalNoteApplicationService(
private val postContentFactoryImpl: PostContentFactoryImpl, private val postContentFactoryImpl: PostContentFactoryImpl,
private val userDetailRepository: UserDetailRepository, private val userDetailRepository: UserDetailRepository,
private val actorRepository: ActorRepository, private val actorRepository: ActorRepository,
) : AbstractApplicationService<UpdateLocalNote, Unit>(transaction, logger) { ) : LocalUserAbstractApplicationService<UpdateLocalNote, Unit>(transaction, logger) {
override suspend fun internalExecute(command: UpdateLocalNote, principal: Principal) { override suspend fun internalExecute(command: UpdateLocalNote, principal: FromApi) {
val post = postRepository.findById(PostId(command.postId))
?: throw IllegalArgumentException("Post ${command.postId} not found.")
if (post.actorId != principal.actorId) {
throw PermissionDeniedException()
}
val userDetail = userDetailRepository.findById(command.userDetailId)!! val userDetail = userDetailRepository.findById(principal.userDetailId)
val actor = actorRepository.findById(userDetail.actorId)!! ?: throw InternalServerException("User detail ${principal.userDetailId} not found.")
val post = postRepository.findById(PostId(command.postId))!! val actor = actorRepository.findById(userDetail.actorId)
?: throw InternalServerException("Actor ${principal.actorId} not found.")
post.setContent(postContentFactoryImpl.create(command.content), actor) post.setContent(postContentFactoryImpl.create(command.content), actor)
post.setOverview(command.overview?.let { PostOverview(it) }, actor) post.setOverview(command.overview?.let { PostOverview(it) }, actor)

View File

@ -16,6 +16,4 @@
package dev.usbharu.hideout.core.application.relationship.acceptfollowrequest package dev.usbharu.hideout.core.application.relationship.acceptfollowrequest
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId data class AcceptFollowRequest(val sourceActorId: Long)
data class AcceptFollowRequest(val sourceActorId: Long, val userDetailId: UserDetailId)

View File

@ -16,14 +16,14 @@
package dev.usbharu.hideout.core.application.relationship.acceptfollowrequest package dev.usbharu.hideout.core.application.relationship.acceptfollowrequest
import dev.usbharu.hideout.core.application.exception.InternalServerException
import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@ -32,18 +32,17 @@ class UserAcceptFollowRequestApplicationService(
private val relationshipRepository: RelationshipRepository, private val relationshipRepository: RelationshipRepository,
transaction: Transaction, transaction: Transaction,
private val actorRepository: ActorRepository, private val actorRepository: ActorRepository,
private val userDetailRepository: UserDetailRepository,
) : ) :
AbstractApplicationService<AcceptFollowRequest, Unit>(transaction, logger) { LocalUserAbstractApplicationService<AcceptFollowRequest, Unit>(transaction, logger) {
override suspend fun internalExecute(command: AcceptFollowRequest, principal: Principal) { override suspend fun internalExecute(command: AcceptFollowRequest, principal: FromApi) {
val userDetail = userDetailRepository.findById(command.userDetailId)!! val actor = actorRepository.findById(principal.actorId)
val actor = actorRepository.findById(userDetail.actorId)!! ?: throw InternalServerException("Actor ${principal.actorId} not found")
val targetId = ActorId(command.sourceActorId) val targetId = ActorId(command.sourceActorId)
val relationship = relationshipRepository.findByActorIdAndTargetId(targetId, actor.id) val relationship = relationshipRepository.findByActorIdAndTargetId(targetId, actor.id)
?: throw Exception("Follow request not found") ?: throw InternalServerException("Follow request not found")
relationship.acceptFollowRequest() relationship.acceptFollowRequest()

View File

@ -16,6 +16,4 @@
package dev.usbharu.hideout.core.application.relationship.block package dev.usbharu.hideout.core.application.relationship.block
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId data class Block(val targetActorId: Long)
data class Block(val targetActorId: Long, val userDetailId: UserDetailId)

View File

@ -16,13 +16,13 @@
package dev.usbharu.hideout.core.application.relationship.block package dev.usbharu.hideout.core.application.relationship.block
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.relationship.Relationship import dev.usbharu.hideout.core.domain.model.relationship.Relationship
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.domain.service.relationship.RelationshipDomainService import dev.usbharu.hideout.core.domain.service.relationship.RelationshipDomainService
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -36,10 +36,10 @@ class UserBlockApplicationService(
private val userDetailRepository: UserDetailRepository, private val userDetailRepository: UserDetailRepository,
private val relationshipDomainService: RelationshipDomainService, private val relationshipDomainService: RelationshipDomainService,
) : ) :
AbstractApplicationService<Block, Unit>(transaction, logger) { LocalUserAbstractApplicationService<Block, Unit>(transaction, logger) {
override suspend fun internalExecute(command: Block, principal: Principal) { override suspend fun internalExecute(command: Block, principal: FromApi) {
val userDetail = userDetailRepository.findById(command.userDetailId)!! val userDetail = userDetailRepository.findById(principal.userDetailId)!!
val actor = actorRepository.findById(userDetail.actorId)!! val actor = actorRepository.findById(userDetail.actorId)!!
val targetId = ActorId(command.targetActorId) val targetId = ActorId(command.targetActorId)

View File

@ -16,6 +16,4 @@
package dev.usbharu.hideout.core.application.relationship.followrequest package dev.usbharu.hideout.core.application.relationship.followrequest
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId data class FollowRequest(val targetActorId: Long)
data class FollowRequest(val targetActorId: Long, val userDetailId: UserDetailId)

View File

@ -16,13 +16,13 @@
package dev.usbharu.hideout.core.application.relationship.followrequest package dev.usbharu.hideout.core.application.relationship.followrequest
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.relationship.Relationship import dev.usbharu.hideout.core.domain.model.relationship.Relationship
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@ -33,14 +33,14 @@ class UserFollowRequestApplicationService(
transaction: Transaction, transaction: Transaction,
private val actorRepository: ActorRepository, private val actorRepository: ActorRepository,
private val userDetailRepository: UserDetailRepository, private val userDetailRepository: UserDetailRepository,
) : AbstractApplicationService<FollowRequest, Unit>( ) : LocalUserAbstractApplicationService<FollowRequest, Unit>(
transaction, transaction,
logger logger
) { ) {
override suspend fun internalExecute(command: FollowRequest, principal: Principal) { override suspend fun internalExecute(command: FollowRequest, principal: FromApi) {
val userDetail = userDetailRepository.findById(command.userDetailId)!! val userDetail = userDetailRepository.findById(principal.userDetailId)!!
val actor = actorRepository.findById(userDetail.actorId)!! val actor = actorRepository.findById(userDetail.actorId)!!
val targetId = ActorId(command.targetActorId) val targetId = ActorId(command.targetActorId)

View File

@ -16,6 +16,4 @@
package dev.usbharu.hideout.core.application.relationship.get package dev.usbharu.hideout.core.application.relationship.get
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId data class GetRelationship(val targetActorId: Long)
data class GetRelationship(val targetActorId: Long, val userDetailId: UserDetailId)

View File

@ -16,14 +16,14 @@
package dev.usbharu.hideout.core.application.relationship.get package dev.usbharu.hideout.core.application.relationship.get
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.actorinstancerelationship.ActorInstanceRelationship import dev.usbharu.hideout.core.domain.model.actorinstancerelationship.ActorInstanceRelationship
import dev.usbharu.hideout.core.domain.model.actorinstancerelationship.ActorInstanceRelationshipRepository import dev.usbharu.hideout.core.domain.model.actorinstancerelationship.ActorInstanceRelationshipRepository
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@ -36,12 +36,12 @@ class GetRelationshipApplicationService(
private val actorInstanceRelationshipRepository: ActorInstanceRelationshipRepository, private val actorInstanceRelationshipRepository: ActorInstanceRelationshipRepository,
transaction: Transaction, transaction: Transaction,
) : ) :
AbstractApplicationService<GetRelationship, Relationship>( LocalUserAbstractApplicationService<GetRelationship, Relationship>(
transaction, transaction,
logger logger
) { ) {
override suspend fun internalExecute(command: GetRelationship, principal: Principal): Relationship { override suspend fun internalExecute(command: GetRelationship, principal: FromApi): Relationship {
val userDetail = userDetailRepository.findById(command.userDetailId)!! val userDetail = userDetailRepository.findById(principal.userDetailId)!!
val actor = actorRepository.findById(userDetail.actorId)!! val actor = actorRepository.findById(userDetail.actorId)!!
val targetId = ActorId(command.targetActorId) val targetId = ActorId(command.targetActorId)
val target = actorRepository.findById(targetId)!! val target = actorRepository.findById(targetId)!!

View File

@ -16,6 +16,4 @@
package dev.usbharu.hideout.core.application.relationship.mute package dev.usbharu.hideout.core.application.relationship.mute
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId data class Mute(val targetActorId: Long)
data class Mute(val targetActorId: Long, val userDetailId: UserDetailId)

View File

@ -17,13 +17,13 @@
package dev.usbharu.hideout.core.application.relationship.mute package dev.usbharu.hideout.core.application.relationship.mute
import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.relationship.Relationship import dev.usbharu.hideout.core.domain.model.relationship.Relationship
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@ -35,10 +35,10 @@ class UserMuteApplicationService(
private val actorRepository: ActorRepository, private val actorRepository: ActorRepository,
private val userDetailRepository: UserDetailRepository, private val userDetailRepository: UserDetailRepository,
) : ) :
AbstractApplicationService<Mute, Unit>(transaction, logger) { LocalUserAbstractApplicationService<Mute, Unit>(transaction, logger) {
override suspend fun internalExecute(command: Mute, principal: Principal) { override suspend fun internalExecute(command: Mute, principal: FromApi) {
val userDetail = userDetailRepository.findById(command.userDetailId)!! val userDetail = userDetailRepository.findById(principal.userDetailId)!!
val actor = actorRepository.findById(userDetail.actorId)!! val actor = actorRepository.findById(userDetail.actorId)!!
val targetId = ActorId(command.targetActorId) val targetId = ActorId(command.targetActorId)

View File

@ -16,6 +16,4 @@
package dev.usbharu.hideout.core.application.relationship.rejectfollowrequest package dev.usbharu.hideout.core.application.relationship.rejectfollowrequest
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId data class RejectFollowRequest(val sourceActorId: Long)
data class RejectFollowRequest(val sourceActorId: Long, val userDetailId: UserDetailId)

View File

@ -17,12 +17,12 @@
package dev.usbharu.hideout.core.application.relationship.rejectfollowrequest package dev.usbharu.hideout.core.application.relationship.rejectfollowrequest
import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@ -34,10 +34,10 @@ class UserRejectFollowRequestApplicationService(
private val actorRepository: ActorRepository, private val actorRepository: ActorRepository,
private val userDetailRepository: UserDetailRepository, private val userDetailRepository: UserDetailRepository,
) : ) :
AbstractApplicationService<RejectFollowRequest, Unit>(transaction, logger) { LocalUserAbstractApplicationService<RejectFollowRequest, Unit>(transaction, logger) {
override suspend fun internalExecute(command: RejectFollowRequest, principal: Principal) { override suspend fun internalExecute(command: RejectFollowRequest, principal: FromApi) {
val userDetail = userDetailRepository.findById(command.userDetailId)!! val userDetail = userDetailRepository.findById(principal.userDetailId)!!
val actor = actorRepository.findById(userDetail.actorId)!! val actor = actorRepository.findById(userDetail.actorId)!!
val targetId = ActorId(command.sourceActorId) val targetId = ActorId(command.sourceActorId)

View File

@ -16,4 +16,4 @@
package dev.usbharu.hideout.core.application.relationship.removefromfollowers package dev.usbharu.hideout.core.application.relationship.removefromfollowers
data class RemoveFromFollowers(val targetActorId: Long, val userDetailId: Long) data class RemoveFromFollowers(val targetActorId: Long)

View File

@ -17,14 +17,13 @@
package dev.usbharu.hideout.core.application.relationship.removefromfollowers package dev.usbharu.hideout.core.application.relationship.removefromfollowers
import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.relationship.Relationship import dev.usbharu.hideout.core.domain.model.relationship.Relationship
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@ -36,10 +35,10 @@ class UserRemoveFromFollowersApplicationService(
private val actorRepository: ActorRepository, private val actorRepository: ActorRepository,
private val userDetailRepository: UserDetailRepository, private val userDetailRepository: UserDetailRepository,
) : ) :
AbstractApplicationService<RemoveFromFollowers, Unit>(transaction, logger) { LocalUserAbstractApplicationService<RemoveFromFollowers, Unit>(transaction, logger) {
override suspend fun internalExecute(command: RemoveFromFollowers, principal: Principal) { override suspend fun internalExecute(command: RemoveFromFollowers, principal: FromApi) {
val userDetail = userDetailRepository.findById(UserDetailId(command.userDetailId))!! val userDetail = userDetailRepository.findById(principal.userDetailId)!!
val actor = actorRepository.findById(userDetail.actorId)!! val actor = actorRepository.findById(userDetail.actorId)!!
val targetId = ActorId(command.targetActorId) val targetId = ActorId(command.targetActorId)

View File

@ -16,6 +16,4 @@
package dev.usbharu.hideout.core.application.relationship.unblock package dev.usbharu.hideout.core.application.relationship.unblock
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId data class Unblock(val targetActorId: Long)
data class Unblock(val targetActorId: Long, val userDetailId: UserDetailId)

View File

@ -17,13 +17,13 @@
package dev.usbharu.hideout.core.application.relationship.unblock package dev.usbharu.hideout.core.application.relationship.unblock
import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.relationship.Relationship import dev.usbharu.hideout.core.domain.model.relationship.Relationship
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@ -35,10 +35,10 @@ class UserUnblockApplicationService(
private val actorRepository: ActorRepository, private val actorRepository: ActorRepository,
private val userDetailRepository: UserDetailRepository, private val userDetailRepository: UserDetailRepository,
) : ) :
AbstractApplicationService<Unblock, Unit>(transaction, logger) { LocalUserAbstractApplicationService<Unblock, Unit>(transaction, logger) {
override suspend fun internalExecute(command: Unblock, principal: Principal) { override suspend fun internalExecute(command: Unblock, principal: FromApi) {
val userDetail = userDetailRepository.findById(command.userDetailId)!! val userDetail = userDetailRepository.findById(principal.userDetailId)!!
val actor = actorRepository.findById(userDetail.actorId)!! val actor = actorRepository.findById(userDetail.actorId)!!
val targetId = ActorId(command.targetActorId) val targetId = ActorId(command.targetActorId)

View File

@ -16,6 +16,4 @@
package dev.usbharu.hideout.core.application.relationship.unfollow package dev.usbharu.hideout.core.application.relationship.unfollow
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId data class Unfollow(val targetActorId: Long)
data class Unfollow(val targetActorId: Long, val userDetailId: UserDetailId)

View File

@ -17,13 +17,13 @@
package dev.usbharu.hideout.core.application.relationship.unfollow package dev.usbharu.hideout.core.application.relationship.unfollow
import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.relationship.Relationship import dev.usbharu.hideout.core.domain.model.relationship.Relationship
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@ -35,10 +35,10 @@ class UserUnfollowApplicationService(
private val actorRepository: ActorRepository, private val actorRepository: ActorRepository,
private val userDetailRepository: UserDetailRepository, private val userDetailRepository: UserDetailRepository,
) : ) :
AbstractApplicationService<Unfollow, Unit>(transaction, logger) { LocalUserAbstractApplicationService<Unfollow, Unit>(transaction, logger) {
override suspend fun internalExecute(command: Unfollow, principal: Principal) { override suspend fun internalExecute(command: Unfollow, principal: FromApi) {
val userDetail = userDetailRepository.findById(command.userDetailId)!! val userDetail = userDetailRepository.findById(principal.userDetailId)!!
val actor = actorRepository.findById(userDetail.actorId)!! val actor = actorRepository.findById(userDetail.actorId)!!
val targetId = ActorId(command.targetActorId) val targetId = ActorId(command.targetActorId)

View File

@ -16,6 +16,4 @@
package dev.usbharu.hideout.core.application.relationship.unmute package dev.usbharu.hideout.core.application.relationship.unmute
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId data class Unmute(val targetActorId: Long)
data class Unmute(val targetActorId: Long, val userDetailId: UserDetailId)

View File

@ -17,13 +17,13 @@
package dev.usbharu.hideout.core.application.relationship.unmute package dev.usbharu.hideout.core.application.relationship.unmute
import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.relationship.Relationship import dev.usbharu.hideout.core.domain.model.relationship.Relationship
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@ -35,14 +35,14 @@ class UserUnmuteApplicationService(
private val actorRepository: ActorRepository, private val actorRepository: ActorRepository,
private val userDetailRepository: UserDetailRepository, private val userDetailRepository: UserDetailRepository,
) : ) :
AbstractApplicationService<Unmute, Unit>(transaction, logger) { LocalUserAbstractApplicationService<Unmute, Unit>(transaction, logger) {
companion object { companion object {
private val logger = LoggerFactory.getLogger(UserBlockApplicationService::class.java) private val logger = LoggerFactory.getLogger(UserBlockApplicationService::class.java)
} }
override suspend fun internalExecute(command: Unmute, principal: Principal) { override suspend fun internalExecute(command: Unmute, principal: FromApi) {
val userDetail = userDetailRepository.findById(command.userDetailId)!! val userDetail = userDetailRepository.findById(principal.userDetailId)!!
val actor = actorRepository.findById(userDetail.actorId)!! val actor = actorRepository.findById(userDetail.actorId)!!
val targetId = ActorId(command.targetActorId) val targetId = ActorId(command.targetActorId)

View File

@ -1,30 +0,0 @@
/*
* 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.shared
interface CommandExecutor {
val executor: String
}
interface UserDetailGettableCommandExecutor : CommandExecutor {
val userDetailId: Long
}
data class DomainEventCommandExecutor(
override val executor: String,
val commandExecutor: CommandExecutor?
) : CommandExecutor

View File

@ -11,5 +11,5 @@ abstract class LocalUserAbstractApplicationService<T : Any, R>(transaction: Tran
return internalExecute(command, principal) return internalExecute(command, principal)
} }
abstract suspend fun internalExecute(command: T, principal: FromApi): R protected abstract suspend fun internalExecute(command: T, principal: FromApi): R
} }

View File

@ -56,6 +56,20 @@ class Filter(
) )
} }
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Filter
return id == other.id
}
override fun hashCode(): Int {
return id.hashCode()
}
companion object { companion object {
fun isAllow(user: UserDetail, action: Action, resource: Filter): Boolean { fun isAllow(user: UserDetail, action: Action, resource: Filter): Boolean {
return when (action) { return when (action) {

View File

@ -4,7 +4,12 @@ import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.support.acct.Acct import dev.usbharu.hideout.core.domain.model.support.acct.Acct
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
class FromApi(actorId: ActorId, override val userDetailId: UserDetailId, override val acct: Acct) : Principal( class FromApi(
actorId, userDetailId, actorId: ActorId,
override val userDetailId: UserDetailId,
override val acct: Acct
) : Principal(
actorId,
userDetailId,
acct acct
) )

View File

@ -0,0 +1,5 @@
package dev.usbharu.hideout.core.domain.model.support.principal
interface PrincipalContextHolder {
suspend fun getPrincipal(): Principal
}

View File

@ -2,4 +2,4 @@ package dev.usbharu.hideout.core.domain.model.timelineobject
import dev.usbharu.hideout.core.domain.model.filter.FilterId import dev.usbharu.hideout.core.domain.model.filter.FilterId
class TimelineObjectWarnFilter(val filterId: FilterId, val matchedKeyword: String) data class TimelineObjectWarnFilter(val filterId: FilterId, val matchedKeyword: String)

View File

@ -17,9 +17,10 @@
package dev.usbharu.hideout.core.domain.service.actor.local package dev.usbharu.hideout.core.domain.service.actor.local
import dev.usbharu.hideout.core.domain.model.actor.Actor import dev.usbharu.hideout.core.domain.model.actor.Actor
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
interface LocalActorMigrationCheckDomainService { interface LocalActorMigrationCheckDomainService {
suspend fun canAccountMigration(from: Actor, to: Actor): AccountMigrationCheck suspend fun canAccountMigration(userDetail: UserDetail, from: Actor, to: Actor): AccountMigrationCheck
} }
sealed class AccountMigrationCheck( sealed class AccountMigrationCheck(
@ -34,4 +35,6 @@ sealed class AccountMigrationCheck(
class AlreadyMoved(val message: String) : AccountMigrationCheck(false) class AlreadyMoved(val message: String) : AccountMigrationCheck(false)
class AlsoKnownAsNotFound(val message: String) : AccountMigrationCheck(false) class AlsoKnownAsNotFound(val message: String) : AccountMigrationCheck(false)
class MigrationCoolDown(val message: String) : AccountMigrationCheck(false)
} }

View File

@ -17,11 +17,23 @@
package dev.usbharu.hideout.core.domain.service.actor.local package dev.usbharu.hideout.core.domain.service.actor.local
import dev.usbharu.hideout.core.domain.model.actor.Actor import dev.usbharu.hideout.core.domain.model.actor.Actor
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.time.Instant
import kotlin.time.Duration.Companion.days
import kotlin.time.toJavaDuration
@Service @Service
class LocalActorMigrationCheckDomainServiceImpl : LocalActorMigrationCheckDomainService { class LocalActorMigrationCheckDomainServiceImpl : LocalActorMigrationCheckDomainService {
override suspend fun canAccountMigration(from: Actor, to: Actor): AccountMigrationCheck { override suspend fun canAccountMigration(userDetail: UserDetail, from: Actor, to: Actor): AccountMigrationCheck {
val lastMigration = userDetail.lastMigration
if (lastMigration != null) {
val instant = lastMigration.plus(30.days.toJavaDuration())
if (instant.isAfter(Instant.now())) {
return AccountMigrationCheck.MigrationCoolDown("You can migration at $instant.")
}
}
if (to == from) { if (to == from) {
return AccountMigrationCheck.SelfReferences() return AccountMigrationCheck.SelfReferences()
} }

View File

@ -0,0 +1,54 @@
package dev.usbharu.hideout.core.domain.service.post
import dev.usbharu.hideout.core.domain.model.post.Post
import dev.usbharu.hideout.core.domain.model.post.Visibility
import dev.usbharu.hideout.core.domain.model.relationship.Relationship
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
import dev.usbharu.hideout.core.domain.model.support.principal.Principal
import org.springframework.stereotype.Component
interface IPostReadAccessControl {
suspend fun isAllow(post: Post, principal: Principal): Boolean
}
@Component
class DefaultPostReadAccessControl(private val relationshipRepository: RelationshipRepository) :
IPostReadAccessControl {
override suspend fun isAllow(post: Post, principal: Principal): Boolean {
val relationship = (relationshipRepository.findByActorIdAndTargetId(post.actorId, principal.actorId)
?: Relationship.default(post.actorId, principal.actorId))
//ブロックされてたら見れない
if (relationship.blocking) {
return false
}
//PublicかUnlistedなら見れる
if (post.visibility == Visibility.PUBLIC || post.visibility == Visibility.UNLISTED) {
return true
}
//principalがAnonymousなら見れない
if (principal is Anonymous) {
return false
}
//DirectでvisibleActorsに含まれていたら見れる
if (post.visibility == Visibility.DIRECT && post.visibleActors.contains(principal.actorId)) {
return true
}
//Followersでフォロワーなら見れる
if (post.visibility == Visibility.FOLLOWERS) {
val inverseRelationship =
relationshipRepository.findByActorIdAndTargetId(principal.actorId, post.actorId) ?: return false
return inverseRelationship.following
}
//その他の場合は見れない
return false
}
}

View File

@ -0,0 +1,37 @@
package dev.usbharu.hideout.core.infrastructure.exposedquery
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors
import dev.usbharu.hideout.core.infrastructure.exposedrepository.UserDetails
import dev.usbharu.hideout.core.query.principal.PrincipalDTO
import dev.usbharu.hideout.core.query.principal.PrincipalQueryService
import org.jetbrains.exposed.sql.selectAll
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Repository
@Repository
class ExposedPrincipalQueryService : PrincipalQueryService, AbstractRepository() {
override suspend fun findByUserDetailId(userDetailId: UserDetailId): PrincipalDTO {
return query {
UserDetails.leftJoin(Actors).selectAll().where { UserDetails.id eq userDetailId.id }.single()
.let {
PrincipalDTO(
UserDetailId(it[UserDetails.id]),
ActorId(it[UserDetails.actorId]),
it[Actors.name],
it[Actors.domain]
)
}
}
}
override val logger: Logger
get() = Companion.logger
companion object {
private val logger: Logger = LoggerFactory.getLogger(ExposedPrincipalQueryService::class.java)
}
}

View File

@ -14,8 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
package dev.usbharu.hideout.core.domain.service.post package dev.usbharu.hideout.core.infrastructure.other
import dev.usbharu.hideout.core.domain.service.post.FormattedPostContent
import dev.usbharu.hideout.core.domain.service.post.PostContentFormatter
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
@ -69,14 +71,31 @@ class DefaultPostContentFormatter(private val policyFactory: PolicyFactory) : Po
if (childNode is Element && childNode.tagName() == "br") { if (childNode is Element && childNode.tagName() == "br") {
brCount++ brCount++
} else if (brCount >= 2) { } else if (brCount >= 2) {
formattedHtml.add(Element("p").appendChildren(childNodes.subList(prevIndex, index - brCount))) formattedHtml.add(
Element(element.tag(), element.baseUri(), element.attributes()).appendChildren(
childNodes.subList(
prevIndex,
index - brCount
)
)
)
prevIndex = index prevIndex = index
} }
} }
formattedHtml.add(Element("p").appendChildren(childNodes.subList(prevIndex, childNodes.size))) formattedHtml.add(
Element(element.tag(), element.baseUri(), element.attributes()).appendChildren(
childNodes.subList(
prevIndex,
childNodes.size
)
)
)
} }
val elements = Elements(formattedHtml) val elements = Elements(formattedHtml)
val document1 = Document("")
document1.outputSettings().syntax(Document.OutputSettings.Syntax.xml)
document1.insertChildren(0, elements)
return FormattedPostContent(elements.outerHtml().replace("\n", ""), printHtml(elements)) return FormattedPostContent(elements.outerHtml().replace("\n", ""), printHtml(elements))
} }

View File

@ -1,36 +0,0 @@
/*
* 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
import dev.usbharu.hideout.core.application.shared.CommandExecutor
import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutorFactory
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.oauth2.jwt.Jwt
import org.springframework.stereotype.Component
@Component
class DelegateCommandExecutorFactory(
private val oauth2CommandExecutorFactory: Oauth2CommandExecutorFactory,
private val mvcCommandExecutorFactory: SpringMvcCommandExecutorFactory,
) {
fun getCommandExecutor(): CommandExecutor {
if (SecurityContextHolder.getContext().authentication.principal is Jwt) {
return oauth2CommandExecutorFactory.getCommandExecutor()
}
return mvcCommandExecutorFactory.getCommandExecutor()
}
}

View File

@ -1,27 +0,0 @@
/*
* 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
import dev.usbharu.hideout.core.application.shared.CommandExecutor
open class HttpCommandExecutor(
override val executor: String,
val ip: String,
val userAgent: String,
) : CommandExecutor {
override fun toString(): String = "HttpCommandExecutor(executor='$executor', ip='$ip', userAgent='$userAgent')"
}

View File

@ -1,31 +0,0 @@
/*
* 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
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.stereotype.Component
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
@Component
class SpringMvcCommandExecutorFactory {
fun getCommandExecutor(): HttpCommandExecutor {
val name = SecurityContextHolder.getContext().authentication?.name ?: "ANONYMOUS"
val request = (RequestContextHolder.currentRequestAttributes() as ServletRequestAttributes).request
return HttpCommandExecutor(name, request.remoteAddr, request.getHeader("user-agent").orEmpty())
}
}

View File

@ -1,24 +0,0 @@
/*
* 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.CommandExecutor
import dev.usbharu.hideout.core.application.shared.UserDetailGettableCommandExecutor
class Oauth2CommandExecutor(override val executor: String, override val userDetailId: Long) :
CommandExecutor,
UserDetailGettableCommandExecutor

View File

@ -1,33 +0,0 @@
/*
* 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.context.SecurityContextHolder
import org.springframework.security.oauth2.jwt.Jwt
import org.springframework.stereotype.Component
@Component
class Oauth2CommandExecutorFactory {
fun getCommandExecutor(): Oauth2CommandExecutor {
val principal = SecurityContextHolder.getContext().authentication.principal as Jwt
return Oauth2CommandExecutor(
principal.subject,
principal.getClaim<String>("uid").toLong()
)
}
}

View File

@ -0,0 +1,27 @@
package dev.usbharu.hideout.core.infrastructure.springframework.oauth2
import dev.usbharu.hideout.core.domain.model.support.acct.Acct
import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.support.principal.PrincipalContextHolder
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import dev.usbharu.hideout.core.query.principal.PrincipalQueryService
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.oauth2.jwt.Jwt
import org.springframework.stereotype.Component
@Component
class SpringSecurityOauth2PrincipalContextHolder(private val principalQueryService: PrincipalQueryService) :
PrincipalContextHolder {
override suspend fun getPrincipal(): FromApi {
val principal = SecurityContextHolder.getContext().authentication?.principal as Jwt
val id = principal.getClaim<String>("uid").toLong()
val userDetail = principalQueryService.findByUserDetailId(UserDetailId(id))
return FromApi(
userDetail.actorId,
userDetail.userDetailId,
Acct(userDetail.username, userDetail.host)
)
}
}

View File

@ -263,7 +263,7 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe
) )
} }
abstract suspend fun getActors(actorIds: List<ActorId>): Map<ActorId, Actor> protected abstract suspend fun getActors(actorIds: List<ActorId>): Map<ActorId, Actor>
abstract suspend fun getUserDetails(userDetailIdList: List<UserDetailId>): Map<UserDetailId, UserDetail> protected abstract suspend fun getUserDetails(userDetailIdList: List<UserDetailId>): Map<UserDetailId, UserDetail>
} }

View File

@ -19,7 +19,6 @@ package dev.usbharu.hideout.core.interfaces.api.auth
import dev.usbharu.hideout.core.application.actor.RegisterLocalActor import dev.usbharu.hideout.core.application.actor.RegisterLocalActor
import dev.usbharu.hideout.core.application.actor.RegisterLocalActorApplicationService import dev.usbharu.hideout.core.application.actor.RegisterLocalActorApplicationService
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
import dev.usbharu.hideout.core.infrastructure.springframework.SpringMvcCommandExecutorFactory
import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletRequest
import org.springframework.stereotype.Controller import org.springframework.stereotype.Controller
import org.springframework.validation.annotation.Validated import org.springframework.validation.annotation.Validated
@ -30,7 +29,6 @@ import org.springframework.web.bind.annotation.PostMapping
@Controller @Controller
class AuthController( class AuthController(
private val registerLocalActorApplicationService: RegisterLocalActorApplicationService, private val registerLocalActorApplicationService: RegisterLocalActorApplicationService,
private val springMvcCommandExecutorFactory: SpringMvcCommandExecutorFactory,
) { ) {
@GetMapping("/auth/sign_up") @GetMapping("/auth/sign_up")
@Suppress("FunctionOnlyReturningConstant") @Suppress("FunctionOnlyReturningConstant")

View File

@ -0,0 +1,6 @@
package dev.usbharu.hideout.core.query.principal
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
data class PrincipalDTO(val userDetailId: UserDetailId, val actorId: ActorId, val username: String, val host: String)

View File

@ -0,0 +1,7 @@
package dev.usbharu.hideout.core.query.principal
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
interface PrincipalQueryService {
suspend fun findByUserDetailId(userDetailId: UserDetailId): PrincipalDTO
}

View File

@ -0,0 +1,78 @@
package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.core.application.exception.InternalServerException
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.whenever
import utils.TestTransaction
@ExtendWith(MockitoExtension::class)
class GetUserDetailApplicationServiceTest {
@InjectMocks
lateinit var service: GetUserDetailApplicationService
@Mock
lateinit var actorRepository: ActorRepository
@Mock
lateinit var userDetailRepository: UserDetailRepository
@Mock
lateinit var customEmojiRepository: CustomEmojiRepository
@Spy
val transaction = TestTransaction
@Test
fun userDetailを取得できる() = runTest {
whenever(userDetailRepository.findById(UserDetailId(1))).doReturn(
UserDetail.create(
UserDetailId(1), ActorId(1),
UserDetailHashedPassword("")
)
)
whenever(actorRepository.findById(ActorId(1))).doReturn(TestActorFactory.create(1))
whenever(customEmojiRepository.findByIds(any())).doReturn(listOf())
service.execute(GetUserDetail(1), Anonymous)
}
@Test
fun userDetailが存在しない場合失敗() = runTest {
assertThrows<IllegalArgumentException> {
service.execute(GetUserDetail(2), Anonymous)
}
}
@Test
fun userDetailが存在するけどActorが存在しない場合はInternalServerException() = runTest {
whenever(userDetailRepository.findById(UserDetailId(2))).doReturn(
UserDetail.create(
UserDetailId(2), ActorId(2),
UserDetailHashedPassword("")
)
)
assertThrows<InternalServerException> {
service.execute(GetUserDetail(2), Anonymous)
}
}
}

View File

@ -0,0 +1,171 @@
package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.core.application.exception.InternalServerException
import dev.usbharu.hideout.core.application.exception.PermissionDeniedException
import dev.usbharu.hideout.core.domain.model.actor.Actor
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory
import dev.usbharu.hideout.core.domain.model.support.acct.Acct
import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.domain.service.actor.local.AccountMigrationCheck
import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorMigrationCheckDomainService
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.*
import utils.TestTransaction
@ExtendWith(MockitoExtension::class)
class MigrationLocalActorApplicationServiceTest {
@InjectMocks
lateinit var service: MigrationLocalActorApplicationService
@Mock
lateinit var actorRepository: ActorRepository
@Mock
lateinit var localActorMigrationCheckDomainService: LocalActorMigrationCheckDomainService
@Mock
lateinit var userDetailRepository: UserDetailRepository
@Spy
val transaction = TestTransaction
@Test
fun pricinpalのactorとfromのactorが違うと失敗() = runTest {
assertThrows<PermissionDeniedException> {
service.execute(
MigrationLocalActor(1, 2),
FromApi(ActorId(3), UserDetailId(3), Acct("test", "example.com"))
)
}
}
@Test
fun fromのactorが見つからなかったら失敗() = runTest {
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword
("")
)
whenever(userDetailRepository.findById(UserDetailId(1))).doReturn(userDetail)
assertThrows<IllegalArgumentException> {
service.execute(
MigrationLocalActor(1, 2),
FromApi(ActorId(1), UserDetailId(1), Acct("test", "example.com"))
)
}
}
@Test
fun toのactorが見つからなかったら失敗() = runTest {
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword
("")
)
whenever(actorRepository.findById(ActorId(1))).doReturn(TestActorFactory.create(1))
whenever(userDetailRepository.findById(UserDetailId(1))).doReturn(userDetail)
assertThrows<IllegalArgumentException> {
service.execute(
MigrationLocalActor(1, 2),
FromApi(ActorId(1), UserDetailId(1), Acct("test", "example.com"))
)
}
}
@Test
fun userDetailが見つからなかったら失敗() = runTest {
assertThrows<InternalServerException> {
service.execute(
MigrationLocalActor(1, 2),
FromApi(ActorId(1), UserDetailId(1), Acct("test", "example.com"))
)
}
}
@Test
fun canMigrationがtrueならmoveToを書き込む() = runTest {
val from = TestActorFactory.create(1)
val to = TestActorFactory.create(2)
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword
("")
)
whenever(actorRepository.findById(ActorId(1))).doReturn(from)
whenever(actorRepository.findById(ActorId(2))).doReturn(to)
whenever(userDetailRepository.findById(UserDetailId(1))).doReturn(userDetail)
whenever(
localActorMigrationCheckDomainService.canAccountMigration(
userDetail,
from,
to
)
).doReturn(AccountMigrationCheck.CanAccountMigration())
service.execute(
MigrationLocalActor(1, 2),
FromApi(ActorId(1), UserDetailId(1), Acct("test", "example.com"))
)
argumentCaptor<Actor> {
verify(actorRepository, times(1)).save(capture())
val first = allValues.first()
assertEquals(first.moveTo, to.id)
}
}
@Test
fun canMigrationがfalseなら例外() = runTest {
val from = TestActorFactory.create(1)
val to = TestActorFactory.create(2)
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword
("")
)
whenever(actorRepository.findById(ActorId(1))).doReturn(from)
whenever(actorRepository.findById(ActorId(2))).doReturn(to)
whenever(userDetailRepository.findById(UserDetailId(1))).doReturn(userDetail)
whenever(
localActorMigrationCheckDomainService.canAccountMigration(
userDetail,
from,
to
)
).doReturn(
AccountMigrationCheck.AlreadyMoved("Message"),
AccountMigrationCheck.CircularReferences("Message"),
AccountMigrationCheck.SelfReferences(),
AccountMigrationCheck.AlsoKnownAsNotFound("Message"),
AccountMigrationCheck.MigrationCoolDown("Message")
)
repeat(5) {
assertThrows<IllegalArgumentException> {
service.execute(
MigrationLocalActor(1, 2),
FromApi(ActorId(1), UserDetailId(1), Acct("test", "example.com"))
)
}
}
verify(actorRepository, never()).save(any())
}
}

View File

@ -0,0 +1,78 @@
package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.core.application.exception.InternalServerException
import dev.usbharu.hideout.core.config.ApplicationConfig
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorDomainService
import dev.usbharu.hideout.core.domain.service.userdetail.UserDetailDomainService
import dev.usbharu.hideout.core.infrastructure.factory.ActorFactoryImpl
import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
import org.mockito.kotlin.whenever
import utils.TestTransaction
import java.net.URL
@ExtendWith(MockitoExtension::class)
class RegisterLocalActorApplicationServiceTest {
@InjectMocks
lateinit var service: RegisterLocalActorApplicationService
@Mock
lateinit var actorDomainService: LocalActorDomainService
@Mock
lateinit var actorRepository: ActorRepository
@Mock
lateinit var actorFactoryImpl: ActorFactoryImpl
@Mock
lateinit var instanceRepository: InstanceRepository
@Mock
lateinit var userDetailDomainService: UserDetailDomainService
@Mock
lateinit var userDetailRepository: UserDetailRepository
@Spy
val transaction = TestTransaction
@Spy
val applicationConfig = ApplicationConfig(URL("http://example.com"))
@Spy
val idGenerateService = TwitterSnowflakeIdGenerateService
@Test
fun usernameがすでに使われていた場合失敗() = runTest {
whenever(actorDomainService.usernameAlreadyUse(eq("test"))).doReturn(true)
assertThrows<IllegalArgumentException> {
service.execute(RegisterLocalActor("test", "password"), Anonymous)
}
}
@Test
fun ローカルインスタンスが見つからない場合失敗() = runTest {
whenever(actorDomainService.usernameAlreadyUse(eq("test"))).doReturn(false)
assertThrows<InternalServerException> {
service.execute(RegisterLocalActor("test", "password"), Anonymous)
}
}
}

View File

@ -0,0 +1,64 @@
package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.core.application.exception.InternalServerException
import dev.usbharu.hideout.core.application.exception.PermissionDeniedException
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory
import dev.usbharu.hideout.core.domain.model.support.acct.Acct
import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.whenever
import utils.TestTransaction
@ExtendWith(MockitoExtension::class)
class StartDeleteLocalActorApplicationServiceTest {
@InjectMocks
lateinit var service: StartDeleteLocalActorApplicationService
@Mock
lateinit var actorRepository: ActorRepository
@Spy
val transaction = TestTransaction
@Test
fun ローカルActorを削除できる() = runTest {
whenever(actorRepository.findById(ActorId(1))).doReturn(TestActorFactory.create(1))
service.execute(
DeleteLocalActor(ActorId(1)),
FromApi(ActorId(1), UserDetailId((1)), Acct("test", "example.com"))
)
}
@Test
fun ログイン中のユーザーと一致しない場合失敗() = runTest {
assertThrows<PermissionDeniedException> {
service.execute(
DeleteLocalActor(ActorId(2)),
FromApi(ActorId(1), UserDetailId((1)), Acct("test", "example.com"))
)
}
}
@Test
fun ユーザーが存在しない場合失敗() = runTest {
assertThrows<InternalServerException> {
service.execute(
DeleteLocalActor(ActorId(1)),
FromApi(ActorId(1), UserDetailId((1)), Acct("test", "example.com"))
)
}
}
}

View File

@ -0,0 +1,84 @@
package dev.usbharu.hideout.core.application.application
import dev.usbharu.hideout.core.domain.model.application.ApplicationRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService
import dev.usbharu.hideout.core.infrastructure.springframework.SpringSecurityPasswordEncoder
import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.SecureTokenGenerator
import kotlinx.coroutines.test.runTest
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.oauth2.core.AuthorizationGrantType
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository
import utils.TestTransaction
import java.net.URI
import java.time.Duration
@ExtendWith(MockitoExtension::class)
class RegisterApplicationApplicationServiceTest {
@InjectMocks
lateinit var service: RegisterApplicationApplicationService
@Mock
lateinit var secureTokenGenerator: SecureTokenGenerator
@Mock
lateinit var registeredClientRepository: RegisteredClientRepository
@Mock
lateinit var applicationRepository: ApplicationRepository
@Spy
val idGenerateService = TwitterSnowflakeIdGenerateService
@Spy
val passwordEncoder = SpringSecurityPasswordEncoder(BCryptPasswordEncoder())
@Spy
val transaction = TestTransaction
@Test
fun applicationを作成できる() = runTest {
whenever(secureTokenGenerator.generate()).doReturn("very-secure-token")
service.execute(
RegisterApplication("test", setOf(URI.create("https://example.com")), false, setOf("write")),
Anonymous
)
argumentCaptor<RegisteredClient> {
verify(registeredClientRepository).save(capture())
val first = allValues.first()
assertThat(first.tokenSettings.accessTokenTimeToLive).isGreaterThanOrEqualTo(Duration.ofSeconds(31536000000))
}
}
@Test
fun refreshTokenを有効化してapplicationを作成できる() = runTest {
whenever(secureTokenGenerator.generate()).doReturn("very-secure-token")
service.execute(
RegisterApplication("test", setOf(URI.create("https://example.com")), true, setOf("write")),
Anonymous
)
argumentCaptor<RegisteredClient> {
verify(registeredClientRepository).save(capture())
val first = allValues.first()
assertThat(first.authorizationGrantTypes).contains(AuthorizationGrantType.REFRESH_TOKEN)
}
}
}

View File

@ -0,0 +1,89 @@
package dev.usbharu.hideout.core.application.filter
import dev.usbharu.hideout.core.application.exception.PermissionDeniedException
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.filter.*
import dev.usbharu.hideout.core.domain.model.filter.Filter
import dev.usbharu.hideout.core.domain.model.filter.FilterKeyword
import dev.usbharu.hideout.core.domain.model.support.acct.Acct
import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.*
import utils.TestTransaction
@ExtendWith(MockitoExtension::class)
class UserDeleteFilterApplicationServiceTest {
@InjectMocks
lateinit var service: UserDeleteFilterApplicationService
@Spy
val transaction = TestTransaction
@Mock
lateinit var filterRepository: FilterRepository
@Test
fun フィルターを削除できる() = runTest {
val filter = Filter(
FilterId(1), UserDetailId(1), FilterName("filter"), setOf(FilterContext.HOME), FilterAction.HIDE, setOf(
FilterKeyword(
FilterKeywordId(1), FilterKeywordKeyword("aaa"), FilterMode.NONE
)
)
)
whenever(filterRepository.findByFilterId(FilterId(1))).doReturn(filter)
service.execute(
DeleteFilter(1), FromApi(
ActorId(1), UserDetailId(1),
Acct("test", "example.com")
)
)
verify(filterRepository, times(1)).delete(eq(filter))
}
@Test
fun フィルターが見つからない場合失敗() = runTest {
assertThrows<IllegalArgumentException> {
service.execute(
DeleteFilter(1), FromApi(
ActorId(1), UserDetailId(1),
Acct("test", "example.com")
)
)
}
}
@Test
fun フィルターのオーナー以外は失敗() = runTest {
val filter = Filter(
FilterId(1), UserDetailId(1), FilterName("filter"), setOf(FilterContext.HOME), FilterAction.HIDE, setOf(
FilterKeyword(
FilterKeywordId(1), FilterKeywordKeyword("aaa"), FilterMode.NONE
)
)
)
whenever(filterRepository.findByFilterId(FilterId(1))).doReturn(filter)
assertThrows<PermissionDeniedException> {
service.execute(
DeleteFilter(1), FromApi(
ActorId(3), UserDetailId(3),
Acct("test", "example.com")
)
)
}
verify(filterRepository, never()).delete(any())
}
}

View File

@ -0,0 +1,86 @@
package dev.usbharu.hideout.core.application.filter
import dev.usbharu.hideout.core.application.exception.PermissionDeniedException
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.filter.*
import dev.usbharu.hideout.core.domain.model.filter.Filter
import dev.usbharu.hideout.core.domain.model.filter.FilterKeyword
import dev.usbharu.hideout.core.domain.model.support.acct.Acct
import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.whenever
import utils.TestTransaction
@ExtendWith(MockitoExtension::class)
class UserGetFilterApplicationServiceTest {
@InjectMocks
lateinit var service: UserGetFilterApplicationService
@Mock
lateinit var filterRepository: FilterRepository
@Spy
val transaction = TestTransaction
@Test
fun オーナーのみ取得できる() = runTest {
val filter = Filter(
FilterId(1), UserDetailId(1), FilterName("filter"), setOf(FilterContext.HOME), FilterAction.HIDE, setOf(
FilterKeyword(
FilterKeywordId(1), FilterKeywordKeyword("aaa"), FilterMode.NONE
)
)
)
whenever(filterRepository.findByFilterId(FilterId(1))).doReturn(filter)
service.execute(
GetFilter(1), FromApi(
ActorId(1), UserDetailId(1),
Acct("test", "example.com")
)
)
}
@Test
fun オーナー以外は失敗() = runTest {
val filter = Filter(
FilterId(1), UserDetailId(1), FilterName("filter"), setOf(FilterContext.HOME), FilterAction.HIDE, setOf(
FilterKeyword(
FilterKeywordId(1), FilterKeywordKeyword("aaa"), FilterMode.NONE
)
)
)
whenever(filterRepository.findByFilterId(FilterId(1))).doReturn(filter)
assertThrows<PermissionDeniedException> {
service.execute(
GetFilter(1), FromApi(
ActorId(3), UserDetailId(3),
Acct("test", "example.com")
)
)
}
}
@Test
fun フィルターが見つからない場合失敗() = runTest {
assertThrows<IllegalArgumentException> {
service.execute(
GetFilter(1), FromApi(
ActorId(3), UserDetailId(3),
Acct("test", "example.com")
)
)
}
}
}

View File

@ -0,0 +1,55 @@
package dev.usbharu.hideout.core.application.post
import dev.usbharu.hideout.core.application.exception.PermissionDeniedException
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory
import dev.usbharu.hideout.core.domain.model.post.PostId
import dev.usbharu.hideout.core.domain.model.post.PostRepository
import dev.usbharu.hideout.core.domain.model.post.TestPostFactory
import dev.usbharu.hideout.core.domain.model.support.acct.Acct
import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.whenever
import utils.TestTransaction
@ExtendWith(MockitoExtension::class)
class DeleteLocalPostApplicationServiceTest {
@InjectMocks
lateinit var service: DeleteLocalPostApplicationService
@Mock
lateinit var postRepository: PostRepository
@Mock
lateinit var actorRepository: ActorRepository
@Spy
val transaction = TestTransaction
@Test
fun Post主はローカルPostを削除できる() = runTest {
whenever(postRepository.findById(PostId(1))).doReturn(TestPostFactory.create(actorId = 2))
whenever(actorRepository.findById(ActorId(2))).doReturn(TestActorFactory.create(id = 2))
service.execute(DeleteLocalPost(1), FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com")))
}
@Test
fun Post主以外はローカルPostを削除できない() = runTest {
whenever(postRepository.findById(PostId(1))).doReturn(TestPostFactory.create(actorId = 2))
assertThrows<PermissionDeniedException> {
service.execute(DeleteLocalPost(1), FromApi(ActorId(3), UserDetailId(3), Acct("test", "example.com")))
}
}
}

View File

@ -0,0 +1,64 @@
package dev.usbharu.hideout.core.application.post
import dev.usbharu.hideout.core.application.exception.PermissionDeniedException
import dev.usbharu.hideout.core.domain.model.post.PostId
import dev.usbharu.hideout.core.domain.model.post.PostRepository
import dev.usbharu.hideout.core.domain.model.post.TestPostFactory
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
import dev.usbharu.hideout.core.domain.service.post.IPostReadAccessControl
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.whenever
import utils.TestTransaction
@ExtendWith(MockitoExtension::class)
class GetPostApplicationServiceTest {
@InjectMocks
lateinit var service: GetPostApplicationService
@Mock
lateinit var postRepository: PostRepository
@Mock
lateinit var iPostReadAccessControl: IPostReadAccessControl
@Spy
val transaction = TestTransaction
@Test
fun postReadAccessControlがtrueを返したらPostが返ってくる() = runTest {
val post = TestPostFactory.create(id = 1)
whenever(postRepository.findById(PostId(1))).doReturn(post)
whenever(iPostReadAccessControl.isAllow(any(), any())).doReturn(true)
val actual = service.execute(GetPost(1), Anonymous)
assertEquals(Post.of(post), actual)
}
@Test
fun postが見つからない場合失敗() = runTest {
assertThrows<IllegalArgumentException> {
service.execute(GetPost(2), Anonymous)
}
}
@Test
fun postReadAccessControlがfalseを返したら失敗() = runTest {
val post = TestPostFactory.create(id = 1)
whenever(postRepository.findById(PostId(1))).doReturn(post)
whenever(iPostReadAccessControl.isAllow(any(), any())).doReturn(false)
assertThrows<PermissionDeniedException> {
service.execute(GetPost(1), Anonymous)
}
}
}

View File

@ -0,0 +1,80 @@
package dev.usbharu.hideout.core.application.post
import dev.usbharu.hideout.core.application.exception.InternalServerException
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory
import dev.usbharu.hideout.core.domain.model.post.PostRepository
import dev.usbharu.hideout.core.domain.model.post.TestPostFactory
import dev.usbharu.hideout.core.domain.model.post.Visibility
import dev.usbharu.hideout.core.domain.model.support.acct.Acct
import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import dev.usbharu.hideout.core.infrastructure.factory.PostFactoryImpl
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.*
import utils.TestTransaction
@ExtendWith(MockitoExtension::class)
class RegisterLocalPostApplicationServiceTest {
@InjectMocks
lateinit var service: RegisterLocalPostApplicationService
@Mock
lateinit var actorRepository: ActorRepository
@Mock
lateinit var postRepository: PostRepository
@Mock
lateinit var postFactoryImpl: PostFactoryImpl
@Spy
val transaction = TestTransaction
@Test
fun postを作成できる() = runTest {
val actor = TestActorFactory.create(id = 1)
val post = TestPostFactory.create()
whenever(actorRepository.findById(ActorId(1))) doReturn actor
whenever(
postFactoryImpl.createLocal(
eq(actor),
anyValueClass(),
isNull(),
eq("content test"),
eq(Visibility.PUBLIC),
isNull(),
isNull(),
eq(false),
eq(emptyList())
)
).doReturn(post)
service.execute(
RegisterLocalPost("content test", null, Visibility.PUBLIC, null, null, false, emptyList()), FromApi(
ActorId(1), UserDetailId(1), Acct("test", "example.com")
)
)
verify(postRepository, times(1)).save(eq(post))
}
@Test
fun actorが見つからないと失敗() = runTest {
assertThrows<InternalServerException> {
service.execute(
RegisterLocalPost("content test", null, Visibility.PUBLIC, null, null, false, emptyList()), FromApi(
ActorId(1), UserDetailId(1), Acct("test", "example.com")
)
)
}
}
}

View File

@ -0,0 +1,152 @@
package dev.usbharu.hideout.core.application.post
import dev.usbharu.hideout.core.application.exception.InternalServerException
import dev.usbharu.hideout.core.application.exception.PermissionDeniedException
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory
import dev.usbharu.hideout.core.domain.model.post.*
import dev.usbharu.hideout.core.domain.model.post.Post
import dev.usbharu.hideout.core.domain.model.support.acct.Acct
import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.infrastructure.factory.PostContentFactoryImpl
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.*
import utils.TestTransaction
@ExtendWith(MockitoExtension::class)
class UpdateLocalNoteApplicationServiceTest {
@InjectMocks
lateinit var service: UpdateLocalNoteApplicationService
@Mock
lateinit var postRepository: PostRepository
@Mock
lateinit var userDetailRepository: UserDetailRepository
@Mock
lateinit var actorRepository: ActorRepository
@Mock
lateinit var postContentFactoryImpl: PostContentFactoryImpl
@Spy
val transaction = TestTransaction
@Test
fun Post主はPostを編集できる() = runTest {
val post = TestPostFactory.create()
whenever(postRepository.findById(post.id)).doReturn(post)
whenever(userDetailRepository.findById(UserDetailId(1))).doReturn(
UserDetail.create(
UserDetailId(1), post.actorId,
UserDetailHashedPassword("")
)
)
whenever(actorRepository.findById(post.actorId)).doReturn(TestActorFactory.create(id = post.actorId.id))
val content = PostContent("<p>test</p>", "test", emptyList())
whenever(postContentFactoryImpl.create(eq("test"))).doReturn(content)
service.execute(
UpdateLocalNote(post.id.id, null, "test", false, emptyList()), FromApi(
post.actorId,
UserDetailId(1),
Acct("test", "example.com")
)
)
argumentCaptor<Post> {
verify(postRepository, times(1)).save(capture())
val first = allValues.first()
assertEquals(
content, first.content
)
}
}
@Test
fun postが見つからない場合失敗() = runTest {
assertThrows<IllegalArgumentException> {
service.execute(
UpdateLocalNote(1, null, "test", false, emptyList()), FromApi(
ActorId(1),
UserDetailId(1), Acct("test", "example.com")
)
)
}
}
@Test
fun post主じゃない場合失敗() = runTest {
whenever(postRepository.findById(PostId(1))).doReturn(TestPostFactory.create(id = 1, actorId = 3))
assertThrows<PermissionDeniedException> {
service.execute(
UpdateLocalNote(1, null, "test", false, emptyList()), FromApi(
ActorId(1),
UserDetailId(1), Acct("test", "example.com")
)
)
}
}
@Test
fun userDetailが見つからない場合失敗() = runTest {
whenever(postRepository.findById(PostId(1))).doReturn(TestPostFactory.create(id = 1, actorId = 1))
assertThrows<InternalServerException> {
service.execute(
UpdateLocalNote(1, null, "test", false, emptyList()), FromApi(
ActorId(1),
UserDetailId(1), Acct("test", "example.com")
)
)
}
verify(userDetailRepository, times(1)).findById(UserDetailId(1))
verify(actorRepository, never()).findById(any())
}
@Test
fun actorが見つからない場合失敗() = runTest {
val post = TestPostFactory.create()
whenever(postRepository.findById(post.id)).doReturn(post)
whenever(userDetailRepository.findById(UserDetailId(1))).doReturn(
UserDetail.create(
UserDetailId(1), post.actorId,
UserDetailHashedPassword("")
)
)
assertThrows<InternalServerException> {
service.execute(
UpdateLocalNote(post.id.id, null, "test", false, emptyList()), FromApi(
post.actorId,
UserDetailId(1),
Acct("test", "example.com")
)
)
}
verify(userDetailRepository, times(1)).findById(UserDetailId(1))
verify(actorRepository, times(1)).findById(ActorId(1))
verify(postRepository, never()).save(any())
}
}

View File

@ -0,0 +1,81 @@
package dev.usbharu.hideout.core.application.relationship.acceptfollowrequest
import dev.usbharu.hideout.core.application.exception.InternalServerException
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory
import dev.usbharu.hideout.core.domain.model.relationship.Relationship
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
import dev.usbharu.hideout.core.domain.model.support.acct.Acct
import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import utils.TestTransaction
@ExtendWith(MockitoExtension::class)
class UserAcceptFollowRequestApplicationServiceTest {
@InjectMocks
lateinit var service: UserAcceptFollowRequestApplicationService
@Mock
lateinit var relationshipRepository: RelationshipRepository
@Mock
lateinit var actorRepository: ActorRepository
@Spy
val transaction = TestTransaction
@Test
fun actorが見つからない場合失敗() = runTest {
assertThrows<InternalServerException> {
service.execute(AcceptFollowRequest(1), FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com")))
}
}
@Test
fun relationshipが見つからない場合失敗() = runTest {
whenever(actorRepository.findById(ActorId(2))).doReturn(TestActorFactory.create(id = 2))
assertThrows<InternalServerException> {
service.execute(AcceptFollowRequest(1), FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com")))
}
}
@Test
fun フォローリクエストを承認できる() = runTest {
whenever(actorRepository.findById(ActorId(2))).doReturn(TestActorFactory.create(id = 2))
whenever(relationshipRepository.findByActorIdAndTargetId(ActorId(1), ActorId(2))).doReturn(
Relationship(
actorId = ActorId(1), targetActorId = ActorId
(2),
following = false,
blocking = false,
muting = false,
followRequesting = true, mutingFollowRequest = false
)
)
service.execute(AcceptFollowRequest(1), FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com")))
argumentCaptor<Relationship> {
verify(relationshipRepository).save(capture())
val first = allValues.first()
assertFalse(first.followRequesting)
assertTrue(first.following)
}
}
}

View File

@ -0,0 +1,24 @@
package dev.usbharu.hideout.core.application.shared
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test
import org.slf4j.LoggerFactory
import utils.TestTransaction
class LocalUserAbstractApplicationServiceTest {
@Test
fun requireFromAPI() = runTest {
val logger = LoggerFactory.getLogger(javaClass)
val value = object : LocalUserAbstractApplicationService<Unit, Unit>(TestTransaction, logger) {
override suspend fun internalExecute(command: Unit, principal: FromApi) {
}
}
org.junit.jupiter.api.assertThrows<IllegalArgumentException> {
value.execute(Unit, Anonymous)
}
}
}

View File

@ -0,0 +1,14 @@
package dev.usbharu.hideout.core.domain.model.support.domain
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
class DomainTest {
@Test
fun `1000超過の長さは失敗`() {
assertThrows<IllegalArgumentException> {
Domain("a".repeat(1001))
}
}
}

View File

@ -0,0 +1,51 @@
package dev.usbharu.hideout.core.domain.service.actor.local
import dev.usbharu.hideout.core.config.ApplicationConfig
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
import org.mockito.kotlin.whenever
import java.net.URL
@ExtendWith(MockitoExtension::class)
class LocalActorDomainServiceImplTest {
@InjectMocks
lateinit var service: LocalActorDomainServiceImpl
@Mock
lateinit var actorRepository: ActorRepository
@Spy
val applicationConfig = ApplicationConfig(URL("http://example.com"))
@Test
fun findByNameAndDomainがnullならfalse() = runTest {
val actual = service.usernameAlreadyUse("test")
assertFalse(actual)
}
@Test
fun findByNameAndDomainがnullならtrue() = runTest {
whenever(actorRepository.findByNameAndDomain(eq("test"), eq("example.com"))).doReturn(TestActorFactory.create())
val actual = service.usernameAlreadyUse("test")
assertTrue(actual)
}
@Test
fun generateKeyPair() = runTest {
service.generateKeyPair()
}
}

View File

@ -2,20 +2,47 @@ package dev.usbharu.hideout.core.domain.service.actor.local
import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertInstanceOf import org.junit.jupiter.api.Assertions.assertInstanceOf
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import java.time.Instant
import kotlin.time.Duration.Companion.days
import kotlin.time.toJavaDuration
class LocalActorMigrationCheckDomainServiceImplTest { class LocalActorMigrationCheckDomainServiceImplTest {
@Test
fun 最終お引越しから30日以内だと失敗() = runTest {
val from = TestActorFactory.create()
val to = TestActorFactory.create()
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword("")
)
userDetail.lastMigration = Instant.now().minusSeconds(100)
val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl()
val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(userDetail, to, from)
assertInstanceOf(AccountMigrationCheck.MigrationCoolDown::class.java, canAccountMigration)
}
@Test @Test
fun 自分自身に引っ越しできない(): Unit = runTest { fun 自分自身に引っ越しできない(): Unit = runTest {
val from = TestActorFactory.create() val from = TestActorFactory.create()
val to = TestActorFactory.create() val to = TestActorFactory.create()
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword("")
)
val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl() val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl()
val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(from, from) val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(userDetail, from, from)
assertInstanceOf(AccountMigrationCheck.SelfReferences::class.java, canAccountMigration) assertInstanceOf(AccountMigrationCheck.SelfReferences::class.java, canAccountMigration)
} }
@ -25,10 +52,13 @@ class LocalActorMigrationCheckDomainServiceImplTest {
val from = TestActorFactory.create() val from = TestActorFactory.create()
val to = TestActorFactory.create(moveTo = 100) val to = TestActorFactory.create(moveTo = 100)
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword("")
)
val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl() val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl()
val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(from, to) val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(userDetail, from, to)
assertInstanceOf(AccountMigrationCheck.AlreadyMoved::class.java, canAccountMigration) assertInstanceOf(AccountMigrationCheck.AlreadyMoved::class.java, canAccountMigration)
} }
@ -37,10 +67,13 @@ class LocalActorMigrationCheckDomainServiceImplTest {
fun 自分自身が引っ越している場合は引っ越しできない() = runTest { fun 自分自身が引っ越している場合は引っ越しできない() = runTest {
val from = TestActorFactory.create(moveTo = 100) val from = TestActorFactory.create(moveTo = 100)
val to = TestActorFactory.create() val to = TestActorFactory.create()
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword("")
)
val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl() val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl()
val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(from, to) val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(userDetail, from, to)
assertInstanceOf(AccountMigrationCheck.AlreadyMoved::class.java, canAccountMigration) assertInstanceOf(AccountMigrationCheck.AlreadyMoved::class.java, canAccountMigration)
} }
@ -49,10 +82,13 @@ class LocalActorMigrationCheckDomainServiceImplTest {
fun 引越し先のalsoKnownAsに引越し元が含まれてない場合失敗する() = runTest { fun 引越し先のalsoKnownAsに引越し元が含まれてない場合失敗する() = runTest {
val from = TestActorFactory.create() val from = TestActorFactory.create()
val to = TestActorFactory.create(alsoKnownAs = setOf(ActorId(100))) val to = TestActorFactory.create(alsoKnownAs = setOf(ActorId(100)))
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword("")
)
val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl() val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl()
val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(from, to) val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(userDetail, from, to)
assertInstanceOf(AccountMigrationCheck.AlsoKnownAsNotFound::class.java, canAccountMigration) assertInstanceOf(AccountMigrationCheck.AlsoKnownAsNotFound::class.java, canAccountMigration)
} }
@ -61,10 +97,29 @@ class LocalActorMigrationCheckDomainServiceImplTest {
fun 正常に設定されている場合は成功する() = runTest { fun 正常に設定されている場合は成功する() = runTest {
val from = TestActorFactory.create() val from = TestActorFactory.create()
val to = TestActorFactory.create(alsoKnownAs = setOf(from.id, ActorId(100))) val to = TestActorFactory.create(alsoKnownAs = setOf(from.id, ActorId(100)))
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword("")
)
val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl() val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl()
val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(from, to) val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(userDetail, from, to)
assertInstanceOf(AccountMigrationCheck.CanAccountMigration::class.java, canAccountMigration)
}
@Test
fun お引越し履歴があっても30日以上経っていたら成功する() = runTest {
val from = TestActorFactory.create()
val to = TestActorFactory.create(alsoKnownAs = setOf(from.id, ActorId(100)))
val userDetail = UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword("")
)
userDetail.lastMigration = Instant.now().minus(31.days.toJavaDuration())
val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl()
val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(userDetail, from, to)
assertInstanceOf(AccountMigrationCheck.CanAccountMigration::class.java, canAccountMigration) assertInstanceOf(AccountMigrationCheck.CanAccountMigration::class.java, canAccountMigration)
} }

View File

@ -0,0 +1,158 @@
package dev.usbharu.hideout.core.domain.service.post
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.post.TestPostFactory
import dev.usbharu.hideout.core.domain.model.post.Visibility
import dev.usbharu.hideout.core.domain.model.relationship.Relationship
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
import dev.usbharu.hideout.core.domain.model.support.acct.Acct
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.whenever
@ExtendWith(MockitoExtension::class)
class DefaultPostReadAccessControlTest {
@InjectMocks
lateinit var service: DefaultPostReadAccessControl
@Mock
lateinit var relationshipRepository: RelationshipRepository
@Test
fun ブロックされてたら見れない() = runTest {
whenever(relationshipRepository.findByActorIdAndTargetId(ActorId(1), ActorId(2))).doReturn(
Relationship(
actorId = ActorId(1),
targetActorId = ActorId(2),
following = false,
blocking = true,
muting = false,
followRequesting = false,
mutingFollowRequest = false,
)
)
val actual = service.isAllow(
TestPostFactory.create(actorId = 1),
FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com"))
)
assertFalse(actual)
}
@Test
fun PublicかUnlistedなら見れる() = runTest {
val actual = service.isAllow(TestPostFactory.create(visibility = Visibility.PUBLIC), Anonymous)
assertTrue(actual)
val actual2 = service.isAllow(TestPostFactory.create(visibility = Visibility.UNLISTED), Anonymous)
assertTrue(actual2)
}
@Test
fun FollowersかDirecのときAnonymousなら見れない() = runTest {
val actual = service.isAllow(TestPostFactory.create(visibility = Visibility.FOLLOWERS), Anonymous)
assertFalse(actual)
val actual2 = service.isAllow(TestPostFactory.create(visibility = Visibility.DIRECT), Anonymous)
assertFalse(actual2)
}
@Test
fun DirectでvisibleActorsに含まれていたら見れる() = runTest {
val actual = service.isAllow(
TestPostFactory.create(actorId = 1, visibility = Visibility.DIRECT, visibleActors = listOf(2)),
FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com"))
)
assertTrue(actual)
}
@Test
fun DirectでvisibleActorsに含まれていなかったら見れない() = runTest {
val actual = service.isAllow(
TestPostFactory.create(actorId = 1, visibility = Visibility.DIRECT, visibleActors = listOf(3)),
FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com"))
)
assertFalse(actual)
}
@Test
fun Followersでフォロワーなら見れる() = runTest {
whenever(relationshipRepository.findByActorIdAndTargetId(ActorId(1), ActorId(2))).doReturn(
Relationship.default(
actorId = ActorId(1),
targetActorId = ActorId(2)
)
)
whenever(relationshipRepository.findByActorIdAndTargetId(ActorId(2), ActorId(1))).doReturn(
Relationship(
actorId = ActorId(2),
targetActorId = ActorId(1),
following = true,
blocking = false,
muting = false,
followRequesting = false,
mutingFollowRequest = false
)
)
val actual = service.isAllow(
TestPostFactory.create(actorId = 1, visibility = Visibility.FOLLOWERS),
FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com"))
)
assertTrue(actual)
}
@Test
fun relationshipが見つからない場合見れない() = runTest {
val actual = service.isAllow(
TestPostFactory.create(actorId = 1, visibility = Visibility.FOLLOWERS),
FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com"))
)
assertFalse(actual)
}
@Test
fun フォロワーじゃない場合は見れない() = runTest {
whenever(relationshipRepository.findByActorIdAndTargetId(ActorId(1), ActorId(2))).doReturn(
Relationship.default(
actorId = ActorId(1),
targetActorId = ActorId(2)
)
)
whenever(relationshipRepository.findByActorIdAndTargetId(ActorId(2), ActorId(1))).doReturn(
Relationship(
actorId = ActorId(2),
targetActorId = ActorId(1),
following = false,
blocking = false,
muting = false,
followRequesting = false,
mutingFollowRequest = false
)
)
val actual = service.isAllow(
TestPostFactory.create(actorId = 1, visibility = Visibility.FOLLOWERS),
FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com"))
)
assertFalse(actual)
}
}

View File

@ -0,0 +1,75 @@
package dev.usbharu.hideout.core.infrastructure.other
import dev.usbharu.hideout.core.config.HtmlSanitizeConfig
import dev.usbharu.hideout.core.domain.service.post.FormattedPostContent
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import kotlin.test.assertEquals
@ExtendWith(MockitoExtension::class)
class DefaultPostContentFormatterTest {
@InjectMocks
lateinit var formatter: DefaultPostContentFormatter
@Spy
val policyFactory = HtmlSanitizeConfig().policy()
@Test
fun 文字だけのHTMLをPで囲む() {
val actual = formatter.format("a")
assertEquals(FormattedPostContent("<p>a</p>", "a"), actual)
}
@Test
fun エレメントはそのまま() {
val actual = formatter.format("<p>a</p>")
assertEquals(FormattedPostContent("<p>a</p>", "a"), actual)
}
@Test
fun コメントは無視() {
val actual = formatter.format("<!-- aa -->")
assertEquals(FormattedPostContent("", ""), actual)
}
@Test
fun brタグを改行に() {
val actual = formatter.format("<p>a<br></p>")
assertEquals(FormattedPostContent("<p>a<br /></p>", "a\n"), actual)
}
@Test
fun brタグ2連続を段落に() {
val format = formatter.format("<p>a<br><br>a</p>")
assertEquals(FormattedPostContent("<p>a</p><p>a</p>", "a\n\na"), format)
}
@Test
fun brタグ3連続以上を段落にして改行2つに変換() {
val format = formatter.format("<p>a<br><br><br>a</p>")
assertEquals(FormattedPostContent("<p>a</p><p>a</p>", "a\n\na"), format)
}
@Test
fun aタグは許可される() {
val format = formatter.format("<a href=\"https://example.com\">p</a>")
assertEquals(FormattedPostContent("<a href=\"https://example.com\">p</a>", "p"), format)
}
@Test
fun pの中のaタグも許可される() {
val actual = formatter.format("<p><a href=\"https://example.com\">a</a></p>")
assertEquals(FormattedPostContent("<p><a href=\"https://example.com\">a</a></p>", "a"), actual)
}
}

View File

@ -0,0 +1,30 @@
package dev.usbharu.hideout.core.infrastructure.other
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class TwitterSnowflakeIdGenerateServiceTest {
@Test
fun noDuplicateTest() = runBlocking {
val mutex = Mutex()
val mutableListOf = mutableListOf<Long>()
coroutineScope {
repeat(500000) {
launch(Dispatchers.IO) {
val id = TwitterSnowflakeIdGenerateService.generateId()
mutex.withLock {
mutableListOf.add(id)
}
}
}
}
assertEquals(0, mutableListOf.size - mutableListOf.toSet().size)
}
}

View File

@ -0,0 +1,91 @@
package dev.usbharu.hideout.core.infrastructure.springframework.oauth2
import dev.usbharu.hideout.core.config.ApplicationConfig
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.*
import org.springframework.security.core.userdetails.UsernameNotFoundException
import utils.TestTransaction
import java.net.URL
import kotlin.test.assertEquals
@ExtendWith(MockitoExtension::class)
class UserDetailsServiceImplTest {
@InjectMocks
lateinit var service: UserDetailsServiceImpl
@Mock
lateinit var actorRepository: ActorRepository
@Mock
lateinit var userDetailRepository: UserDetailRepository
@Spy
val applicationConfig = ApplicationConfig(URL("http://example.com"))
@Spy
val transaction = TestTransaction
@Test
fun usernameがnullなら失敗() = runTest {
assertThrows<UsernameNotFoundException> {
service.loadUserByUsername(null)
}
verify(actorRepository, never()).findByNameAndDomain(any(), any())
}
@Test
fun actorが見つからない場合失敗() = runTest {
assertThrows<UsernameNotFoundException> {
service.loadUserByUsername("test")
}
verify(actorRepository, times(1)).findByNameAndDomain(eq("test"), eq("example.com"))
}
@Test
fun userDetailが見つからない場合失敗() = runTest {
whenever(actorRepository.findByNameAndDomain(eq("test"), eq("example.com"))).doReturn(
TestActorFactory.create(
actorName = "test", id = 1
)
)
assertThrows<UsernameNotFoundException> {
service.loadUserByUsername("test")
}
verify(actorRepository, times(1)).findByNameAndDomain(eq("test"), eq("example.com"))
verify(userDetailRepository, times(1)).findByActorId(eq(1))
}
@Test
fun 全部見つかったら成功() = runTest {
whenever(
actorRepository.findByNameAndDomain(
eq("test"),
eq("example.com")
)
).doReturn(TestActorFactory.create(id = 1))
whenever(userDetailRepository.findByActorId(eq(1))).doReturn(
UserDetail.create(
UserDetailId(1),
ActorId(1), UserDetailHashedPassword("")
)
)
val actual = service.loadUserByUsername("test")
assertEquals(HideoutUserDetails(HashSet(), "", "test-1", 1), actual)
}
}

View File

@ -0,0 +1,494 @@
package dev.usbharu.hideout.core.infrastructure.timeline
import dev.usbharu.hideout.core.config.DefaultTimelineStoreConfig
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.filter.*
import dev.usbharu.hideout.core.domain.model.post.PostRepository
import dev.usbharu.hideout.core.domain.model.post.TestPostFactory
import dev.usbharu.hideout.core.domain.model.post.Visibility
import dev.usbharu.hideout.core.domain.model.timeline.*
import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObject
import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObjectWarnFilter
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationship
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipId
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipRepository
import dev.usbharu.hideout.core.domain.model.timelinerelationship.Visible
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.domain.service.filter.FilterDomainService
import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService
import kotlinx.coroutines.test.runTest
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.*
@ExtendWith(MockitoExtension::class)
class DefaultTimelineStoreTest {
@InjectMocks
lateinit var timelineStore: DefaultTimelineStore
@Mock
lateinit var timelineRepository: TimelineRepository
@Mock
lateinit var timelineRelationshipRepository: TimelineRelationshipRepository
@Mock
lateinit var filterRepository: FilterRepository
@Mock
lateinit var postRepository: PostRepository
@Mock
lateinit var filterDomainService: FilterDomainService
@Mock
lateinit var internalTimelineObjectRepository: InternalTimelineObjectRepository
@Mock
lateinit var userDetailRepository: UserDetailRepository
@Mock
lateinit var actorRepository: ActorRepository
@Spy
val defaultTimelineStoreConfig = DefaultTimelineStoreConfig(500)
@Spy
val idGenerateService = TwitterSnowflakeIdGenerateService
@Test
fun addPost() = runTest {
val post = TestPostFactory.create()
whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn(
listOf(
TimelineRelationship(
TimelineRelationshipId(1),
TimelineId(12),
post.actorId,
Visible.PUBLIC
)
)
)
whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn(
listOf(
Timeline(
id = TimelineId(12),
userDetailId = UserDetailId(post.actorId.id),
name = TimelineName("timeline"),
visibility = TimelineVisibility.PUBLIC,
isSystem = false
)
)
)
val filters = listOf(
Filter(
id = FilterId(13),
userDetailId = UserDetailId(post.actorId.id),
name = FilterName("filter"),
filterContext = setOf(FilterContext.HOME),
filterAction = FilterAction.HIDE,
filterKeywords = setOf(
FilterKeyword(FilterKeywordId(14), FilterKeywordKeyword("aa"), FilterMode.NONE)
)
)
)
whenever(filterRepository.findByUserDetailId(UserDetailId(post.actorId.id))).doReturn(filters)
whenever(filterDomainService.apply(post, FilterContext.HOME, filters)).doReturn(
FilteredPost(
post, listOf(
FilterResult(filters.first(), "aaa")
)
)
)
timelineStore.addPost(post)
argumentCaptor<List<TimelineObject>> {
verify(internalTimelineObjectRepository, times(1)).saveAll(capture())
val timelineObjectList = allValues.first()
assertThat(timelineObjectList).allSatisfy {
assertThat(it.postId).isEqualTo(post.id)
assertThat(it.postActorId).isEqualTo(post.actorId)
assertThat(it.replyId).isNull()
assertThat(it.replyActorId).isNull()
assertThat(it.repostId).isNull()
assertThat(it.repostActorId).isNull()
assertThat(it.userDetailId).isEqualTo(UserDetailId(post.actorId.id))
assertThat(it.timelineId).isEqualTo(TimelineId(12))
assertThat(it.warnFilters).contains(TimelineObjectWarnFilter(FilterId(13), "aaa"))
}
}
}
@Test
fun `addPost direct投稿は追加されない`() = runTest {
val post = TestPostFactory.create(visibility = Visibility.DIRECT)
whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn(
listOf(
TimelineRelationship(
TimelineRelationshipId(1),
TimelineId(12),
post.actorId,
Visible.PUBLIC
)
)
)
whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn(
listOf(
Timeline(
id = TimelineId(12),
userDetailId = UserDetailId(post.actorId.id),
name = TimelineName("timeline"),
visibility = TimelineVisibility.PUBLIC,
isSystem = false
)
)
)
timelineStore.addPost(post)
argumentCaptor<List<TimelineObject>> {
verify(internalTimelineObjectRepository, times(1)).saveAll(capture())
val timelineObjectList = allValues.first()
assertThat(timelineObjectList).isEmpty()
}
}
@Test
fun timelineがpublicでpostがUNLISTEDの時追加されない() = runTest {
val post = TestPostFactory.create(visibility = Visibility.UNLISTED)
whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn(
listOf(
TimelineRelationship(
TimelineRelationshipId(1),
TimelineId(12),
post.actorId,
Visible.PUBLIC
)
)
)
whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn(
listOf(
Timeline(
id = TimelineId(12),
userDetailId = UserDetailId(post.actorId.id),
name = TimelineName("timeline"),
visibility = TimelineVisibility.PUBLIC,
isSystem = false
)
)
)
timelineStore.addPost(post)
argumentCaptor<List<TimelineObject>> {
verify(internalTimelineObjectRepository, times(1)).saveAll(capture())
val timelineObjectList = allValues.first()
assertThat(timelineObjectList).isEmpty()
}
}
@Test
fun timelineがpublicでpostがFOLLOWERSの時追加されない() = runTest {
val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS)
whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn(
listOf(
TimelineRelationship(
TimelineRelationshipId(1),
TimelineId(12),
post.actorId,
Visible.PUBLIC
)
)
)
whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn(
listOf(
Timeline(
id = TimelineId(12),
userDetailId = UserDetailId(post.actorId.id),
name = TimelineName("timeline"),
visibility = TimelineVisibility.PUBLIC,
isSystem = false
)
)
)
timelineStore.addPost(post)
argumentCaptor<List<TimelineObject>> {
verify(internalTimelineObjectRepository, times(1)).saveAll(capture())
val timelineObjectList = allValues.first()
assertThat(timelineObjectList).isEmpty()
}
}
@Test
fun timelineがUNLISTEDでpostがFOLLOWERSの時追加されない() = runTest {
val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS)
whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn(
listOf(
TimelineRelationship(
TimelineRelationshipId(1),
TimelineId(12),
post.actorId,
Visible.PUBLIC
)
)
)
whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn(
listOf(
Timeline(
id = TimelineId(12),
userDetailId = UserDetailId(post.actorId.id),
name = TimelineName("timeline"),
visibility = TimelineVisibility.UNLISTED,
isSystem = false
)
)
)
timelineStore.addPost(post)
argumentCaptor<List<TimelineObject>> {
verify(internalTimelineObjectRepository, times(1)).saveAll(capture())
val timelineObjectList = allValues.first()
assertThat(timelineObjectList).isEmpty()
}
}
@Test
fun timelineがPRIVATEでpostがFOLLOWERSの時追加される() = runTest {
val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS)
whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn(
listOf(
TimelineRelationship(
TimelineRelationshipId(1),
TimelineId(12),
post.actorId,
Visible.PUBLIC
)
)
)
whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn(
listOf(
Timeline(
id = TimelineId(12),
userDetailId = UserDetailId(post.actorId.id),
name = TimelineName("timeline"),
visibility = TimelineVisibility.PRIVATE,
isSystem = false
)
)
)
val filters = listOf(
Filter(
id = FilterId(13),
userDetailId = UserDetailId(post.actorId.id),
name = FilterName("filter"),
filterContext = setOf(FilterContext.HOME),
filterAction = FilterAction.HIDE,
filterKeywords = setOf(
FilterKeyword(FilterKeywordId(14), FilterKeywordKeyword("aa"), FilterMode.NONE)
)
)
)
whenever(filterRepository.findByUserDetailId(UserDetailId(post.actorId.id))).doReturn(filters)
whenever(filterDomainService.apply(post, FilterContext.HOME, filters)).doReturn(
FilteredPost(
post, listOf(
FilterResult(filters.first(), "aaa")
)
)
)
timelineStore.addPost(post)
argumentCaptor<List<TimelineObject>> {
verify(internalTimelineObjectRepository, times(1)).saveAll(capture())
val timelineObjectList = allValues.first()
assertThat(timelineObjectList).allSatisfy {
assertThat(it.postId).isEqualTo(post.id)
assertThat(it.postActorId).isEqualTo(post.actorId)
assertThat(it.replyId).isNull()
assertThat(it.replyActorId).isNull()
assertThat(it.repostId).isNull()
assertThat(it.repostActorId).isNull()
assertThat(it.userDetailId).isEqualTo(UserDetailId(post.actorId.id))
assertThat(it.timelineId).isEqualTo(TimelineId(12))
assertThat(it.warnFilters).contains(TimelineObjectWarnFilter(FilterId(13), "aaa"))
}
}
}
@Test
fun repostがあるときはRepostを含めたTimelineObjectが作成される() = runTest {
val repost = TestPostFactory.create()
val post = TestPostFactory.create(repostId = repost.id.id)
whenever(postRepository.findById(repost.id)).doReturn(repost)
whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn(
listOf(
TimelineRelationship(
TimelineRelationshipId(1),
TimelineId(12),
post.actorId,
Visible.PUBLIC
)
)
)
whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn(
listOf(
Timeline(
id = TimelineId(12),
userDetailId = UserDetailId(post.actorId.id),
name = TimelineName("timeline"),
visibility = TimelineVisibility.PUBLIC,
isSystem = false
)
)
)
val filters = listOf(
Filter(
id = FilterId(13),
userDetailId = UserDetailId(post.actorId.id),
name = FilterName("filter"),
filterContext = setOf(FilterContext.HOME),
filterAction = FilterAction.HIDE,
filterKeywords = setOf(
FilterKeyword(FilterKeywordId(14), FilterKeywordKeyword("aa"), FilterMode.NONE)
)
)
)
whenever(filterRepository.findByUserDetailId(UserDetailId(post.actorId.id))).doReturn(filters)
whenever(filterDomainService.apply(post, FilterContext.HOME, filters)).doReturn(
FilteredPost(
post, listOf(
FilterResult(filters.first(), "aaa")
)
)
)
timelineStore.addPost(post)
argumentCaptor<List<TimelineObject>> {
verify(internalTimelineObjectRepository, times(1)).saveAll(capture())
val timelineObjectList = allValues.first()
assertThat(timelineObjectList).allSatisfy {
assertThat(it.postId).isEqualTo(post.id)
assertThat(it.postActorId).isEqualTo(post.actorId)
assertThat(it.replyId).isNull()
assertThat(it.replyActorId).isNull()
assertThat(it.repostId).isEqualTo(repost.id)
assertThat(it.repostActorId).isEqualTo(repost.actorId)
assertThat(it.userDetailId).isEqualTo(UserDetailId(post.actorId.id))
assertThat(it.timelineId).isEqualTo(TimelineId(12))
assertThat(it.warnFilters).contains(TimelineObjectWarnFilter(FilterId(13), "aaa"))
}
}
}
@Test
fun replyがあるときはReplyを含めたTimeineObjectが作成される() = runTest {
val reply = TestPostFactory.create()
val post = TestPostFactory.create(replyId = reply.id.id)
whenever(postRepository.findById(reply.id)).doReturn(reply)
whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn(
listOf(
TimelineRelationship(
TimelineRelationshipId(1),
TimelineId(12),
post.actorId,
Visible.PUBLIC
)
)
)
whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn(
listOf(
Timeline(
id = TimelineId(12),
userDetailId = UserDetailId(post.actorId.id),
name = TimelineName("timeline"),
visibility = TimelineVisibility.PUBLIC,
isSystem = false
)
)
)
val filters = listOf(
Filter(
id = FilterId(13),
userDetailId = UserDetailId(post.actorId.id),
name = FilterName("filter"),
filterContext = setOf(FilterContext.HOME),
filterAction = FilterAction.HIDE,
filterKeywords = setOf(
FilterKeyword(FilterKeywordId(14), FilterKeywordKeyword("aa"), FilterMode.NONE)
)
)
)
whenever(filterRepository.findByUserDetailId(UserDetailId(post.actorId.id))).doReturn(filters)
whenever(filterDomainService.apply(post, FilterContext.HOME, filters)).doReturn(
FilteredPost(
post, listOf(
FilterResult(filters.first(), "aaa")
)
)
)
timelineStore.addPost(post)
argumentCaptor<List<TimelineObject>> {
verify(internalTimelineObjectRepository, times(1)).saveAll(capture())
val timelineObjectList = allValues.first()
assertThat(timelineObjectList).allSatisfy {
assertThat(it.postId).isEqualTo(post.id)
assertThat(it.postActorId).isEqualTo(post.actorId)
assertThat(it.repostId).isNull()
assertThat(it.repostActorId).isNull()
assertThat(it.replyId).isEqualTo(reply.id)
assertThat(it.replyActorId).isEqualTo(reply.actorId)
assertThat(it.userDetailId).isEqualTo(UserDetailId(post.actorId.id))
assertThat(it.timelineId).isEqualTo(TimelineId(12))
assertThat(it.warnFilters).contains(TimelineObjectWarnFilter(FilterId(13), "aaa"))
}
}
}
}

View File

@ -38,8 +38,7 @@ import dev.usbharu.hideout.core.application.relationship.unfollow.Unfollow
import dev.usbharu.hideout.core.application.relationship.unfollow.UserUnfollowApplicationService import dev.usbharu.hideout.core.application.relationship.unfollow.UserUnfollowApplicationService
import dev.usbharu.hideout.core.application.relationship.unmute.Unmute import dev.usbharu.hideout.core.application.relationship.unmute.Unmute
import dev.usbharu.hideout.core.application.relationship.unmute.UserUnmuteApplicationService import dev.usbharu.hideout.core.application.relationship.unmute.UserUnmuteApplicationService
import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutor import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.SpringSecurityOauth2PrincipalContextHolder
import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutorFactory
import dev.usbharu.hideout.mastodon.application.accounts.GetAccount import dev.usbharu.hideout.mastodon.application.accounts.GetAccount
import dev.usbharu.hideout.mastodon.application.accounts.GetAccountApplicationService import dev.usbharu.hideout.mastodon.application.accounts.GetAccountApplicationService
import dev.usbharu.hideout.mastodon.interfaces.api.generated.AccountApi import dev.usbharu.hideout.mastodon.interfaces.api.generated.AccountApi
@ -49,7 +48,6 @@ import org.springframework.stereotype.Controller
@Controller @Controller
class SpringAccountApi( class SpringAccountApi(
private val oauth2CommandExecutorFactory: Oauth2CommandExecutorFactory,
private val getUserDetailApplicationService: GetUserDetailApplicationService, private val getUserDetailApplicationService: GetUserDetailApplicationService,
private val getAccountApplicationService: GetAccountApplicationService, private val getAccountApplicationService: GetAccountApplicationService,
private val userFollowRequestApplicationService: UserFollowRequestApplicationService, private val userFollowRequestApplicationService: UserFollowRequestApplicationService,
@ -62,31 +60,33 @@ class SpringAccountApi(
private val userRejectFollowRequestApplicationService: UserRejectFollowRequestApplicationService, private val userRejectFollowRequestApplicationService: UserRejectFollowRequestApplicationService,
private val userRemoveFromFollowersApplicationService: UserRemoveFromFollowersApplicationService, private val userRemoveFromFollowersApplicationService: UserRemoveFromFollowersApplicationService,
private val userUnfollowApplicationService: UserUnfollowApplicationService, private val userUnfollowApplicationService: UserUnfollowApplicationService,
private val principalContextHolder: SpringSecurityOauth2PrincipalContextHolder
) : AccountApi { ) : AccountApi {
override suspend fun apiV1AccountsIdBlockPost(id: String): ResponseEntity<Relationship> { override suspend fun apiV1AccountsIdBlockPost(id: String): ResponseEntity<Relationship> {
val executor = oauth2CommandExecutorFactory.getCommandExecutor() userBlockApplicationService.execute(Block(id.toLong()), principalContextHolder.getPrincipal())
userBlockApplicationService.execute(Block(id.toLong())) return fetchRelationship(id)
return fetchRelationship(id, executor)
} }
override suspend fun apiV1AccountsIdFollowPost( override suspend fun apiV1AccountsIdFollowPost(
id: String, id: String,
followRequestBody: FollowRequestBody?, followRequestBody: FollowRequestBody?,
): ResponseEntity<Relationship> { ): ResponseEntity<Relationship> {
val executor = oauth2CommandExecutorFactory.getCommandExecutor()
userFollowRequestApplicationService.execute( userFollowRequestApplicationService.execute(
FollowRequest(id.toLong()) FollowRequest(id.toLong()), principalContextHolder.getPrincipal()
) )
return fetchRelationship(id, executor) return fetchRelationship(id)
} }
private suspend fun fetchRelationship( private suspend fun fetchRelationship(
id: String, id: String,
executor: Oauth2CommandExecutor,
): ResponseEntity<Relationship> { ): ResponseEntity<Relationship> {
val relationship = getRelationshipApplicationService.execute(GetRelationship(id.toLong())) val relationship = getRelationshipApplicationService.execute(
GetRelationship(id.toLong()),
principalContextHolder.getPrincipal()
)
return ResponseEntity.ok( return ResponseEntity.ok(
Relationship( Relationship(
id = relationship.targetId.toString(), id = relationship.targetId.toString(),
@ -109,49 +109,44 @@ class SpringAccountApi(
override suspend fun apiV1AccountsIdGet(id: String): ResponseEntity<Account> { override suspend fun apiV1AccountsIdGet(id: String): ResponseEntity<Account> {
return ResponseEntity.ok( return ResponseEntity.ok(
getAccountApplicationService.execute( getAccountApplicationService.execute(
GetAccount(id) GetAccount(id), principalContextHolder.getPrincipal()
) )
) )
} }
override suspend fun apiV1AccountsIdMutePost(id: String): ResponseEntity<Relationship> { override suspend fun apiV1AccountsIdMutePost(id: String): ResponseEntity<Relationship> {
val executor = oauth2CommandExecutorFactory.getCommandExecutor()
userMuteApplicationService.execute( userMuteApplicationService.execute(
Mute(id.toLong()) Mute(id.toLong()), principalContextHolder.getPrincipal()
) )
return fetchRelationship(id, executor) return fetchRelationship(id)
} }
override suspend fun apiV1AccountsIdRemoveFromFollowersPost(id: String): ResponseEntity<Relationship> { override suspend fun apiV1AccountsIdRemoveFromFollowersPost(id: String): ResponseEntity<Relationship> {
val executor = oauth2CommandExecutorFactory.getCommandExecutor()
userRemoveFromFollowersApplicationService.execute( userRemoveFromFollowersApplicationService.execute(
RemoveFromFollowers(id.toLong()) RemoveFromFollowers(id.toLong()), principalContextHolder.getPrincipal()
) )
return fetchRelationship(id, executor) return fetchRelationship(id)
} }
override suspend fun apiV1AccountsIdUnblockPost(id: String): ResponseEntity<Relationship> { override suspend fun apiV1AccountsIdUnblockPost(id: String): ResponseEntity<Relationship> {
val executor = oauth2CommandExecutorFactory.getCommandExecutor()
userUnblockApplicationService.execute( userUnblockApplicationService.execute(
Unblock(id.toLong()) Unblock(id.toLong()), principalContextHolder.getPrincipal()
) )
return fetchRelationship(id, executor) return fetchRelationship(id)
} }
override suspend fun apiV1AccountsIdUnfollowPost(id: String): ResponseEntity<Relationship> { override suspend fun apiV1AccountsIdUnfollowPost(id: String): ResponseEntity<Relationship> {
val executor = oauth2CommandExecutorFactory.getCommandExecutor()
userUnfollowApplicationService.execute( userUnfollowApplicationService.execute(
Unfollow(id.toLong()) Unfollow(id.toLong()), principalContextHolder.getPrincipal()
) )
return fetchRelationship(id, executor) return fetchRelationship(id)
} }
override suspend fun apiV1AccountsIdUnmutePost(id: String): ResponseEntity<Relationship> { override suspend fun apiV1AccountsIdUnmutePost(id: String): ResponseEntity<Relationship> {
val executor = oauth2CommandExecutorFactory.getCommandExecutor()
userUnmuteApplicationService.execute( userUnmuteApplicationService.execute(
Unmute(id.toLong()) Unmute(id.toLong()), principalContextHolder.getPrincipal()
) )
return fetchRelationship(id, executor) return fetchRelationship(id)
} }
override suspend fun apiV1AccountsPost(accountsCreateRequest: AccountsCreateRequest): ResponseEntity<Unit> { override suspend fun apiV1AccountsPost(accountsCreateRequest: AccountsCreateRequest): ResponseEntity<Unit> {
@ -163,9 +158,9 @@ class SpringAccountApi(
} }
override suspend fun apiV1AccountsVerifyCredentialsGet(): ResponseEntity<CredentialAccount> { override suspend fun apiV1AccountsVerifyCredentialsGet(): ResponseEntity<CredentialAccount> {
val commandExecutor = oauth2CommandExecutorFactory.getCommandExecutor() val principal = principalContextHolder.getPrincipal()
val localActor = val localActor =
getUserDetailApplicationService.execute(GetUserDetail(commandExecutor.userDetailId)) getUserDetailApplicationService.execute(GetUserDetail(principal.userDetailId.id), principal)
return ResponseEntity.ok( return ResponseEntity.ok(
CredentialAccount( CredentialAccount(
@ -215,19 +210,19 @@ class SpringAccountApi(
} }
override suspend fun apiV1FollowRequestsAccountIdAuthorizePost(accountId: String): ResponseEntity<Relationship> { override suspend fun apiV1FollowRequestsAccountIdAuthorizePost(accountId: String): ResponseEntity<Relationship> {
val executor = oauth2CommandExecutorFactory.getCommandExecutor()
userAcceptFollowRequestApplicationService.execute( userAcceptFollowRequestApplicationService.execute(
AcceptFollowRequest(accountId.toLong()) AcceptFollowRequest(accountId.toLong()), principalContextHolder.getPrincipal()
) )
return fetchRelationship(accountId, executor) return fetchRelationship(accountId)
} }
override suspend fun apiV1FollowRequestsAccountIdRejectPost(accountId: String): ResponseEntity<Relationship> { override suspend fun apiV1FollowRequestsAccountIdRejectPost(accountId: String): ResponseEntity<Relationship> {
val executor = oauth2CommandExecutorFactory.getCommandExecutor()
userRejectFollowRequestApplicationService.execute( userRejectFollowRequestApplicationService.execute(
RejectFollowRequest(accountId.toLong()) RejectFollowRequest(accountId.toLong()), principalContextHolder.getPrincipal()
) )
return fetchRelationship(accountId, executor) return fetchRelationship(accountId)
} }
} }

View File

@ -18,6 +18,7 @@ package dev.usbharu.hideout.mastodon.interfaces.api
import dev.usbharu.hideout.core.application.application.RegisterApplication import dev.usbharu.hideout.core.application.application.RegisterApplication
import dev.usbharu.hideout.core.application.application.RegisterApplicationApplicationService import dev.usbharu.hideout.core.application.application.RegisterApplicationApplicationService
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
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
@ -35,7 +36,7 @@ class SpringAppApi(private val registerApplicationApplicationService: RegisterAp
false, false,
appsRequest.scopes?.split(" ").orEmpty().toSet().ifEmpty { setOf("read") } appsRequest.scopes?.split(" ").orEmpty().toSet().ifEmpty { setOf("read") }
) )
val registeredApplication = registerApplicationApplicationService.register(registerApplication) val registeredApplication = registerApplicationApplicationService.execute(registerApplication, Anonymous)
return ResponseEntity.ok( return ResponseEntity.ok(
Application( Application(
registeredApplication.name, registeredApplication.name,

View File

@ -20,7 +20,7 @@ import dev.usbharu.hideout.core.application.filter.*
import dev.usbharu.hideout.core.domain.model.filter.FilterAction import dev.usbharu.hideout.core.domain.model.filter.FilterAction
import dev.usbharu.hideout.core.domain.model.filter.FilterContext import dev.usbharu.hideout.core.domain.model.filter.FilterContext
import dev.usbharu.hideout.core.domain.model.filter.FilterMode import dev.usbharu.hideout.core.domain.model.filter.FilterMode
import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutorFactory import dev.usbharu.hideout.core.domain.model.support.principal.PrincipalContextHolder
import dev.usbharu.hideout.mastodon.application.filter.DeleteFilterV1 import dev.usbharu.hideout.mastodon.application.filter.DeleteFilterV1
import dev.usbharu.hideout.mastodon.application.filter.DeleteFilterV1ApplicationService import dev.usbharu.hideout.mastodon.application.filter.DeleteFilterV1ApplicationService
import dev.usbharu.hideout.mastodon.application.filter.GetFilterV1 import dev.usbharu.hideout.mastodon.application.filter.GetFilterV1
@ -36,18 +36,18 @@ import org.springframework.stereotype.Controller
@Controller @Controller
class SpringFilterApi( class SpringFilterApi(
private val oauth2CommandExecutorFactory: Oauth2CommandExecutorFactory,
private val userRegisterFilterApplicationService: UserRegisterFilterApplicationService, private val userRegisterFilterApplicationService: UserRegisterFilterApplicationService,
private val getFilterV1ApplicationService: GetFilterV1ApplicationService, private val getFilterV1ApplicationService: GetFilterV1ApplicationService,
private val deleteFilterV1ApplicationService: DeleteFilterV1ApplicationService, private val deleteFilterV1ApplicationService: DeleteFilterV1ApplicationService,
private val userDeleteFilterApplicationService: UserDeleteFilterApplicationService, private val userDeleteFilterApplicationService: UserDeleteFilterApplicationService,
private val userGetFilterApplicationService: UserGetFilterApplicationService, private val userGetFilterApplicationService: UserGetFilterApplicationService,
private val principalContextHolder: PrincipalContextHolder
) : FilterApi { ) : FilterApi {
override suspend fun apiV1FiltersIdDelete(id: String): ResponseEntity<Any> { override suspend fun apiV1FiltersIdDelete(id: String): ResponseEntity<Any> {
return ResponseEntity.ok( return ResponseEntity.ok(
deleteFilterV1ApplicationService.execute( deleteFilterV1ApplicationService.execute(
DeleteFilterV1(id.toLong()) DeleteFilterV1(id.toLong()), principalContextHolder.getPrincipal()
) )
) )
} }
@ -55,7 +55,7 @@ class SpringFilterApi(
override suspend fun apiV1FiltersIdGet(id: String): ResponseEntity<V1Filter> { override suspend fun apiV1FiltersIdGet(id: String): ResponseEntity<V1Filter> {
return ResponseEntity.ok( return ResponseEntity.ok(
getFilterV1ApplicationService.execute( getFilterV1ApplicationService.execute(
GetFilterV1(id.toLong()) GetFilterV1(id.toLong()), principalContextHolder.getPrincipal()
) )
) )
} }
@ -72,7 +72,7 @@ class SpringFilterApi(
} }
override suspend fun apiV1FiltersPost(v1FilterPostRequest: V1FilterPostRequest): ResponseEntity<V1Filter> { override suspend fun apiV1FiltersPost(v1FilterPostRequest: V1FilterPostRequest): ResponseEntity<V1Filter> {
val executor = oauth2CommandExecutorFactory.getCommandExecutor()
val filterMode = if (v1FilterPostRequest.wholeWord == true) { val filterMode = if (v1FilterPostRequest.wholeWord == true) {
FilterMode.WHOLE_WORD FilterMode.WHOLE_WORD
} else { } else {
@ -91,11 +91,11 @@ class SpringFilterApi(
RegisterFilter( RegisterFilter(
v1FilterPostRequest.phrase, filterContext, FilterAction.WARN, v1FilterPostRequest.phrase, filterContext, FilterAction.WARN,
setOf(RegisterFilterKeyword(v1FilterPostRequest.phrase, filterMode)) setOf(RegisterFilterKeyword(v1FilterPostRequest.phrase, filterMode))
) ), principalContextHolder.getPrincipal()
) )
return ResponseEntity.ok( return ResponseEntity.ok(
getFilterV1ApplicationService.execute( getFilterV1ApplicationService.execute(
GetFilterV1(filter.filterKeywords.first().id) GetFilterV1(filter.filterKeywords.first().id), principalContextHolder.getPrincipal()
) )
) )
} }
@ -116,14 +116,14 @@ class SpringFilterApi(
override suspend fun apiV2FiltersIdDelete(id: String): ResponseEntity<Any> { override suspend fun apiV2FiltersIdDelete(id: String): ResponseEntity<Any> {
userDeleteFilterApplicationService.execute( userDeleteFilterApplicationService.execute(
DeleteFilter(id.toLong()) DeleteFilter(id.toLong()), principalContextHolder.getPrincipal()
) )
return ResponseEntity.ok(Unit) return ResponseEntity.ok(Unit)
} }
override suspend fun apiV2FiltersIdGet(id: String): ResponseEntity<Filter> { override suspend fun apiV2FiltersIdGet(id: String): ResponseEntity<Filter> {
val filter = userGetFilterApplicationService.execute( val filter = userGetFilterApplicationService.execute(
GetFilter(id.toLong()) GetFilter(id.toLong()), principalContextHolder.getPrincipal()
) )
return ResponseEntity.ok( return ResponseEntity.ok(
filter(filter) filter(filter)
@ -186,7 +186,7 @@ class SpringFilterApi(
} }
override suspend fun apiV2FiltersPost(filterPostRequest: FilterPostRequest): ResponseEntity<Filter> { override suspend fun apiV2FiltersPost(filterPostRequest: FilterPostRequest): ResponseEntity<Filter> {
val executor = oauth2CommandExecutorFactory.getCommandExecutor()
val filter = userRegisterFilterApplicationService.execute( val filter = userRegisterFilterApplicationService.execute(
RegisterFilter( RegisterFilter(
filterName = filterPostRequest.title, filterName = filterPostRequest.title,
@ -216,7 +216,7 @@ class SpringFilterApi(
} }
) )
}.toSet() }.toSet()
) ), principalContextHolder.getPrincipal()
) )
return ResponseEntity.ok(filter(filter)) return ResponseEntity.ok(filter(filter))
} }

View File

@ -19,7 +19,7 @@ package dev.usbharu.hideout.mastodon.interfaces.api
import dev.usbharu.hideout.core.application.media.UploadMedia import dev.usbharu.hideout.core.application.media.UploadMedia
import dev.usbharu.hideout.core.application.media.UploadMediaApplicationService import dev.usbharu.hideout.core.application.media.UploadMediaApplicationService
import dev.usbharu.hideout.core.domain.model.media.FileType.* import dev.usbharu.hideout.core.domain.model.media.FileType.*
import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutorFactory import dev.usbharu.hideout.core.domain.model.support.principal.PrincipalContextHolder
import dev.usbharu.hideout.mastodon.interfaces.api.generated.MediaApi import dev.usbharu.hideout.mastodon.interfaces.api.generated.MediaApi
import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.MediaAttachment import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.MediaAttachment
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
@ -30,7 +30,7 @@ import java.nio.file.Files
@Controller @Controller
class SpringMediaApi( class SpringMediaApi(
private val uploadMediaApplicationService: UploadMediaApplicationService, private val uploadMediaApplicationService: UploadMediaApplicationService,
private val oauth2CommandExecutorFactory: Oauth2CommandExecutorFactory private val principalContextHolder: PrincipalContextHolder
) : MediaApi { ) : MediaApi {
override suspend fun apiV1MediaPost( override suspend fun apiV1MediaPost(
file: MultipartFile, file: MultipartFile,
@ -52,7 +52,7 @@ class SpringMediaApi(
file.originalFilename ?: file.name, file.originalFilename ?: file.name,
null, null,
description description
) ), principalContextHolder.getPrincipal()
) )
return ResponseEntity.ok( return ResponseEntity.ok(

View File

@ -19,8 +19,7 @@ package dev.usbharu.hideout.mastodon.interfaces.api
import dev.usbharu.hideout.core.application.post.RegisterLocalPost import dev.usbharu.hideout.core.application.post.RegisterLocalPost
import dev.usbharu.hideout.core.application.post.RegisterLocalPostApplicationService import dev.usbharu.hideout.core.application.post.RegisterLocalPostApplicationService
import dev.usbharu.hideout.core.domain.model.post.Visibility import dev.usbharu.hideout.core.domain.model.post.Visibility
import dev.usbharu.hideout.core.infrastructure.springframework.DelegateCommandExecutorFactory import dev.usbharu.hideout.core.domain.model.support.principal.PrincipalContextHolder
import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutor
import dev.usbharu.hideout.mastodon.application.status.GetStatus import dev.usbharu.hideout.mastodon.application.status.GetStatus
import dev.usbharu.hideout.mastodon.application.status.GetStatusApplicationService import dev.usbharu.hideout.mastodon.application.status.GetStatusApplicationService
import dev.usbharu.hideout.mastodon.interfaces.api.generated.StatusApi import dev.usbharu.hideout.mastodon.interfaces.api.generated.StatusApi
@ -32,9 +31,9 @@ import org.springframework.stereotype.Controller
@Controller @Controller
class SpringStatusApi( class SpringStatusApi(
private val delegateCommandExecutorFactory: DelegateCommandExecutorFactory,
private val registerLocalPostApplicationService: RegisterLocalPostApplicationService, private val registerLocalPostApplicationService: RegisterLocalPostApplicationService,
private val getStatusApplicationService: GetStatusApplicationService, private val getStatusApplicationService: GetStatusApplicationService,
private val principalContextHolder: PrincipalContextHolder
) : StatusApi { ) : StatusApi {
override suspend fun apiV1StatusesIdEmojiReactionsEmojiDelete(id: String, emoji: String): ResponseEntity<Status> { override suspend fun apiV1StatusesIdEmojiReactionsEmojiDelete(id: String, emoji: String): ResponseEntity<Status> {
return super.apiV1StatusesIdEmojiReactionsEmojiDelete(id, emoji) return super.apiV1StatusesIdEmojiReactionsEmojiDelete(id, emoji)
@ -48,16 +47,15 @@ class SpringStatusApi(
return ResponseEntity.ok( return ResponseEntity.ok(
getStatusApplicationService.execute( getStatusApplicationService.execute(
GetStatus(id) GetStatus(id), principalContextHolder.getPrincipal()
) )
) )
} }
override suspend fun apiV1StatusesPost(statusesRequest: StatusesRequest): ResponseEntity<Status> { override suspend fun apiV1StatusesPost(statusesRequest: StatusesRequest): ResponseEntity<Status> {
val executor = delegateCommandExecutorFactory.getCommandExecutor() as Oauth2CommandExecutor
val execute = registerLocalPostApplicationService.execute( val execute = registerLocalPostApplicationService.execute(
RegisterLocalPost( RegisterLocalPost(
userDetailId = executor.userDetailId,
content = statusesRequest.status.orEmpty(), content = statusesRequest.status.orEmpty(),
overview = statusesRequest.spoilerText, overview = statusesRequest.spoilerText,
visibility = when (statusesRequest.visibility) { visibility = when (statusesRequest.visibility) {
@ -71,11 +69,12 @@ class SpringStatusApi(
replyId = statusesRequest.inReplyToId?.toLong(), replyId = statusesRequest.inReplyToId?.toLong(),
sensitive = statusesRequest.sensitive == true, sensitive = statusesRequest.sensitive == true,
mediaIds = statusesRequest.mediaIds.orEmpty().map { it.toLong() } mediaIds = statusesRequest.mediaIds.orEmpty().map { it.toLong() }
) ), principalContextHolder.getPrincipal()
) )
val status = getStatusApplicationService.execute(GetStatus(execute.toString())) val status =
getStatusApplicationService.execute(GetStatus(execute.toString()), principalContextHolder.getPrincipal())
return ResponseEntity.ok( return ResponseEntity.ok(
status status
) )