| 1 | package com.edtech.service; | |
| 2 | ||
| 3 | import com.edtech.model.RecoveryToken; | |
| 4 | import com.edtech.model.User; | |
| 5 | import com.edtech.repository.RecoveryTokenRepository; | |
| 6 | import com.edtech.repository.UserRepository; | |
| 7 | import java.security.SecureRandom; | |
| 8 | import java.time.LocalDateTime; | |
| 9 | import java.util.Locale; | |
| 10 | import java.util.Optional; | |
| 11 | import org.springframework.security.crypto.password.PasswordEncoder; | |
| 12 | import org.springframework.stereotype.Service; | |
| 13 | import org.springframework.transaction.annotation.Transactional; | |
| 14 | ||
| 15 | /** Documentação para RecoveryService. */ | |
| 16 | @Service | |
| 17 | public class RecoveryService { | |
| 18 | ||
| 19 | private static final SecureRandom SECURE_RANDOM = new SecureRandom(); | |
| 20 | ||
| 21 | private final UserRepository userRepository; | |
| 22 | private final RecoveryTokenRepository recoveryTokenRepository; | |
| 23 | private final EmailService emailService; | |
| 24 | private final PasswordEncoder passwordEncoder; | |
| 25 | ||
| 26 | /** Documentação. */ | |
| 27 | public RecoveryService( | |
| 28 | UserRepository userRepository, | |
| 29 | RecoveryTokenRepository recoveryTokenRepository, | |
| 30 | EmailService emailService, | |
| 31 | PasswordEncoder passwordEncoder) { | |
| 32 | this.userRepository = userRepository; | |
| 33 | this.recoveryTokenRepository = recoveryTokenRepository; | |
| 34 | this.emailService = emailService; | |
| 35 | this.passwordEncoder = passwordEncoder; | |
| 36 | } | |
| 37 | ||
| 38 | /** Documentação. */ | |
| 39 | @Transactional | |
| 40 | public void requestRecovery(String email) { | |
| 41 | String normalizedEmail = email.trim().toLowerCase(Locale.ROOT); | |
| 42 | Optional<User> userOpt = userRepository.findByEmailIgnoreCase(normalizedEmail); | |
| 43 | ||
| 44 | // Demo accounts cannot recover password | |
| 45 |
1
1. requestRecovery : negated conditional → KILLED |
if (normalizedEmail.endsWith(".demo@unb.br")) { |
| 46 | return; | |
| 47 | } | |
| 48 | ||
| 49 | // Sempre retorna sucesso rapidamente por seguranca (evitar email enumeration), mas soh envia se | |
| 50 | // existir | |
| 51 |
1
1. requestRecovery : negated conditional → KILLED |
if (userOpt.isPresent()) { |
| 52 |
1
1. requestRecovery : removed call to com/edtech/repository/RecoveryTokenRepository::deleteByEmail → KILLED |
recoveryTokenRepository.deleteByEmail(normalizedEmail); // Limpa tokens antigos |
| 53 | ||
| 54 | String code = String.format("%06d", SECURE_RANDOM.nextInt(999999)); | |
| 55 | RecoveryToken token = | |
| 56 | new RecoveryToken(code, normalizedEmail, LocalDateTime.now().plusMinutes(15)); | |
| 57 | recoveryTokenRepository.save(token); | |
| 58 | ||
| 59 |
1
1. requestRecovery : removed call to com/edtech/service/EmailService::sendRecoveryEmail → KILLED |
emailService.sendRecoveryEmail(normalizedEmail, code); |
| 60 | } | |
| 61 | } | |
| 62 | ||
| 63 | /** Documentação. */ | |
| 64 | @Transactional(readOnly = true) | |
| 65 | public boolean verifyCode(String email, String code) { | |
| 66 | String normalizedEmail = email.trim().toLowerCase(Locale.ROOT); | |
| 67 | Optional<RecoveryToken> tokenOpt = | |
| 68 | recoveryTokenRepository.findByEmailAndToken(normalizedEmail, code); | |
| 69 | ||
| 70 |
2
1. verifyCode : negated conditional → KILLED 2. verifyCode : negated conditional → KILLED |
if (tokenOpt.isPresent() && !tokenOpt.get().isExpired()) { |
| 71 |
1
1. verifyCode : replaced boolean return with false for com/edtech/service/RecoveryService::verifyCode → KILLED |
return true; |
| 72 | } | |
| 73 |
1
1. verifyCode : replaced boolean return with true for com/edtech/service/RecoveryService::verifyCode → KILLED |
return false; |
| 74 | } | |
| 75 | ||
| 76 | /** Documentação. */ | |
| 77 | @Transactional | |
| 78 | public boolean resetPassword(String email, String code, String newPassword) { | |
| 79 | String normalizedEmail = email.trim().toLowerCase(Locale.ROOT); | |
| 80 | ||
| 81 | // Demo accounts cannot reset password | |
| 82 |
1
1. resetPassword : negated conditional → KILLED |
if (normalizedEmail.endsWith(".demo@unb.br")) { |
| 83 |
1
1. resetPassword : replaced boolean return with true for com/edtech/service/RecoveryService::resetPassword → KILLED |
return false; |
| 84 | } | |
| 85 | ||
| 86 | Optional<RecoveryToken> tokenOpt = | |
| 87 | recoveryTokenRepository.findByEmailAndToken(normalizedEmail, code); | |
| 88 | ||
| 89 |
2
1. resetPassword : negated conditional → KILLED 2. resetPassword : negated conditional → KILLED |
if (tokenOpt.isPresent() && !tokenOpt.get().isExpired()) { |
| 90 | Optional<User> userOpt = userRepository.findByEmailIgnoreCase(normalizedEmail); | |
| 91 |
1
1. resetPassword : negated conditional → KILLED |
if (userOpt.isPresent()) { |
| 92 | User user = userOpt.get(); | |
| 93 |
1
1. resetPassword : removed call to com/edtech/model/User::setPasswordHash → KILLED |
user.setPasswordHash(passwordEncoder.encode(newPassword)); |
| 94 | userRepository.save(user); | |
| 95 |
1
1. resetPassword : removed call to com/edtech/repository/RecoveryTokenRepository::deleteByEmail → KILLED |
recoveryTokenRepository.deleteByEmail(normalizedEmail); |
| 96 |
1
1. resetPassword : replaced boolean return with false for com/edtech/service/RecoveryService::resetPassword → KILLED |
return true; |
| 97 | } | |
| 98 | } | |
| 99 |
1
1. resetPassword : replaced boolean return with true for com/edtech/service/RecoveryService::resetPassword → KILLED |
return false; |
| 100 | } | |
| 101 | } | |
Mutations | ||
| 45 |
1.1 |
|
| 51 |
1.1 |
|
| 52 |
1.1 |
|
| 59 |
1.1 |
|
| 70 |
1.1 2.2 |
|
| 71 |
1.1 |
|
| 73 |
1.1 |
|
| 82 |
1.1 |
|
| 83 |
1.1 |
|
| 89 |
1.1 2.2 |
|
| 91 |
1.1 |
|
| 93 |
1.1 |
|
| 95 |
1.1 |
|
| 96 |
1.1 |
|
| 99 |
1.1 |