Skip to content

Commit

Permalink
fix: Handle specific exceptions while email verification (#191)
Browse files Browse the repository at this point in the history
  • Loading branch information
iamareebjamal authored Sep 3, 2018
1 parent c77fde2 commit 1f76302
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
package amu.zhcet.auth.verification;

import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
@EqualsAndHashCode(callSuper = false)
public class DuplicateEmailException extends RuntimeException {

private String email;

public DuplicateEmailException(String email) {
super("'" + email + "' is already registered by another user");
this.email = email;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,13 @@ private void sendVerificationLink(String email, RedirectAttributes redirectAttri
} catch (DuplicateEmailException de) {
log.warn("Duplicate Email", de);
redirectAttributes.addFlashAttribute("email_error", de.getMessage());
} catch (RecentVerificationException re) {
log.warn("Recently Sent Email", re);
redirectAttributes.addFlashAttribute("email_error", RecentVerificationException.MESSAGE);
} catch (RuntimeException re) {
log.warn("Error sending verification link", re);
redirectAttributes.addFlashAttribute("email_error", re.getMessage());
redirectAttributes.addFlashAttribute("email_error",
"There was some error sending the email");
}
}

Expand Down Expand Up @@ -77,9 +81,9 @@ public String verifyEmail(Model model, @RequestParam("auth") String token) {
try {
emailVerificationService.verifyEmail(token);
model.addAttribute("success", "Your email was successfully verified!");
} catch (IllegalStateException | DuplicateEmailException ie) {
log.warn("Email Verification Error {}", ie.getMessage());
model.addAttribute("error", ie.getMessage());
} catch (DuplicateEmailException | TokenVerificationException de) {
log.warn("Email Verification Error {}", de.getMessage());
model.addAttribute("error", de.getMessage());
}

return "user/verify_email";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.mail.MailException;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
Expand Down Expand Up @@ -45,8 +46,9 @@ public EmailVerificationService(UserService userService, LinkMailService linkMai
private VerificationToken createVerificationToken(String email) {
User user = userService.getLoggedInUser().orElseThrow(() -> new IllegalStateException("No user logged in"));
// Check if link was already sent
if (emailCache.getIfPresent(user.getUserId()) != null)
throw new RuntimeException("Verification link was recently sent. Please wait for some time");
LocalDateTime sentTime = emailCache.getIfPresent(user.getUserId());
if (sentTime != null)
throw new RecentVerificationException(sentTime);

emailCache.put(user.getUserId(), LocalDateTime.now());

Expand Down Expand Up @@ -101,22 +103,22 @@ public void generate(String email) {
/**
* Retrieves the verification token from database and validates it by performing checks
* that it exists, is not already used and not expired. If any of the validation fails,
* throws an {@link IllegalStateException} with the corresponding message
* throws an {@link TokenVerificationException} with the corresponding message
* @param token String token ID
* @return {@link VerificationToken}
*/
private VerificationToken getAndValidateOrThrow(String token) {
VerificationToken verificationToken = verificationTokenRepository.findByToken(token);

if (verificationToken == null)
throw new IllegalStateException("Token: " + token + " is invalid");
throw new TokenVerificationException("Token: " + token + " is invalid");

if (verificationToken.isUsed())
throw new IllegalStateException("Token: " + token + " is already used! Please request another link!");
throw new TokenVerificationException("Token: " + token + " is already used! Please request another link!");

Calendar cal = Calendar.getInstance();
if ((verificationToken.getExpiry().getTime() - cal.getTime().getTime()) <= 0)
throw new IllegalStateException("Token: " + token + " has expired");
throw new TokenVerificationException("Token: " + token + " has expired");

return verificationToken;
}
Expand Down Expand Up @@ -186,17 +188,25 @@ public void sendMail(VerificationToken token) {
String relativeUrl = "/login/email/verify?auth=" + token.getToken();
log.debug("Verification link generated : {}", relativeUrl);

linkMailService.sendEmail(getPayLoad(token.getEmail(), token.getUser(), relativeUrl), false);

// Now we set the email to the user and disable email verified
User user = token.getUser();
log.debug("Saving user email {} -> {}", user.getUserId(), token.getEmail());
user.setEmail(token.getEmail());
user.setEmailVerified(false);

userService.save(user);
log.debug("Saved user email");
eventPublisher.publishEvent(new EmailVerifiedEvent(user));
try {
linkMailService.sendEmail(getPayLoad(token.getEmail(), token.getUser(), relativeUrl), false);
// Now we set the email to the user and disable email verified
User user = token.getUser();
log.debug("Saving user email {} -> {}", user.getUserId(), token.getEmail());
user.setEmail(token.getEmail());
user.setEmailVerified(false);

userService.save(user);
log.debug("Saved user email");
eventPublisher.publishEvent(new EmailVerifiedEvent(user));
} catch (MailException mailException) {
// There was an error sending the mail, so remove the token and sent time
verificationTokenRepository.delete(token);
emailCache.invalidate(token.getUser().getUserId());
log.warn("Email sending for {} '{}' failed, so we removed the verification token",
token.getUser(), token.getEmail(), mailException);
throw mailException;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package amu.zhcet.auth.verification;

import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.lang.Nullable;

import java.time.LocalDateTime;

@Data
@EqualsAndHashCode(callSuper = false)
public class RecentVerificationException extends RuntimeException {

public static final String MESSAGE = "Verification link was recently sent. Please wait for some time";

@Nullable
private LocalDateTime sentTime;

public RecentVerificationException() {
super(MESSAGE);
}

public RecentVerificationException(LocalDateTime sentTime) {
super(String.format(
"Verification link was recently sent at %s. Please wait for some time",
sentTime.toString()));
this.sentTime = sentTime;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package amu.zhcet.auth.verification;

public class TokenVerificationException extends IllegalStateException {

public TokenVerificationException(String message) {
super(message);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package amu.zhcet.auth.verification;

import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.*;

public class DuplicateEmailExceptionTest {

private DuplicateEmailException duplicateEmailException;

@Before
public void setUp() {
duplicateEmailException = new DuplicateEmailException("[email protected]");
}

@Test
public void testConstructorMessage() {
assertEquals(
"'[email protected]' is already registered by another user",
duplicateEmailException.getMessage());
}

@Test
public void testGettingEmail() {
assertEquals("[email protected]", duplicateEmailException.getEmail());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package amu.zhcet.auth.verification;

import org.junit.Test;

import java.time.LocalDateTime;

import static org.junit.Assert.*;

public class RecentVerificationExceptionTest {

private LocalDateTime localDateTime = LocalDateTime.of(2018, 9, 1, 12, 23);

@Test
public void testNoConstructorMessage() {
RecentVerificationException recentVerificationException = new RecentVerificationException();
assertEquals(
"Verification link was recently sent. Please wait for some time",
recentVerificationException.getMessage());
}

@Test
public void testConstructorMessage() {
RecentVerificationException recentVerificationException = new RecentVerificationException(localDateTime);
assertEquals(
"Verification link was recently sent at 2018-09-01T12:23. Please wait for some time",
recentVerificationException.getMessage());
}

@Test
public void testGettingSentTime() {
RecentVerificationException recentVerificationException = new RecentVerificationException(localDateTime);
assertEquals(localDateTime, recentVerificationException.getSentTime());
}

}

0 comments on commit 1f76302

Please sign in to comment.