Commit 67266a04 authored by Matija Obreza's avatar Matija Obreza
Browse files

Email verification updates

parent 4ea27d63
......@@ -16,8 +16,11 @@
package org.genesys2.server.model.impl;
import java.util.UUID;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import org.genesys2.server.model.AuditedModel;
......@@ -28,7 +31,7 @@ import org.genesys2.server.model.AuditedModel;
@Entity
@Table(name = "verificationtoken")
public class VerificationToken extends AuditedModel {
@Column(length = 36, nullable = false)
@Column(length = 36, nullable = false, unique = true)
public String uuid;
@Column(name = "secret", length = 4, nullable = false)
......@@ -40,6 +43,12 @@ public class VerificationToken extends AuditedModel {
@Column(length = 120, nullable = true)
private String data;
@PrePersist
void generateUuid() {
if (this.uuid == null)
this.uuid = UUID.randomUUID().toString();
}
public String getUuid() {
return uuid;
}
......
......@@ -21,6 +21,6 @@ import org.springframework.data.jpa.repository.JpaRepository;
public interface VerificationTokenRepository extends JpaRepository<VerificationToken, Long> {
VerificationToken findByUuidAndKey(String tokenUuid, String key);
VerificationToken findByPurposeAndUuid(String string, String tokenUuid);
}
......@@ -24,4 +24,6 @@ public interface EMailVerificationService {
boolean validateEMail(String tokenUuid, String key);
void cancelValidation(String tokenUuid);
}
......@@ -61,7 +61,6 @@ public class EMailServiceImpl implements EMailService {
printDebugInfo(subject, text, emailFrom, emailTo);
final MimeMessagePreparator preparator = new MimeMessagePreparator() {
@SuppressWarnings("unchecked")
@Override
public void prepare(MimeMessage mimeMessage) throws Exception {
MimeMessageHelper message = new MimeMessageHelper(mimeMessage, "UTF-8");
......
......@@ -17,19 +17,22 @@
package org.genesys2.server.service.impl;
import java.text.MessageFormat;
import java.util.UUID;
import java.util.Locale;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.genesys2.server.exception.UserException;
import org.genesys2.server.model.impl.Article;
import org.genesys2.server.model.impl.User;
import org.genesys2.server.model.impl.VerificationToken;
import org.genesys2.server.persistence.domain.VerificationTokenRepository;
import org.genesys2.server.service.ContentService;
import org.genesys2.server.service.EMailService;
import org.genesys2.server.service.EMailVerificationService;
import org.genesys2.server.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
......@@ -48,38 +51,60 @@ public class EMailVerificationServiceImpl implements EMailVerificationService {
@Autowired
private UserService userService;
@Autowired
private ContentService contentService;
@Value("${base.url}")
private String baseUrl;
@Transactional
public void sendVerificationEmail(User user) {
// Generate new token
VerificationToken verificationToken = generateToken("email-validation", user.getUuid());
VerificationToken verificationToken = generateToken("email-verification", user.getUuid());
Article article = contentService.getGlobalArticle("smtp.email-verification", Locale.ENGLISH);
String mailSubject = "Genesys account verification";
String mailBody = MessageFormat.format("Click http://sandbox.genesys-pgr.org/profile/{0}/validate and enter key {1}", verificationToken.getUuid(),
verificationToken.getKey());
String mailSubject = article.getTitle();
String mailBody = MessageFormat.format(article.getBody(), baseUrl, verificationToken.getUuid(), user.getEmail(), verificationToken.getKey());
emailService.sendMail(user.getEmail(), user.getName(), mailSubject, mailBody);
}
private VerificationToken generateToken(String tokenPurpose, String dataUuid) {
private VerificationToken generateToken(String tokenPurpose, String data) {
VerificationToken token = new VerificationToken();
token.setPurpose(tokenPurpose);
// Store data
token.setData(dataUuid);
token.setUuid(UUID.nameUUIDFromBytes(dataUuid.getBytes()).toString());
token.setData(data);
token.setKey(RandomStringUtils.randomAlphanumeric(4));
verificationTokenRepository.save(token);
return token;
}
@Override
@Transactional
public void cancelValidation(String tokenUuid) {
VerificationToken verificationToken = verificationTokenRepository.findByPurposeAndUuid("email-verification", tokenUuid);
if (verificationToken == null) {
LOG.warn("Canceling verification token failed. No such verification token " + tokenUuid);
} else {
LOG.warn("Canceling verification token " + tokenUuid);
verificationTokenRepository.delete(verificationToken);
}
}
@Override
@Transactional
public boolean validateEMail(String tokenUuid, String key) {
VerificationToken verificationToken = verificationTokenRepository.findByUuidAndKey(tokenUuid, key);
VerificationToken verificationToken = verificationTokenRepository.findByPurposeAndUuid("email-verification", tokenUuid);
if (verificationToken == null) {
LOG.warn("No such verification token " + tokenUuid + " key=" + key);
return false;
}
if (!verificationToken.getKey().equals(key)) {
LOG.error("Email verification key invalid for token=" + verificationToken.getUuid() + " providedKey=" + key);
}
try {
userService.userEmailValidated(verificationToken.getData());
......
......@@ -31,6 +31,7 @@ import org.genesys2.server.model.UserRole;
import org.genesys2.server.model.impl.User;
import org.genesys2.server.model.wrapper.UserWrapper;
import org.genesys2.server.persistence.domain.UserPersistence;
import org.genesys2.server.security.AuthUserDetails;
import org.genesys2.server.service.UserService;
import org.genesys2.spring.SecurityContextUtil;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -342,18 +343,33 @@ public class UserServiceImpl implements UserService {
// Since it's a set, we can just add
userRoles.add(UserRole.VALIDATEDUSER);
addRole(UserRole.VALIDATEDUSER.getName());
addRoleToCurrentUser(user, UserRole.VALIDATEDUSER.getName());
updateUser(user);
LOG.info("Ensured VALIDATEDUSER role for user " + user);
}
private void addRole(String role) {
List<GrantedAuthority> authorities = new ArrayList<>();
private void addRoleToCurrentUser(User user, String role) {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof AuthUserDetails) {
if (((AuthUserDetails) principal).getUser().getId() != user.getId()) {
LOG.warn("Not adding role, user != principal");
return;
}
} else {
LOG.warn("Not adding role to current principal: " + principal);
return;
}
List<GrantedAuthority> authorities = new ArrayList<>(SecurityContextHolder.getContext().getAuthentication().getAuthorities());
for (GrantedAuthority authority : authorities) {
if (authority.getAuthority().equals(role))
return;
}
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(role);
authorities.add(simpleGrantedAuthority);
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Authentication authentication = new UsernamePasswordAuthenticationToken(principal, principal, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
......
......@@ -81,6 +81,7 @@ public class UserProfileController extends BaseController {
}
@RequestMapping("/{uuid:.+}")
@PreAuthorize("isAuthenticated()")
public String someProfile(ModelMap model, @PathVariable("uuid") String uuid) {
User user = userService.getUserByUuid(uuid);
if (user == null) {
......@@ -94,40 +95,42 @@ public class UserProfileController extends BaseController {
}
@RequestMapping("/{uuid:.+}/edit")
@PreAuthorize("hasRole('ADMINISTRATOR') || principal.user.email == #email")
@PreAuthorize("hasRole('ADMINISTRATOR') || principal.user.uuid == #uuid")
public String edit(ModelMap model, @PathVariable("uuid") String uuid) {
someProfile(model, uuid);
return "/user/edit";
}
@RequestMapping(value = "/{tokenUuid:.+}/validate", method = RequestMethod.GET)
public String validateEmail(ModelMap model, @PathVariable("tokenUuid") String tokenUuid) {
model.addAttribute("tokenUuid", tokenUuid);
return "/user/validateemail";
}
@RequestMapping(value = "/{uuid}/send", method = RequestMethod.GET)
@PreAuthorize("hasRole('ADMINISTRATOR') || principal.user.uuid == #uuid")
public String sendEmail(ModelMap model, @PathVariable("uuid") String uuid) {
User user = userService.getUserByUuid(uuid);
emailVerificationService.sendVerificationEmail(user);
model.addAttribute("tokenUuid", uuid);
return "redirect:/profile/" + user.getUuid();
}
@RequestMapping(value = "/{tokenUuid:.+}/cancel", method = RequestMethod.GET)
public String cancelValidation(ModelMap model, @PathVariable("tokenUuid") String tokenUuid) {
emailVerificationService.cancelValidation(tokenUuid);
return "redirect:/";
}
@RequestMapping(value = "/{tokenUuid:.+}/validate", method = RequestMethod.GET)
public String validateEmail(ModelMap model, @PathVariable("tokenUuid") String tokenUuid) {
model.addAttribute("tokenUuid", tokenUuid);
return "/user/validateemail";
}
@RequestMapping(value = "/{tokenUuid:.+}/validate", method = RequestMethod.POST)
public String validateEmail2(ModelMap model, @PathVariable("tokenUuid") String tokenUuid, @RequestParam("key") String key) {
if (emailVerificationService.validateEMail(tokenUuid, key)) {
// Valid
return "redirect:/profile";
} else {
// Not valid
model.addAttribute("tokenUuid", tokenUuid);
model.addAttribute("error", "error");
......
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2013 Global Crop Diversity Trust
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
......
......@@ -40,12 +40,12 @@
</security:authorize>
</div>
<div class="form-group">
<security:authorize access="(not hasRole('VALIDATEDUSER') && principal.user.id == #user.id)">
<a href="<c:url value="/profile/${user.uuid}/send"/>" class="btn btn-default"/>Send validation email</a>
</security:authorize>
<security:authorize access="hasRole('ADMINISTRATOR')">
<div class="form-group">
<button class="btn" id="acccount-lock">Lock</button>
<button class="btn" id="acccount-unlock">Unlock</button>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment