diff --git a/src/main/java/org/wise/portal/dao/user/UserDao.java b/src/main/java/org/wise/portal/dao/user/UserDao.java index b0d8715c3b..fbc10f77d3 100644 --- a/src/main/java/org/wise/portal/dao/user/UserDao.java +++ b/src/main/java/org/wise/portal/dao/user/UserDao.java @@ -46,6 +46,7 @@ List retrieveStudentsByNameAndBirthday(String firstName, String lastName, List retrieveTeachersByFirstName(String firstName); List retrieveTeachersByLastName(String lastName); User retrieveTeacherByUsername(String username); + User retrieveTeacherByVerificationCode(String verificationCode); List retrieveTeachersByDisplayName(String displayName); List retrieveTeachersByCity(String city); List retrieveTeachersByState(String state); diff --git a/src/main/java/org/wise/portal/dao/user/impl/HibernateUserDao.java b/src/main/java/org/wise/portal/dao/user/impl/HibernateUserDao.java index 6a97b31d53..8a8f64f0de 100644 --- a/src/main/java/org/wise/portal/dao/user/impl/HibernateUserDao.java +++ b/src/main/java/org/wise/portal/dao/user/impl/HibernateUserDao.java @@ -148,8 +148,7 @@ public List retrieveTeachersByLastName(String lastName) { } public User retrieveTeacherByUsername(String username) { - List resultList = retrieveTeachersByFieldValue("username", username); - return resultList.isEmpty() ? null : resultList.get(0); + return retrieveTeacherByFieldValue("username", username); } public List retrieveTeachersByDisplayName(String displayName) { @@ -176,6 +175,15 @@ public List retrieveTeachersByEmail(String emailAddress) { return retrieveTeachersByFieldValue("emailAddress", emailAddress); } + public User retrieveTeacherByVerificationCode(String verificationCode) { + return retrieveTeacherByFieldValue("verificationCode", verificationCode); + } + + private User retrieveTeacherByFieldValue(String field, String value) { + List resultList = retrieveTeachersByFieldValue(field, value); + return resultList.isEmpty() ? null : resultList.get(0); + } + @SuppressWarnings("unchecked") private List retrieveTeachersByFieldValue(String field, String value) { CriteriaBuilder cb = getCriteriaBuilder(); diff --git a/src/main/java/org/wise/portal/domain/authentication/impl/TeacherUserDetails.java b/src/main/java/org/wise/portal/domain/authentication/impl/TeacherUserDetails.java index 76f7e51340..cd7babf46a 100644 --- a/src/main/java/org/wise/portal/domain/authentication/impl/TeacherUserDetails.java +++ b/src/main/java/org/wise/portal/domain/authentication/impl/TeacherUserDetails.java @@ -93,6 +93,12 @@ public class TeacherUserDetails extends PersistentUserDetails implements Mutable @Transient private static final String COLUMN_NAME_HOW_HEAR = "howDidYouHearAboutUs"; + @Transient + private static final String COLUMN_NAME_VERIFIED = "isVerified"; + + @Transient + private static final String COLUMN_NAME_VERIFICATION_CODE = "verificationCode"; + @Transient private static final long serialVersionUID = 1L; @@ -164,6 +170,16 @@ public class TeacherUserDetails extends PersistentUserDetails implements Mutable @Setter private String howDidYouHearAboutUs; + @Column(name = TeacherUserDetails.COLUMN_NAME_VERIFIED, nullable = false) + @Getter + @Setter + private boolean verified = false; + + @Column(name = TeacherUserDetails.COLUMN_NAME_VERIFICATION_CODE, unique = true) + @Getter + @Setter + private String verificationCode; + public String getCoreUsername() { return (firstname + lastname).replaceAll("[\\s-]+", ""); } diff --git a/src/main/java/org/wise/portal/presentation/web/controllers/teacher/TeacherAPIController.java b/src/main/java/org/wise/portal/presentation/web/controllers/teacher/TeacherAPIController.java index 92e6baadd5..a9355122f5 100644 --- a/src/main/java/org/wise/portal/presentation/web/controllers/teacher/TeacherAPIController.java +++ b/src/main/java/org/wise/portal/presentation/web/controllers/teacher/TeacherAPIController.java @@ -6,6 +6,7 @@ import java.util.Locale; import java.util.Set; import java.util.TreeSet; +import java.util.UUID; import javax.servlet.http.HttpServletRequest; @@ -326,4 +327,20 @@ HashMap editRunIsLockedAfterEndDate(Authentication authenticatio } return response; } + + @GetMapping("/is-verified") + @Secured({ "ROLE_ANONYMOUS" }) + public boolean isVerifiedTeacherOrNonTeacher(@RequestParam String username) { + User user = userService.retrieveTeacherByUsername(username); + return !this.isTeacher(user) || this.isTeacherVerified(user); // Only teachers need to verify their accounts + } + + protected boolean isTeacher(User user) { + return user != null && !user.getRoles().contains("ROLE_STUDENT"); + } + + private boolean isTeacherVerified(User user) { + TeacherUserDetails tud = (TeacherUserDetails) user.getUserDetails(); + return tud.isVerified(); + } } diff --git a/src/main/java/org/wise/portal/presentation/web/controllers/teacher/TeacherRegistrationAPIController.java b/src/main/java/org/wise/portal/presentation/web/controllers/teacher/TeacherRegistrationAPIController.java index cc18cd734e..9537dc4e27 100644 --- a/src/main/java/org/wise/portal/presentation/web/controllers/teacher/TeacherRegistrationAPIController.java +++ b/src/main/java/org/wise/portal/presentation/web/controllers/teacher/TeacherRegistrationAPIController.java @@ -1,17 +1,22 @@ package org.wise.portal.presentation.web.controllers.teacher; +import java.io.IOException; import java.util.Locale; import java.util.Map; +import java.util.UUID; -import javax.mail.MessagingException; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.RandomStringUtils; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.ResponseEntity; import org.springframework.security.access.annotation.Secured; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.wise.portal.domain.authentication.Schoollevel; import org.wise.portal.domain.authentication.impl.TeacherUserDetails; @@ -26,12 +31,6 @@ @RestController @RequestMapping("/api/teacher/register") public class TeacherRegistrationAPIController extends TeacherAPIController { - - private final String emailCodePrefix = - "presentation.web.controllers.teacher.registerTeacherController.welcomeTeacherEmail"; - private final String welcomeBodyCode = this.emailCodePrefix + "Body"; - private final String welcomeSocialAccountBodyCode = this.emailCodePrefix + "BodyNoUsername"; - private final String welcomeSubjectCode = this.emailCodePrefix + "Subject"; @PostMapping() @Secured({ "ROLE_ANONYMOUS" }) @@ -47,60 +46,26 @@ ResponseEntity> createTeacherAccount( passwordService.getErrors(teacherFields.get("password"))); } Locale locale = request.getLocale(); - TeacherUserDetails tud = createTeacherUserDetails(teacherFields, locale); + boolean isSocialAccount = isSocialAccount(teacherFields); + TeacherUserDetails tud = createTeacherUserDetails(teacherFields, isSocialAccount, locale); User createdUser = this.userService.createUser(tud); String username = createdUser.getUserDetails().getUsername(); - if (isSendEmailEnabled()) { - sendWelcomeTeacherEmail(tud.getEmailAddress(), tud.getDisplayname(), username, - isSocialAccount(tud), locale, request); - } + sendNewTeacherEmail(request, locale, isSocialAccount, tud, username); return createRegisterSuccessResponse(username); } - private boolean isSendEmailEnabled() { - String sendEmailEnabledStr = appProperties.getProperty("send_email_enabled", "false"); - return Boolean.valueOf(sendEmailEnabledStr); - } - - private boolean isSocialAccount(TeacherUserDetails tud) { - return isSet(tud.getGoogleUserId()) || isSet(tud.getMicrosoftUserId()); - } - - private void sendWelcomeTeacherEmail(String email, String displayName, String username, - boolean socialAccount, Locale locale, - HttpServletRequest request) { - String subject = getEmailMessage(this.welcomeSubjectCode, this.welcomeSubjectCode, null, locale); - String body = getWelcomeTeacherBody(displayName, username, socialAccount, locale, request); - this.sendEmail(email, subject, body); - } - - private String getEmailMessage(String defaultCode, String code, Object[] args, Locale locale) { - String defaultMessage = messageSource.getMessage(defaultCode, args, Locale.US); - return messageSource.getMessage(code, args, defaultMessage, locale); - } - - private String getWelcomeTeacherBody(String displayName, String username, boolean socialAccount, - Locale locale, HttpServletRequest request) { - String gettingStartedUrl = getGettingStartedUrl(request); - String code = socialAccount ? this.welcomeSocialAccountBodyCode : this.welcomeBodyCode; - Object[] args = socialAccount - ? new Object[] { displayName, gettingStartedUrl } - : new Object[] { displayName, username, gettingStartedUrl }; - return getEmailMessage(this.welcomeBodyCode, code, args, locale); - } - - private String getGettingStartedUrl(HttpServletRequest request) { - return ControllerUtil.getPortalUrlString(request) + "/help/getting-started"; + private void sendNewTeacherEmail(HttpServletRequest request, Locale locale, boolean isSocialAccount, + TeacherUserDetails tud, String username) { + if (isSocialAccount) { + this.mailService.sendWelcomeTeacherEmail(tud.getEmailAddress(), tud.getDisplayname(), username, + true, locale, request); + } else { + this.mailService.sendVerifyTeacherEmail(tud.getEmailAddress(), tud.getVerificationCode(), locale, request); + } } - private void sendEmail(String email, String subject, String body) { - String fromEmail = appProperties.getProperty("portalemailaddress"); - String[] recipients = { email }; - try { - mailService.postMail(recipients, subject, body, fromEmail); - } catch (MessagingException e) { - e.printStackTrace(); - } + private boolean isSocialAccount(Map teacherFields) { + return isSet(teacherFields.get("googleUserId")) || isSet(teacherFields.get("microsoftUserId")); } private void validateTeacherFields(Map teacherFields) @@ -133,7 +98,7 @@ private void validatePassword(String password) throws InvalidPasswordException { } private TeacherUserDetails createTeacherUserDetails(Map teacherFields, - Locale locale) { + boolean isSocialAccount, Locale locale) { TeacherUserDetails tud = new TeacherUserDetails(); tud.setFirstname(teacherFields.get("firstName")); tud.setLastname(teacherFields.get("lastName")); @@ -148,6 +113,8 @@ private TeacherUserDetails createTeacherUserDetails(Map teacherF tud.setLanguage(locale.getLanguage()); setPassword(teacherFields, tud); tud.setEmailValid(true); + tud.setVerified(isSocialAccount); + setVerificationCode(tud); return tud; } @@ -164,6 +131,18 @@ private void setPassword(Map teacherFields, TeacherUserDetails t tud.setPassword(teacherFields.get("password")); } } + + private void setVerificationCode(TeacherUserDetails tud) { + boolean isCodeSet = false; + while (!isCodeSet) { + try { + tud.setVerificationCode(UUID.randomUUID().toString()); + isCodeSet = true; + } catch (DataIntegrityViolationException e) { + continue; + } + } + } private boolean isSet(String value) { return value != null && !value.isEmpty(); @@ -172,4 +151,70 @@ private boolean isSet(String value) { private void setRandomPassword(TeacherUserDetails tud) { tud.setPassword(RandomStringUtils.random(10, true, true)); } + + @GetMapping("/verify") + @Secured({ "ROLE_ANONYMOUS" }) + public void verifyTeacherAndRedirect(@RequestParam String code, HttpServletResponse response, + HttpServletRequest request) throws IOException { + User user = userService.retrieveTeacherByVerificationCode(code); + String link = verifyTeacherIfNecessaryAndGetLoginLink(request, user); + sendWelcomeEmailIfNecessary(user, link, request); + response.sendRedirect(link); + } + + private String verifyTeacherIfNecessaryAndGetLoginLink(HttpServletRequest request, User user) { + String link; + if (user == null) { + link = "/login?verified=error"; + } else if (!isTeacher(user)) { + link = getRedirectLink(user, false); + } else { + TeacherUserDetails tud = (TeacherUserDetails) user.getUserDetails(); + boolean verified = verifyTeacherAccount(user, tud, request); + link = getRedirectLink(user, verified); + } + return link; + } + + private void sendWelcomeEmailIfNecessary(User user, String link, HttpServletRequest request) { + if (link.contains("verified=true")) { + TeacherUserDetails tud = (TeacherUserDetails) user.getUserDetails(); + this.mailService.sendWelcomeTeacherEmail(tud.getEmailAddress(), tud.getDisplayname(), tud.getUsername(), + false, request.getLocale(), request); + } + } + + private String getRedirectLink(User user, boolean verified) { + return String.format("/login?verified=%s&username=%s", + verified, user.getUserDetails().getUsername()); + } + + private boolean verifyTeacherAccount(User user, TeacherUserDetails tud, HttpServletRequest request) { + if (!tud.isVerified()) { + tud.setVerified(true); + userService.updateUser(user); + return true; + } else { + return false; + } + } + + @PostMapping("send-verify-email") + @Secured({ "ROLE_ANONYMOUS" }) + ResponseEntity> sendVerificationEmail(@RequestParam String username, + HttpServletRequest request) { + User user = userService.retrieveTeacherByUsername(username); + if (isTeacher(user)) { + TeacherUserDetails tud = (TeacherUserDetails) user.getUserDetails(); + if (tud.isVerified()) { + return ResponseEntityGenerator.createError("Teacher already verified"); + } else { + this.mailService.sendVerifyTeacherEmail(tud.getEmailAddress(), tud.getVerificationCode(), + request.getLocale(), request); + return createRegisterSuccessResponse(username); + } + } else { + return ResponseEntityGenerator.createError("Not a teacher"); + } + } } diff --git a/src/main/java/org/wise/portal/presentation/web/controllers/user/UserAPIController.java b/src/main/java/org/wise/portal/presentation/web/controllers/user/UserAPIController.java index ca965b81fb..a08b962c13 100644 --- a/src/main/java/org/wise/portal/presentation/web/controllers/user/UserAPIController.java +++ b/src/main/java/org/wise/portal/presentation/web/controllers/user/UserAPIController.java @@ -118,6 +118,7 @@ HashMap getUserInfo(Authentication auth, info.put("country", tud.getCountry()); info.put("schoolName", tud.getSchoolname()); info.put("schoolLevel", tud.getSchoollevel()); + info.put("isVerified", tud.isVerified()); } return info; } else { diff --git a/src/main/java/org/wise/portal/service/mail/IMailFacade.java b/src/main/java/org/wise/portal/service/mail/IMailFacade.java index 02af361d9b..8036ddd6ef 100644 --- a/src/main/java/org/wise/portal/service/mail/IMailFacade.java +++ b/src/main/java/org/wise/portal/service/mail/IMailFacade.java @@ -23,7 +23,12 @@ */ package org.wise.portal.service.mail; +import java.util.Locale; + import javax.mail.MessagingException; +import javax.servlet.http.HttpServletRequest; + +import org.wise.portal.domain.authentication.impl.TeacherUserDetails; /** * @author Anthony Perritano @@ -46,4 +51,9 @@ void postMail(String recipients[], String subject, String message , String from) void postMail(String recipients[], String subject, String message , String from, String[] cc) throws MessagingException; + void sendWelcomeTeacherEmail(String email, String displayName, String username, + boolean socialAccount, Locale locale, HttpServletRequest request); + + void sendVerifyTeacherEmail(String email, String verificationCode, Locale locale, + HttpServletRequest request); } diff --git a/src/main/java/org/wise/portal/service/mail/MailService.java b/src/main/java/org/wise/portal/service/mail/MailService.java index 9a9c764cfb..b6bda64f05 100644 --- a/src/main/java/org/wise/portal/service/mail/MailService.java +++ b/src/main/java/org/wise/portal/service/mail/MailService.java @@ -23,13 +23,20 @@ */ package org.wise.portal.service.mail; +import java.util.Locale; + import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; +import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.core.env.Environment; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; +import org.wise.portal.domain.authentication.impl.TeacherUserDetails; +import org.wise.portal.presentation.web.controllers.ControllerUtil; /** * Compose and send email using the JavaMail Framework @@ -38,9 +45,23 @@ @Service public class MailService implements IMailFacade { + private final String emailCodePrefix = + "presentation.web.controllers.teacher.registerTeacherController."; + private final String verifyBodyCode = this.emailCodePrefix + "verifyTeacherEmailBody"; + private final String verifySubjectCode = this.emailCodePrefix + "verifyTeacherEmailSubject"; + private final String welcomeBodyCode = this.emailCodePrefix + "welcomeTeacherEmailBody"; + private final String welcomeSocialAccountBodyCode = this.emailCodePrefix + "welcomeTeacherEmailBodyNoUsername"; + private final String welcomeSubjectCode = this.emailCodePrefix + "welcomeTeacherEmailSubject"; + + @Autowired + protected Environment appProperties; + @Autowired private JavaMailSender javaMailSender; + @Autowired + private MessageSource messageSource; + public void postMail(String[] recipients, String subject, String message, String from) throws MessagingException { postMail(recipients, subject, message, from, null); @@ -65,4 +86,63 @@ public void postMail(String[] recipients, String subject, String message, } } } + + public void sendWelcomeTeacherEmail(String email, String displayName, String username, + boolean socialAccount, Locale locale, HttpServletRequest request) { + if (isSendEmailEnabled()) { + String subject = getEmailMessage(this.welcomeSubjectCode, this.welcomeSubjectCode, null, locale); + String body = getWelcomeTeacherBody(displayName, username, socialAccount, locale, request); + this.sendEmail(email, subject, body); + } + } + + public void sendVerifyTeacherEmail(String email, String verificationCode, + Locale locale, HttpServletRequest request) { + if (isSendEmailEnabled()) { + String subject = getEmailMessage(this.verifySubjectCode, this.verifySubjectCode, null, locale); + String verificationUrl = getVerificationUrl(verificationCode, request); + Object[] args = new Object[] { verificationUrl }; + String body = getEmailMessage(this.verifyBodyCode, this.verifyBodyCode, args, locale); + this.sendEmail(email, subject, body); + } + } + + private boolean isSendEmailEnabled() { + String sendEmailEnabledStr = appProperties.getProperty("send_email_enabled", "false"); + return Boolean.valueOf(sendEmailEnabledStr); + } + + private String getEmailMessage(String defaultCode, String code, Object[] args, Locale locale) { + String defaultMessage = messageSource.getMessage(defaultCode, args, Locale.US); + return messageSource.getMessage(code, args, defaultMessage, locale); + } + + private String getWelcomeTeacherBody(String displayName, String username, boolean socialAccount, + Locale locale, HttpServletRequest request) { + String gettingStartedUrl = getGettingStartedUrl(request); + String code = socialAccount ? this.welcomeSocialAccountBodyCode : this.welcomeBodyCode; + Object[] args = socialAccount + ? new Object[] { displayName, gettingStartedUrl } + : new Object[] { displayName, username, gettingStartedUrl }; + return getEmailMessage(this.welcomeBodyCode, code, args, locale); + } + + private String getGettingStartedUrl(HttpServletRequest request) { + return ControllerUtil.getPortalUrlString(request) + "/help/getting-started"; + } + + private String getVerificationUrl(String verificationCode, HttpServletRequest request) { + return String.format("%s/api/teacher/register/verify?code=%s", + ControllerUtil.getPortalUrlString(request), verificationCode); + } + + private void sendEmail(String email, String subject, String body) { + String fromEmail = appProperties.getProperty("portalemailaddress"); + String[] recipients = { email }; + try { + this.postMail(recipients, subject, body, fromEmail); + } catch (MessagingException e) { + e.printStackTrace(); + } + } } diff --git a/src/main/java/org/wise/portal/service/user/UserService.java b/src/main/java/org/wise/portal/service/user/UserService.java index 0d1cdbcc8e..4bcd3aa627 100644 --- a/src/main/java/org/wise/portal/service/user/UserService.java +++ b/src/main/java/org/wise/portal/service/user/UserService.java @@ -155,6 +155,8 @@ List retrieveStudentsByNameAndBirthday(String firstName, String lastName, User retrieveTeacherByUsername(String username); + User retrieveTeacherByVerificationCode(String verificationCode); + List retrieveTeachersByDisplayName(String displayName); List retrieveTeachersByCity(String city); diff --git a/src/main/java/org/wise/portal/service/user/impl/UserServiceImpl.java b/src/main/java/org/wise/portal/service/user/impl/UserServiceImpl.java index 79446d49a5..d7c9a0d479 100644 --- a/src/main/java/org/wise/portal/service/user/impl/UserServiceImpl.java +++ b/src/main/java/org/wise/portal/service/user/impl/UserServiceImpl.java @@ -208,6 +208,10 @@ public User retrieveTeacherByUsername(String username) { return userDao.retrieveTeacherByUsername(username); } + public User retrieveTeacherByVerificationCode(String verificationCode) { + return userDao.retrieveTeacherByVerificationCode(verificationCode); + } + public List retrieveTeachersByDisplayName(String displayName) { return userDao.retrieveTeachersByDisplayName(displayName); } diff --git a/src/main/resources/i18n/i18n.properties b/src/main/resources/i18n/i18n.properties index 92d0bd5d2f..270f3a530a 100644 --- a/src/main/resources/i18n/i18n.properties +++ b/src/main/resources/i18n/i18n.properties @@ -3754,6 +3754,11 @@ presentation.web.controllers.teacher.project.customized.ShareProjectController.s presentation.web.controllers.teacher.project.customized.ShareProjectController.shareProjectConfirmationEmailBody={0} shared a project with you on WISE:\n\nProject Name: {1}\nProject ID: {2}\nShared with username: {3}\nDate this project was shared: {4}\n\nGo to this URL to preview this project: {5}\n\nThanks,\nWISE Team presentation.web.controllers.teacher.project.customized.ShareProjectController.shareProjectConfirmationEmailBody.description=Message body of the email notifying user that someone shared a project with them. +presentation.web.controllers.teacher.registerTeacherController.verifyTeacherEmailSubject=Verify your WISE account +presentation.web.controllers.teacher.registerTeacherController.verifyTeacherEmailSubject.description=Subject line of the email including the link that teachers must be click to verify their account. +presentation.web.controllers.teacher.registerTeacherController.verifyTeacherEmailBody=Please click the following link to verify your email address and complete the registration process:\n{0}\n\nIf you did not make this request, you can ignore this email. +presentation.web.controllers.teacher.registerTeacherController.verifyTeacherEmailBody.description=Message body of the email including the link that teachers must be click to verify their account. + presentation.web.controllers.teacher.registerTeacherController.welcomeTeacherEmailSubject=Welcome to WISE! presentation.web.controllers.teacher.registerTeacherController.welcomeTeacherEmailSubject.description=Text "Welcome to WISE!" to welcome new teachers in a welcome email. presentation.web.controllers.teacher.registerTeacherController.welcomeTeacherEmailBody=Hi {0}!\n\nYour username is: {1}\n\nWelcome to our WISE learning environment. Our research over the past many years demonstrates that students have significant learning gains when using WISE projects.\n\nFor more information on how to get started, please visit our Getting Started page.\n{2}\n\nThank you for joining WISE,\nWISE Team diff --git a/src/test/java/org/wise/portal/dao/authentication/impl/HibernateUserDetailsDaoTest.java b/src/test/java/org/wise/portal/dao/authentication/impl/HibernateUserDetailsDaoTest.java index 796f3c981e..48099afcf2 100644 --- a/src/test/java/org/wise/portal/dao/authentication/impl/HibernateUserDetailsDaoTest.java +++ b/src/test/java/org/wise/portal/dao/authentication/impl/HibernateUserDetailsDaoTest.java @@ -77,7 +77,7 @@ public void retrieveByName_WithExistingStudentName_ShouldReturnUser() { @Test public void retrieveAllTeacherUsernames_WhenThereAreTeachers_ShouldReturnTeacherUsernames() { List usernames = userDetailsDao.retrieveAllTeacherUsernames(); - assertEquals(4, usernames.size()); + assertEquals(2, usernames.size()); } @Test diff --git a/src/test/java/org/wise/portal/dao/user/impl/HibernateUserDaoTest.java b/src/test/java/org/wise/portal/dao/user/impl/HibernateUserDaoTest.java index 641be7c6f0..92115478d9 100644 --- a/src/test/java/org/wise/portal/dao/user/impl/HibernateUserDaoTest.java +++ b/src/test/java/org/wise/portal/dao/user/impl/HibernateUserDaoTest.java @@ -32,8 +32,10 @@ import org.springframework.security.core.userdetails.UserDetails; import org.wise.portal.domain.authentication.Gender; import org.wise.portal.domain.authentication.Schoollevel; +import org.wise.portal.domain.authentication.impl.TeacherUserDetails; import org.wise.portal.domain.user.User; import org.wise.portal.junit.AbstractTransactionalDbTests; +import org.wise.portal.service.authentication.DuplicateUsernameException; /** * @author Geoffrey Kwan @@ -48,17 +50,30 @@ public class HibernateUserDaoTest extends AbstractTransactionalDbTests { @BeforeEach public void setUp() throws Exception { - teacher1 = createTeacherUser("Mrs", "Puff", "MrsPuff", "Mrs. Puff", "boat", "Bikini Bottom", + teacher1 = createVerificationTeacherUser("Mrs", "Puff", "MrsPuff", "Mrs. Puff", "boat", "Bikini Bottom", "Water State", "Pacific Ocean", "mrspuff@bikinibottom.com", "Boating School", - Schoollevel.COLLEGE, "1234567890"); - teacher2 = createTeacherUser("Mr", "Krabs", "MrKrabs", "Mr. Krabs", "restaurant", + Schoollevel.COLLEGE, "1234567890", "abcd1234", true); + teacher2 = createVerificationTeacherUser("Mr", "Krabs", "MrKrabs", "Mr. Krabs", "restaurant", "Bikini Bottom", "Water State", "Pacific Ocean", "mrkrabs@bikinibottom.com", "Krusty Krab", - Schoollevel.HIGH_SCHOOL, "abcdefghij"); + Schoollevel.HIGH_SCHOOL, "abcdefghij", "1234abcd", false); student1 = createStudentUser("Spongebob", "Squarepants", "SpongebobS0101", "burger", 1, 1, Gender.MALE); student2 = createStudentUser("Patrick", "Star", "PatrickS0101", "rock", 1, 1, Gender.MALE); } + public User createVerificationTeacherUser(String firstName, String lastName, String username, + String displayName, String password, String city, String state, String country, String email, + String schoolName, Schoollevel schoolLevel, String googleUserId, String verificationCode, + boolean isVerified) + throws DuplicateUsernameException { + User teacher = createTeacherUser(firstName, lastName, username, displayName, password, city, + state, country, email, schoolName, schoolLevel, googleUserId); + TeacherUserDetails tud = (TeacherUserDetails) teacher.getUserDetails(); + tud.setVerificationCode(verificationCode); + tud.setVerified(isVerified); + return teacher; + } + @Test public void retrieveByUserDetails_ShouldReturnTheUser() { UserDetails userDetails = teacher1.getUserDetails(); @@ -121,7 +136,7 @@ public void retrieveDisabledUsers_WhenThereIsOneDisabledUser_ShouldReturnOneUser @Test public void retrieveAllTeachers_ShouldReturnAllTeachers() { List users = userDao.retrieveAllTeachers(); - assertEquals(4, users.size()); + assertEquals(2, users.size()); } @Test @@ -244,6 +259,18 @@ public void retrieveTeachersByEmail_WithExistingEmail_ShouldSucceed() { assertEquals(1, users.size()); } + @Test + public void retrieveTeacherByVerificationCode_WithNonExistingCode_ShouldNotReturnAnyUser() { + User user = userDao.retrieveTeacherByVerificationCode("wxyz6789"); + assertNull(user); + } + + @Test + public void retrieveTeacherByVerificationCode_WithExistingCode_ShouldSucceed() { + User user = userDao.retrieveTeacherByVerificationCode("abcd1234"); + assertEquals(teacher1.getId(), user.getId()); + } + @Test public void retrieveTeachersBySchoolLevel_WithNotUsedSchoolLevel_ShouldNotReturnAnyUser() { List users = userDao @@ -254,7 +281,7 @@ public void retrieveTeachersBySchoolLevel_WithNotUsedSchoolLevel_ShouldNotReturn @Test public void retrieveTeachersBySchoolLevel_WithExistingSchoolLevel_ShouldSucceed() { List users = userDao.retrieveTeachersBySchoolLevel(Schoollevel.COLLEGE.toString()); - assertEquals(3, users.size()); + assertEquals(1, users.size()); } @Test diff --git a/src/test/java/org/wise/portal/presentation/web/controllers/APIControllerTest.java b/src/test/java/org/wise/portal/presentation/web/controllers/APIControllerTest.java index 4a9c0cf5f0..0780308806 100644 --- a/src/test/java/org/wise/portal/presentation/web/controllers/APIControllerTest.java +++ b/src/test/java/org/wise/portal/presentation/web/controllers/APIControllerTest.java @@ -136,7 +136,7 @@ public void setUp() { createWorkgroups(); } - private void createStudents() { + protected void createStudents() { student1UserDetails = createStudentUserDetails(STUDENT_FIRSTNAME, STUDENT_LASTNAME, STUDENT_USERNAME, Gender.MALE, 5, STUDENT1_GOOGLE_ID, STUDENT1_ACCOUNT_ANSWER); student1 = createStudent(student1Id, student1UserDetails); @@ -147,13 +147,13 @@ private void createStudents() { studentAuth2 = createAuthentication(student2UserDetails); } - private void createTeachers() { + protected void createTeachers() { teacher1UserDetails = createTeacherUserDetails(TEACHER_FIRSTNAME, TEACHER_LASTNAME, - TEACHER_USERNAME, Schoollevel.COLLEGE, 5); + TEACHER_USERNAME, Schoollevel.COLLEGE, 5, true, "abcd1234"); teacher1 = createTeacher(teacher1Id, teacher1UserDetails); teacherAuth = createAuthentication(teacher1UserDetails); teacher2UserDetails = createTeacherUserDetails(TEACHER2_FIRSTNAME, TEACHER2_LASTNAME, - TEACHER2_USERNAME, Schoollevel.COLLEGE, 5); + TEACHER2_USERNAME, Schoollevel.COLLEGE, 5, false, "efgh5678"); teacher2 = createTeacher(teacher2Id, teacher2UserDetails); teacherAuth2 = createAuthentication(teacher2UserDetails); } @@ -262,13 +262,16 @@ protected User createTeacher(Long id, TeacherUserDetails teacherUserDetails) { } protected TeacherUserDetails createTeacherUserDetails(String firstName, String lastName, - String username, Schoollevel schoolLevel, Integer numberOfLogins) { + String username, Schoollevel schoolLevel, Integer numberOfLogins, boolean isVerified, + String verificationCode) { TeacherUserDetails teacherUserDetails = new TeacherUserDetails(); teacherUserDetails.setFirstname(firstName); teacherUserDetails.setLastname(lastName); teacherUserDetails.setUsername(username); teacherUserDetails.setSchoollevel(schoolLevel); teacherUserDetails.setNumberOfLogins(numberOfLogins); + teacherUserDetails.setVerified(isVerified); + teacherUserDetails.setVerificationCode(verificationCode); PersistentGrantedAuthority teacherAuthority = new PersistentGrantedAuthority(); teacherAuthority.setAuthority(UserDetailsService.TEACHER_ROLE); teacherUserDetails.setAuthorities(new GrantedAuthority[] { teacherAuthority }); diff --git a/src/test/java/org/wise/portal/presentation/web/controllers/teacher/TeacherAPIControllerTest.java b/src/test/java/org/wise/portal/presentation/web/controllers/teacher/TeacherAPIControllerTest.java index 76902310e4..dfb5a8b0a7 100644 --- a/src/test/java/org/wise/portal/presentation/web/controllers/teacher/TeacherAPIControllerTest.java +++ b/src/test/java/org/wise/portal/presentation/web/controllers/teacher/TeacherAPIControllerTest.java @@ -6,7 +6,9 @@ import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.ArrayList; @@ -433,4 +435,42 @@ private void expectGetRunMapToBeCalled() { expect(projectService.getLicensePath(isA(Project.class))) .andReturn("http://localhost:8080/curriculum/1/license.txt"); } + + @Test + public void isVerifiedTeacherOrNonTeacher_UserIsAStudent_ReturnTrue() { + expect(userService.retrieveTeacherByUsername(STUDENT_USERNAME)).andReturn(null); + replay(userService); + boolean isVerifiedTeacher = teacherAPIController.isVerifiedTeacherOrNonTeacher(STUDENT_USERNAME); + assertTrue(isVerifiedTeacher); + verify(userService); + } + + @Test + public void isVerifiedTeacherOrNonTeacher_UserDoesNotExist_ReturnTrue() { + expect(userService.retrieveTeacherByUsername("")).andReturn(null); + replay(userService); + boolean isVerifiedTeacher = teacherAPIController.isVerifiedTeacherOrNonTeacher(""); + assertTrue(isVerifiedTeacher); + verify(userService); + } + + @Test + public void isVerifiedTeacherOrNonTeacher_TeacherIsVerified_ReturnTrue() { + this.createTeachers(); + expect(userService.retrieveTeacherByUsername(TEACHER_USERNAME)).andReturn(teacher1); + replay(userService); + boolean isVerifiedTeacher = teacherAPIController.isVerifiedTeacherOrNonTeacher(TEACHER_USERNAME); + assertTrue(isVerifiedTeacher); + verify(userService); + } + + @Test + public void isVerifiedTeacherOrNonTeacher_TeacherIsUnverified_ReturnFalse() { + this.createTeachers(); + expect(userService.retrieveTeacherByUsername(TEACHER2_USERNAME)).andReturn(teacher2); + replay(userService); + boolean isVerifiedTeacher = teacherAPIController.isVerifiedTeacherOrNonTeacher(TEACHER2_USERNAME); + assertFalse(isVerifiedTeacher); + verify(userService); + } } diff --git a/src/test/java/org/wise/portal/presentation/web/controllers/teacher/TeacherRegistrationAPIControllerTest.java b/src/test/java/org/wise/portal/presentation/web/controllers/teacher/TeacherRegistrationAPIControllerTest.java index 87bb71b758..9e94e56af3 100644 --- a/src/test/java/org/wise/portal/presentation/web/controllers/teacher/TeacherRegistrationAPIControllerTest.java +++ b/src/test/java/org/wise/portal/presentation/web/controllers/teacher/TeacherRegistrationAPIControllerTest.java @@ -1,20 +1,25 @@ package org.wise.portal.presentation.web.controllers.teacher; import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.expectLastCall; import static org.easymock.EasyMock.isA; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; import static org.junit.jupiter.api.Assertions.assertEquals; +import java.io.IOException; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import javax.servlet.http.HttpServletResponse; + import org.easymock.EasyMockExtension; import org.easymock.Mock; import org.easymock.TestSubject; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.context.MessageSource; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.wise.portal.domain.authentication.impl.TeacherUserDetails; @@ -22,6 +27,7 @@ import org.wise.portal.presentation.web.exception.InvalidNameException; import org.wise.portal.service.authentication.DuplicateUsernameException; import org.wise.portal.service.authentication.UserDetailsService; +import org.wise.portal.service.mail.MailService; import org.wise.portal.service.password.PasswordService; import org.wise.portal.service.password.impl.PasswordServiceImpl; import org.wise.portal.service.usertags.UserTagsService; @@ -32,6 +38,15 @@ public class TeacherRegistrationAPIControllerTest extends APIControllerTest { @TestSubject private TeacherRegistrationAPIController teacherRegistrationAPIController = new TeacherRegistrationAPIController(); + @Mock + private HttpServletResponse response; + + @Mock + private MailService mailService; + + @Mock + private MessageSource messageSource; + @Mock private UserDetailsService userDetailsService; @@ -66,11 +81,16 @@ public void createTeacherAccount_WithGoogleUserId_CreateUser() replay(request); expect(userService.createUser(isA(TeacherUserDetails.class))).andReturn(teacher1); replay(userService); + mailService.sendWelcomeTeacherEmail("", TEACHER_FIRSTNAME + " " + TEACHER_LASTNAME, TEACHER_USERNAME, + true, Locale.US, request); + expectLastCall(); + replay(mailService); ResponseEntity> response = teacherRegistrationAPIController .createTeacherAccount(teacherFields, request); assertEquals(TEACHER_USERNAME, response.getBody().get("username")); verify(request); verify(userService); + verify(mailService); } private HashMap createDefaultTeacherFields() { @@ -83,4 +103,86 @@ private HashMap createDefaultTeacherFields() { fields.put("gender", "MALE"); return fields; } + + @Test + public void verifyTeacherAndRedirect_TeacherUnverified_RedirectsWithVerifiedTrue() throws IOException { + createTeachers(); + expect(userService.retrieveTeacherByVerificationCode("efgh5678")).andReturn(teacher2); + userService.updateUser(teacher2); + expectLastCall(); + replay(userService); + TeacherUserDetails tud = (TeacherUserDetails) teacher2.getUserDetails(); + mailService.sendWelcomeTeacherEmail(tud.getEmailAddress(), tud.getDisplayname(), tud.getUsername(), false, null, request); + expectLastCall(); + replay(mailService); + response.sendRedirect("/login?verified=true&username=" + tud.getUsername()); + expectLastCall(); + replay(response); + teacherRegistrationAPIController.verifyTeacherAndRedirect("efgh5678", response, request); + verify(userService); + verify(mailService); + verify(response); + } + + @Test + public void verifyTeacherAndRedirect_TeacherAlreadyVerified_RedirectsWithVerifiedFalse() throws IOException { + createTeachers(); + expect(userService.retrieveTeacherByVerificationCode("abcd1234")).andReturn(teacher1); + replay(userService); + response.sendRedirect("/login?verified=false&username=" + teacher1.getUserDetails().getUsername()); + expectLastCall(); + replay(response); + teacherRegistrationAPIController.verifyTeacherAndRedirect("abcd1234", response, request); + verify(userService); + verify(response); + } + + @Test + public void verifyTeacherAndRedirect_InvalidVerificationCode_RedirectsWithVerificationError() throws IOException { + expect(userService.retrieveTeacherByVerificationCode("")).andReturn(null); + replay(userService); + response.sendRedirect("/login?verified=error"); + expectLastCall(); + replay(response); + teacherRegistrationAPIController.verifyTeacherAndRedirect("", response, request); + verify(userService); + verify(response); + } + + @Test + public void sendVerificationEmail_UserIsUnverifiedTeacher_SendEmail() { + this.createTeachers(); + expect(userService.retrieveTeacherByUsername(TEACHER2_USERNAME)).andReturn(teacher2); + replay(userService); + mailService.sendVerifyTeacherEmail("", "efgh5678", null, request); + expectLastCall(); + replay(mailService); + ResponseEntity> response = + teacherRegistrationAPIController.sendVerificationEmail(TEACHER2_USERNAME, request); + assertEquals(TEACHER2_USERNAME, response.getBody().get("username")); + verify(userService); + verify(mailService); + + } + + @Test + public void sendVerificationEmail_UserIsVerifiedTeacher_ReturnError() { + this.createTeachers(); + expect(userService.retrieveTeacherByUsername(TEACHER_USERNAME)).andReturn(teacher1); + replay(userService); + ResponseEntity> response = + teacherRegistrationAPIController.sendVerificationEmail(TEACHER_USERNAME, request); + assertEquals("Teacher already verified", response.getBody().get("messageCode")); + verify(userService); + } + + @Test + public void sendVerificationEmail_UserIsNotTeacher_ReturnError() { + expect(userService.retrieveTeacherByUsername(STUDENT_USERNAME)).andReturn(null); + replay(userService); + ResponseEntity> response = + teacherRegistrationAPIController.sendVerificationEmail(STUDENT_USERNAME, request); + assertEquals("Not a teacher", response.getBody().get("messageCode")); + verify(userService); + } }