/** * ============================================================================= * * 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.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang.StringUtils; import org.orcid.core.constants.EmailConstants; import org.orcid.core.manager.AddressManager; import org.orcid.core.manager.AdminManager; import org.orcid.core.manager.BiographyManager; import org.orcid.core.manager.EmailManager; import org.orcid.core.manager.EncryptionManager; import org.orcid.core.manager.GivenPermissionToManager; import org.orcid.core.manager.NotificationManager; import org.orcid.core.manager.OrcidSocialManager; import org.orcid.core.manager.PreferenceManager; import org.orcid.core.manager.ProfileEntityCacheManager; import org.orcid.core.manager.ProfileEntityManager; import org.orcid.core.manager.RecordNameManager; import org.orcid.core.manager.RegistrationManager; import org.orcid.core.manager.UserConnectionManager; import org.orcid.core.utils.JsonUtils; import org.orcid.core.utils.RecordNameUtils; import org.orcid.jaxb.model.message.SendEmailFrequency; import org.orcid.jaxb.model.record_v2.Addresses; import org.orcid.jaxb.model.record_v2.Biography; import org.orcid.jaxb.model.record_v2.Emails; import org.orcid.jaxb.model.record_v2.Name; import org.orcid.password.constants.OrcidPasswordConstants; import org.orcid.persistence.jpa.entities.EmailEntity; import org.orcid.persistence.jpa.entities.GivenPermissionToEntity; import org.orcid.persistence.jpa.entities.ProfileEntity; import org.orcid.persistence.jpa.entities.UserconnectionEntity; import org.orcid.pojo.ApplicationSummary; import org.orcid.pojo.ChangePassword; import org.orcid.pojo.DelegateForm; import org.orcid.pojo.DeprecateProfile; import org.orcid.pojo.ManageDelegate; import org.orcid.pojo.ManageSocialAccount; import org.orcid.pojo.SecurityQuestion; import org.orcid.pojo.ajaxForm.AddressForm; import org.orcid.pojo.ajaxForm.AddressesForm; import org.orcid.pojo.ajaxForm.BiographyForm; import org.orcid.pojo.ajaxForm.Errors; import org.orcid.pojo.ajaxForm.NamesForm; import org.orcid.pojo.ajaxForm.PojoUtil; import org.orcid.pojo.ajaxForm.Text; import org.orcid.pojo.ajaxForm.Visibility; import org.orcid.utils.DateUtils; import org.orcid.utils.NullUtils; import org.orcid.utils.OrcidStringUtils; import org.springframework.stereotype.Controller; import org.springframework.validation.MapBindingResult; import org.springframework.validation.ObjectError; import org.springframework.web.bind.annotation.ModelAttribute; 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; /** * @author Declan Newman (declan) Date: 22/02/2012 */ @Controller("manageProfileController") @RequestMapping(value = { "/account", "/manage" }) public class ManageProfileController extends BaseWorkspaceController { private static final String IS_SELF = "isSelf"; private static final String FOUND = "found"; @Resource private EncryptionManager encryptionManager; @Resource private NotificationManager notificationManager; @Resource private ProfileEntityManager profileEntityManager; @Resource private GivenPermissionToManager givenPermissionToManager; @Resource private EmailManager emailManager; @Resource private UserConnectionManager userConnectionManager; @Resource private OrcidSocialManager orcidSocialManager; @Resource private ProfileEntityCacheManager profileEntityCacheManager; @Resource private AddressManager addressManager; @Resource private BiographyManager biographyManager; @Resource private RegistrationManager registrationManager; @Resource private RecordNameManager recordNameManager; @Resource private PreferenceManager preferenceManager; @RequestMapping public ModelAndView manageProfile() { return new ModelAndView("manage"); } @RequestMapping(value = "/search-for-delegate-by-email/{email}/") public @ResponseBody Map<String, Boolean> searchForDelegateByEmail(@PathVariable String email) { Map<String, Boolean> map = new HashMap<>(); EmailEntity emailEntity = emailManager.findCaseInsensitive(email); if (emailEntity == null) { map.put(FOUND, Boolean.FALSE); return map; } else { map.put(FOUND, Boolean.TRUE); map.put(IS_SELF, emailEntity.getProfile().getId().equals(getCurrentUserOrcid())); return map; } } @RequestMapping(value = "/delegates.json", method = RequestMethod.GET) public @ResponseBody List<DelegateForm> getDelegates() { String currentOrcid = getCurrentUserOrcid(); ProfileEntity currentProfile = profileEntityCacheManager.retrieve(currentOrcid); List<DelegateForm> list = new ArrayList<DelegateForm>(); if(currentProfile.getGivenPermissionTo() != null && !currentProfile.getGivenPermissionTo().isEmpty()) { for(GivenPermissionToEntity entity : currentProfile.getGivenPermissionTo()) { DelegateForm form = new DelegateForm(); form.setGiverOrcid(Text.valueOf(currentOrcid)); form.setReceiverOrcid(Text.valueOf(entity.getReceiver().getId())); form.setReceiverName(Text.valueOf(entity.getReceiver().getDisplayName())); form.setApprovalDate(DateUtils.convertToXMLGregorianCalendar(entity.getApprovalDate())); list.add(form); } } return list; } @RequestMapping(value = "/addDelegate.json") public @ResponseBody ManageDelegate addDelegate(@RequestBody ManageDelegate addDelegate) { // Check password String password = addDelegate.getPassword(); ProfileEntity profile = profileEntityCacheManager.retrieve(getCurrentUserOrcid()); if (orcidSecurityManager.isPasswordConfirmationRequired() && (StringUtils.isBlank(password) || !encryptionManager.hashMatches(password, profile.getEncryptedPassword()))) { addDelegate.getErrors().add(getMessage("check_password_modal.incorrect_password")); return addDelegate; } givenPermissionToManager.create(getCurrentUserOrcid(), addDelegate.getDelegateToManage()); return addDelegate; } @RequestMapping(value = "/addDelegateByEmail.json") public @ResponseBody ManageDelegate addDelegateByEmail(@RequestBody ManageDelegate addDelegate) { EmailEntity emailEntity = emailManager.findCaseInsensitive(addDelegate.getDelegateEmail()); addDelegate.setDelegateToManage(emailEntity.getProfile().getId()); return addDelegate(addDelegate); } @RequestMapping(value = "/revokeDelegate.json", method = RequestMethod.POST) public @ResponseBody ManageDelegate revokeDelegate(@RequestBody ManageDelegate manageDelegate) { // Check password String password = manageDelegate.getPassword(); ProfileEntity profile = profileEntityCacheManager.retrieve(getCurrentUserOrcid()); if (orcidSecurityManager.isPasswordConfirmationRequired() && (StringUtils.isBlank(password) || !encryptionManager.hashMatches(password, profile.getEncryptedPassword()))) { manageDelegate.getErrors().add(getMessage("check_password_modal.incorrect_password")); return manageDelegate; } givenPermissionToManager.remove(getCurrentUserOrcid(), manageDelegate.getDelegateToManage()); return manageDelegate; } @RequestMapping(value = "/socialAccounts.json", method = RequestMethod.GET) public @ResponseBody List<UserconnectionEntity> getSocialAccountsJson(HttpServletRequest request) throws NoSuchRequestHandlingMethodException { String orcid = getCurrentUserOrcid(); List<UserconnectionEntity> userConnectionEntities = userConnectionManager.findByOrcid(orcid); return userConnectionEntities; } @RequestMapping(value = "/revokeSocialAccount.json", method = RequestMethod.POST) public @ResponseBody ManageSocialAccount revokeSocialAccount(@RequestBody ManageSocialAccount manageSocialAccount) { // Check password String password = manageSocialAccount.getPassword(); ProfileEntity profile = profileEntityCacheManager.retrieve(getCurrentUserOrcid()); if (orcidSecurityManager.isPasswordConfirmationRequired() && (StringUtils.isBlank(password) || !encryptionManager.hashMatches(password, profile.getEncryptedPassword()))) { manageSocialAccount.getErrors().add(getMessage("check_password_modal.incorrect_password")); return manageSocialAccount; } userConnectionManager.remove(getEffectiveUserOrcid(), manageSocialAccount.getIdToManage()); return manageSocialAccount; } @RequestMapping(value = "/revoke-application", method = RequestMethod.POST) public @ResponseBody boolean revokeApplication(@RequestParam("tokenId") String tokenId) { String userOrcid = getCurrentUserOrcid(); profileEntityManager.disableApplication(Long.valueOf(tokenId), userOrcid); return true; } @RequestMapping(value = "/admin-switch-user", method = RequestMethod.GET) public ModelAndView adminSwitchUser(HttpServletRequest request, @RequestParam("orcid") String targetOrcid, RedirectAttributes redirectAttributes) { // Redirect to the new way of switching user, which includes admin // access ModelAndView mav = null; if (StringUtils.isNotBlank(targetOrcid)) targetOrcid = targetOrcid.trim(); if (profileEntityManager.orcidExists(targetOrcid)) { mav = new ModelAndView("redirect:/switch-user?username=" + targetOrcid); } else { redirectAttributes.addFlashAttribute("invalidOrcid", true); mav = new ModelAndView("redirect:/my-orcid"); } return mav; } @ModelAttribute("securityQuestions") public Map<String, String> getSecurityQuestions() { Map<String, String> securityQuestions = securityQuestionManager.retrieveSecurityQuestionsAsInternationalizedMap(); Map<String, String> securityQuestionsWithMessages = new LinkedHashMap<String, String>(); for (String key : securityQuestions.keySet()) { securityQuestionsWithMessages.put(key, getMessage(securityQuestions.get(key))); } return securityQuestionsWithMessages; } @RequestMapping(value = "/security-question.json", method = RequestMethod.GET) public @ResponseBody SecurityQuestion getSecurityQuestion() { ProfileEntity profile = profileEntityCacheManager.retrieve(getCurrentUserOrcid()); SecurityQuestion securityQuestion = new SecurityQuestion(); if(profile.getSecurityQuestion() != null) { Long id = Long.valueOf(profile.getSecurityQuestion().getId()); String encryptedAnswer = profile.getEncryptedSecurityAnswer(); if(!PojoUtil.isEmpty(encryptedAnswer)) { securityQuestion.setSecurityAnswer(encryptionManager.decryptForInternalUse(encryptedAnswer)); } securityQuestion.setSecurityQuestionId(id); } return securityQuestion; } @RequestMapping(value = "/security-question.json", method = RequestMethod.POST) public @ResponseBody SecurityQuestion setSecurityQuestion(@RequestBody SecurityQuestion securityQuestion) { List<String> errors = new ArrayList<String>(); ProfileEntity profile = profileEntityCacheManager.retrieve(getCurrentUserOrcid()); if (securityQuestion.getSecurityQuestionId() != 0 && (securityQuestion.getSecurityAnswer() == null || securityQuestion.getSecurityAnswer().trim() == "")) errors.add(getMessage("manage.pleaseProvideAnAnswer")); if (orcidSecurityManager.isPasswordConfirmationRequired() && (securityQuestion.getPassword() == null || !encryptionManager.hashMatches(securityQuestion.getPassword(), profile.getEncryptedPassword()))) { errors.add(getMessage("check_password_modal.incorrect_password")); } // If the security question is empty, clean the security answer field if (securityQuestion.getSecurityQuestionId() == 0) securityQuestion.setSecurityAnswer(new String()); if (errors.size() == 0) { Integer id = Long.valueOf(securityQuestion.getSecurityQuestionId()).intValue(); profileEntityManager.updateSecurityQuestion(getCurrentUserOrcid(), id, securityQuestion.getSecurityAnswer()); errors.add(getMessage("manage.securityQuestionUpdated")); } securityQuestion.setErrors(errors); return securityQuestion; } @RequestMapping(value = "/preferences.json", method = RequestMethod.GET) public @ResponseBody Map<String, Object> getDefaultPreference(HttpServletRequest request) { Map<String, Object> preferences = new HashMap<String, Object>(); ProfileEntity entity = profileEntityCacheManager.retrieve(getCurrentUserOrcid()); preferences.put("email_frequency", String.valueOf(entity.getSendEmailFrequencyDays())); preferences.put("default_visibility", entity.getActivitiesVisibilityDefault()); preferences.put("developer_tools_enabled", entity.getEnableDeveloperTools()); preferences.put("notifications_enabled", entity.getEnableNotifications() == null ? false : entity.getEnableNotifications()); preferences.put("send_administrative_change_notifications", entity.getSendAdministrativeChangeNotifications() == null ? false : entity.getSendAdministrativeChangeNotifications()); preferences.put("send_change_notifications", entity.getSendChangeNotifications() == null ? false : entity.getSendChangeNotifications()); preferences.put("send_member_update_requests", entity.getSendMemberUpdateRequests() == null ? false : entity.getSendMemberUpdateRequests()); preferences.put("send_orcid_news", entity.getSendOrcidNews() == null ? false : entity.getSendOrcidNews()); return preferences; } @RequestMapping(value = "/email_preferences.json", method = RequestMethod.POST) public @ResponseBody String setEmailFrequency(@RequestBody String emailFrequencyDays) throws IllegalArgumentException { SendEmailFrequency newFrequency = null; for(SendEmailFrequency f : SendEmailFrequency.values()) { if(f.value().equals(emailFrequencyDays)) { newFrequency = f; break; } } if(newFrequency == null) { throw new IllegalArgumentException("Invalid value: " + emailFrequencyDays); } preferenceManager.updateEmailFrequencyDays(getCurrentUserOrcid(), newFrequency); return newFrequency.value(); } @RequestMapping(value = "/notification_preferences.json", method = RequestMethod.POST) public @ResponseBody Map<String, Object> setEmailFrequency(@RequestBody Map<String, Object> preferences) throws IllegalArgumentException { Boolean sendAdministrativeChangeNotifications = (Boolean) preferences.get("send_administrative_change_notifications"); Boolean sendChangeNotifications = (Boolean) preferences.get("send_change_notifications"); Boolean sendMemberUpdateRequests = (Boolean) preferences.get("send_member_update_requests"); Boolean sendOrcidNews = (Boolean) preferences.get("send_orcid_news"); if(NullUtils.anyNull(sendAdministrativeChangeNotifications, sendChangeNotifications, sendMemberUpdateRequests, sendOrcidNews)) { throw new IllegalArgumentException("Please provide all notification preferences: " + preferences.toString()); } preferenceManager.updateNotifications(getCurrentUserOrcid(), sendChangeNotifications, sendAdministrativeChangeNotifications, sendOrcidNews, sendMemberUpdateRequests); return preferences; } @RequestMapping(value = "/default_visibility.json", method = RequestMethod.POST) public @ResponseBody String setDefaultVisibility(@RequestBody String defaultVisibility) throws IllegalArgumentException { try { org.orcid.jaxb.model.common_v2.Visibility visibility = org.orcid.jaxb.model.common_v2.Visibility.fromValue(defaultVisibility); if(org.orcid.jaxb.model.common_v2.Visibility.REGISTERED_ONLY.equals(visibility) || org.orcid.jaxb.model.common_v2.Visibility.SYSTEM.equals(visibility)) { throw new IllegalArgumentException(); } preferenceManager.updateDefaultVisibility(getCurrentUserOrcid(), visibility); } catch(IllegalArgumentException e) { throw new IllegalArgumentException("Invalid visibility provided: " + defaultVisibility); } return defaultVisibility; } @RequestMapping(value = { "/change-password.json" }, method = RequestMethod.GET) public @ResponseBody ChangePassword getChangedPasswordJson(HttpServletRequest request) { return new ChangePassword(); } @RequestMapping(value = { "/change-password.json" }, method = RequestMethod.POST) public @ResponseBody ChangePassword changedPasswordJson(HttpServletRequest request, @RequestBody ChangePassword cp) { List<String> errors = new ArrayList<String>(); ProfileEntity profile = profileEntityCacheManager.retrieve(getCurrentUserOrcid()); if (cp.getPassword() == null || !cp.getPassword().matches(OrcidPasswordConstants.ORCID_PASSWORD_REGEX)) { errors.add(getMessage("NotBlank.registrationForm.confirmedPassword")); } else if (!cp.getPassword().equals(cp.getRetypedPassword())) { errors.add(getMessage("FieldMatch.registrationForm")); } if (registrationManager.passwordIsCommon(cp.getPassword())) { errors.add(getMessage("password.too_common", cp.getPassword())); } if (cp.getOldPassword() == null || !encryptionManager.hashMatches(cp.getOldPassword(), profile.getEncryptedPassword())) { errors.add(getMessage("orcid.frontend.change.password.current_password_incorrect")); } if (errors.size() == 0) { profileEntityManager.updatePassword(getCurrentUserOrcid(), cp.getPassword()); cp = new ChangePassword(); errors.add(getMessage("orcid.frontend.change.password.change.successfully")); } cp.setErrors(errors); return cp; } @RequestMapping(value = "/deprecate-profile.json") public @ResponseBody DeprecateProfile getDeprecateProfile() { return new DeprecateProfile(); } @RequestMapping(value = "/validate-deprecate-profile.json", method = RequestMethod.POST) public @ResponseBody DeprecateProfile validateDeprecateProfile(@RequestBody DeprecateProfile deprecateProfile) { validateFormData(deprecateProfile); if (!deprecateProfile.getErrors().isEmpty()) { return deprecateProfile; } String currentUserOrcid = getCurrentUserOrcid(); ProfileEntity primaryEntity = profileEntityCacheManager.retrieve(currentUserOrcid); ProfileEntity deprecatingEntity = getDeprecatingEntity(deprecateProfile); validateDeprecatingEntity(deprecatingEntity, primaryEntity, deprecateProfile); if (deprecateProfile.getErrors() != null && !deprecateProfile.getErrors().isEmpty()) { return deprecateProfile; } validateDeprecateAccountRequest(deprecateProfile, deprecatingEntity); if (deprecateProfile.getErrors() != null && !deprecateProfile.getErrors().isEmpty()) { return deprecateProfile; } Emails deprecatingEmails = emailManager.getEmails(deprecatingEntity.getId(), 0l); Emails primaryEmails = emailManager.getEmails(primaryEntity.getId(), 0l); String primaryAccountName = RecordNameUtils.getPublicName(primaryEntity.getRecordNameEntity()); String deprecatingAccountName = RecordNameUtils.getPublicName(deprecatingEntity.getRecordNameEntity()); deprecateProfile.setPrimaryAccountName(primaryAccountName); deprecateProfile.setPrimaryOrcid(currentUserOrcid); deprecateProfile.setDeprecatingAccountName(deprecatingAccountName); deprecateProfile.setDeprecatingOrcid(deprecatingEntity.getId()); if (deprecatingEmails != null) { deprecateProfile.setDeprecatingEmails( deprecatingEmails.getEmails().stream().map(e -> e.getEmail()).collect(Collectors.toList())); } if (primaryEmails != null) { deprecateProfile.setPrimaryEmails( primaryEmails.getEmails().stream().map(e -> e.getEmail()).collect(Collectors.toList())); } return deprecateProfile; } @RequestMapping(value = "/confirm-deprecate-profile.json", method = RequestMethod.POST) public @ResponseBody DeprecateProfile confirmDeprecateProfile(@RequestBody DeprecateProfile deprecateProfile) { validateFormData(deprecateProfile); if (deprecateProfile.getErrors() != null && !deprecateProfile.getErrors().isEmpty()) { return deprecateProfile; } ProfileEntity primaryEntity = profileEntityCacheManager.retrieve(getCurrentUserOrcid()); ProfileEntity deprecatingEntity = getDeprecatingEntity(deprecateProfile); validateDeprecatingEntity(deprecatingEntity, primaryEntity, deprecateProfile); if (deprecateProfile.getErrors() != null && !deprecateProfile.getErrors().isEmpty()) { return deprecateProfile; } validateDeprecateAccountRequest(deprecateProfile, deprecatingEntity); if (deprecateProfile.getErrors() != null && !deprecateProfile.getErrors().isEmpty()) { return deprecateProfile; } boolean deprecated = profileEntityManager.deprecateProfile(deprecatingEntity.getId(), primaryEntity.getId()); if (!deprecated) { deprecateProfile.setErrors(Arrays.asList(getMessage("deprecate_orcid.problem_deprecating"))); } return deprecateProfile; } private void validateDeprecatingEntity(ProfileEntity deprecatingEntity, ProfileEntity primaryEntity, DeprecateProfile deprecateProfile) { if (deprecatingEntity == null) { deprecateProfile.setErrors(Arrays.asList(getMessage("deprecate_orcid.profile_does_not_exist"))); } else if (primaryEntity.getId().equals(deprecatingEntity.getId())) { deprecateProfile.setErrors(Arrays.asList(getMessage("deprecate_orcid.profile_matches_current"))); } } private void validateFormData(DeprecateProfile deprecateProfile) { List<String> errors = new ArrayList<>(); if (deprecateProfile.getDeprecatingPassword() == null || deprecateProfile.getDeprecatingPassword().trim().isEmpty()) { errors.add(getMessage("deprecate_orcid.no_password_specified")); } if (deprecateProfile.getDeprecatingOrcidOrEmail() == null) { errors.add(getMessage("deprecate_orcid.no_profile_specified")); } deprecateProfile.setErrors(errors); } private void validateDeprecateAccountRequest(DeprecateProfile deprecateProfile, ProfileEntity deprecatingEntity) { if (!encryptionManager.hashMatches(deprecateProfile.getDeprecatingPassword(), deprecatingEntity.getEncryptedPassword())) { deprecateProfile.setErrors(Arrays.asList(getMessage("check_password_modal.incorrect_password"))); } else if (deprecatingEntity.getDeprecatedDate() != null) { deprecateProfile.setErrors(Arrays.asList(getMessage("deprecate_orcid.already_deprecated", deprecatingEntity.getId()))); } else if (deprecatingEntity.getDeactivationDate() != null) { deprecateProfile.setErrors(Arrays.asList(getMessage("deprecate_orcid.already_deactivated", deprecatingEntity.getId()))); } } private ProfileEntity getDeprecatingEntity(DeprecateProfile deprecateProfile) { if (deprecateProfile.getDeprecatingOrcidOrEmail().contains("@")) { EmailEntity emailEntity = emailManager.findCaseInsensitive(deprecateProfile.getDeprecatingOrcidOrEmail()); if (emailEntity != null) { return emailEntity.getProfile(); } } else { ProfileEntity profileEntity = profileEntityCacheManager.retrieve(deprecateProfile.getDeprecatingOrcidOrEmail()); if (profileEntity != null) { return profileEntity; } } return null; } @RequestMapping(value = { "deactivate-orcid", "/view-deactivate-orcid-account" }, method = RequestMethod.GET) public ModelAndView viewDeactivateOrcidAccount() { return new ModelAndView("deactivate_orcid"); } @RequestMapping(value = "/confirm-deactivate-orcid/{encryptedEmail}", method = RequestMethod.GET) public ModelAndView confirmDeactivateOrcidAccount(HttpServletRequest request, HttpServletResponse response, @PathVariable("encryptedEmail") String encryptedEmail, RedirectAttributes redirectAttributes) throws Exception { ModelAndView result = null; String decryptedEmail = encryptionManager.decryptForExternalUse(new String(Base64.decodeBase64(encryptedEmail), "UTF-8")); String primaryEmail = emailManager.findPrimaryEmail(getCurrentUserOrcid()).getEmail(); if (decryptedEmail.equals(primaryEmail)) { profileEntityManager.deactivateRecord(getCurrentUserOrcid()); logoutCurrentUser(request, response); result = new ModelAndView("redirect:/signin#deactivated"); } else { redirectAttributes.addFlashAttribute("emailDoesntMatch", true); return new ModelAndView("redirect:/my-orcid"); } return result; } @RequestMapping(value = "/verifyEmail.json", method = RequestMethod.GET) public @ResponseBody Errors verifyEmail(HttpServletRequest request, @RequestParam("email") String email) { String currentUserOrcid = getCurrentUserOrcid(); String primaryEmail = emailManager.findPrimaryEmail(currentUserOrcid).getEmail(); if (primaryEmail.equals(email)) request.getSession().setAttribute(EmailConstants.CHECK_EMAIL_VALIDATED, false); notificationManager.sendVerificationEmail(currentUserOrcid, email); return new Errors(); } @RequestMapping(value = "/delayVerifyEmail.json", method = RequestMethod.GET) public @ResponseBody Errors delayVerifyEmail(HttpServletRequest request) { request.getSession().setAttribute(EmailConstants.CHECK_EMAIL_VALIDATED, false); return new Errors(); } @RequestMapping(value = "/send-deactivate-account.json", method = RequestMethod.GET) public @ResponseBody String startDeactivateOrcidAccount(HttpServletRequest request) { String currentUserOrcid = getCurrentUserOrcid(); notificationManager.sendOrcidDeactivateEmail(currentUserOrcid); return emailManager.findPrimaryEmail(currentUserOrcid).getEmail(); } @RequestMapping(value = "/emails.json", method = RequestMethod.GET) public @ResponseBody org.orcid.pojo.ajaxForm.Emails getEmails(HttpServletRequest request) throws NoSuchRequestHandlingMethodException { Emails v2Emails = emailManager.getEmails(getCurrentUserOrcid(), profileEntityManager.getLastModified(getCurrentUserOrcid())); return org.orcid.pojo.ajaxForm.Emails.valueOf(v2Emails); } @RequestMapping(value = "/addEmail.json", method = RequestMethod.POST) public @ResponseBody org.orcid.pojo.ajaxForm.Email addEmails(HttpServletRequest request, @RequestBody org.orcid.pojo.AddEmail email) { List<String> errors = new ArrayList<String>(); ProfileEntity profile = profileEntityCacheManager.retrieve(getCurrentUserOrcid()); // Check password if (orcidSecurityManager.isPasswordConfirmationRequired() && (email.getPassword() == null || !encryptionManager.hashMatches(email.getPassword(), profile.getEncryptedPassword()))) { errors.add(getMessage("check_password_modal.incorrect_password")); } // if blank if (PojoUtil.isEmpty(email.getValue())) { errors.add(getMessage("Email.personalInfoForm.email")); } MapBindingResult mbr = new MapBindingResult(new HashMap<String, String>(), "Email"); // make sure there are no dups validateEmailAddress(email.getValue(), false, false, request, mbr); for (ObjectError oe : mbr.getAllErrors()) { errors.add(getMessage(oe.getCode(), email.getValue())); } if (errors.isEmpty()) { // clear errors email.setErrors(new ArrayList<String>()); String currentUserOrcid = getCurrentUserOrcid(); emailManager.addEmail(request, currentUserOrcid, email.toV2Email()); } else { email.setErrors(errors); } return email; } @RequestMapping(value = "/deleteEmail.json", method = RequestMethod.DELETE) public @ResponseBody org.orcid.pojo.ajaxForm.Email deleteEmailJson(HttpServletRequest request, @RequestBody org.orcid.pojo.ajaxForm.Email email) { List<String> emailErrors = new ArrayList<String>(); String currentUserOrcid = getCurrentUserOrcid(); // clear erros email.setErrors(new ArrayList<String>()); // if blank if (email.getValue() == null || email.getValue().trim().equals("")) { emailErrors.add(getMessage("Email.personalInfoForm.email")); } if (emailManager.isPrimaryEmail(currentUserOrcid, email.getValue())) { emailErrors.add(getMessage("manage.email.primaryEmailDeletion")); } email.setErrors(emailErrors); if (emailErrors.size() == 0) { emailManager.removeEmail(currentUserOrcid, email.getValue()); } return email; } @RequestMapping(value = "/emails.json", method = RequestMethod.POST) public @ResponseBody org.orcid.pojo.ajaxForm.Emails postEmailsJson(HttpServletRequest request, @RequestBody org.orcid.pojo.ajaxForm.Emails emails) { org.orcid.pojo.ajaxForm.Email newPrime = null; List<String> allErrors = new ArrayList<String>(); for (org.orcid.pojo.ajaxForm.Email email : emails.getEmails()) { MapBindingResult mbr = new MapBindingResult(new HashMap<String, String>(), "Email"); validateEmailAddress(email.getValue(), request, mbr); List<String> emailErrors = new ArrayList<String>(); for (ObjectError oe : mbr.getAllErrors()) { String msg = getMessage(oe.getCode(), email.getValue()); emailErrors.add(getMessage(oe.getCode(), email.getValue())); allErrors.add(msg); } email.setErrors(emailErrors); if (email.isPrimary()) newPrime = email; } if (newPrime == null) { allErrors.add("A Primary Email Must be selected"); } emails.setErrors(allErrors); if (allErrors.size() == 0) { emailManager.updateEmails(request, getCurrentUserOrcid(), emails.toV2Emails()); } return emails; } @RequestMapping(value = "/countryForm.json", method = RequestMethod.GET) public @ResponseBody AddressesForm getProfileCountryJson(HttpServletRequest request) throws NoSuchRequestHandlingMethodException { long lastModifiedTime = profileEntityManager.getLastModified(getCurrentUserOrcid()); Addresses addresses = addressManager.getAddresses(getCurrentUserOrcid(), lastModifiedTime); AddressesForm form = AddressesForm.valueOf(addresses); // Set country name if (form != null && form.getAddresses() != null) { Map<String, String> countries = retrieveIsoCountries(); for (AddressForm addressForm : form.getAddresses()) { addressForm.setCountryName(countries.get(addressForm.getIso2Country().getValue().name())); } } ProfileEntity profile = profileEntityCacheManager.retrieve(getCurrentUserOrcid()); // Set the default visibility if (profile.getActivitiesVisibilityDefault() != null) { form.setVisibility(org.orcid.pojo.ajaxForm.Visibility.valueOf(profile.getActivitiesVisibilityDefault())); } // Return an empty country in case we dont have any if (form.getAddresses() == null) { form.setAddresses(new ArrayList<AddressForm>()); } if (form.getAddresses().isEmpty()) { AddressForm address = new AddressForm(); address.setDisplayIndex(1L); address.setVisibility(org.orcid.pojo.ajaxForm.Visibility.valueOf(profile.getActivitiesVisibilityDefault())); form.getAddresses().add(address); } return form; } @RequestMapping(value = "/countryForm.json", method = RequestMethod.POST) public @ResponseBody AddressesForm setProfileCountryJson(HttpServletRequest request, @RequestBody AddressesForm addressesForm) throws NoSuchRequestHandlingMethodException { addressesForm.setErrors(new ArrayList<String>()); Map<String, String> countries = retrieveIsoCountries(); if (addressesForm != null) { if (addressesForm.getAddresses() != null) { for (AddressForm form : addressesForm.getAddresses()) { if (form.getIso2Country() == null || form.getIso2Country().getValue() == null) { form.getErrors().add(getMessage("common.invalid_country")); } else { form.setCountryName(countries.get(form.getIso2Country().getValue().name())); } copyErrors(form, addressesForm); } } if (!addressesForm.getErrors().isEmpty()) { return addressesForm; } Addresses addresses = addressesForm.toAddresses(); addressManager.updateAddresses(getCurrentUserOrcid(), addresses); } return addressesForm; } @RequestMapping(value = "/nameForm.json", method = RequestMethod.GET) public @ResponseBody NamesForm getNameForm() throws NoSuchRequestHandlingMethodException { String currentOrcid = getCurrentUserOrcid(); Name name = recordNameManager.getRecordName(currentOrcid, profileEntityManager.getLastModified(currentOrcid)); return NamesForm.valueOf(name); } @RequestMapping(value = "/nameForm.json", method = RequestMethod.POST) public @ResponseBody NamesForm setNameFormJson(@RequestBody NamesForm nf) throws NoSuchRequestHandlingMethodException { nf.setErrors(new ArrayList<String>()); // Strip any html code from names before validating them if (!PojoUtil.isEmpty(nf.getFamilyName())) { nf.getFamilyName().setValue(OrcidStringUtils.stripHtml(nf.getFamilyName().getValue())); } if (!PojoUtil.isEmpty(nf.getGivenNames())) { nf.getGivenNames().setValue(OrcidStringUtils.stripHtml(nf.getGivenNames().getValue())); } if (!PojoUtil.isEmpty(nf.getCreditName())) { nf.getCreditName().setValue(OrcidStringUtils.stripHtml(nf.getCreditName().getValue())); } if (nf.getGivenNames() == null) nf.setGivenNames(new Text()); givenNameValidate(nf.getGivenNames()); copyErrors(nf.getGivenNames(), nf); if (nf.getErrors().size() > 0) return nf; Name name = nf.toName(); String orcid = getCurrentUserOrcid(); if (recordNameManager.exists(orcid)) { recordNameManager.updateRecordName(orcid, name); } else { recordNameManager.createRecordName(orcid, name); } return nf; } @RequestMapping(value = "/biographyForm.json", method = RequestMethod.GET) public @ResponseBody BiographyForm getBiographyForm() { Biography bio = biographyManager.getBiography(getCurrentUserOrcid(), profileEntityManager.getLastModified(getCurrentUserOrcid())); BiographyForm form = BiographyForm.valueOf(bio); if(form.getVisiblity() == null) { ProfileEntity profile = profileEntityCacheManager.retrieve(getCurrentUserOrcid()); form.setVisiblity(Visibility.valueOf(profile.getActivitiesVisibilityDefault())); } return form; } @RequestMapping(value = "/biographyForm.json", method = RequestMethod.POST) public @ResponseBody BiographyForm setBiographyFormJson(@RequestBody BiographyForm bf) { bf.setErrors(new ArrayList<String>()); if (bf.getBiography() != null) { validateBiography(bf.getBiography()); copyErrors(bf.getBiography(), bf); if (bf.getErrors().size() > 0) return bf; Biography bio = new Biography(); if (bf.getBiography() != null) { bio.setContent(bf.getBiography().getValue()); } if (bf.getVisiblity() != null && bf.getVisiblity().getVisibility() != null) { org.orcid.jaxb.model.common_v2.Visibility v = org.orcid.jaxb.model.common_v2.Visibility.fromValue(bf.getVisiblity().getVisibility().value()); bio.setVisibility(v); } String orcid = getCurrentUserOrcid(); if (biographyManager.exists(orcid)) { biographyManager.updateBiography(orcid, bio); } else { biographyManager.createBiography(orcid, bio); } } return bf; } /** * Check if the user have twitter enabled */ @RequestMapping(value = { "/twitter/check-twitter-status" }, method = RequestMethod.GET) public @ResponseBody boolean isTwitterEnabled() { return orcidSocialManager.isTwitterEnabled(getEffectiveUserOrcid()); } /** * Get a user request to authorize twitter and return the authorization URL */ @RequestMapping(value = { "/twitter" }, method = RequestMethod.POST) public @ResponseBody String goToTwitterAuthPage() throws Exception { return orcidSocialManager.getTwitterAuthorizationUrl(getEffectiveUserOrcid()); } /** * Get the twitter credentials and enable it on the user profile */ @RequestMapping(value = { "/twitter" }, method = RequestMethod.GET) public ModelAndView setTwitterKeyToProfileGET(@RequestParam("oauth_token") String token, @RequestParam("oauth_verifier") String verifier) throws Exception { ModelAndView mav = new ModelAndView("manage"); mav.addObject("showPrivacy", true); mav.addObject("activeTab", "profile-tab"); mav.addObject("securityQuestions", getSecurityQuestions()); return mav; } /** * Disable twitter access */ @RequestMapping(value = { "/disable-twitter" }, method = RequestMethod.POST) public @ResponseBody boolean disableTwitter() throws Exception { orcidSocialManager.disableTwitter(getEffectiveUserOrcid()); return true; } /** * Authorize a delegate request done by an admin */ @RequestMapping(value = { "/authorize-delegates" }, method = RequestMethod.GET) public ModelAndView authorizeDelegatesRequest(@RequestParam("key") String key) { ModelAndView mav = new ModelAndView("manage"); try { Map<String, String> params = decryptDelegationKey(key); if (params.containsKey(AdminManager.MANAGED_USER_PARAM) && params.containsKey(AdminManager.TRUSTED_USER_PARAM)) { String managedOrcid = params.get(AdminManager.MANAGED_USER_PARAM); String trustedOrcid = params.get(AdminManager.TRUSTED_USER_PARAM); // Check if managed user is the same than the logged user if (managedOrcid.equals(getEffectiveUserOrcid())) { // Check if the managed user email is verified, if not, // verify it verifyPrimaryEmailIfNeeded(managedOrcid); givenPermissionToManager.create(getCurrentUserOrcid(), trustedOrcid); mav.addObject("admin_delegate_approved", getMessage("admin.delegate.success", trustedOrcid)); } else { // Exception, the email was not for you mav.addObject("admin_delegate_not_you", getMessage("wrong_user.Wronguser")); } } else { // Error mav.addObject("admin_delegate_failed", getMessage("admin.delegate.error.invalid_link")); } } catch (UnsupportedEncodingException uee) { mav.addObject("admin_delegate_failed", getMessage("admin.delegate.error.invalid_link")); } return mav; } @RequestMapping(value = { "/get-trusted-orgs" }, method = RequestMethod.GET) public @ResponseBody List<ApplicationSummary> getTrustedOrgs() { return profileEntityManager.getApplications(getCurrentUserOrcid()); } /** * @throws UnsupportedEncodingException */ @SuppressWarnings("unchecked") private Map<String, String> decryptDelegationKey(String encryptedKey) throws UnsupportedEncodingException { String jsonString = encryptionManager.decryptForExternalUse(new String(Base64.decodeBase64(encryptedKey), "UTF-8")); Map<String, String> params = (Map<String, String>) JsonUtils.readObjectFromJsonString(jsonString, Map.class); return params; } /** * Verify a primary email if it is not verified yet. * * @param orcid * The profile id to check */ private void verifyPrimaryEmailIfNeeded(String orcid) { if (!emailManager.isPrimaryEmailVerified(orcid)) { emailManager.verifyPrimaryEmail(orcid); } } }