From 723fa5dd1237ad314507cbe0a7c2c175d25eafbb Mon Sep 17 00:00:00 2001 From: rlaisqls Date: Tue, 9 May 2023 22:11:55 +0900 Subject: [PATCH 01/13] setting: (#490) aws kms dependency --- buildSrc/src/main/kotlin/Dependencies.kt | 1 + buildSrc/src/main/kotlin/DependencyVersions.kt | 1 + dms-infrastructure/build.gradle.kts | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index b9537ac49..023c23179 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -43,6 +43,7 @@ object Dependencies { // aws const val SPRING_AWS = "org.springframework.cloud:spring-cloud-starter-aws:${DependencyVersions.AWS_VERSION}" const val AWS_SES = "com.amazonaws:aws-java-sdk-ses:${DependencyVersions.SES_VERSION}" + const val AWS_KMS = "com.amazonaws:aws-java-sdk-kms:${DependencyVersions.KMS_VERSION}" // test const val SPRING_TEST = "org.springframework.boot:spring-boot-starter-test:${PluginVersions.SPRING_BOOT_VERSION}" diff --git a/buildSrc/src/main/kotlin/DependencyVersions.kt b/buildSrc/src/main/kotlin/DependencyVersions.kt index 350906584..39e660b2a 100644 --- a/buildSrc/src/main/kotlin/DependencyVersions.kt +++ b/buildSrc/src/main/kotlin/DependencyVersions.kt @@ -3,6 +3,7 @@ object DependencyVersions { const val JWT_VERSION = "0.9.1" const val AWS_VERSION = "2.2.6.RELEASE" const val SES_VERSION = "1.11.852" + const val KMS_VERSION ="1.12.464" const val REDIS_VERSION = "2.7.2" const val SERVLET_VERSION = "4.0.1" const val UUID_TIME_VERSION = "3.1.4" diff --git a/dms-infrastructure/build.gradle.kts b/dms-infrastructure/build.gradle.kts index b49b646f3..74601e61a 100644 --- a/dms-infrastructure/build.gradle.kts +++ b/dms-infrastructure/build.gradle.kts @@ -24,8 +24,9 @@ dependencies { implementation(Dependencies.JWT) // aws - implementation(Dependencies.AWS_SES) implementation(Dependencies.SPRING_AWS) + implementation(Dependencies.AWS_SES) + implementation(Dependencies.AWS_KMS) // configuration kapt(Dependencies.CONFIGURATION_PROCESSOR) From 82b1dacf27671e76d000de457058d2f3ba452a7f Mon Sep 17 00:00:00 2001 From: rlaisqls Date: Tue, 9 May 2023 23:38:00 +0900 Subject: [PATCH 02/13] setting: (#490) kms arn --- dms-infrastructure/src/main/resources/application.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dms-infrastructure/src/main/resources/application.yml b/dms-infrastructure/src/main/resources/application.yml index 8b4a7f243..926d9ff31 100644 --- a/dms-infrastructure/src/main/resources/application.yml +++ b/dms-infrastructure/src/main/resources/application.yml @@ -61,6 +61,8 @@ cloud: bucket: ${BUCKET_NAME:dms} ses: source: ${SES_SENDER:asdf} + kms: + key: ${KMS_ARN:asdf} sentry: dsn: ${SENTRY_DSN:asdf} From 5273ab505f6b13e98925fdfe05ab0ebe8edaacde Mon Sep 17 00:00:00 2001 From: rlaisqls Date: Wed, 10 May 2023 14:42:03 +0900 Subject: [PATCH 03/13] feat: (#490) kms config & adapter --- .../team/aliens/dms/common/spi/EncryptPort.kt | 12 +++ .../dms/thirdparty/encrypt/AwsKMSAdapter.kt | 90 +++++++++++++++++++ .../thirdparty/encrypt/AwsKMSConfiguration.kt | 25 ++++++ .../thirdparty/encrypt/AwsKMSProperties.kt | 10 +++ .../thirdparty/encrypt/EncryptProperties.kt | 11 +++ .../encrypt/exception/KMSException.kt | 8 ++ 6 files changed, 156 insertions(+) create mode 100644 dms-core/src/main/kotlin/team/aliens/dms/common/spi/EncryptPort.kt create mode 100644 dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSAdapter.kt create mode 100644 dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSConfiguration.kt create mode 100644 dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSProperties.kt create mode 100644 dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/EncryptProperties.kt create mode 100644 dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/exception/KMSException.kt diff --git a/dms-core/src/main/kotlin/team/aliens/dms/common/spi/EncryptPort.kt b/dms-core/src/main/kotlin/team/aliens/dms/common/spi/EncryptPort.kt new file mode 100644 index 000000000..dc4476d4d --- /dev/null +++ b/dms-core/src/main/kotlin/team/aliens/dms/common/spi/EncryptPort.kt @@ -0,0 +1,12 @@ +package team.aliens.dms.common.spi + +interface EncryptPort { + + fun symmetricEncrypt(secretKey: String, plainText: String): String + + fun symmetricDecrypt(secretKey: String, cipherText: String): String + + fun asymmetricEncrypt(plainText: String): String + + fun asymmetricDecrypt(cipherText: String): String +} diff --git a/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSAdapter.kt b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSAdapter.kt new file mode 100644 index 000000000..f73249d7a --- /dev/null +++ b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSAdapter.kt @@ -0,0 +1,90 @@ +package team.aliens.dms.thirdparty.encrypt + +import com.amazonaws.services.kms.AWSKMSAsync +import com.amazonaws.services.kms.model.DecryptRequest +import com.amazonaws.services.kms.model.EncryptRequest +import com.amazonaws.services.kms.model.EncryptionAlgorithmSpec +import org.springframework.cache.annotation.Cacheable +import org.springframework.stereotype.Component +import team.aliens.dms.common.spi.EncryptPort +import team.aliens.dms.thirdparty.encrypt.exception.KMSException +import java.nio.ByteBuffer +import java.util.Base64 +import javax.crypto.Cipher +import javax.crypto.spec.SecretKeySpec + +@Component +class AwsKMSAdapter( + private val awskmsAsync: AWSKMSAsync, + private val awsKMSProperties: AwsKMSProperties, + private val encryptProperties: EncryptProperties +) : EncryptPort { + + override fun symmetricEncrypt( + secretKey: String, + plainText: String, + ): String { + val cipher = getCipher(Cipher.ENCRYPT_MODE, secretKey) + return String( + cipher.doFinal(plainText.toByteArray()).encodeBase64() + ) + } + + override fun symmetricDecrypt( + secretKey: String, + cipherText: String, + ): String { + val cipher = getCipher(Cipher.DECRYPT_MODE, secretKey) + return String( + cipher.doFinal(cipherText.decodeBase64()) + ) + } + + @Cacheable("cipher") + protected fun getCipher(opmode: Int, key: String): Cipher = + Cipher.getInstance(encryptProperties.transformation).apply { + init(opmode, SecretKeySpec(key.toByteArray(), encryptProperties.algorithm)) + } + + override fun asymmetricEncrypt(plainText: String): String { + + val request = EncryptRequest() + .withKeyId(awsKMSProperties.key) + .withPlaintext(ByteBuffer.wrap(plainText.toByteArray())) + .withEncryptionAlgorithm(EncryptionAlgorithmSpec.RSAES_OAEP_SHA_256) + + val encryptedByte = runCatching { + awskmsAsync.encrypt(request).ciphertextBlob.array() + }.onFailure { e -> + e.printStackTrace() + throw KMSException + }.getOrThrow() + + return String( + encryptedByte.encodeBase64() + ) + } + + @Cacheable("decryptedText") + override fun asymmetricDecrypt(cipherText: String): String { + + val request = DecryptRequest() + .withKeyId(awsKMSProperties.key) + .withCiphertextBlob(ByteBuffer.wrap(cipherText.decodeBase64())) + .withEncryptionAlgorithm(EncryptionAlgorithmSpec.RSAES_OAEP_SHA_256) + + val decryptedByte = runCatching { + awskmsAsync.decrypt(request).plaintext.array() + }.onFailure { e -> + e.printStackTrace() + throw KMSException + }.getOrThrow() + + return String(decryptedByte) + } + + // byteArray를 String으로 바로 변환하면 padding이 깨지는 문제가 발생하기 때문에 + // encoding 결과를 Base64로 암호화하여 저장한다. + private fun ByteArray.encodeBase64() = Base64.getEncoder().encode(this) + private fun String.decodeBase64() = Base64.getDecoder().decode(this) +} diff --git a/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSConfiguration.kt b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSConfiguration.kt new file mode 100644 index 000000000..7462d0802 --- /dev/null +++ b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSConfiguration.kt @@ -0,0 +1,25 @@ +package team.aliens.dms.thirdparty.encrypt + +import com.amazonaws.auth.AWSStaticCredentialsProvider +import com.amazonaws.auth.BasicAWSCredentials +import com.amazonaws.regions.Regions +import com.amazonaws.services.kms.AWSKMSAsync +import com.amazonaws.services.kms.AWSKMSAsyncClient +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import team.aliens.dms.thirdparty.AwsProperties + +@Configuration +class AwsKMSConfiguration( + private val awsProperties: AwsProperties +) { + @Bean + fun awsKMSAsync(): AWSKMSAsync { + val basicAWSCredentials = BasicAWSCredentials(awsProperties.accessKey, awsProperties.secretKey) + + return AWSKMSAsyncClient.asyncBuilder() + .withCredentials(AWSStaticCredentialsProvider(basicAWSCredentials)) + .withRegion(Regions.AP_NORTHEAST_2) + .build() + } +} diff --git a/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSProperties.kt b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSProperties.kt new file mode 100644 index 000000000..2624c1629 --- /dev/null +++ b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSProperties.kt @@ -0,0 +1,10 @@ +package team.aliens.dms.thirdparty.encrypt + +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.boot.context.properties.ConstructorBinding + +@ConfigurationProperties("cloud.aws.kms") +@ConstructorBinding +class AwsKMSProperties( + val key: String +) diff --git a/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/EncryptProperties.kt b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/EncryptProperties.kt new file mode 100644 index 000000000..3e0c91aa3 --- /dev/null +++ b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/EncryptProperties.kt @@ -0,0 +1,11 @@ +package team.aliens.dms.thirdparty.encrypt + +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.boot.context.properties.ConstructorBinding + +@ConfigurationProperties("encrypt") +@ConstructorBinding +class EncryptProperties( + val transformation: String, + val algorithm: String +) \ No newline at end of file diff --git a/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/exception/KMSException.kt b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/exception/KMSException.kt new file mode 100644 index 000000000..b3a00f1bd --- /dev/null +++ b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/exception/KMSException.kt @@ -0,0 +1,8 @@ +package team.aliens.dms.thirdparty.encrypt.exception + +import team.aliens.dms.common.error.DmsException +import team.aliens.dms.global.error.GlobalErrorCode + +object KMSException : DmsException( + GlobalErrorCode.KEY_MANAGEMENT_SERVICE +) From 1db24a4ba338dae525bd948ef3c98f3a5db2ee33 Mon Sep 17 00:00:00 2001 From: rlaisqls Date: Wed, 10 May 2023 07:07:39 +0900 Subject: [PATCH 04/13] feat: (#490) schoolSecret MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ㅍ --- .../aliens/dms/common/model/SchoolSecret.kt | 9 +++++ .../service/security/SecurityService.kt | 2 ++ .../service/security/SecurityServiceImpl.kt | 16 ++++++++- .../aliens/dms/common/spi/SchoolSecretPort.kt | 11 +++++++ .../team/aliens/dms/common/util/StringUtil.kt | 11 +++++-- .../SchoolSecretPersistenceAdapter.kt | 28 ++++++++++++++++ .../security/entity/SchoolSecretJpaEntity.kt | 32 ++++++++++++++++++ .../security/mapper/SchoolSecretMapper.kt | 33 +++++++++++++++++++ .../repository/SchoolSecretJpaRepository.kt | 9 +++++ 9 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 dms-core/src/main/kotlin/team/aliens/dms/common/model/SchoolSecret.kt create mode 100644 dms-core/src/main/kotlin/team/aliens/dms/common/spi/SchoolSecretPort.kt create mode 100644 dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/SchoolSecretPersistenceAdapter.kt create mode 100644 dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/entity/SchoolSecretJpaEntity.kt create mode 100644 dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/mapper/SchoolSecretMapper.kt create mode 100644 dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/repository/SchoolSecretJpaRepository.kt diff --git a/dms-core/src/main/kotlin/team/aliens/dms/common/model/SchoolSecret.kt b/dms-core/src/main/kotlin/team/aliens/dms/common/model/SchoolSecret.kt new file mode 100644 index 000000000..764faa25e --- /dev/null +++ b/dms-core/src/main/kotlin/team/aliens/dms/common/model/SchoolSecret.kt @@ -0,0 +1,9 @@ +package team.aliens.dms.common.model + +import java.io.Serializable +import java.util.UUID + +data class SchoolSecret( + val schoolId: UUID, + val secretKey: String +) : Serializable diff --git a/dms-core/src/main/kotlin/team/aliens/dms/common/service/security/SecurityService.kt b/dms-core/src/main/kotlin/team/aliens/dms/common/service/security/SecurityService.kt index 7a3d6f35d..ef9af7eb5 100644 --- a/dms-core/src/main/kotlin/team/aliens/dms/common/service/security/SecurityService.kt +++ b/dms-core/src/main/kotlin/team/aliens/dms/common/service/security/SecurityService.kt @@ -9,4 +9,6 @@ interface SecurityService { fun getCurrentUserId(): UUID fun checkIsPasswordMatches(rawPassword: String, encodedPassword: String) + + fun createSchoolSecretBySchoolId(schoolId: UUID) } diff --git a/dms-core/src/main/kotlin/team/aliens/dms/common/service/security/SecurityServiceImpl.kt b/dms-core/src/main/kotlin/team/aliens/dms/common/service/security/SecurityServiceImpl.kt index 515113a10..49ae2f00b 100644 --- a/dms-core/src/main/kotlin/team/aliens/dms/common/service/security/SecurityServiceImpl.kt +++ b/dms-core/src/main/kotlin/team/aliens/dms/common/service/security/SecurityServiceImpl.kt @@ -1,12 +1,17 @@ package team.aliens.dms.common.service.security +import java.util.UUID import team.aliens.dms.common.annotation.Service +import team.aliens.dms.common.model.SchoolSecret +import team.aliens.dms.common.spi.SchoolSecretPort import team.aliens.dms.common.spi.SecurityPort +import team.aliens.dms.common.util.StringUtil import team.aliens.dms.domain.auth.exception.PasswordMismatchException @Service class SecurityServiceImpl( - private val securityPort: SecurityPort + private val securityPort: SecurityPort, + private val schoolSecretPort: SchoolSecretPort ) : SecurityService { override fun encodePassword(password: String) = @@ -20,4 +25,13 @@ class SecurityServiceImpl( throw PasswordMismatchException } } + + override fun createSchoolSecretBySchoolId(schoolId: UUID) { + schoolSecretPort.saveSchoolSecret( + SchoolSecret( + schoolId = schoolId, + secretKey = StringUtil.randomKey() + ) + ) + } } diff --git a/dms-core/src/main/kotlin/team/aliens/dms/common/spi/SchoolSecretPort.kt b/dms-core/src/main/kotlin/team/aliens/dms/common/spi/SchoolSecretPort.kt new file mode 100644 index 000000000..4617a7956 --- /dev/null +++ b/dms-core/src/main/kotlin/team/aliens/dms/common/spi/SchoolSecretPort.kt @@ -0,0 +1,11 @@ +package team.aliens.dms.common.spi + +import java.util.UUID +import team.aliens.dms.common.model.SchoolSecret + +interface SchoolSecretPort { + + fun saveSchoolSecret(schoolSecret: SchoolSecret) + + fun querySchoolSecretBySchoolId(schoolId: UUID): SchoolSecret? +} diff --git a/dms-core/src/main/kotlin/team/aliens/dms/common/util/StringUtil.kt b/dms-core/src/main/kotlin/team/aliens/dms/common/util/StringUtil.kt index 12ed12f52..d00aeb35c 100644 --- a/dms-core/src/main/kotlin/team/aliens/dms/common/util/StringUtil.kt +++ b/dms-core/src/main/kotlin/team/aliens/dms/common/util/StringUtil.kt @@ -1,6 +1,7 @@ package team.aliens.dms.common.util import java.security.SecureRandom +import java.util.Base64 object StringUtil { @@ -18,17 +19,23 @@ object StringUtil { return sb.toString() } + private val RANDOM = SecureRandom() + fun randomNumber(number: Int): String { - val random = SecureRandom() val codeList: List = listOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') val authCodeList: MutableList = mutableListOf() for (i: Int in 0 until number) { - authCodeList.add(i, codeList[random.nextInt(codeList.size)].toString()) + authCodeList.add(i, codeList[RANDOM.nextInt(codeList.size)].toString()) } return authCodeList.toString().replace("[^0-9]".toRegex(), "") } + fun randomKey(byteSize: Int = 24): String = + Base64.getUrlEncoder().encodeToString( + ByteArray(byteSize).also { RANDOM.nextBytes(it) } + ) + fun List.toStringWithoutBracket() = toString().replace("[\\[\\]]".toRegex(), "") } diff --git a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/SchoolSecretPersistenceAdapter.kt b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/SchoolSecretPersistenceAdapter.kt new file mode 100644 index 000000000..91b44bd78 --- /dev/null +++ b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/SchoolSecretPersistenceAdapter.kt @@ -0,0 +1,28 @@ +package team.aliens.dms.persistence.security + +import org.springframework.cache.annotation.Cacheable +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Component +import team.aliens.dms.common.model.SchoolSecret +import team.aliens.dms.common.spi.SchoolSecretPort +import team.aliens.dms.persistence.security.mapper.SchoolSecretMapper +import team.aliens.dms.persistence.security.repository.SchoolSecretJpaRepository +import java.util.UUID + +@Component +class SchoolSecretPersistenceAdapter( + private val schoolSecretRepository: SchoolSecretJpaRepository, + private val schoolSecretMapper: SchoolSecretMapper +) : SchoolSecretPort { + + @Cacheable("schoolSecret") + override fun querySchoolSecretBySchoolId(schoolId: UUID) = + schoolSecretRepository.findByIdOrNull(schoolId) + ?.let { schoolSecretMapper.toDomain(it) } + + override fun saveSchoolSecret(schoolSecret: SchoolSecret) { + schoolSecretRepository.save( + schoolSecretMapper.toEntity(schoolSecret) + ) + } +} diff --git a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/entity/SchoolSecretJpaEntity.kt b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/entity/SchoolSecretJpaEntity.kt new file mode 100644 index 000000000..a3cdc11f8 --- /dev/null +++ b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/entity/SchoolSecretJpaEntity.kt @@ -0,0 +1,32 @@ +package team.aliens.dms.persistence.security.entity + +import team.aliens.dms.common.annotation.EncryptType +import team.aliens.dms.common.annotation.EncryptedColumn +import team.aliens.dms.persistence.school.entity.SchoolJpaEntity +import java.util.UUID +import javax.persistence.Column +import javax.persistence.Entity +import javax.persistence.FetchType +import javax.persistence.Id +import javax.persistence.JoinColumn +import javax.persistence.MapsId +import javax.persistence.OneToOne +import javax.persistence.Table + +@Entity +@Table(name = "tbl_school_secret") +class SchoolSecretJpaEntity( + + @Id + val schoolId: UUID, + + @MapsId + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "school_id", columnDefinition = "BINARY(16)", nullable = false) + val school: SchoolJpaEntity?, + + @EncryptedColumn(type = EncryptType.ASYMMETRIC) + @Column(columnDefinition = "TEXT", nullable = false) + val secretKey: String + +) diff --git a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/mapper/SchoolSecretMapper.kt b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/mapper/SchoolSecretMapper.kt new file mode 100644 index 000000000..93c38b8df --- /dev/null +++ b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/mapper/SchoolSecretMapper.kt @@ -0,0 +1,33 @@ +package team.aliens.dms.persistence.security.mapper + +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Component +import team.aliens.dms.common.model.SchoolSecret +import team.aliens.dms.domain.school.exception.SchoolNotFoundException +import team.aliens.dms.persistence.EncryptableGenericMapper +import team.aliens.dms.persistence.school.repository.SchoolJpaRepository +import team.aliens.dms.persistence.security.entity.SchoolSecretJpaEntity + +@Component +class SchoolSecretMapper( + private val schoolRepository: SchoolJpaRepository +) : EncryptableGenericMapper { + + override fun toDomain(entity: SchoolSecretJpaEntity?): SchoolSecret? { + return entity?.let { + SchoolSecret( + schoolId = it.schoolId, + secretKey = it.secretKey + ) + } + } + + override fun toEntity(domain: SchoolSecret): SchoolSecretJpaEntity { + val school = schoolRepository.findByIdOrNull(domain.schoolId) ?: throw SchoolNotFoundException + return SchoolSecretJpaEntity( + schoolId = domain.schoolId, + school = school, + secretKey = domain.secretKey + ) + } +} diff --git a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/repository/SchoolSecretJpaRepository.kt b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/repository/SchoolSecretJpaRepository.kt new file mode 100644 index 000000000..9025130ad --- /dev/null +++ b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/repository/SchoolSecretJpaRepository.kt @@ -0,0 +1,9 @@ +package team.aliens.dms.persistence.security.repository + +import org.springframework.data.repository.CrudRepository +import org.springframework.stereotype.Repository +import team.aliens.dms.persistence.security.entity.SchoolSecretJpaEntity +import java.util.UUID + +@Repository +interface SchoolSecretJpaRepository : CrudRepository From faf7bbc0d36c91581a0020245baefb1911aea5cb Mon Sep 17 00:00:00 2001 From: rlaisqls Date: Wed, 10 May 2023 07:14:22 +0900 Subject: [PATCH 05/13] chore: (#490) createSchool api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 테스트용 api. url 권한은 설정하지 않았습니다. ㅇ . --- .../domain/school/dto/CreateSchoolRequest.kt | 37 +++++++++++++++++++ .../aliens/dms/domain/school/model/School.kt | 2 +- .../school/service/CommandSchoolService.kt | 3 ++ .../service/CommandSchoolServiceImpl.kt | 4 ++ .../domain/school/spi/CommandSchoolPort.kt | 3 ++ .../school/usecase/CreateSchoolUseCase.kt | 21 +++++++++++ .../dms/domain/school/SchoolWebAdapter.kt | 35 +++++++++++++++--- .../dto/request/CreateSchoolWebRequest.kt | 11 ++++++ 8 files changed, 109 insertions(+), 7 deletions(-) create mode 100644 dms-core/src/main/kotlin/team/aliens/dms/domain/school/dto/CreateSchoolRequest.kt create mode 100644 dms-core/src/main/kotlin/team/aliens/dms/domain/school/usecase/CreateSchoolUseCase.kt create mode 100644 dms-presentation/src/main/kotlin/team/aliens/dms/domain/school/dto/request/CreateSchoolWebRequest.kt diff --git a/dms-core/src/main/kotlin/team/aliens/dms/domain/school/dto/CreateSchoolRequest.kt b/dms-core/src/main/kotlin/team/aliens/dms/domain/school/dto/CreateSchoolRequest.kt new file mode 100644 index 000000000..98149ed16 --- /dev/null +++ b/dms-core/src/main/kotlin/team/aliens/dms/domain/school/dto/CreateSchoolRequest.kt @@ -0,0 +1,37 @@ +package team.aliens.dms.domain.school.dto + +import java.time.LocalDate +import java.util.UUID +import team.aliens.dms.common.util.StringUtil +import team.aliens.dms.domain.school.model.AvailableFeature +import team.aliens.dms.domain.school.model.School + +class CreateSchoolRequest( + val schoolName: String, + val schoolAddress: String, + val mealService: Boolean, + val noticeService: Boolean, + val pointService: Boolean, + val studyRoomService: Boolean, + val remainService: Boolean +) { + fun toSchool() = + School( + name = schoolName, + code = StringUtil.randomNumber(School.SCHOOL_CODE_SIZE), + question = "우리 학교 이름은?", + answer = schoolName, + address = schoolAddress, + contractStartedAt = LocalDate.now() + ) + + fun toAvailableFeature(schoolId: UUID) = + AvailableFeature( + schoolId = schoolId, + mealService = mealService, + noticeService = noticeService, + pointService = pointService, + studyRoomService = studyRoomService, + remainService = remainService + ) +} diff --git a/dms-core/src/main/kotlin/team/aliens/dms/domain/school/model/School.kt b/dms-core/src/main/kotlin/team/aliens/dms/domain/school/model/School.kt index 377eba22a..18767eb00 100644 --- a/dms-core/src/main/kotlin/team/aliens/dms/domain/school/model/School.kt +++ b/dms-core/src/main/kotlin/team/aliens/dms/domain/school/model/School.kt @@ -22,7 +22,7 @@ data class School( val contractStartedAt: LocalDate, - val contractEndedAt: LocalDate? + val contractEndedAt: LocalDate? = null ) { companion object { diff --git a/dms-core/src/main/kotlin/team/aliens/dms/domain/school/service/CommandSchoolService.kt b/dms-core/src/main/kotlin/team/aliens/dms/domain/school/service/CommandSchoolService.kt index 80d3435f7..1c6a2d9a6 100644 --- a/dms-core/src/main/kotlin/team/aliens/dms/domain/school/service/CommandSchoolService.kt +++ b/dms-core/src/main/kotlin/team/aliens/dms/domain/school/service/CommandSchoolService.kt @@ -1,8 +1,11 @@ package team.aliens.dms.domain.school.service +import team.aliens.dms.domain.school.model.AvailableFeature import team.aliens.dms.domain.school.model.School interface CommandSchoolService { fun saveSchool(school: School): School + + fun saveAvailableFeature(availableFeature: AvailableFeature): AvailableFeature } diff --git a/dms-core/src/main/kotlin/team/aliens/dms/domain/school/service/CommandSchoolServiceImpl.kt b/dms-core/src/main/kotlin/team/aliens/dms/domain/school/service/CommandSchoolServiceImpl.kt index cdc6e7007..47ac40050 100644 --- a/dms-core/src/main/kotlin/team/aliens/dms/domain/school/service/CommandSchoolServiceImpl.kt +++ b/dms-core/src/main/kotlin/team/aliens/dms/domain/school/service/CommandSchoolServiceImpl.kt @@ -1,6 +1,7 @@ package team.aliens.dms.domain.school.service import team.aliens.dms.common.annotation.Service +import team.aliens.dms.domain.school.model.AvailableFeature import team.aliens.dms.domain.school.model.School import team.aliens.dms.domain.school.spi.CommandSchoolPort @@ -11,4 +12,7 @@ class CommandSchoolServiceImpl( override fun saveSchool(school: School) = commandSchoolPort.saveSchool(school) + + override fun saveAvailableFeature(availableFeature: AvailableFeature) = + commandSchoolPort.saveAvailableFeature(availableFeature) } diff --git a/dms-core/src/main/kotlin/team/aliens/dms/domain/school/spi/CommandSchoolPort.kt b/dms-core/src/main/kotlin/team/aliens/dms/domain/school/spi/CommandSchoolPort.kt index 919ae2510..6eb1aa2d6 100644 --- a/dms-core/src/main/kotlin/team/aliens/dms/domain/school/spi/CommandSchoolPort.kt +++ b/dms-core/src/main/kotlin/team/aliens/dms/domain/school/spi/CommandSchoolPort.kt @@ -1,8 +1,11 @@ package team.aliens.dms.domain.school.spi +import team.aliens.dms.domain.school.model.AvailableFeature import team.aliens.dms.domain.school.model.School interface CommandSchoolPort { fun saveSchool(school: School): School + + fun saveAvailableFeature(availableFeature: AvailableFeature): AvailableFeature } diff --git a/dms-core/src/main/kotlin/team/aliens/dms/domain/school/usecase/CreateSchoolUseCase.kt b/dms-core/src/main/kotlin/team/aliens/dms/domain/school/usecase/CreateSchoolUseCase.kt new file mode 100644 index 000000000..0c5f5811e --- /dev/null +++ b/dms-core/src/main/kotlin/team/aliens/dms/domain/school/usecase/CreateSchoolUseCase.kt @@ -0,0 +1,21 @@ +package team.aliens.dms.domain.school.usecase + +import team.aliens.dms.common.annotation.UseCase +import team.aliens.dms.common.service.security.SecurityService +import team.aliens.dms.domain.school.dto.CreateSchoolRequest +import team.aliens.dms.domain.school.service.SchoolService + +@UseCase +class CreateSchoolUseCase( + private val schoolService: SchoolService, + private val securityService: SecurityService +) { + + fun execute(request: CreateSchoolRequest) { + val school = schoolService.saveSchool( + request.toSchool() + ) + schoolService.saveAvailableFeature(request.toAvailableFeature(school.id)) + securityService.createSchoolSecretBySchoolId(school.id) + } +} \ No newline at end of file diff --git a/dms-presentation/src/main/kotlin/team/aliens/dms/domain/school/SchoolWebAdapter.kt b/dms-presentation/src/main/kotlin/team/aliens/dms/domain/school/SchoolWebAdapter.kt index 8c37edfe3..4f25b7df2 100644 --- a/dms-presentation/src/main/kotlin/team/aliens/dms/domain/school/SchoolWebAdapter.kt +++ b/dms-presentation/src/main/kotlin/team/aliens/dms/domain/school/SchoolWebAdapter.kt @@ -1,34 +1,38 @@ package team.aliens.dms.domain.school +import java.util.UUID +import javax.validation.Valid +import javax.validation.constraints.NotBlank +import javax.validation.constraints.NotNull +import javax.validation.constraints.Size import org.springframework.http.HttpStatus import org.springframework.validation.annotation.Validated import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PatchMapping import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.ResponseStatus import org.springframework.web.bind.annotation.RestController +import team.aliens.dms.domain.school.dto.CreateSchoolRequest import team.aliens.dms.domain.school.dto.QueryAvailableFeaturesResponse import team.aliens.dms.domain.school.dto.SchoolsResponse import team.aliens.dms.domain.school.dto.UpdateQuestionRequest +import team.aliens.dms.domain.school.dto.request.CreateSchoolWebRequest import team.aliens.dms.domain.school.dto.request.UpdateQuestionWebRequest import team.aliens.dms.domain.school.dto.response.ReissueSchoolCodeResponse import team.aliens.dms.domain.school.dto.response.SchoolIdResponse import team.aliens.dms.domain.school.dto.response.SchoolQuestionResponse import team.aliens.dms.domain.school.usecase.CheckSchoolAnswerUseCase import team.aliens.dms.domain.school.usecase.CheckSchoolCodeUseCase +import team.aliens.dms.domain.school.usecase.CreateSchoolUseCase import team.aliens.dms.domain.school.usecase.QueryAvailableFeaturesUseCase import team.aliens.dms.domain.school.usecase.QuerySchoolQuestionUseCase import team.aliens.dms.domain.school.usecase.QuerySchoolsUseCase import team.aliens.dms.domain.school.usecase.ReissueSchoolCodeUseCase import team.aliens.dms.domain.school.usecase.UpdateQuestionUseCase -import java.util.UUID -import javax.validation.Valid -import javax.validation.constraints.NotBlank -import javax.validation.constraints.NotNull -import javax.validation.constraints.Size @Validated @RequestMapping("/schools") @@ -40,7 +44,8 @@ class SchoolWebAdapter( private val checkSchoolCodeUseCase: CheckSchoolCodeUseCase, private val updateQuestionUseCase: UpdateQuestionUseCase, private val reissueSchoolCodeUseCase: ReissueSchoolCodeUseCase, - private val queryAvailableFeaturesUseCase: QueryAvailableFeaturesUseCase + private val queryAvailableFeaturesUseCase: QueryAvailableFeaturesUseCase, + private val createSchoolUseCase: CreateSchoolUseCase ) { @GetMapping @@ -97,4 +102,22 @@ class SchoolWebAdapter( updateQuestionUseCase.execute(request) } + + @ResponseStatus(HttpStatus.NO_CONTENT) + @PostMapping + fun createSchool(@RequestBody @Valid request: CreateSchoolWebRequest) { + createSchoolUseCase.execute( + request.run { + CreateSchoolRequest( + schoolName = schoolName, + schoolAddress = schoolAddress, + mealService = mealService, + noticeService = noticeService, + pointService = pointService, + studyRoomService = studyRoomService, + remainService = remainService + ) + } + ) + } } diff --git a/dms-presentation/src/main/kotlin/team/aliens/dms/domain/school/dto/request/CreateSchoolWebRequest.kt b/dms-presentation/src/main/kotlin/team/aliens/dms/domain/school/dto/request/CreateSchoolWebRequest.kt new file mode 100644 index 000000000..40cc1ca48 --- /dev/null +++ b/dms-presentation/src/main/kotlin/team/aliens/dms/domain/school/dto/request/CreateSchoolWebRequest.kt @@ -0,0 +1,11 @@ +package team.aliens.dms.domain.school.dto.request + +class CreateSchoolWebRequest( + val schoolName: String, + val schoolAddress: String, + val mealService: Boolean, + val noticeService: Boolean, + val pointService: Boolean, + val studyRoomService: Boolean, + val remainService: Boolean +) \ No newline at end of file From d223fa91009ee033bc1296a6b1ea0a5659e39b18 Mon Sep 17 00:00:00 2001 From: rlaisqls Date: Wed, 10 May 2023 07:15:33 +0900 Subject: [PATCH 06/13] feat: (#490) EntityEncryptAspect --- .../dms/global/error/GlobalErrorCode.kt | 1 + .../encrypt/aop/EntityEncryptAspect.kt | 122 ++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/aop/EntityEncryptAspect.kt diff --git a/dms-infrastructure/src/main/kotlin/team/aliens/dms/global/error/GlobalErrorCode.kt b/dms-infrastructure/src/main/kotlin/team/aliens/dms/global/error/GlobalErrorCode.kt index b7572cf60..28e3c386c 100644 --- a/dms-infrastructure/src/main/kotlin/team/aliens/dms/global/error/GlobalErrorCode.kt +++ b/dms-infrastructure/src/main/kotlin/team/aliens/dms/global/error/GlobalErrorCode.kt @@ -14,6 +14,7 @@ enum class GlobalErrorCode( BAD_REQUEST(ErrorStatus.BAD_REQUEST, "Bad Request", 3), INVALID_FILE(ErrorStatus.BAD_REQUEST, "Invalid File", 4), BAD_EXCEL_FORMAT(ErrorStatus.BAD_REQUEST, "%s행 등 %s개 행의 데이터 형식이 잘못되었습니다.", 5), + KEY_MANAGEMENT_SERVICE(ErrorStatus.BAD_REQUEST, "Key processing failed", 6), EXTENSION_MISMATCH(401, "File Extension Mismatch", 1), diff --git a/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/aop/EntityEncryptAspect.kt b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/aop/EntityEncryptAspect.kt new file mode 100644 index 000000000..801d95b8c --- /dev/null +++ b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/aop/EntityEncryptAspect.kt @@ -0,0 +1,122 @@ +package team.aliens.dms.thirdparty.encrypt.aop + +import org.aspectj.lang.JoinPoint +import org.aspectj.lang.ProceedingJoinPoint +import org.aspectj.lang.annotation.AfterReturning +import org.aspectj.lang.annotation.Around +import org.aspectj.lang.annotation.Aspect +import org.aspectj.lang.reflect.MethodSignature +import team.aliens.dms.common.annotation.Decrypted +import team.aliens.dms.common.annotation.EncryptType +import team.aliens.dms.common.annotation.EncryptedColumn +import team.aliens.dms.common.annotation.UseCase +import team.aliens.dms.common.spi.EncryptPort +import team.aliens.dms.common.spi.SchoolSecretPort +import team.aliens.dms.common.spi.SecurityPort +import team.aliens.dms.domain.school.exception.SchoolNotFoundException +import java.lang.reflect.Field +import java.util.function.Consumer +import java.util.function.Function + +@UseCase +@Aspect +class EntityEncryptAspect( + private val securityPort: SecurityPort, + private val schoolSecretPort: SchoolSecretPort, + private val encryptPort: EncryptPort +) { + + @AfterReturning( + "@annotation(team.aliens.dms.common.annotation.Encrypt)", + returning = "ret" + ) + fun encryptReturnValue(joinPoint: JoinPoint, ret: Any) { + if (ret is List<*>) ret.map { obj -> + doOnEncryptedColumn(obj!!) { field -> + setEncryptedValue(obj, field) + } + } else { + doOnEncryptedColumn(ret) { field -> + setEncryptedValue(ret, field) + } + } + } + + @AfterReturning( + "@annotation(team.aliens.dms.common.annotation.Decrypt)", + returning = "ret" + ) + fun decryptReturnValue(joinPoint: JoinPoint, ret: Any) { + if (ret is List<*>) ret.map { obj -> + doOnEncryptedColumn(obj!!) { field -> + setDecryptedValue(obj, field) + } + } else { + doOnEncryptedColumn(ret) { field -> + setDecryptedValue(ret, field) + } + } + } + + @Around("execution(* *(.., @team.aliens.dms.common.annotation.Decrypted() *, ..))+") + fun decryptParameterValue(joinPoint: ProceedingJoinPoint) { + (joinPoint.signature as MethodSignature).method.parameters + .forEachIndexed { idx, param -> + if (param.annotations.contains(Decrypted::class as Annotation)) { + doOnEncryptedColumn(joinPoint.args[idx]) { field -> + setDecryptedValue(joinPoint.args[idx], field) + } + } + } + } + + private fun doOnEncryptedColumn(obj: Any, behavior: Consumer) { + obj::class.java.declaredFields.map { field: Field -> + if (field.isAnnotationPresent(EncryptedColumn::class.java)) { + behavior.accept(field) + } + } + } + + private fun setEncryptedValue(obj: Any, field: Field) { + getAnnotationAndSet(obj, field) { f -> + when (f.getAnnotation(EncryptedColumn::class.java).type) { + EncryptType.SYMMETRIC -> encryptPort.symmetricEncrypt( + secretKey = getSchoolKey(), + plainText = f.get(obj) as String + ) + EncryptType.ASYMMETRIC -> encryptPort.asymmetricEncrypt( + plainText = f.get(obj) as String + ) + } + } + } + + private fun setDecryptedValue(obj: Any, field: Field) { + getAnnotationAndSet(obj, field) { f -> + when (f.getAnnotation(EncryptedColumn::class.java).type) { + EncryptType.SYMMETRIC -> encryptPort.symmetricDecrypt( + secretKey = getSchoolKey(), + cipherText = f.get(obj) as String + ) + EncryptType.ASYMMETRIC -> encryptPort.asymmetricDecrypt( + cipherText = f.get(obj) as String + ) + } + } + } + + private fun getAnnotationAndSet(obj: Any, field: Field, function: Function) { + field.run { + check(type == String::class.java) + isAccessible = true + set(obj, function.apply(this)) + } + } + + private fun getSchoolKey(): String { + val schoolId = securityPort.getCurrentUserSchoolId() + val schoolSecret = schoolSecretPort.querySchoolSecretBySchoolId(schoolId) ?: throw SchoolNotFoundException + return encryptPort.asymmetricDecrypt(schoolSecret.secretKey) + } +} From ebc48cd2dc697a63bcbb9534183323139f26a006 Mon Sep 17 00:00:00 2001 From: rlaisqls Date: Wed, 10 May 2023 07:19:14 +0900 Subject: [PATCH 07/13] =?UTF-8?q?chore:=20(#490)=20aop=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=EC=9D=84=20=EC=9C=84=ED=95=9C=20dto=20=EC=83=81?= =?UTF-8?q?=EC=86=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dto --- .../domain/manager/spi/vo/StudentWithTag.kt | 4 +- .../domain/point/spi/vo/StudentWithPointVO.kt | 4 +- .../repository/vo/QueryStudentWithPointVO.kt | 25 +++++++---- .../repository/vo/QueryStudentsWithTagVO.kt | 42 ++++++++++++++----- 4 files changed, 54 insertions(+), 21 deletions(-) diff --git a/dms-core/src/main/kotlin/team/aliens/dms/domain/manager/spi/vo/StudentWithTag.kt b/dms-core/src/main/kotlin/team/aliens/dms/domain/manager/spi/vo/StudentWithTag.kt index eb3ad7cb2..db170c72e 100644 --- a/dms-core/src/main/kotlin/team/aliens/dms/domain/manager/spi/vo/StudentWithTag.kt +++ b/dms-core/src/main/kotlin/team/aliens/dms/domain/manager/spi/vo/StudentWithTag.kt @@ -5,9 +5,9 @@ import team.aliens.dms.domain.student.model.Student import team.aliens.dms.domain.tag.model.Tag import java.util.UUID -data class StudentWithTag( +open class StudentWithTag( val id: UUID, - val name: String, + open val name: String, val grade: Int, val classRoom: Int, val number: Int, diff --git a/dms-core/src/main/kotlin/team/aliens/dms/domain/point/spi/vo/StudentWithPointVO.kt b/dms-core/src/main/kotlin/team/aliens/dms/domain/point/spi/vo/StudentWithPointVO.kt index d15482384..1b7c934b3 100644 --- a/dms-core/src/main/kotlin/team/aliens/dms/domain/point/spi/vo/StudentWithPointVO.kt +++ b/dms-core/src/main/kotlin/team/aliens/dms/domain/point/spi/vo/StudentWithPointVO.kt @@ -3,8 +3,8 @@ package team.aliens.dms.domain.point.spi.vo import team.aliens.dms.domain.point.model.PointType import team.aliens.dms.domain.student.model.Student -data class StudentWithPointVO( - val name: String, +open class StudentWithPointVO( + open val name: String, val grade: Int, val classRoom: Int, val number: Int, diff --git a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/repository/vo/QueryStudentWithPointVO.kt b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/repository/vo/QueryStudentWithPointVO.kt index b003b7106..cb10bc347 100644 --- a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/repository/vo/QueryStudentWithPointVO.kt +++ b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/repository/vo/QueryStudentWithPointVO.kt @@ -1,12 +1,23 @@ package team.aliens.dms.persistence.student.repository.vo import com.querydsl.core.annotations.QueryProjection +import team.aliens.dms.common.annotation.EncryptType +import team.aliens.dms.common.annotation.EncryptedColumn +import team.aliens.dms.domain.point.spi.vo.StudentWithPointVO -data class QueryStudentWithPointVO @QueryProjection constructor( - val name: String, - val grade: Int, - val classRoom: Int, - val number: Int, - val bonusTotal: Int?, - val minusTotal: Int? +class QueryStudentWithPointVO @QueryProjection constructor( + @EncryptedColumn(type = EncryptType.SYMMETRIC) + override val name: String, + grade: Int, + classRoom: Int, + number: Int, + bonusTotal: Int?, + minusTotal: Int?, +) : StudentWithPointVO( + name = name, + grade = grade, + classRoom = classRoom, + number = number, + bonusTotal = bonusTotal ?: 0, + minusTotal = minusTotal ?: 0 ) diff --git a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/repository/vo/QueryStudentsWithTagVO.kt b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/repository/vo/QueryStudentsWithTagVO.kt index ee7e8b0a6..b20dc6dcb 100644 --- a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/repository/vo/QueryStudentsWithTagVO.kt +++ b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/repository/vo/QueryStudentsWithTagVO.kt @@ -1,18 +1,40 @@ package team.aliens.dms.persistence.student.repository.vo import com.querydsl.core.annotations.QueryProjection +import team.aliens.dms.common.annotation.EncryptType +import team.aliens.dms.common.annotation.EncryptedColumn +import team.aliens.dms.domain.manager.spi.vo.StudentWithTag import team.aliens.dms.domain.student.model.Sex +import team.aliens.dms.domain.tag.model.Tag import team.aliens.dms.persistence.tag.entity.TagJpaEntity import java.util.UUID -data class QueryStudentsWithTagVO @QueryProjection constructor( - val id: UUID, - val name: String, - val grade: Int, - val classRoom: Int, - val number: Int, - val roomNumber: String, - val profileImageUrl: String, - val sex: Sex, - val tags: List +class QueryStudentsWithTagVO @QueryProjection constructor( + id: UUID, + @EncryptedColumn(type = EncryptType.SYMMETRIC) + override val name: String, + grade: Int, + classRoom: Int, + number: Int, + roomNumber: String, + profileImageUrl: String, + sex: Sex, + tags: List +) : StudentWithTag( + id = id, + name = name, + grade = grade, + classRoom = classRoom, + number = number, + roomNumber = roomNumber, + profileImageUrl = profileImageUrl, + sex = sex, + tags = tags.map { + Tag( + id = it.id!!, + name = it.name, + color = it.color, + schoolId = it.school!!.id!! + ) + } ) From 5eb2e1da7db0058d48504b157c92979a3dc5a2f9 Mon Sep 17 00:00:00 2001 From: rlaisqls Date: Wed, 10 May 2023 07:15:53 +0900 Subject: [PATCH 08/13] create: (#490) AspectjConfig --- .../kotlin/team/aliens/dms/global/config/AspectjConfig.kt | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 dms-infrastructure/src/main/kotlin/team/aliens/dms/global/config/AspectjConfig.kt diff --git a/dms-infrastructure/src/main/kotlin/team/aliens/dms/global/config/AspectjConfig.kt b/dms-infrastructure/src/main/kotlin/team/aliens/dms/global/config/AspectjConfig.kt new file mode 100644 index 000000000..e363b3371 --- /dev/null +++ b/dms-infrastructure/src/main/kotlin/team/aliens/dms/global/config/AspectjConfig.kt @@ -0,0 +1,8 @@ +package team.aliens.dms.global.config + +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.EnableAspectJAutoProxy + +@Configuration +@EnableAspectJAutoProxy(exposeProxy = true) +class AspectjConfig From 9707aa1782f610278eea8029a862cc5d24b8f305 Mon Sep 17 00:00:00 2001 From: rlaisqls Date: Wed, 10 May 2023 07:15:59 +0900 Subject: [PATCH 09/13] create: (#490) CacheConfig --- .../kotlin/team/aliens/dms/global/config/CacheConfig.kt | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 dms-infrastructure/src/main/kotlin/team/aliens/dms/global/config/CacheConfig.kt diff --git a/dms-infrastructure/src/main/kotlin/team/aliens/dms/global/config/CacheConfig.kt b/dms-infrastructure/src/main/kotlin/team/aliens/dms/global/config/CacheConfig.kt new file mode 100644 index 000000000..bc747b060 --- /dev/null +++ b/dms-infrastructure/src/main/kotlin/team/aliens/dms/global/config/CacheConfig.kt @@ -0,0 +1,8 @@ +package team.aliens.dms.global.config + +import org.springframework.cache.annotation.EnableCaching +import org.springframework.context.annotation.Configuration + +@Configuration +@EnableCaching +class CacheConfig \ No newline at end of file From b05be2a8bb7e24d9071158799351e08ef9c74f25 Mon Sep 17 00:00:00 2001 From: rlaisqls Date: Wed, 10 May 2023 07:19:46 +0900 Subject: [PATCH 10/13] feat: (#490) EncryptAnnotation --- .../dms/domain/room/service/RoomService.kt | 2 +- .../common/annotation/EncryptAnnotations.kt | 47 +++++++++++++++++++ .../persistence/EncryptableGenericMapper.kt | 12 +++++ .../notice/entity/NoticeJpaEntity.kt | 2 +- .../point/entity/PointHistoryJpaEntity.kt | 12 +++-- .../point/mapper/PointHistoryMapper.kt | 4 +- .../student/StudentPersistenceAdapter.kt | 36 +++----------- .../student/entity/StudentJpaEntity.kt | 16 ++++--- .../student/mapper/StudentMapper.kt | 6 +-- 9 files changed, 90 insertions(+), 47 deletions(-) create mode 100644 dms-persistence/src/main/kotlin/team/aliens/dms/common/annotation/EncryptAnnotations.kt create mode 100644 dms-persistence/src/main/kotlin/team/aliens/dms/persistence/EncryptableGenericMapper.kt diff --git a/dms-core/src/main/kotlin/team/aliens/dms/domain/room/service/RoomService.kt b/dms-core/src/main/kotlin/team/aliens/dms/domain/room/service/RoomService.kt index 12beb096f..e5c22d442 100644 --- a/dms-core/src/main/kotlin/team/aliens/dms/domain/room/service/RoomService.kt +++ b/dms-core/src/main/kotlin/team/aliens/dms/domain/room/service/RoomService.kt @@ -1,6 +1,6 @@ package team.aliens.dms.domain.room.service -import org.springframework.stereotype.Service +import team.aliens.dms.common.annotation.Service @Service class RoomService( diff --git a/dms-persistence/src/main/kotlin/team/aliens/dms/common/annotation/EncryptAnnotations.kt b/dms-persistence/src/main/kotlin/team/aliens/dms/common/annotation/EncryptAnnotations.kt new file mode 100644 index 000000000..352c32c4f --- /dev/null +++ b/dms-persistence/src/main/kotlin/team/aliens/dms/common/annotation/EncryptAnnotations.kt @@ -0,0 +1,47 @@ +package team.aliens.dms.common.annotation + +import java.lang.annotation.Inherited + +/** + * 저장시 암호화되는 컬럼을 명시해주기 위한 어노테이션 + */ +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FIELD) +annotation class EncryptedColumn( + val type: EncryptType +) + +enum class EncryptType { + SYMMETRIC, + ASYMMETRIC +} + +/** + * 메서드의 반환값에서 EncryptedColmn이 붙은 필드을 암호화하는 어노테이션 + * @see EncryptedColumn + * @see team.aliens.dms.persistence.EncryptableGenericMapper + */ +@Inherited +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +annotation class Encrypt + +/** + * 메서드의 반환값에서 EncryptedColmn이 붙은 필드을 복호화하는 어노테이션 + * @see EncryptedColumn + */ +@Inherited +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +annotation class Decrypt + +/** + * 파라미터 전달시에 암호화된 엔티티를 복호화하는 어노테이션 + * @see team.aliens.dms.persistence.EncryptableGenericMapper + */ +@Inherited +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.VALUE_PARAMETER) +annotation class Decrypted( + val value: String = "" +) diff --git a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/EncryptableGenericMapper.kt b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/EncryptableGenericMapper.kt new file mode 100644 index 000000000..4e10d0b43 --- /dev/null +++ b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/EncryptableGenericMapper.kt @@ -0,0 +1,12 @@ +package team.aliens.dms.persistence + +import team.aliens.dms.common.annotation.Decrypted +import team.aliens.dms.common.annotation.Encrypt + +interface EncryptableGenericMapper : GenericMapper { + + override fun toDomain(@Decrypted entity: E?): D? + + @Encrypt + override fun toEntity(domain: D): E +} diff --git a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/notice/entity/NoticeJpaEntity.kt b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/notice/entity/NoticeJpaEntity.kt index 1d20e6778..61583414f 100644 --- a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/notice/entity/NoticeJpaEntity.kt +++ b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/notice/entity/NoticeJpaEntity.kt @@ -24,7 +24,7 @@ class NoticeJpaEntity( @Column(columnDefinition = "VARCHAR(100)", nullable = false) val title: String, - @Column(columnDefinition = "VARCHAR(1000)", nullable = false) + @Column(columnDefinition = "TEXT", nullable = false) val content: String, override val createdAt: LocalDateTime, diff --git a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/point/entity/PointHistoryJpaEntity.kt b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/point/entity/PointHistoryJpaEntity.kt index 901e2bb3c..5548f8c2b 100644 --- a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/point/entity/PointHistoryJpaEntity.kt +++ b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/point/entity/PointHistoryJpaEntity.kt @@ -1,8 +1,5 @@ package team.aliens.dms.persistence.point.entity -import team.aliens.dms.domain.point.model.PointType -import team.aliens.dms.persistence.BaseEntity -import team.aliens.dms.persistence.school.entity.SchoolJpaEntity import java.time.LocalDateTime import java.util.UUID import javax.persistence.Column @@ -13,6 +10,11 @@ import javax.persistence.FetchType import javax.persistence.JoinColumn import javax.persistence.ManyToOne import javax.persistence.Table +import team.aliens.dms.common.annotation.EncryptType +import team.aliens.dms.common.annotation.EncryptedColumn +import team.aliens.dms.domain.point.model.PointType +import team.aliens.dms.persistence.BaseEntity +import team.aliens.dms.persistence.school.entity.SchoolJpaEntity @Entity @Table(name = "tbl_point_history") @@ -20,10 +22,12 @@ class PointHistoryJpaEntity( id: UUID?, + @EncryptedColumn(type = EncryptType.SYMMETRIC) @Column(columnDefinition = "VARCHAR(30)", nullable = false) val studentName: String, - @Column(columnDefinition = "VARCHAR(5)", nullable = false) + @EncryptedColumn(type = EncryptType.SYMMETRIC) + @Column(columnDefinition = "TEXT", nullable = false) val studentGcn: String, @Column(columnDefinition = "INT", nullable = false) diff --git a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/point/mapper/PointHistoryMapper.kt b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/point/mapper/PointHistoryMapper.kt index 18b0ceadb..9745627aa 100644 --- a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/point/mapper/PointHistoryMapper.kt +++ b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/point/mapper/PointHistoryMapper.kt @@ -3,14 +3,14 @@ package team.aliens.dms.persistence.point.mapper import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Component import team.aliens.dms.domain.point.model.PointHistory -import team.aliens.dms.persistence.GenericMapper +import team.aliens.dms.persistence.EncryptableGenericMapper import team.aliens.dms.persistence.point.entity.PointHistoryJpaEntity import team.aliens.dms.persistence.school.repository.SchoolJpaRepository @Component class PointHistoryMapper( private val schoolRepository: SchoolJpaRepository -) : GenericMapper { +) : EncryptableGenericMapper { override fun toDomain(entity: PointHistoryJpaEntity?): PointHistory? { return entity?.let { diff --git a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/StudentPersistenceAdapter.kt b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/StudentPersistenceAdapter.kt index dc89bbc90..a503204b7 100644 --- a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/StudentPersistenceAdapter.kt +++ b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/StudentPersistenceAdapter.kt @@ -11,8 +11,10 @@ import com.querydsl.core.types.dsl.CaseBuilder import com.querydsl.core.types.dsl.Expressions import com.querydsl.jpa.JPAExpressions.select import com.querydsl.jpa.impl.JPAQueryFactory +import java.util.UUID import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Component +import team.aliens.dms.common.annotation.Decrypt import team.aliens.dms.domain.manager.dto.PointFilter import team.aliens.dms.domain.manager.dto.PointFilterType import team.aliens.dms.domain.manager.dto.Sort @@ -32,7 +34,6 @@ import team.aliens.dms.persistence.tag.entity.QStudentTagJpaEntity.studentTagJpa import team.aliens.dms.persistence.tag.entity.QTagJpaEntity.tagJpaEntity import team.aliens.dms.persistence.tag.mapper.TagMapper import team.aliens.dms.persistence.user.entity.QUserJpaEntity.userJpaEntity -import java.util.UUID @Component class StudentPersistenceAdapter( @@ -148,6 +149,7 @@ class StudentPersistenceAdapter( return tuple.toTypedArray() } + @Decrypt override fun queryStudentsByNameAndSortAndFilter( name: String?, sort: Sort, @@ -158,8 +160,7 @@ class StudentPersistenceAdapter( return queryFactory .selectFrom(studentJpaEntity) .join(studentJpaEntity.room, roomJpaEntity) - .join(studentJpaEntity.user, userJpaEntity) - .join(userJpaEntity.school, schoolJpaEntity) + .join(roomJpaEntity.school, schoolJpaEntity) .leftJoin(studentTagJpaEntity) .on(studentJpaEntity.id.eq(studentTagJpaEntity.student.id)).fetchJoin() .leftJoin(tagJpaEntity) @@ -167,7 +168,7 @@ class StudentPersistenceAdapter( .leftJoin(pointHistoryJpaEntity) .on(eqStudentRecentPointHistory()) .where( - userJpaEntity.school.id.eq(schoolId), + roomJpaEntity.school.id.eq(schoolId), nameContains(name), pointTotalBetween(pointFilter), hasAllTags(tagIds) @@ -194,22 +195,6 @@ class StudentPersistenceAdapter( ) ) ) - .map { - StudentWithTag( - id = it.id, - name = it.name, - grade = it.grade, - classRoom = it.classRoom, - number = it.number, - roomNumber = it.roomNumber, - profileImageUrl = it.profileImageUrl, - sex = it.sex, - tags = it.tags - .map { tag -> - tagMapper.toDomain(tag)!! - } - ) - } } private fun nameContains(name: String?) = name?.run { studentJpaEntity.name.contains(this) } @@ -294,6 +279,7 @@ class StudentPersistenceAdapter( } } + @Decrypt override fun queryStudentsWithPointHistory(studentIds: List): List { return queryFactory .select( @@ -314,16 +300,6 @@ class StudentPersistenceAdapter( studentJpaEntity.id.`in`(studentIds) ) .fetch() - .map { - StudentWithPointVO( - name = it.name, - grade = it.grade, - classRoom = it.classRoom, - number = it.number, - bonusTotal = it.bonusTotal ?: 0, - minusTotal = it.minusTotal ?: 0 - ) - } } private fun eqStudentRecentPointHistory(): BooleanExpression? { diff --git a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/entity/StudentJpaEntity.kt b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/entity/StudentJpaEntity.kt index ee19349d7..0ca70ab60 100644 --- a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/entity/StudentJpaEntity.kt +++ b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/entity/StudentJpaEntity.kt @@ -1,10 +1,5 @@ package team.aliens.dms.persistence.student.entity -import org.hibernate.annotations.Where -import team.aliens.dms.domain.student.model.Sex -import team.aliens.dms.persistence.BaseUUIDEntity -import team.aliens.dms.persistence.room.entity.RoomJpaEntity -import team.aliens.dms.persistence.user.entity.UserJpaEntity import java.time.LocalDateTime import java.util.UUID import javax.persistence.CascadeType @@ -17,6 +12,13 @@ import javax.persistence.JoinColumn import javax.persistence.ManyToOne import javax.persistence.OneToOne import javax.persistence.Table +import org.hibernate.annotations.Where +import team.aliens.dms.common.annotation.EncryptType +import team.aliens.dms.common.annotation.EncryptedColumn +import team.aliens.dms.domain.student.model.Sex +import team.aliens.dms.persistence.BaseUUIDEntity +import team.aliens.dms.persistence.room.entity.RoomJpaEntity +import team.aliens.dms.persistence.user.entity.UserJpaEntity @Entity @Table(name = "tbl_student") @@ -45,7 +47,8 @@ class StudentJpaEntity( @Column(columnDefinition = "TINYINT UNSIGNED", nullable = false) val number: Int, - @Column(columnDefinition = "VARCHAR(10)", nullable = false) + @EncryptedColumn(type = EncryptType.SYMMETRIC) + @Column(columnDefinition = "TEXT", nullable = false) val name: String, @Column(columnDefinition = "VARCHAR(500)", nullable = false) @@ -57,4 +60,5 @@ class StudentJpaEntity( @Column(columnDefinition = "DATETIME") val deletedAt: LocalDateTime? + ) : BaseUUIDEntity(id) diff --git a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/mapper/StudentMapper.kt b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/mapper/StudentMapper.kt index b204206da..944eb6aae 100644 --- a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/mapper/StudentMapper.kt +++ b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/student/mapper/StudentMapper.kt @@ -3,7 +3,7 @@ package team.aliens.dms.persistence.student.mapper import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Component import team.aliens.dms.domain.student.model.Student -import team.aliens.dms.persistence.GenericMapper +import team.aliens.dms.persistence.EncryptableGenericMapper import team.aliens.dms.persistence.room.repository.RoomJpaRepository import team.aliens.dms.persistence.student.entity.StudentJpaEntity import team.aliens.dms.persistence.user.repository.UserJpaRepository @@ -12,7 +12,7 @@ import team.aliens.dms.persistence.user.repository.UserJpaRepository class StudentMapper( private val roomRepository: RoomJpaRepository, private val userRepository: UserJpaRepository -) : GenericMapper { +) : EncryptableGenericMapper { override fun toDomain(entity: StudentJpaEntity?): Student? { return entity?.let { @@ -47,7 +47,7 @@ class StudentMapper( classRoom = domain.classRoom, number = domain.number, name = domain.name, - profileImageUrl = domain.profileImageUrl!!, + profileImageUrl = domain.profileImageUrl, sex = domain.sex, deletedAt = domain.deletedAt ) From 4860209ad87bb59b2401bc431ac1dcb97e38e9ae Mon Sep 17 00:00:00 2001 From: rlaisqls Date: Wed, 10 May 2023 14:24:35 +0900 Subject: [PATCH 11/13] chore: (#490) redisCache --- .../aliens/dms/common/model/SchoolSecret.kt | 3 +- .../aliens/dms/common/spi/SchoolSecretPort.kt | 2 +- dms-infrastructure/build.gradle.kts | 3 ++ .../aliens/dms/global/config/CacheConfig.kt | 8 ---- .../dms/global/config/RedisCacheConfig.kt | 46 +++++++++++++++++++ .../dms/thirdparty/encrypt/AwsKMSAdapter.kt | 11 ++--- .../encrypt/aop/EntityEncryptAspect.kt | 8 ++-- .../src/main/resources/application.yml | 3 ++ .../school/SchoolPersistenceAdapter.kt | 7 +++ .../SchoolSecretPersistenceAdapter.kt | 4 +- 10 files changed, 72 insertions(+), 23 deletions(-) delete mode 100644 dms-infrastructure/src/main/kotlin/team/aliens/dms/global/config/CacheConfig.kt create mode 100644 dms-infrastructure/src/main/kotlin/team/aliens/dms/global/config/RedisCacheConfig.kt diff --git a/dms-core/src/main/kotlin/team/aliens/dms/common/model/SchoolSecret.kt b/dms-core/src/main/kotlin/team/aliens/dms/common/model/SchoolSecret.kt index 764faa25e..d8c66c231 100644 --- a/dms-core/src/main/kotlin/team/aliens/dms/common/model/SchoolSecret.kt +++ b/dms-core/src/main/kotlin/team/aliens/dms/common/model/SchoolSecret.kt @@ -1,9 +1,8 @@ package team.aliens.dms.common.model -import java.io.Serializable import java.util.UUID data class SchoolSecret( val schoolId: UUID, val secretKey: String -) : Serializable +) \ No newline at end of file diff --git a/dms-core/src/main/kotlin/team/aliens/dms/common/spi/SchoolSecretPort.kt b/dms-core/src/main/kotlin/team/aliens/dms/common/spi/SchoolSecretPort.kt index 4617a7956..5668e8646 100644 --- a/dms-core/src/main/kotlin/team/aliens/dms/common/spi/SchoolSecretPort.kt +++ b/dms-core/src/main/kotlin/team/aliens/dms/common/spi/SchoolSecretPort.kt @@ -7,5 +7,5 @@ interface SchoolSecretPort { fun saveSchoolSecret(schoolSecret: SchoolSecret) - fun querySchoolSecretBySchoolId(schoolId: UUID): SchoolSecret? + fun querySchoolSecretBySchoolId(schoolId: UUID): String? } diff --git a/dms-infrastructure/build.gradle.kts b/dms-infrastructure/build.gradle.kts index 74601e61a..4940cdbec 100644 --- a/dms-infrastructure/build.gradle.kts +++ b/dms-infrastructure/build.gradle.kts @@ -49,6 +49,9 @@ dependencies { // logging implementation(Dependencies.SENTRY) + + // redis + implementation(Dependencies.REDIS) } kapt { diff --git a/dms-infrastructure/src/main/kotlin/team/aliens/dms/global/config/CacheConfig.kt b/dms-infrastructure/src/main/kotlin/team/aliens/dms/global/config/CacheConfig.kt deleted file mode 100644 index bc747b060..000000000 --- a/dms-infrastructure/src/main/kotlin/team/aliens/dms/global/config/CacheConfig.kt +++ /dev/null @@ -1,8 +0,0 @@ -package team.aliens.dms.global.config - -import org.springframework.cache.annotation.EnableCaching -import org.springframework.context.annotation.Configuration - -@Configuration -@EnableCaching -class CacheConfig \ No newline at end of file diff --git a/dms-infrastructure/src/main/kotlin/team/aliens/dms/global/config/RedisCacheConfig.kt b/dms-infrastructure/src/main/kotlin/team/aliens/dms/global/config/RedisCacheConfig.kt new file mode 100644 index 000000000..be01b839a --- /dev/null +++ b/dms-infrastructure/src/main/kotlin/team/aliens/dms/global/config/RedisCacheConfig.kt @@ -0,0 +1,46 @@ +package team.aliens.dms.global.config + +import com.fasterxml.jackson.databind.ObjectMapper +import java.time.Duration +import org.springframework.cache.CacheManager +import org.springframework.cache.annotation.EnableCaching +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.data.redis.cache.RedisCacheConfiguration +import org.springframework.data.redis.cache.RedisCacheManager.RedisCacheManagerBuilder +import org.springframework.data.redis.connection.RedisConnectionFactory +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer +import org.springframework.data.redis.serializer.RedisSerializationContext +import org.springframework.data.redis.serializer.StringRedisSerializer + + +@Configuration +@EnableCaching +class RedisCacheConfig { + + @Bean + fun redisCacheManager( + connectionFactory: RedisConnectionFactory, + objectMapper: ObjectMapper, + ): CacheManager { + + val redisCacheConfiguration = RedisCacheConfiguration + .defaultCacheConfig() + .serializeKeysWith( + RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer()) + ) + .serializeValuesWith( + RedisSerializationContext.SerializationPair.fromSerializer(GenericJackson2JsonRedisSerializer()) + ) + .entryTtl(CACHE_DURATION) + + return RedisCacheManagerBuilder + .fromConnectionFactory(connectionFactory) + .cacheDefaults(redisCacheConfiguration) + .build() + } + + companion object { + private val CACHE_DURATION = Duration.ofMinutes(30L) + } +} \ No newline at end of file diff --git a/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSAdapter.kt b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSAdapter.kt index f73249d7a..ef8ef0c4d 100644 --- a/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSAdapter.kt +++ b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSAdapter.kt @@ -4,14 +4,14 @@ import com.amazonaws.services.kms.AWSKMSAsync import com.amazonaws.services.kms.model.DecryptRequest import com.amazonaws.services.kms.model.EncryptRequest import com.amazonaws.services.kms.model.EncryptionAlgorithmSpec -import org.springframework.cache.annotation.Cacheable -import org.springframework.stereotype.Component -import team.aliens.dms.common.spi.EncryptPort -import team.aliens.dms.thirdparty.encrypt.exception.KMSException import java.nio.ByteBuffer import java.util.Base64 import javax.crypto.Cipher import javax.crypto.spec.SecretKeySpec +import org.springframework.cache.annotation.Cacheable +import org.springframework.stereotype.Component +import team.aliens.dms.common.spi.EncryptPort +import team.aliens.dms.thirdparty.encrypt.exception.KMSException @Component class AwsKMSAdapter( @@ -40,7 +40,7 @@ class AwsKMSAdapter( ) } - @Cacheable("cipher") + @Cacheable("cipher", cacheManager = "redisCacheManager") protected fun getCipher(opmode: Int, key: String): Cipher = Cipher.getInstance(encryptProperties.transformation).apply { init(opmode, SecretKeySpec(key.toByteArray(), encryptProperties.algorithm)) @@ -65,7 +65,6 @@ class AwsKMSAdapter( ) } - @Cacheable("decryptedText") override fun asymmetricDecrypt(cipherText: String): String { val request = DecryptRequest() diff --git a/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/aop/EntityEncryptAspect.kt b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/aop/EntityEncryptAspect.kt index 801d95b8c..a59414006 100644 --- a/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/aop/EntityEncryptAspect.kt +++ b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/aop/EntityEncryptAspect.kt @@ -1,5 +1,8 @@ package team.aliens.dms.thirdparty.encrypt.aop +import java.lang.reflect.Field +import java.util.function.Consumer +import java.util.function.Function import org.aspectj.lang.JoinPoint import org.aspectj.lang.ProceedingJoinPoint import org.aspectj.lang.annotation.AfterReturning @@ -14,9 +17,6 @@ import team.aliens.dms.common.spi.EncryptPort import team.aliens.dms.common.spi.SchoolSecretPort import team.aliens.dms.common.spi.SecurityPort import team.aliens.dms.domain.school.exception.SchoolNotFoundException -import java.lang.reflect.Field -import java.util.function.Consumer -import java.util.function.Function @UseCase @Aspect @@ -117,6 +117,6 @@ class EntityEncryptAspect( private fun getSchoolKey(): String { val schoolId = securityPort.getCurrentUserSchoolId() val schoolSecret = schoolSecretPort.querySchoolSecretBySchoolId(schoolId) ?: throw SchoolNotFoundException - return encryptPort.asymmetricDecrypt(schoolSecret.secretKey) + return encryptPort.asymmetricDecrypt(schoolSecret) } } diff --git a/dms-infrastructure/src/main/resources/application.yml b/dms-infrastructure/src/main/resources/application.yml index 926d9ff31..0070b5c0f 100644 --- a/dms-infrastructure/src/main/resources/application.yml +++ b/dms-infrastructure/src/main/resources/application.yml @@ -39,6 +39,9 @@ spring: max-file-size: 20MB max-request-size: 20MB + cache: + type: redis + secret: secret-key: ${SECRET_KEY:asdfghgfds} access-exp: ${ACCESS_EXP:1800} diff --git a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/school/SchoolPersistenceAdapter.kt b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/school/SchoolPersistenceAdapter.kt index a23f4cba9..72d2a7875 100644 --- a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/school/SchoolPersistenceAdapter.kt +++ b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/school/SchoolPersistenceAdapter.kt @@ -2,6 +2,7 @@ package team.aliens.dms.persistence.school import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Component +import team.aliens.dms.domain.school.model.AvailableFeature import team.aliens.dms.domain.school.model.School import team.aliens.dms.domain.school.spi.SchoolPort import team.aliens.dms.persistence.school.mapper.AvailableFeatureMapper @@ -28,6 +29,12 @@ class SchoolPersistenceAdapter( ) )!! + override fun saveAvailableFeature(availableFeature: AvailableFeature) = availableFeatureMapper.toDomain( + availableFeatureRepository.save( + availableFeatureMapper.toEntity(availableFeature) + ) + )!! + override fun queryAvailableFeaturesBySchoolId(schoolId: UUID) = availableFeatureMapper.toDomain( availableFeatureRepository.findByIdOrNull(schoolId) ) diff --git a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/SchoolSecretPersistenceAdapter.kt b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/SchoolSecretPersistenceAdapter.kt index 91b44bd78..501890d0c 100644 --- a/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/SchoolSecretPersistenceAdapter.kt +++ b/dms-persistence/src/main/kotlin/team/aliens/dms/persistence/security/SchoolSecretPersistenceAdapter.kt @@ -15,10 +15,10 @@ class SchoolSecretPersistenceAdapter( private val schoolSecretMapper: SchoolSecretMapper ) : SchoolSecretPort { - @Cacheable("schoolSecret") + @Cacheable("schoolSecret", cacheManager = "redisCacheManager") override fun querySchoolSecretBySchoolId(schoolId: UUID) = schoolSecretRepository.findByIdOrNull(schoolId) - ?.let { schoolSecretMapper.toDomain(it) } + ?.let { schoolSecretMapper.toDomain(it)?.secretKey } override fun saveSchoolSecret(schoolSecret: SchoolSecret) { schoolSecretRepository.save( From c5a996cd1b4b11c68266466ad1916f3a3b2513f1 Mon Sep 17 00:00:00 2001 From: rlaisqls Date: Wed, 10 May 2023 14:44:15 +0900 Subject: [PATCH 12/13] chore: (#490) code smell --- .../team/aliens/dms/thirdparty/encrypt/AwsKMSAdapter.kt | 2 -- .../dms/thirdparty/encrypt/aop/EntityEncryptAspect.kt | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSAdapter.kt b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSAdapter.kt index ef8ef0c4d..bfe384d0d 100644 --- a/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSAdapter.kt +++ b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/AwsKMSAdapter.kt @@ -8,7 +8,6 @@ import java.nio.ByteBuffer import java.util.Base64 import javax.crypto.Cipher import javax.crypto.spec.SecretKeySpec -import org.springframework.cache.annotation.Cacheable import org.springframework.stereotype.Component import team.aliens.dms.common.spi.EncryptPort import team.aliens.dms.thirdparty.encrypt.exception.KMSException @@ -40,7 +39,6 @@ class AwsKMSAdapter( ) } - @Cacheable("cipher", cacheManager = "redisCacheManager") protected fun getCipher(opmode: Int, key: String): Cipher = Cipher.getInstance(encryptProperties.transformation).apply { init(opmode, SecretKeySpec(key.toByteArray(), encryptProperties.algorithm)) diff --git a/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/aop/EntityEncryptAspect.kt b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/aop/EntityEncryptAspect.kt index a59414006..d8451546b 100644 --- a/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/aop/EntityEncryptAspect.kt +++ b/dms-infrastructure/src/main/kotlin/team/aliens/dms/thirdparty/encrypt/aop/EntityEncryptAspect.kt @@ -83,10 +83,10 @@ class EntityEncryptAspect( when (f.getAnnotation(EncryptedColumn::class.java).type) { EncryptType.SYMMETRIC -> encryptPort.symmetricEncrypt( secretKey = getSchoolKey(), - plainText = f.get(obj) as String + plainText = f[obj] as String ) EncryptType.ASYMMETRIC -> encryptPort.asymmetricEncrypt( - plainText = f.get(obj) as String + plainText = f[obj] as String ) } } @@ -97,10 +97,10 @@ class EntityEncryptAspect( when (f.getAnnotation(EncryptedColumn::class.java).type) { EncryptType.SYMMETRIC -> encryptPort.symmetricDecrypt( secretKey = getSchoolKey(), - cipherText = f.get(obj) as String + cipherText = f[obj] as String ) EncryptType.ASYMMETRIC -> encryptPort.asymmetricDecrypt( - cipherText = f.get(obj) as String + cipherText = f[obj] as String ) } } From cab6f4751fad09b83b064316315444b48ec5017f Mon Sep 17 00:00:00 2001 From: rlaisqls Date: Wed, 10 May 2023 15:22:27 +0900 Subject: [PATCH 13/13] setting: (#490) encrypt algorithm yml --- dms-infrastructure/src/main/resources/application.yml | 4 ++++ .../team/aliens/dms/common/annotation/EncryptAnnotations.kt | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/dms-infrastructure/src/main/resources/application.yml b/dms-infrastructure/src/main/resources/application.yml index 0070b5c0f..d598908e7 100644 --- a/dms-infrastructure/src/main/resources/application.yml +++ b/dms-infrastructure/src/main/resources/application.yml @@ -67,6 +67,10 @@ cloud: kms: key: ${KMS_ARN:asdf} +encrypt: + transformation: ${ENCRYPT_TRANSFORMATION} + algorithm: ${ENCRYPT_ALGORITHM} + sentry: dsn: ${SENTRY_DSN:asdf} traces-sample-rate: 1.0 diff --git a/dms-persistence/src/main/kotlin/team/aliens/dms/common/annotation/EncryptAnnotations.kt b/dms-persistence/src/main/kotlin/team/aliens/dms/common/annotation/EncryptAnnotations.kt index 352c32c4f..d28b9ff56 100644 --- a/dms-persistence/src/main/kotlin/team/aliens/dms/common/annotation/EncryptAnnotations.kt +++ b/dms-persistence/src/main/kotlin/team/aliens/dms/common/annotation/EncryptAnnotations.kt @@ -17,7 +17,7 @@ enum class EncryptType { } /** - * 메서드의 반환값에서 EncryptedColmn이 붙은 필드을 암호화하는 어노테이션 + * 메서드의 반환값에서 EncryptedColumn이 붙은 필드을 암호화하는 어노테이션 * @see EncryptedColumn * @see team.aliens.dms.persistence.EncryptableGenericMapper */ @@ -27,7 +27,7 @@ enum class EncryptType { annotation class Encrypt /** - * 메서드의 반환값에서 EncryptedColmn이 붙은 필드을 복호화하는 어노테이션 + * 메서드의 반환값에서 EncryptedColumn이 붙은 필드을 복호화하는 어노테이션 * @see EncryptedColumn */ @Inherited