From de79c91710112744bb3f028756dcf1bbd27de583 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 11 Aug 2023 11:25:26 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20=E4=B8=8D=E8=A6=81=E3=81=AA?= =?UTF-8?q?=E3=82=B3=E3=83=BC=E3=83=89=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/JwtRefreshTokenQueryService.kt | 13 +++ .../query/JwtRefreshTokenQueryServiceImpl.kt | 38 +++++++++ .../repository/IJwtRefreshTokenRepository.kt | 7 -- .../JwtRefreshTokenRepositoryImpl.kt | 83 ++++--------------- .../hideout/repository/MetaRepositoryImpl.kt | 48 +++++------ .../hideout/service/auth/JwtServiceImpl.kt | 17 ++-- .../JwtRefreshTokenRepositoryImplTest.kt | 29 ++++--- .../service/auth/JwtServiceImplTest.kt | 25 +++--- 8 files changed, 129 insertions(+), 131 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/query/JwtRefreshTokenQueryService.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/query/JwtRefreshTokenQueryServiceImpl.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/query/JwtRefreshTokenQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/query/JwtRefreshTokenQueryService.kt new file mode 100644 index 00000000..9ce1ad57 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/query/JwtRefreshTokenQueryService.kt @@ -0,0 +1,13 @@ +package dev.usbharu.hideout.query + +import dev.usbharu.hideout.domain.model.hideout.entity.JwtRefreshToken + +interface JwtRefreshTokenQueryService { + suspend fun findById(id: Long): JwtRefreshToken + suspend fun findByToken(token: String): JwtRefreshToken + suspend fun findByUserId(userId: Long): JwtRefreshToken + suspend fun deleteById(id: Long) + suspend fun deleteByToken(token: String) + suspend fun deleteByUserId(userId: Long) + suspend fun deleteAll() +} diff --git a/src/main/kotlin/dev/usbharu/hideout/query/JwtRefreshTokenQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/query/JwtRefreshTokenQueryServiceImpl.kt new file mode 100644 index 00000000..672b3b43 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/query/JwtRefreshTokenQueryServiceImpl.kt @@ -0,0 +1,38 @@ +package dev.usbharu.hideout.query + +import dev.usbharu.hideout.domain.model.hideout.entity.JwtRefreshToken +import dev.usbharu.hideout.repository.JwtRefreshTokens +import dev.usbharu.hideout.repository.toJwtRefreshToken +import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.deleteAll +import org.jetbrains.exposed.sql.deleteWhere +import org.jetbrains.exposed.sql.select +import org.koin.core.annotation.Single + +@Single +class JwtRefreshTokenQueryServiceImpl : JwtRefreshTokenQueryService { + override suspend fun findById(id: Long): JwtRefreshToken = + JwtRefreshTokens.select { JwtRefreshTokens.id.eq(id) }.single().toJwtRefreshToken() + + override suspend fun findByToken(token: String): JwtRefreshToken = + JwtRefreshTokens.select { JwtRefreshTokens.refreshToken.eq(token) }.single().toJwtRefreshToken() + + override suspend fun findByUserId(userId: Long): JwtRefreshToken = + JwtRefreshTokens.select { JwtRefreshTokens.userId.eq(userId) }.single().toJwtRefreshToken() + + override suspend fun deleteById(id: Long) { + JwtRefreshTokens.deleteWhere { JwtRefreshTokens.id eq id } + } + + override suspend fun deleteByToken(token: String) { + JwtRefreshTokens.deleteWhere { refreshToken eq token } + } + + override suspend fun deleteByUserId(userId: Long) { + JwtRefreshTokens.deleteWhere { JwtRefreshTokens.userId eq userId } + } + + override suspend fun deleteAll() { + JwtRefreshTokens.deleteAll() + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/IJwtRefreshTokenRepository.kt b/src/main/kotlin/dev/usbharu/hideout/repository/IJwtRefreshTokenRepository.kt index 9e6a3c96..19f8774a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/repository/IJwtRefreshTokenRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/repository/IJwtRefreshTokenRepository.kt @@ -8,13 +8,6 @@ interface IJwtRefreshTokenRepository { suspend fun save(token: JwtRefreshToken) suspend fun findById(id: Long): JwtRefreshToken? - suspend fun findByToken(token: String): JwtRefreshToken? - suspend fun findByUserId(userId: Long): JwtRefreshToken? suspend fun delete(token: JwtRefreshToken) - suspend fun deleteById(id: Long) - suspend fun deleteByToken(token: String) - suspend fun deleteByUserId(userId: Long) - - suspend fun deleteAll() } diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/JwtRefreshTokenRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/repository/JwtRefreshTokenRepositoryImpl.kt index 32657d10..d93797d2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/repository/JwtRefreshTokenRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/repository/JwtRefreshTokenRepositoryImpl.kt @@ -2,10 +2,8 @@ package dev.usbharu.hideout.repository import dev.usbharu.hideout.domain.model.hideout.entity.JwtRefreshToken import dev.usbharu.hideout.service.core.IdGenerateService -import kotlinx.coroutines.Dispatchers import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction import org.jetbrains.exposed.sql.transactions.transaction import org.koin.core.annotation.Single import java.time.Instant @@ -24,79 +22,32 @@ class JwtRefreshTokenRepositoryImpl( } } - @Suppress("InjectDispatcher") - suspend fun query(block: suspend () -> T): T = - newSuspendedTransaction(Dispatchers.IO) { block() } - override suspend fun generateId(): Long = idGenerateService.generateId() override suspend fun save(token: JwtRefreshToken) { - query { - if (JwtRefreshTokens.select { JwtRefreshTokens.id.eq(token.id) }.empty()) { - JwtRefreshTokens.insert { - it[id] = token.id - it[userId] = token.userId - it[refreshToken] = token.refreshToken - it[createdAt] = token.createdAt.toEpochMilli() - it[expiresAt] = token.expiresAt.toEpochMilli() - } - } else { - JwtRefreshTokens.update({ JwtRefreshTokens.id eq token.id }) { - it[userId] = token.userId - it[refreshToken] = token.refreshToken - it[createdAt] = token.createdAt.toEpochMilli() - it[expiresAt] = token.expiresAt.toEpochMilli() - } + if (JwtRefreshTokens.select { JwtRefreshTokens.id.eq(token.id) }.empty()) { + JwtRefreshTokens.insert { + it[id] = token.id + it[userId] = token.userId + it[refreshToken] = token.refreshToken + it[createdAt] = token.createdAt.toEpochMilli() + it[expiresAt] = token.expiresAt.toEpochMilli() + } + } else { + JwtRefreshTokens.update({ JwtRefreshTokens.id eq token.id }) { + it[userId] = token.userId + it[refreshToken] = token.refreshToken + it[createdAt] = token.createdAt.toEpochMilli() + it[expiresAt] = token.expiresAt.toEpochMilli() } } } - override suspend fun findById(id: Long): JwtRefreshToken? { - return query { - JwtRefreshTokens.select { JwtRefreshTokens.id.eq(id) }.singleOrNull()?.toJwtRefreshToken() - } - } - - override suspend fun findByToken(token: String): JwtRefreshToken? { - return query { - JwtRefreshTokens.select { JwtRefreshTokens.refreshToken.eq(token) }.singleOrNull()?.toJwtRefreshToken() - } - } - - override suspend fun findByUserId(userId: Long): JwtRefreshToken? { - return query { - JwtRefreshTokens.select { JwtRefreshTokens.userId.eq(userId) }.singleOrNull()?.toJwtRefreshToken() - } - } + override suspend fun findById(id: Long): JwtRefreshToken? = + JwtRefreshTokens.select { JwtRefreshTokens.id.eq(id) }.singleOrNull()?.toJwtRefreshToken() override suspend fun delete(token: JwtRefreshToken) { - return query { - JwtRefreshTokens.deleteWhere { id eq token.id } - } - } - - override suspend fun deleteById(id: Long) { - return query { - JwtRefreshTokens.deleteWhere { JwtRefreshTokens.id eq id } - } - } - - override suspend fun deleteByToken(token: String) { - return query { - JwtRefreshTokens.deleteWhere { refreshToken eq token } - } - } - - override suspend fun deleteByUserId(userId: Long) { - return query { - JwtRefreshTokens.deleteWhere { JwtRefreshTokens.userId eq userId } - } - } - - override suspend fun deleteAll() { - return query { - JwtRefreshTokens.deleteAll() - } + JwtRefreshTokens.deleteWhere { id eq token.id } } } diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/MetaRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/repository/MetaRepositoryImpl.kt index a479512d..86dbc786 100644 --- a/src/main/kotlin/dev/usbharu/hideout/repository/MetaRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/repository/MetaRepositoryImpl.kt @@ -1,9 +1,7 @@ package dev.usbharu.hideout.repository import dev.usbharu.hideout.domain.model.hideout.entity.Jwt -import kotlinx.coroutines.Dispatchers import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction import org.jetbrains.exposed.sql.transactions.transaction import org.koin.core.annotation.Single import java.util.* @@ -18,39 +16,31 @@ class MetaRepositoryImpl(private val database: Database) : IMetaRepository { } } - @Suppress("InjectDispatcher") - suspend fun query(block: suspend () -> T): T = - newSuspendedTransaction(Dispatchers.IO) { block() } - override suspend fun save(meta: dev.usbharu.hideout.domain.model.hideout.entity.Meta) { - return query { - if (Meta.select { Meta.id eq 1 }.empty()) { - Meta.insert { - it[id] = 1 - it[this.version] = meta.version - it[kid] = UUID.randomUUID().toString() - it[this.jwtPrivateKey] = meta.jwt.privateKey - it[this.jwtPublicKey] = meta.jwt.publicKey - } - } else { - Meta.update({ Meta.id eq 1 }) { - it[this.version] = meta.version - it[kid] = UUID.randomUUID().toString() - it[this.jwtPrivateKey] = meta.jwt.privateKey - it[this.jwtPublicKey] = meta.jwt.publicKey - } + if (Meta.select { Meta.id eq 1 }.empty()) { + Meta.insert { + it[id] = 1 + it[this.version] = meta.version + it[kid] = UUID.randomUUID().toString() + it[this.jwtPrivateKey] = meta.jwt.privateKey + it[this.jwtPublicKey] = meta.jwt.publicKey + } + } else { + Meta.update({ Meta.id eq 1 }) { + it[this.version] = meta.version + it[kid] = UUID.randomUUID().toString() + it[this.jwtPrivateKey] = meta.jwt.privateKey + it[this.jwtPublicKey] = meta.jwt.publicKey } } } override suspend fun get(): dev.usbharu.hideout.domain.model.hideout.entity.Meta? { - return query { - Meta.select { Meta.id eq 1 }.singleOrNull()?.let { - dev.usbharu.hideout.domain.model.hideout.entity.Meta( - it[Meta.version], - Jwt(UUID.fromString(it[Meta.kid]), it[Meta.jwtPrivateKey], it[Meta.jwtPublicKey]) - ) - } + return Meta.select { Meta.id eq 1 }.singleOrNull()?.let { + dev.usbharu.hideout.domain.model.hideout.entity.Meta( + it[Meta.version], + Jwt(UUID.fromString(it[Meta.kid]), it[Meta.jwtPrivateKey], it[Meta.jwtPublicKey]) + ) } } } diff --git a/src/main/kotlin/dev/usbharu/hideout/service/auth/JwtServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/auth/JwtServiceImpl.kt index d611ebdd..54195905 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/auth/JwtServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/auth/JwtServiceImpl.kt @@ -8,6 +8,7 @@ import dev.usbharu.hideout.domain.model.hideout.entity.JwtRefreshToken import dev.usbharu.hideout.domain.model.hideout.entity.User import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken import dev.usbharu.hideout.exception.InvalidRefreshTokenException +import dev.usbharu.hideout.query.JwtRefreshTokenQueryService import dev.usbharu.hideout.query.UserQueryService import dev.usbharu.hideout.repository.IJwtRefreshTokenRepository import dev.usbharu.hideout.service.core.IMetaService @@ -25,7 +26,8 @@ import java.util.* class JwtServiceImpl( private val metaService: IMetaService, private val refreshTokenRepository: IJwtRefreshTokenRepository, - private val userQueryService: UserQueryService + private val userQueryService: UserQueryService, + private val refreshTokenQueryService: JwtRefreshTokenQueryService ) : IJwtService { private val privateKey by lazy { @@ -69,8 +71,11 @@ class JwtServiceImpl( } override suspend fun refreshToken(refreshToken: RefreshToken): JwtToken { - val token = refreshTokenRepository.findByToken(refreshToken.refreshToken) - ?: throw InvalidRefreshTokenException("Invalid Refresh Token") + val token = try { + refreshTokenQueryService.findByToken(refreshToken.refreshToken) + } catch (_: NoSuchElementException) { + throw InvalidRefreshTokenException("Invalid Refresh Token") + } val user = userQueryService.findById(token.userId) @@ -87,14 +92,14 @@ class JwtServiceImpl( } override suspend fun revokeToken(refreshToken: RefreshToken) { - refreshTokenRepository.deleteByToken(refreshToken.refreshToken) + refreshTokenQueryService.deleteByToken(refreshToken.refreshToken) } override suspend fun revokeToken(user: User) { - refreshTokenRepository.deleteByUserId(user.id) + refreshTokenQueryService.deleteByUserId(user.id) } override suspend fun revokeAll() { - refreshTokenRepository.deleteAll() + refreshTokenQueryService.deleteAll() } } diff --git a/src/test/kotlin/dev/usbharu/hideout/repository/JwtRefreshTokenRepositoryImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/repository/JwtRefreshTokenRepositoryImplTest.kt index 00666e9d..47eb928d 100644 --- a/src/test/kotlin/dev/usbharu/hideout/repository/JwtRefreshTokenRepositoryImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/repository/JwtRefreshTokenRepositoryImplTest.kt @@ -10,6 +10,7 @@ import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.insert import org.jetbrains.exposed.sql.select +import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction import org.jetbrains.exposed.sql.transactions.transaction import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach @@ -53,9 +54,11 @@ class JwtRefreshTokenRepositoryImplTest { val expiresAt = now.plus(10, ChronoUnit.MINUTES) val expect = JwtRefreshToken(1L, 2L, "refreshToken", now, expiresAt) - repository.save(expect) - val actual = repository.findById(1L) - assertEquals(expect, actual) + newSuspendedTransaction { + repository.save(expect) + val actual = repository.findById(1L) + assertEquals(expect, actual) + } } @Test @@ -68,7 +71,7 @@ class JwtRefreshTokenRepositoryImplTest { } } ) - transaction { + newSuspendedTransaction { JwtRefreshTokens.insert { it[id] = 1L it[userId] = 2L @@ -76,17 +79,17 @@ class JwtRefreshTokenRepositoryImplTest { it[createdAt] = Instant.now().toEpochMilli() it[expiresAt] = Instant.now().plus(10, ChronoUnit.MINUTES).toEpochMilli() } + repository.save( + JwtRefreshToken( + id = 1L, + userId = 2L, + refreshToken = "refreshToken2", + createdAt = Instant.now(), + expiresAt = Instant.now().plus(10, ChronoUnit.MINUTES) + ) + ) } - repository.save( - JwtRefreshToken( - id = 1L, - userId = 2L, - refreshToken = "refreshToken2", - createdAt = Instant.now(), - expiresAt = Instant.now().plus(10, ChronoUnit.MINUTES) - ) - ) transaction { val toJwtRefreshToken = JwtRefreshTokens.select { JwtRefreshTokens.id.eq(1L) }.single().toJwtRefreshToken() assertEquals("refreshToken2", toJwtRefreshToken.refreshToken) diff --git a/src/test/kotlin/dev/usbharu/hideout/service/auth/JwtServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/service/auth/JwtServiceImplTest.kt index 9c8fbee8..454312a4 100644 --- a/src/test/kotlin/dev/usbharu/hideout/service/auth/JwtServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/service/auth/JwtServiceImplTest.kt @@ -12,6 +12,7 @@ import dev.usbharu.hideout.domain.model.hideout.entity.JwtRefreshToken import dev.usbharu.hideout.domain.model.hideout.entity.User import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken import dev.usbharu.hideout.exception.InvalidRefreshTokenException +import dev.usbharu.hideout.query.JwtRefreshTokenQueryService import dev.usbharu.hideout.query.UserQueryService import dev.usbharu.hideout.repository.IJwtRefreshTokenRepository import dev.usbharu.hideout.service.core.IMetaService @@ -21,6 +22,7 @@ import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.mockito.kotlin.doReturn +import org.mockito.kotlin.doThrow import org.mockito.kotlin.mock import java.security.KeyPairGenerator import java.security.interfaces.RSAPrivateKey @@ -50,7 +52,7 @@ class JwtServiceImplTest { val refreshTokenRepository = mock { onBlocking { generateId() } doReturn 1L } - val jwtService = JwtServiceImpl(metaService, refreshTokenRepository, mock()) + val jwtService = JwtServiceImpl(metaService, refreshTokenRepository, mock(), mock()) val token = jwtService.createToken( User( id = 1L, @@ -93,6 +95,10 @@ class JwtServiceImplTest { val generateKeyPair = keyPairGenerator.generateKeyPair() val refreshTokenRepository = mock { + onBlocking { generateId() } doReturn 2L + } + + val jwtRefreshTokenQueryService = mock { onBlocking { findByToken("refreshToken") } doReturn JwtRefreshToken( id = 1L, userId = 1L, @@ -100,7 +106,6 @@ class JwtServiceImplTest { createdAt = Instant.now().minus(60, ChronoUnit.MINUTES), expiresAt = Instant.now().plus(14, ChronoUnit.DAYS).minus(60, ChronoUnit.MINUTES) ) - onBlocking { generateId() } doReturn 2L } val userService = mock { onBlocking { findById(1L) } doReturn User( @@ -125,7 +130,7 @@ class JwtServiceImplTest { Base64Util.encode(generateKeyPair.public.encoded) ) } - val jwtService = JwtServiceImpl(metaService, refreshTokenRepository, userService) + val jwtService = JwtServiceImpl(metaService, refreshTokenRepository, userService, jwtRefreshTokenQueryService) val refreshToken = jwtService.refreshToken(RefreshToken("refreshToken")) assertNotEquals("", refreshToken.token) assertNotEquals("", refreshToken.refreshToken) @@ -147,16 +152,16 @@ class JwtServiceImplTest { @Test fun `refreshToken 無効なリフレッシュトークンは失敗する`() = runTest { - val refreshTokenRepository = mock { - onBlocking { findByToken("InvalidRefreshToken") } doReturn null + val refreshTokenRepository = mock { + onBlocking { findByToken("InvalidRefreshToken") } doThrow NoSuchElementException() } - val jwtService = JwtServiceImpl(mock(), refreshTokenRepository, mock()) + val jwtService = JwtServiceImpl(mock(), mock(), mock(), refreshTokenRepository) assertThrows { jwtService.refreshToken(RefreshToken("InvalidRefreshToken")) } } @Test fun `refreshToken 未来に作成されたリフレッシュトークンは失敗する`() = runTest { - val refreshTokenRepository = mock { + val refreshTokenRepository = mock { onBlocking { findByToken("refreshToken") } doReturn JwtRefreshToken( id = 1L, userId = 1L, @@ -165,13 +170,13 @@ class JwtServiceImplTest { expiresAt = Instant.now().plus(10, ChronoUnit.MINUTES).plus(14, ChronoUnit.DAYS) ) } - val jwtService = JwtServiceImpl(mock(), refreshTokenRepository, mock()) + val jwtService = JwtServiceImpl(mock(), mock(), mock(), refreshTokenRepository) assertThrows { jwtService.refreshToken(RefreshToken("refreshToken")) } } @Test fun `refreshToken 期限切れのリフレッシュトークンでは失敗する`() = runTest { - val refreshTokenRepository = mock { + val refreshTokenRepository = mock { onBlocking { findByToken("refreshToken") } doReturn JwtRefreshToken( id = 1L, userId = 1L, @@ -180,7 +185,7 @@ class JwtServiceImplTest { expiresAt = Instant.now().minus(16, ChronoUnit.DAYS) ) } - val jwtService = JwtServiceImpl(mock(), refreshTokenRepository, mock()) + val jwtService = JwtServiceImpl(mock(), mock(), mock(), refreshTokenRepository) assertThrows { jwtService.refreshToken(RefreshToken("refreshToken")) } } }