/**
* =============================================================================
*
* ORCID (R) Open Source
* http://orcid.org
*
* Copyright (c) 2012-2014 ORCID, Inc.
* Licensed under an MIT-Style License (MIT)
* http://orcid.org/open-source-license
*
* This copyright and license information (including a link to the full license)
* shall be included in its entirety in all copies or substantial portion of
* the software.
*
* =============================================================================
*/
package org.orcid.frontend.web.controllers;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.binary.Base64;
import org.jasypt.exceptions.EncryptionOperationNotPossibleException;
import org.orcid.core.exception.OrcidBadRequestException;
import org.orcid.core.manager.EncryptionManager;
import org.orcid.core.manager.NotificationManager;
import org.orcid.core.manager.OrcidProfileCacheManager;
import org.orcid.core.manager.ProfileEntityCacheManager;
import org.orcid.core.manager.ProfileEntityManager;
import org.orcid.jaxb.model.notification.amended_v2.AmendedSection;
import org.orcid.persistence.jpa.entities.ProfileEntity;
import org.orcid.pojo.EmailRequest;
import org.orcid.pojo.ajaxForm.Claim;
import org.orcid.pojo.ajaxForm.PojoUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.exceptions.InvalidRequestException;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.support.RequestContextUtils;
@Controller
public class ClaimController extends BaseController {
private static final Logger LOGGER = LoggerFactory.getLogger(ClaimController.class);
@Resource
private EncryptionManager encryptionManager;
@Resource
private ProfileEntityCacheManager profileEntityCacheManager;
@Resource
private NotificationManager notificationManager;
@Resource
private AuthenticationManager authenticationManager;
@Resource
private ProfileEntityManager profileEntityManager;
@Resource
private OrcidProfileCacheManager orcidProfileCacheManager;
@Resource
private TransactionTemplate transactionTemplate;
@RequestMapping(value = "/claimPasswordConfirmValidate.json", method = RequestMethod.POST)
public @ResponseBody Claim claimPasswordConfirmValidate(@RequestBody Claim claim) {
passwordConfirmValidate(claim.getPasswordConfirm(), claim.getPassword());
return claim;
}
@RequestMapping(value = "/claimPasswordValidate.json", method = RequestMethod.POST)
public @ResponseBody Claim claimPasswordValidate(@RequestBody Claim claim) {
passwordValidate(claim.getPasswordConfirm(), claim.getPassword());
return claim;
}
@RequestMapping(value = "/claimTermsOfUseValidate.json", method = RequestMethod.POST)
public @ResponseBody Claim claimTermsOfUseValidate(@RequestBody Claim claim) {
termsOfUserValidate(claim.getTermsOfUse());
return claim;
}
@RequestMapping(value = "/claim/{encryptedEmail}.json", method = RequestMethod.GET)
public @ResponseBody Claim verifyClaimJson(HttpServletRequest request, @PathVariable("encryptedEmail") String encryptedEmail, RedirectAttributes redirectAttributes)
throws NoSuchRequestHandlingMethodException, UnsupportedEncodingException {
Claim c = new Claim();
c.getSendChangeNotifications().setValue(true);
c.getSendOrcidNews().setValue(true);
c.getTermsOfUse().setValue(false);
claimTermsOfUseValidate(c);
return c;
}
@RequestMapping(value = "/claim/{encryptedEmail}", method = RequestMethod.GET)
public ModelAndView verifyClaim(HttpServletRequest request, @PathVariable("encryptedEmail") String encryptedEmail, RedirectAttributes redirectAttributes)
throws NoSuchRequestHandlingMethodException, UnsupportedEncodingException {
try {
String decryptedEmail = encryptionManager.decryptForExternalUse(new String(Base64.decodeBase64(encryptedEmail), "UTF-8"));
if (!isEmailOkForCurrentUser(decryptedEmail)) {
return new ModelAndView("wrong_user");
}
if (profileEntityManager.isProfileClaimedByEmail(decryptedEmail)) {
return new ModelAndView("redirect:/signin?alreadyClaimed");
}
ModelAndView result = new ModelAndView("claim");
result.addObject("noIndex", true);
return result;
} catch (EncryptionOperationNotPossibleException e) {
LOGGER.warn("Error decypting claim email from the claim profile link");
return new ModelAndView("redirect:/signin?invalidClaimUrl");
}
}
@RequestMapping(value = "/claim/{encryptedEmail}.json", method = RequestMethod.POST)
public @ResponseBody Claim submitClaimJson(HttpServletRequest request, HttpServletResponse response, @PathVariable("encryptedEmail") String encryptedEmail,
@RequestBody Claim claim) throws NoSuchRequestHandlingMethodException, UnsupportedEncodingException {
claim.setErrors(new ArrayList<String>());
String decryptedEmail = encryptionManager.decryptForExternalUse(new String(Base64.decodeBase64(encryptedEmail), "UTF-8")).trim();
if (!isEmailOkForCurrentUser(decryptedEmail)) {
claim.setUrl(getBaseUri() + "/claim/wrong_user");
return claim;
}
String orcid = emailManager.findOrcidIdByEmail(decryptedEmail);
if (PojoUtil.isEmpty(orcid)) {
throw new OrcidBadRequestException("Unable to find an ORCID ID for the given email: " + decryptedEmail);
}
ProfileEntity profile = profileEntityCacheManager.retrieve(orcid);
if (profile != null && profile.getClaimed() != null && profile.getClaimed()) {
// Already claimed so send to sign in page
claim.setUrl(getBaseUri() + "/signin?alreadyClaimed");
return claim;
}
claimPasswordValidate(claim);
claimPasswordConfirmValidate(claim);
claimTermsOfUseValidate(claim);
copyErrors(claim.getPassword(), claim);
copyErrors(claim.getPasswordConfirm(), claim);
copyErrors(claim.getTermsOfUse(), claim);
if (claim.getErrors().size() > 0) {
return claim;
}
// Do it in a transaction
try {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(TransactionStatus status) {
Locale requestLocale = RequestContextUtils.getLocale(request);
org.orcid.jaxb.model.common_v2.Locale userLocale = (requestLocale == null) ? null
: org.orcid.jaxb.model.common_v2.Locale.fromValue(requestLocale.toString());
boolean claimed = profileEntityManager.claimProfileAndUpdatePreferences(orcid, decryptedEmail, userLocale, claim);
if (!claimed) {
throw new IllegalStateException("Unable to claim record " + orcid);
}
// Update the password
profileEntityManager.updatePassword(orcid, claim.getPassword().getValue());
// Notify
notificationManager.sendAmendEmail(orcid, AmendedSection.UNKNOWN, null);
}
});
} catch (Exception e) {
throw new InvalidRequestException("Unable to claim record due: " + e.getMessage(), e.getCause());
}
automaticallyLogin(request, claim.getPassword().getValue(), orcid);
// detech this situation
String targetUrl = orcidUrlManager.determineFullTargetUrlFromSavedRequest(request, response);
if (targetUrl == null)
claim.setUrl(getBaseUri() + "/my-orcid?recordClaimed");
else
claim.setUrl(targetUrl);
return claim;
}
@RequestMapping(value = "/claim/wrong_user", method = RequestMethod.GET)
public ModelAndView claimWrongUser(HttpServletRequest request) {
return new ModelAndView("wrong_user");
}
@RequestMapping(value = "/resend-claim", method = RequestMethod.GET)
public ModelAndView viewResendClaimEmail(@RequestParam(value = "email", required = false) String email) {
ModelAndView mav = new ModelAndView("resend_claim");
return mav;
}
@RequestMapping(value = "/resend-claim.json", method = RequestMethod.GET)
public @ResponseBody EmailRequest getResendClaimRequest() {
return new EmailRequest();
}
@RequestMapping(value = "/validate-resend-claim.json", method = RequestMethod.POST)
public @ResponseBody EmailRequest validateResendClaimRequest(@RequestBody EmailRequest resendClaimRequest) {
List<String> errors = new ArrayList<>();
resendClaimRequest.setErrors(errors);
if (!validateEmailAddress(resendClaimRequest.getEmail())) {
errors.add(getMessage("Email.resendClaim.invalidEmail"));
}
return resendClaimRequest;
}
@RequestMapping(value = "/resend-claim.json", method = RequestMethod.POST)
public @ResponseBody EmailRequest resendClaimEmail(@RequestBody EmailRequest resendClaimRequest) {
String email = resendClaimRequest.getEmail();
List<String> errors = new ArrayList<>();
resendClaimRequest.setErrors(errors);
if (!validateEmailAddress(email)) {
errors.add(getMessage("Email.resetPasswordForm.invalidEmail"));
return resendClaimRequest;
}
if (!emailManager.emailExists(email)) {
errors.add(getMessage("orcid.frontend.reset.password.email_not_found", email));
return resendClaimRequest;
}
String orcid = emailManager.findOrcidIdByEmail(email);
ProfileEntity profile = profileEntityCacheManager.retrieve(orcid);
if (profile != null && profile.getClaimed()) {
errors.add(getMessage("orcid.frontend.security.already_claimed_with_link"));
return resendClaimRequest;
}
notificationManager.sendApiRecordCreationEmail(email, orcid);
resendClaimRequest.setSuccessMessage(getMessage("resend_claim.successful_resend"));
return resendClaimRequest;
}
private void automaticallyLogin(HttpServletRequest request, String password, String orcid) {
UsernamePasswordAuthenticationToken token = null;
try {
token = new UsernamePasswordAuthenticationToken(orcid, password);
token.setDetails(new WebAuthenticationDetails(request));
Authentication authentication = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (AuthenticationException e) {
// this should never happen
SecurityContextHolder.getContext().setAuthentication(null);
LOGGER.warn("User {0} should have been logged-in, but we unable to due to a problem", e, (token != null ? token.getPrincipal() : "empty principle"));
}
}
}