Parameter 4 of constructor in ApplicationUserService required a bean of type 'ConfirmationToken' that could not be found

Viewed 23

I am creating a complete user login and registration back-end system with email verification and usage of PostgreSQL to store the user's credentials. I am sending the user's credentials and e-mail address via Postman in POST request, and then I am generating an e-mail with a link for activation that lasts 15 minutes.

I've come to a point where I am dealing with a bug, in which I am posting my payload via Postman, the e-mail gets registered but if the time limit expires I can't send another e-mail since the latter has already been registered in my DB. To debug this, I am currently modifying my service class as follows:

Registration Service Class

package com.andrekreou.iot.registration;

import com.andrekreou.iot.auth.ApplicationUser;
import com.andrekreou.iot.auth.ApplicationUserService;
import com.andrekreou.iot.email.EmailSender;
import com.andrekreou.iot.registration.token.ConfirmationToken;
import com.andrekreou.iot.registration.token.ConfirmationTokenService;
import com.andrekreou.iot.security.ApplicationUserRole;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

@Service
@AllArgsConstructor
public class RegistrationService {

    private final ApplicationUserService applicationUserService;
    private final EmailValidator emailValidator;
    private final ConfirmationTokenService confirmationTokenService;
    private final EmailSender emailSender;

    public String register(RegistrationRequest request) {
        boolean isValidEmail = emailValidator.
                test(request.getEmail());
        if (!isValidEmail) {
            throw new IllegalStateException("Email not valid");
        }


        String token = applicationUserService.signUpUser(
                new ApplicationUser(
                        request.getFirstName(),
                        request.getLastName(),
                        request.getEmail(),
                        request.getPassword(),
                        ApplicationUserRole.USER
                )
        );

        String link = "https://localhost:8443/api/v1/registration/confirm?token=" + token;

        emailSender.send(
                request.getEmail(),
                buildEmail(request.getFirstName(),
                        link));

        return token;
    }

    @Transactional
    public String confirmToken(String token) {
        ConfirmationToken confirmationToken = confirmationTokenService
                .getToken(token)
                .orElseThrow(() ->
                        new IllegalStateException("token not found"));

        if (confirmationToken.getConfirmedAt() != null) {
            throw new IllegalStateException("email already confirmed");
        }

        LocalDateTime expiredAt = confirmationToken.getExpiresAt();

        if (expiredAt.isBefore(LocalDateTime.now())) {
            throw new IllegalStateException("token expired");
        }

        confirmationTokenService.setConfirmedAt(token);
        applicationUserService.enableApplicationUser(
                confirmationToken.getApplicationUser().getEmail());
        return "confirmed";
    }

    private String buildEmail(String name, String link) {
        return " HTML CODE";
    }
}

Confirmation Token Class

package com.andrekreou.iot.registration.token;

import com.andrekreou.iot.auth.ApplicationUser;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.persistence.*;
import java.time.LocalDateTime;

@Getter
@Setter
@NoArgsConstructor
@EqualsAndHashCode
@Entity
public class ConfirmationToken {

    @Id
    @SequenceGenerator(
            name = "confirmation_token_sequence",
            sequenceName = "confirmation_token_sequence",
            allocationSize = 1
    )
    @GeneratedValue(
            strategy = GenerationType.SEQUENCE,
            generator = "confirmation_token_sequence"
    )
    private Long id;

    @Column(nullable = false)
    private String token;

    @Column(nullable = false)
    private LocalDateTime createdAt;

    @Column(nullable = false)
    private LocalDateTime expiresAt;

    private LocalDateTime confirmedAt;

    @ManyToOne
    @JoinColumn(
            nullable = false,
            name = "app_user_id"
    )
    private ApplicationUser applicationUser;

    public ConfirmationToken(String token,
                             LocalDateTime createdAt,
                             LocalDateTime expiresAt,
                             ApplicationUser applicationUser) {
        this.token = token;
        this.createdAt = createdAt;
        this.expiresAt = expiresAt;
        this.applicationUser = applicationUser;
    }
}

ApplicationUserService class

package com.andrekreou.iot.auth;

import com.andrekreou.iot.registration.RegistrationRequest;
import com.andrekreou.iot.registration.RegistrationService;
import com.andrekreou.iot.registration.token.ConfirmationToken;
import com.andrekreou.iot.registration.token.ConfirmationTokenRepository;
import com.andrekreou.iot.registration.token.ConfirmationTokenService;
import lombok.AllArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.UUID;

@Service
@AllArgsConstructor
public class ApplicationUserService implements UserDetailsService {

    private final static String USER_NOT_FOUND_MSG =
            "User with email %s not found";

    private final UserRepository userRepository;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;
    private final ConfirmationTokenService confirmationTokenService;
    private final ConfirmationTokenRepository confirmationTokenRepository;
    private final ConfirmationToken confirmationToken;
    private final RegistrationService registrationService;
    private final RegistrationRequest request;

    @Override
    public UserDetails loadUserByUsername(String email)
            throws UsernameNotFoundException {
        return userRepository.findByEmail(email)
                .orElseThrow(() ->
                        new UsernameNotFoundException(String.format(
                                String.format(USER_NOT_FOUND_MSG, email))));
    }

    public String signUpUser(ApplicationUser applicationUser){
        boolean mailExists = isMailExists(applicationUser);

        boolean tokenExists = isTokenExists(confirmationToken);

        if (mailExists && !tokenExists) {
            registrationService.register(request);
        } else {
            throw new IllegalStateException("email already taken");
        }

        String encodedPassword = bCryptPasswordEncoder.encode(applicationUser.getPassword());

        applicationUser.setPassword(encodedPassword);

        userRepository.save(applicationUser);

        String token = UUID.randomUUID().toString();

        ConfirmationToken confirmationToken = new ConfirmationToken(
                token,
                LocalDateTime.now(),
                LocalDateTime.now().plusMinutes(15),
                applicationUser
        );

        confirmationTokenService.saveConfirmationToken(confirmationToken);

        return token;
    }

    private boolean isTokenExists(ConfirmationToken confirmationToken){
        return confirmationTokenRepository.findByToken(confirmationToken.getToken()).isPresent();
    }

    private boolean isMailExists(ApplicationUser applicationUser) {
        return userRepository.findByEmail(applicationUser.getEmail()).isPresent();
    }

    public int enableApplicationUser(String email) {
        return userRepository.enableAppUser(email);
    }
}

I've created two boolean variables which check if the mail exists in my DB and if the e-mail token doesn't. When these two conditions are true, I want to send the e-mail confirmation again back to the user. But I am dealing with the following error:

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 4 of constructor in com.andrekreou.iot.auth.ApplicationUserService required a bean of type 'com.andrekreou.iot.registration.token.ConfirmationToken' that could not be found.


Action:

Consider defining a bean of type 'com.andrekreou.iot.registration.token.ConfirmationToken' in your configuration.


Process finished with exit code 1

I know that this is a common error, but I don't know how to address it. Any ideas?

0 Answers
Related