This repository has been archived by the owner on Nov 21, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
416 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
user-api/src/main/kotlin/com/sns/user/component/authcode/application/AuthCodeCommand.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package com.sns.user.component.authcode.application | ||
|
||
import com.sns.user.component.authcode.domain.AuthCode | ||
import com.sns.user.component.authcode.domain.Purpose | ||
import com.sns.user.component.authcode.repositories.AuthCodeRepository | ||
import com.sns.user.component.user.repositories.DefaultUserRepository | ||
import com.sns.user.core.exceptions.NoAuthorityException | ||
import com.sns.user.core.infrastructures.mail.MailService | ||
import org.springframework.data.repository.findByIdOrNull | ||
import org.springframework.stereotype.Service | ||
|
||
@Service | ||
class AuthCodeCommand( | ||
val authCodeRepository: AuthCodeRepository, | ||
val mailService: MailService, | ||
val userRepository: DefaultUserRepository | ||
) { | ||
fun create(userId: String): AuthCode { | ||
val user = userRepository.findByIdOrNull(userId) ?: throw NoAuthorityException() | ||
|
||
val authCode = AuthCode.createSignUp(user.id) | ||
authCodeRepository.save(authCode) | ||
|
||
mailService.sendSignUpAuthCodeMail(authCode.code, user.infoEmailAddress) | ||
return authCode | ||
} | ||
|
||
fun verify(userId: String, purpose: Purpose, code: String): Boolean { | ||
val authCode: AuthCode? = authCodeRepository.findByUserIdAndPurpose(userId, purpose) | ||
return authCode?.isCorrect(userId, code, purpose) | ||
.takeIf { it == true }.apply { | ||
// TOOD update STATUS userRepository.save() | ||
} ?: false | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
user-api/src/main/kotlin/com/sns/user/component/authcode/domain/AuthCode.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package com.sns.user.component.authcode.domain | ||
|
||
import kotlin.random.Random | ||
import java.sql.ResultSet | ||
import java.time.Instant | ||
import javax.validation.constraints.NotBlank | ||
import org.springframework.data.annotation.CreatedDate | ||
import org.springframework.data.annotation.Id | ||
import org.springframework.jdbc.core.RowMapper | ||
|
||
data class AuthCode( | ||
@Id | ||
val id: Int? = null, | ||
// TODO 복합키 구현가능한지 확인. | ||
@NotBlank | ||
val purpose: Purpose, | ||
@NotBlank | ||
val userId: String, | ||
@NotBlank | ||
val code: String = (1..CODE_LENGTH) | ||
.map { Random.nextInt(0, charPool.size) } | ||
.map(charPool::get) | ||
.joinToString(""), | ||
@CreatedDate | ||
val createdAt: Instant = Instant.MIN | ||
) { | ||
|
||
fun isCorrect(userId: String, code: String, purpose: Purpose): Boolean = | ||
(this.userId == userId) and (this.code == code) and (this.purpose == purpose) | ||
|
||
companion object { | ||
private const val CODE_LENGTH = 10; | ||
private val charPool: List<Char> = ('a'..'z') + ('A'..'Z') + ('0'..'9') | ||
fun createSignUp(userId: String) = AuthCode(purpose = Purpose.SIGN_UP, userId = userId) | ||
val MAPPER: RowMapper<AuthCode> = AuthCodeRowMapper() | ||
} | ||
} | ||
|
||
// purpose enum 매핑이 안되서 수동으로 작성함. 확인필요. | ||
class AuthCodeRowMapper : RowMapper<AuthCode> { | ||
override fun mapRow(rs: ResultSet, rowNum: Int): AuthCode? { | ||
return AuthCode( | ||
id = rs.getInt("id"), | ||
purpose = Purpose.valueOf(rs.getString("purpose")), | ||
userId = rs.getString("user_id"), | ||
code = rs.getString("code"), | ||
createdAt = Instant.ofEpochMilli(rs.getTimestamp("created_at").time), | ||
) | ||
} | ||
} | ||
|
||
enum class Purpose { | ||
SIGN_UP | ||
} |
9 changes: 9 additions & 0 deletions
9
...pi/src/main/kotlin/com/sns/user/component/authcode/repositories/AuthCodeCrudRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.sns.user.component.authcode.repositories | ||
|
||
import com.sns.user.component.authcode.domain.AuthCode | ||
import org.springframework.data.repository.CrudRepository | ||
import org.springframework.stereotype.Repository | ||
|
||
@Repository | ||
interface AuthCodeCrudRepository : CrudRepository<AuthCode, Int> { | ||
} |
11 changes: 11 additions & 0 deletions
11
user-api/src/main/kotlin/com/sns/user/component/authcode/repositories/AuthCodeRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.sns.user.component.authcode.repositories | ||
|
||
import com.sns.user.component.authcode.domain.AuthCode | ||
import com.sns.user.component.authcode.domain.Purpose | ||
import org.springframework.data.repository.CrudRepository | ||
import org.springframework.data.repository.NoRepositoryBean | ||
|
||
@NoRepositoryBean | ||
interface AuthCodeRepository : CrudRepository<AuthCode, Int> { | ||
fun findByUserIdAndPurpose(userId: String, purpose: Purpose): AuthCode? | ||
} |
25 changes: 25 additions & 0 deletions
25
...src/main/kotlin/com/sns/user/component/authcode/repositories/DefaultAuthCodeRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package com.sns.user.component.authcode.repositories | ||
|
||
import com.sns.user.component.authcode.domain.AuthCode | ||
import com.sns.user.component.authcode.domain.Purpose | ||
import org.springframework.data.repository.CrudRepository | ||
import org.springframework.jdbc.core.JdbcTemplate | ||
import org.springframework.stereotype.Repository | ||
|
||
@Repository | ||
class DefaultAuthCodeRepository( | ||
val jdbcTemplate: JdbcTemplate, | ||
val authCodeCrudRepository: AuthCodeCrudRepository | ||
) : AuthCodeRepository, CrudRepository<AuthCode, Int> by authCodeCrudRepository { | ||
|
||
override fun findByUserIdAndPurpose(userId: String, purpose: Purpose): AuthCode? = jdbcTemplate.queryForObject( | ||
""" | ||
SELECT id,user_id,`code`,created_at,purpose | ||
FROM auth_code | ||
WHERE user_id = ? AND purpose = ? | ||
ORDER BY id DESC | ||
LIMIT 1 | ||
""".trimIndent(), | ||
AuthCode.MAPPER, userId, purpose.name, | ||
) | ||
} |
7 changes: 7 additions & 0 deletions
7
user-api/src/main/kotlin/com/sns/user/core/config/SwaggerTag.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.sns.user.core.config | ||
|
||
class SwaggerTag { | ||
companion object { | ||
const val SIGN_UP: String = "SIGN_UP" | ||
} | ||
} |
4 changes: 4 additions & 0 deletions
4
user-api/src/main/kotlin/com/sns/user/core/exceptions/NoAuthorityException.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package com.sns.user.core.exceptions | ||
|
||
class NoAuthorityException(msg: String? = "권한이 없습니다") : RuntimeException(msg) { | ||
} |
38 changes: 38 additions & 0 deletions
38
user-api/src/main/kotlin/com/sns/user/core/infrastructures/mail/MailService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package com.sns.user.core.infrastructures.mail | ||
|
||
import java.nio.charset.StandardCharsets | ||
import java.util.* | ||
import javax.mail.Message | ||
import javax.mail.internet.InternetAddress | ||
import javax.mail.internet.MimeMessage | ||
import org.springframework.beans.factory.annotation.Value | ||
import org.springframework.mail.javamail.JavaMailSender | ||
import org.springframework.stereotype.Service | ||
import org.thymeleaf.context.Context | ||
import org.thymeleaf.spring5.ISpringTemplateEngine | ||
|
||
@Service | ||
class MailService( | ||
val javaMailSender: JavaMailSender, | ||
val templateEngine: ISpringTemplateEngine, | ||
@Value("\${spring.mail.username}") val fromId: String | ||
) { | ||
/** | ||
* 가입 인증코드 메일 발송 | ||
* @param authCode 인증 코드 | ||
* @param toAddress 수신인 주소 | ||
*/ | ||
fun sendSignUpAuthCodeMail(authCode: String, toAddress: String) { | ||
javaMailSender.send(javaMailSender.createMimeMessage().setBase("가입 인증 코드", createSignUpAuthCodeMailTemplate(authCode), toAddress)) | ||
} | ||
|
||
private fun createSignUpAuthCodeMailTemplate(authCode: String): String = | ||
templateEngine.process("signUpAuthCode", Context(Locale.KOREAN, mapOf<String, Any>("code" to authCode))) | ||
|
||
fun MimeMessage.setBase(title: String, content: String, toAddress: String): MimeMessage { | ||
setRecipient(Message.RecipientType.TO, InternetAddress(toAddress)) | ||
setSubject("[DDD SNS] $title", StandardCharsets.UTF_8.displayName()) | ||
setContent(content, "text/html;charset=euc-kr") | ||
return this | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 0 additions & 6 deletions
6
user-api/src/main/kotlin/com/sns/user/endpoints/user/responses/IdExistsCheckResponse.kt
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<!DOCTYPE html> | ||
|
||
<html lang="en" xmlns:th="http://www.thymeleaf.org"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<title>DDD SNS 인증 코드</title> | ||
</head> | ||
<body> | ||
<div class="p-5 mb-4 bg-light rounded-3"> | ||
<div class="container-fluid py-5"> | ||
<h1 class="display-5 fw-bold">DDD SNS 가입 인증 코드</h1> | ||
<p class="col-md-8 fs-4">가입페이지에 인증코드를 입력해주세요. 바로 인증 링크는 TODO</p> | ||
<p class="col-md-8 fs-4">인증 코드 : | ||
<span th:text="${code}"></span> | ||
</p> | ||
<button class="btn btn-primary btn-lg" type="button">인증 하기</button> | ||
</div> | ||
</div> | ||
</body> | ||
</html> |
74 changes: 74 additions & 0 deletions
74
...pi/src/test/kotlin/com/sns/user/component/authcode/application/AuthCodeCommandMockTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package com.sns.user.component.authcode.application | ||
|
||
import com.sns.user.component.authcode.domain.AuthCode | ||
import com.sns.user.component.authcode.domain.Purpose | ||
import com.sns.user.component.authcode.repositories.AuthCodeRepository | ||
import com.sns.user.component.user.domains.User | ||
import com.sns.user.component.user.repositories.DefaultUserRepository | ||
import com.sns.user.core.infrastructures.mail.MailService | ||
import com.sns.user.isEqualTo | ||
import io.mockk.MockKAnnotations | ||
import io.mockk.every | ||
import io.mockk.impl.annotations.InjectMockKs | ||
import io.mockk.impl.annotations.MockK | ||
import io.mockk.verify | ||
import org.junit.jupiter.api.BeforeEach | ||
import org.junit.jupiter.api.DisplayName | ||
import org.junit.jupiter.api.Test | ||
import org.springframework.data.repository.findByIdOrNull | ||
|
||
class AuthCodeCommandMockTest() { | ||
@MockK | ||
private lateinit var authCodeRepository: AuthCodeRepository | ||
|
||
@MockK | ||
private lateinit var mailService: MailService | ||
|
||
@MockK | ||
private lateinit var userRepository: DefaultUserRepository | ||
|
||
@InjectMockKs | ||
private lateinit var authCodeCommand: AuthCodeCommand | ||
|
||
@BeforeEach | ||
internal fun setUp() { | ||
MockKAnnotations.init(this) | ||
every { mailService.sendSignUpAuthCodeMail(ofType(String::class), ofType(String::class)) } returns Unit | ||
every { authCodeRepository.save(any()) } returnsArgument 0 | ||
every { userRepository.findByIdOrNull(any()) } returns User.create("id", "pass", "name", "[email protected]") | ||
} | ||
|
||
@Test | ||
fun create() { | ||
val authCode = authCodeCommand.create("id") | ||
|
||
verify { authCodeRepository.save(eq(authCode)) } | ||
verify { mailService.sendSignUpAuthCodeMail(any(), any()) } | ||
} | ||
|
||
@DisplayName("userId, purpose에 맞는 authcode 기록이 없다면, 인증 실패해야한다.") | ||
@Test | ||
fun verify_null() { | ||
every { authCodeRepository.findByUserIdAndPurpose(ofType(String::class), ofType(Purpose::class)) } returns null | ||
|
||
authCodeCommand.verify("userId", Purpose.SIGN_UP, "123") isEqualTo false | ||
} | ||
|
||
@DisplayName("정상 케이스인 경우, 인증에 성공해야한다.") | ||
@Test | ||
fun verify_success() { | ||
val authCode = AuthCode.createSignUp("userId") | ||
every { authCodeRepository.findByUserIdAndPurpose(ofType(String::class), ofType(Purpose::class)) } returns authCode | ||
|
||
authCodeCommand.verify("userId", Purpose.SIGN_UP, authCode.code) isEqualTo true | ||
} | ||
|
||
@DisplayName("인증 코드가 다른 경우, 인증에 실패해야한다.") | ||
@Test | ||
fun verify_different_code() { | ||
val authCode = AuthCode.createSignUp("userId") | ||
every { authCodeRepository.findByUserIdAndPurpose(ofType(String::class), ofType(Purpose::class)) } returns authCode | ||
|
||
authCodeCommand.verify("userId", Purpose.SIGN_UP, "different") isEqualTo false | ||
} | ||
} |
Oops, something went wrong.