diff --git a/src/main/java/com/erudika/scoold/controllers/ProfileController.java b/src/main/java/com/erudika/scoold/controllers/ProfileController.java index 6ff54859..c24e1a08 100755 --- a/src/main/java/com/erudika/scoold/controllers/ProfileController.java +++ b/src/main/java/com/erudika/scoold/controllers/ProfileController.java @@ -185,6 +185,7 @@ public String edit(@PathVariable(required = false) String id, @RequestParam(requ HttpServletRequest req, Model model) { Profile authUser = utils.getAuthUser(req); Profile showUser = getProfileForEditing(id, authUser); + String queryString = ""; if (showUser != null) { boolean updateProfile = false; if (!StringUtils.equals(showUser.getLocation(), location)) { @@ -206,6 +207,7 @@ public String edit(@PathVariable(required = false) String id, @RequestParam(requ changeEmail(showUser.getUser(), showUser, email); } else { updateProfile = updateProfile || sendConfirmationEmail(showUser.getUser(), showUser, email, req); + queryString = updateProfile ? "?code=signin.verify.start&success=true" : "?code=9&error=true"; } } @@ -216,7 +218,7 @@ public String edit(@PathVariable(required = false) String id, @RequestParam(requ } model.addAttribute("user", showUser); } - return "redirect:" + PROFILELINK + (isMyid(authUser, id) ? "" : "/" + id); + return "redirect:" + PROFILELINK + (isMyid(authUser, id) ? "" : "/" + id) + queryString; } @SuppressWarnings("unchecked") @@ -322,18 +324,28 @@ public ResponseEntity toggleBadge(@PathVariable String id, @PathVariable Stri @GetMapping(path = "/confirm-email") public String confirmEmail(@RequestParam(name = "id", required = false) String id, @RequestParam(name = "token", required = false) String token, + @RequestParam(name = "token2", required = false) String token2, HttpServletRequest req, Model model) { Profile authUser = utils.getAuthUser(req); if (authUser == null || !CONF.passwordAuthEnabled()) { return "redirect:" + SIGNINLINK + "?returnto=" + PROFILELINK; } - if (id != null && token != null) { + if (id != null && (!StringUtils.isBlank(token) || !StringUtils.isBlank(token2))) { User u = (User) pc.read(id); Sysprop s = pc.read(u.getIdentifier()); - if (s != null && token.equals(s.getProperty(Config._EMAIL_TOKEN))) { + if (s != null && StringUtils.equals(token, (String) s.getProperty(Config._EMAIL_TOKEN))) { s.addProperty(Config._EMAIL_TOKEN, ""); pc.update(s); - changeEmail(u, authUser, authUser.getPendingEmail()); + if (StringUtils.isBlank((String) s.getProperty(Config._EMAIL_TOKEN + "2"))) { + return changeEmail(u, authUser, authUser.getPendingEmail()); + } + return "redirect:" + PROFILELINK + "?code=signin.verify.done&success=true"; + } else if (s != null && StringUtils.equals(token2, (String) s.getProperty(Config._EMAIL_TOKEN + "2"))) { + s.addProperty(Config._EMAIL_TOKEN + "2", ""); + pc.update(s); + if (StringUtils.isBlank((String) s.getProperty(Config._EMAIL_TOKEN))) { + return changeEmail(u, authUser, authUser.getPendingEmail()); + } return "redirect:" + PROFILELINK + "?code=signin.verify.done&success=true"; } else { return "redirect:" + SIGNINLINK; @@ -354,7 +366,7 @@ public String retryChangeEmail(HttpServletRequest req, Model model) { return "redirect:" + PROFILELINK + "?code=7&error=true"; } } - return "redirect:" + PROFILELINK + "?code=signin.verify.text&success=true"; + return "redirect:" + PROFILELINK + "?code=signin.verify.start&success=true"; } @PostMapping(path = "/cancel-change-email") @@ -381,7 +393,7 @@ public String toggleEditorRole(HttpServletRequest req, Model model) { return "redirect:" + HttpUtils.getBackToUrl(req, true); } - private void changeEmail(User u, Profile showUser, String email) { + private String changeEmail(User u, Profile showUser, String email) { boolean approvedDomain = utils.isEmailDomainApproved(email); if (approvedDomain && canChangeEmail(u, email)) { Sysprop s = pc.read(u.getEmail()); @@ -392,27 +404,31 @@ private void changeEmail(User u, Profile showUser, String email) { u.setEmail(email); showUser.setPendingEmail(""); pc.updateAll(List.of(u, showUser)); + return "redirect:" + PROFILELINK + "?code=signin.verify.changed&success=true"; } else { logger.info("Failed to change email for user {} - email {} has already been taken.", u.getId(), email); + return "redirect:" + PROFILELINK + "?code=1&error=true"; } } + return "redirect:" + PROFILELINK + "?code=9&error=true"; } private boolean sendConfirmationEmail(User user, Profile showUser, String email, HttpServletRequest req) { - if (pc.read(email) == null) { - showUser.setPendingEmail(email); + if (pc.read(email) == null && pc.findTerms(Utils.type(User.class), Map.of(Config._EMAIL, email), true).isEmpty()) { Sysprop ident = pc.read(user.getEmail()); if (ident != null) { if (!ident.hasProperty("confirmationTimestamp") || Utils.timestamp() > ((long) ident.getProperty("confirmationTimestamp") + TimeUnit.HOURS.toMillis(6))) { - utils.sendVerificationEmail(ident, PROFILELINK + "/confirm-email", req); + showUser.setPendingEmail(email); + utils.sendVerificationEmail(ident, email, PROFILELINK + "/confirm-email", req); return true; } else { logger.warn("Failed to send email confirmation to '{}' - this can only be done once every 6h.", email); } } + } else { + logger.info("Failed to send confirmation email to user {} - email {} has already been taken.", user.getId(), email); } - logger.info("Failed to send confirmation email to user {} - email {} has already been taken.", user.getId(), email); return false; } diff --git a/src/main/java/com/erudika/scoold/controllers/SigninController.java b/src/main/java/com/erudika/scoold/controllers/SigninController.java index fea66fa4..f9f6bc9b 100755 --- a/src/main/java/com/erudika/scoold/controllers/SigninController.java +++ b/src/main/java/com/erudika/scoold/controllers/SigninController.java @@ -199,7 +199,7 @@ public String resend(@RequestParam String email, HttpServletRequest req, HttpSer ((long) ident.getProperty("confirmationTimestamp") + TimeUnit.HOURS.toMillis(6))) { User u = pc.read(Utils.type(User.class), ident.getCreatorid()); if (u != null && !u.getActive()) { - utils.sendVerificationEmail(ident, "", req); + utils.sendVerificationEmail(ident, "", "", req); } } else { logger.warn("Failed to send email confirmation to '{}' - this can only be done once every 6h.", email); diff --git a/src/main/java/com/erudika/scoold/utils/ScooldUtils.java b/src/main/java/com/erudika/scoold/utils/ScooldUtils.java index 6f1d45a0..1ac727d1 100755 --- a/src/main/java/com/erudika/scoold/utils/ScooldUtils.java +++ b/src/main/java/com/erudika/scoold/utils/ScooldUtils.java @@ -435,27 +435,40 @@ public void sendWelcomeEmail(User user, boolean verifyEmail, HttpServletRequest } } - public void sendVerificationEmail(Sysprop identifier, String redirectUrl, HttpServletRequest req) { + public void sendVerificationEmail(Sysprop identifier, String newEmail, String redirectUrl, HttpServletRequest req) { if (identifier != null) { Map model = new HashMap(); Map lang = getLang(req); - String subject = CONF.appName() + " - " + lang.get("msgcode.6"); + String subject = CONF.appName() + " - " + (StringUtils.isBlank(newEmail) ? lang.get("msgcode.6") : + lang.get("signin.verify.change") + newEmail); String body = getDefaultEmailSignature(CONF.emailsWelcomeText3(lang)); redirectUrl = StringUtils.isBlank(redirectUrl) ? SIGNINLINK + "/register" : redirectUrl; - String token = Utils.base64encURL(Utils.generateSecurityToken().getBytes()); - identifier.addProperty(Config._EMAIL_TOKEN, token); + String token1 = Utils.base64encURL(Utils.generateSecurityToken().getBytes()); + String token2 = Utils.base64encURL(Utils.generateSecurityToken().getBytes()); + identifier.addProperty(Config._EMAIL_TOKEN, token1); + identifier.addProperty(Config._EMAIL_TOKEN + "2", token2); identifier.addProperty("confirmationTimestamp", Utils.timestamp()); pc.update(identifier); - token = CONF.serverUrl() + CONF.serverContextPath() + redirectUrl + "?id=" + - identifier.getCreatorid() + "&token=" + token; - body = "" + lang.get("signin.welcome.verify") + "

" + body; + token1 = CONF.serverUrl() + CONF.serverContextPath() + redirectUrl + "?id=" + + identifier.getCreatorid() + "&token=" + token1; + token2 = CONF.serverUrl() + CONF.serverContextPath() + redirectUrl + "?id=" + + identifier.getCreatorid() + "&token2=" + token2; + + body = "" + (StringUtils.isBlank(newEmail) ? lang.get("signin.welcome.verify") : + lang.get("signin.verify.change")) + " " + newEmail + "

" + body; model.put("subject", escapeHtml(subject)); model.put("logourl", getSmallLogoUrl()); model.put("heading", lang.get("hello")); - model.put("body", body); + model.put("body", Utils.formatMessage(body, token1)); + emailer.sendEmail(Arrays.asList(identifier.getId()), subject, compileEmailTemplate(model)); + + if (!StringUtils.isBlank(newEmail)) { + model.put("body", Utils.formatMessage(body, token2)); + emailer.sendEmail(Arrays.asList(newEmail), subject, compileEmailTemplate(model)); + } } } diff --git a/src/main/resources/lang_en.properties b/src/main/resources/lang_en.properties index 24120a83..102feb0e 100644 --- a/src/main/resources/lang_en.properties +++ b/src/main/resources/lang_en.properties @@ -242,6 +242,9 @@ signin.welcome.title = Hello {0}, signin.welcome.body1 = You are now part of {0} - a friendly Q&A community where you can get answers to your questions. signin.welcome.body2 = To get started, simply navigate to the "Ask question" page and ask a question. You can also help people by answering their questions and you will get reputation points. signin.welcome.verify = Verify your email here! +signin.verify.start = Follow both confirmation links that were sent to your old and new email addresses. +signin.verify.change = Confirm email change to: +signin.verify.changed = Your email address was successfully changed. signin.verify.text = Done! Check your email and follow the link to verify it. signin.verify.done = Email verified! signin.facebook = Continue with Facebook