Skip to content

Commit 2c26eeb

Browse files
authored
Merge pull request #20 from career-seekers/feat/password-changing
Feat/password changing mechanism
2 parents 62e6c4b + 9df8f38 commit 2c26eeb

File tree

11 files changed

+154
-8
lines changed

11 files changed

+154
-8
lines changed
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package org.careerseekers.csmailservice.cache
22

3-
interface CacheLoader<T> : CacheClient<T> {
4-
fun preloadCache(): Any
3+
import org.springframework.data.redis.core.RedisTemplate
4+
5+
interface CacheLoader<T> {
6+
val cacheKey: String
7+
val redisTemplate: RedisTemplate<String, T>
8+
59
fun loadItemToCache(item: T): Any
610
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.careerseekers.csmailservice.cache
2+
3+
interface CachePreloader {
4+
5+
fun preloadCache(): Any
6+
}

src/main/kotlin/org/careerseekers/csmailservice/cache/CacheClient.kt renamed to src/main/kotlin/org/careerseekers/csmailservice/cache/CacheRetriever.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package org.careerseekers.csmailservice.cache
22

33
import org.springframework.data.redis.core.RedisTemplate
44

5-
interface CacheClient<T> {
5+
interface CacheRetriever<T> {
66
val cacheKey: String
77
val redisTemplate: RedisTemplate<String, T>
88

src/main/kotlin/org/careerseekers/csmailservice/cache/UsersCacheClient.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import org.springframework.stereotype.Component
1515
class UsersCacheClient(
1616
override val redisTemplate: RedisTemplate<String, UsersCacheDto>,
1717
cacheManager: CacheManager,
18-
) : CacheClient<UsersCacheDto> {
18+
) : CacheRetriever<UsersCacheDto> {
1919
override val cacheKey = "users"
2020
private val cache = cacheManager.getCache(cacheKey)
2121

@@ -30,7 +30,7 @@ class UsersCacheClient(
3030
return user
3131
}
3232

33-
private fun getUser(id: Long): UsersCacheDto? {
33+
private fun getUser(id: Long): UsersCacheDto {
3434
try {
3535
return usersServiceStub.getById(
3636
UserId.newBuilder()
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.careerseekers.csmailservice.cache
2+
3+
import org.careerseekers.csmailservice.dto.VerificationCodeDto
4+
import org.springframework.cache.CacheManager
5+
import org.springframework.data.redis.core.RedisTemplate
6+
import org.springframework.stereotype.Component
7+
8+
@Component
9+
class VerificationCodesCache(
10+
override val redisTemplate: RedisTemplate<String, VerificationCodeDto>,
11+
cacheManager: CacheManager,
12+
) : CacheLoader<VerificationCodeDto>, CacheRetriever<VerificationCodeDto> {
13+
override val cacheKey = "verification_codes"
14+
private val cache = cacheManager.getCache(cacheKey)
15+
16+
override fun loadItemToCache(item: VerificationCodeDto) {
17+
cache?.put(item.userId, item)
18+
}
19+
20+
21+
override fun getItemFromCache(key: Any): VerificationCodeDto? {
22+
return cache?.get(key)?.let { it.get() as? VerificationCodeDto }
23+
}
24+
}

src/main/kotlin/org/careerseekers/csmailservice/config/RedisTemplatesConfig.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.careerseekers.csmailservice.config
22

33
import org.careerseekers.csmailservice.dto.CachesDto
44
import org.careerseekers.csmailservice.dto.UsersCacheDto
5+
import org.careerseekers.csmailservice.dto.VerificationCodeDto
56
import org.careerseekers.csmailservice.serializers.PolymorphicRedisSerializer
67
import org.springframework.beans.factory.annotation.Qualifier
78
import org.springframework.context.annotation.Bean
@@ -32,4 +33,22 @@ class RedisTemplatesConfig(
3233
template.afterPropertiesSet()
3334
return template
3435
}
36+
37+
@Bean
38+
@Qualifier("verificationCodes")
39+
fun verificationCodesRedisTemplate(
40+
connectionFactory: RedisConnectionFactory
41+
): RedisTemplate<String, VerificationCodeDto> {
42+
val template = RedisTemplate<String, VerificationCodeDto>()
43+
template.connectionFactory = connectionFactory
44+
45+
template.keySerializer = StringRedisSerializer()
46+
template.valueSerializer = serializer
47+
48+
template.hashKeySerializer = StringRedisSerializer()
49+
template.hashValueSerializer = serializer
50+
51+
template.afterPropertiesSet()
52+
return template
53+
}
3554
}

src/main/kotlin/org/careerseekers/csmailservice/dto/CachesDto.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ data class UsersCacheDto(
3131
val verified: Boolean,
3232
) : CachesDto()
3333

34+
@Serializable
35+
@SerialName("VerificationCodeDto")
36+
data class VerificationCodeDto(
37+
val userId: Long,
38+
val code: String,
39+
var retries: Int
40+
) : CachesDto()
41+
3442
val cacheModule = SerializersModule {
3543
polymorphic(CachesDto::class) {
3644
subclass(UsersCacheDto::class, UsersCacheDto.serializer())
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.careerseekers.csmailservice.services
2+
3+
import org.careerseekers.csmailservice.dto.EmailSendingTaskDto
4+
import org.careerseekers.csmailservice.enums.MailEventTypes
5+
6+
interface EmailProcessingService {
7+
val eventType: MailEventTypes
8+
9+
fun processEmail(message: EmailSendingTaskDto)
10+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package org.careerseekers.csmailservice.services
2+
3+
import org.careerseekers.csmailservice.cache.VerificationCodesCache
4+
import org.careerseekers.csmailservice.dto.EmailSendingTaskDto
5+
import org.careerseekers.csmailservice.dto.VerificationCodeDto
6+
import org.careerseekers.csmailservice.enums.MailEventTypes
7+
import org.careerseekers.csmailservice.exceptions.NotFoundException
8+
import org.careerseekers.csmailservice.utils.CodeGenerator.generateVerificationCode
9+
import org.careerseekers.csmailservice.utils.JwtUtil
10+
import org.springframework.beans.factory.annotation.Value
11+
import org.springframework.mail.SimpleMailMessage
12+
import org.springframework.mail.javamail.JavaMailSender
13+
import org.springframework.security.crypto.password.PasswordEncoder
14+
import org.springframework.stereotype.Service
15+
16+
@Service
17+
class PasswordResetEmailService(
18+
private val jwtUtil: JwtUtil,
19+
private val mailer: JavaMailSender,
20+
private val passwordEncoder: PasswordEncoder,
21+
private val verificationCodesCache: VerificationCodesCache
22+
) : EmailProcessingService {
23+
@Value("\${spring.mail.username}")
24+
private val senderEmail: String? = null
25+
26+
override val eventType = MailEventTypes.PASSWORD_RESET
27+
28+
override fun processEmail(message: EmailSendingTaskDto) {
29+
jwtUtil.getUserFromToken(message.token)?.let { user ->
30+
val code = generateVerificationCode()
31+
verificationCodesCache.loadItemToCache(VerificationCodeDto(
32+
userId = user.id,
33+
code = passwordEncoder.encode(code),
34+
retries = 0
35+
))
36+
37+
val message = SimpleMailMessage()
38+
39+
message.from = senderEmail
40+
message.setTo(user.email)
41+
message.subject = "Изменение пароля"
42+
message.text = """
43+
Уважаемый(-ая) ${user.lastName} ${user.firstName} ${user.patronymic}!
44+
45+
Для подтверждения изменения пароля введите следующий верификационный код:
46+
47+
$code
48+
49+
Если вы не запрашивали этот код, просто проигнорируйте это письмо.
50+
51+
Спасибо,
52+
Команда поддержки Искателей профессий.
53+
""".trimIndent()
54+
55+
mailer.send(message)
56+
} ?: throw NotFoundException("User not found")
57+
58+
}
59+
}

src/main/kotlin/org/careerseekers/csmailservice/services/kafka/consumers/KafkaEmailSendingConsumer.kt

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ package org.careerseekers.csmailservice.services.kafka.consumers
22

33
import org.apache.kafka.clients.consumer.ConsumerRecord
44
import org.careerseekers.csmailservice.dto.EmailSendingTaskDto
5+
import org.careerseekers.csmailservice.services.EmailProcessingService
56
import org.springframework.kafka.annotation.KafkaListener
67
import org.springframework.kafka.support.Acknowledgment
78
import org.springframework.stereotype.Service
89

910
@Service
10-
class KafkaEmailSendingConsumer : CustomKafkaConsumer<String, EmailSendingTaskDto> {
11+
class KafkaEmailSendingConsumer(
12+
private val emailProcessingServices: List<EmailProcessingService>
13+
) : CustomKafkaConsumer<String, EmailSendingTaskDto> {
1114

1215
@KafkaListener(
1316
topics = ["EMAIL_SENDING_TASKS"],
@@ -17,9 +20,14 @@ class KafkaEmailSendingConsumer : CustomKafkaConsumer<String, EmailSendingTaskDt
1720
consumerRecord: ConsumerRecord<String, EmailSendingTaskDto>,
1821
acknowledgment: Acknowledgment
1922
) {
20-
println(consumerRecord.value())
23+
for (service in emailProcessingServices) {
24+
if (service.eventType == consumerRecord.value().eventType) {
25+
service.processEmail(consumerRecord.value())
26+
acknowledgment.acknowledge()
2127

22-
acknowledgment.acknowledge()
28+
break
29+
}
30+
}
2331
}
2432

2533
}

0 commit comments

Comments
 (0)