RecoveryService.java

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
Location : requestRecovery
Killed by : com.edtech.service.RecoveryServiceTest.[engine:junit-jupiter]/[class:com.edtech.service.RecoveryServiceTest]/[method:requestRecovery_WhenUserExists_ShouldGenerateTokenAndSendEmail()]
negated conditional → KILLED

51

1.1
Location : requestRecovery
Killed by : com.edtech.service.RecoveryServiceTest.[engine:junit-jupiter]/[class:com.edtech.service.RecoveryServiceTest]/[method:requestRecovery_WhenUserExists_ShouldGenerateTokenAndSendEmail()]
negated conditional → KILLED

52

1.1
Location : requestRecovery
Killed by : com.edtech.service.RecoveryServiceTest.[engine:junit-jupiter]/[class:com.edtech.service.RecoveryServiceTest]/[method:requestRecovery_WhenUserExists_ShouldGenerateTokenAndSendEmail()]
removed call to com/edtech/repository/RecoveryTokenRepository::deleteByEmail → KILLED

59

1.1
Location : requestRecovery
Killed by : com.edtech.service.RecoveryServiceTest.[engine:junit-jupiter]/[class:com.edtech.service.RecoveryServiceTest]/[method:requestRecovery_WhenUserExists_ShouldGenerateTokenAndSendEmail()]
removed call to com/edtech/service/EmailService::sendRecoveryEmail → KILLED

70

1.1
Location : verifyCode
Killed by : com.edtech.service.RecoveryServiceTest.[engine:junit-jupiter]/[class:com.edtech.service.RecoveryServiceTest]/[method:verifyCode_WhenCodeIsValid_ShouldReturnTrue()]
negated conditional → KILLED

2.2
Location : verifyCode
Killed by : com.edtech.service.RecoveryServiceTest.[engine:junit-jupiter]/[class:com.edtech.service.RecoveryServiceTest]/[method:verifyCode_WhenCodeIsValid_ShouldReturnTrue()]
negated conditional → KILLED

71

1.1
Location : verifyCode
Killed by : com.edtech.service.RecoveryServiceTest.[engine:junit-jupiter]/[class:com.edtech.service.RecoveryServiceTest]/[method:verifyCode_WhenCodeIsValid_ShouldReturnTrue()]
replaced boolean return with false for com/edtech/service/RecoveryService::verifyCode → KILLED

73

1.1
Location : verifyCode
Killed by : com.edtech.service.RecoveryServiceTest.[engine:junit-jupiter]/[class:com.edtech.service.RecoveryServiceTest]/[method:verifyCode_WhenCodeIsExpired_ShouldReturnFalse()]
replaced boolean return with true for com/edtech/service/RecoveryService::verifyCode → KILLED

82

1.1
Location : resetPassword
Killed by : com.edtech.service.RecoveryServiceTest.[engine:junit-jupiter]/[class:com.edtech.service.RecoveryServiceTest]/[method:resetPassword_WhenTokenIsInvalid_ShouldReturnFalse()]
negated conditional → KILLED

83

1.1
Location : resetPassword
Killed by : com.edtech.service.RecoveryServiceTest.[engine:junit-jupiter]/[class:com.edtech.service.RecoveryServiceTest]/[method:resetPassword_WhenIsDemoAccount_ShouldReturnFalse()]
replaced boolean return with true for com/edtech/service/RecoveryService::resetPassword → KILLED

89

1.1
Location : resetPassword
Killed by : com.edtech.service.RecoveryServiceTest.[engine:junit-jupiter]/[class:com.edtech.service.RecoveryServiceTest]/[method:resetPassword_WhenTokenIsInvalid_ShouldReturnFalse()]
negated conditional → KILLED

2.2
Location : resetPassword
Killed by : com.edtech.service.RecoveryServiceTest.[engine:junit-jupiter]/[class:com.edtech.service.RecoveryServiceTest]/[method:resetPassword_WhenUserDoesNotExist_ShouldReturnFalse()]
negated conditional → KILLED

91

1.1
Location : resetPassword
Killed by : com.edtech.service.RecoveryServiceTest.[engine:junit-jupiter]/[class:com.edtech.service.RecoveryServiceTest]/[method:resetPassword_WhenUserDoesNotExist_ShouldReturnFalse()]
negated conditional → KILLED

93

1.1
Location : resetPassword
Killed by : com.edtech.service.RecoveryServiceTest.[engine:junit-jupiter]/[class:com.edtech.service.RecoveryServiceTest]/[method:resetPassword_WhenCodeIsValidAndUserExists_ShouldUpdatePassword()]
removed call to com/edtech/model/User::setPasswordHash → KILLED

95

1.1
Location : resetPassword
Killed by : com.edtech.service.RecoveryServiceTest.[engine:junit-jupiter]/[class:com.edtech.service.RecoveryServiceTest]/[method:resetPassword_WhenCodeIsValidAndUserExists_ShouldUpdatePassword()]
removed call to com/edtech/repository/RecoveryTokenRepository::deleteByEmail → KILLED

96

1.1
Location : resetPassword
Killed by : com.edtech.service.RecoveryServiceTest.[engine:junit-jupiter]/[class:com.edtech.service.RecoveryServiceTest]/[method:resetPassword_WhenCodeIsValidAndUserExists_ShouldUpdatePassword()]
replaced boolean return with false for com/edtech/service/RecoveryService::resetPassword → KILLED

99

1.1
Location : resetPassword
Killed by : com.edtech.service.RecoveryServiceTest.[engine:junit-jupiter]/[class:com.edtech.service.RecoveryServiceTest]/[method:resetPassword_WhenTokenIsInvalid_ShouldReturnFalse()]
replaced boolean return with true for com/edtech/service/RecoveryService::resetPassword → KILLED

Active mutators

Tests examined


Report generated by PIT 1.16.1