/** * ============================================================================= * * 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.core.manager.impl; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import javax.annotation.Resource; import org.orcid.core.constants.DefaultPreferences; import org.orcid.core.manager.AdminManager; import org.orcid.core.manager.EmailManager; import org.orcid.core.manager.EncryptionManager; import org.orcid.core.manager.NotificationManager; import org.orcid.core.manager.OrcidGenerationManager; import org.orcid.core.manager.ProfileEntityCacheManager; import org.orcid.core.manager.ProfileEntityManager; import org.orcid.core.manager.RegistrationManager; import org.orcid.core.security.OrcidWebRole; import org.orcid.core.utils.VerifyRegistrationToken; import org.orcid.jaxb.model.common_v2.Visibility; import org.orcid.jaxb.model.message.CreationMethod; import org.orcid.jaxb.model.message.OrcidProfile; import org.orcid.jaxb.model.common_v2.OrcidType; import org.orcid.persistence.dao.ProfileDao; import org.orcid.persistence.jpa.entities.EmailEntity; import org.orcid.persistence.jpa.entities.OrcidGrantedAuthority; import org.orcid.persistence.jpa.entities.ProfileEntity; import org.orcid.persistence.jpa.entities.RecordNameEntity; import org.orcid.pojo.ProfileDeprecationRequest; import org.orcid.pojo.ajaxForm.PojoUtil; import org.orcid.pojo.ajaxForm.Registration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Required; import org.springframework.security.oauth2.common.exceptions.InvalidRequestException; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; /** * * @author Will Simpson * */ public class RegistrationManagerImpl implements RegistrationManager, InitializingBean { private static final Logger LOGGER = LoggerFactory.getLogger(RegistrationManagerImpl.class); private static final String COMMON_PASSWORDS_FILENAME = "common_passwords.txt"; private EncryptionManager encryptionManager; private NotificationManager notificationManager; @Resource private ProfileDao profileDao; @Resource private EmailManager emailManager; @Resource private ProfileEntityManager profileEntityManager; @Resource private ProfileEntityCacheManager profileEntityCacheManager; @Resource private AdminManager adminManager; @Resource private TransactionTemplate transactionTemplate; @Resource private OrcidGenerationManager orcidGenerationManager; private List<String> commonPasswords; @Required public void setEncryptionManager(EncryptionManager encryptionManager) { this.encryptionManager = encryptionManager; } @Required public void setNotificationManager(NotificationManager notificationManager) { this.notificationManager = notificationManager; } @Override public void resetUserPassword(String toEmail, OrcidProfile orcidProfile) { LOGGER.debug("Resetting password for Orcid: {}", orcidProfile.getOrcidIdentifier().getPath()); if (!orcidProfile.getOrcidHistory().isClaimed()) { LOGGER.debug("Profile is not claimed so re-sending claim email instead of password reset: {}", orcidProfile.getOrcidIdentifier().getPath()); notificationManager.sendApiRecordCreationEmail(toEmail, orcidProfile); } else { notificationManager.sendPasswordResetEmail(toEmail, orcidProfile); } } @Override public Long getCount() { return profileDao.getConfirmedProfileCount(); } @Override public VerifyRegistrationToken parseEncyrptedParamsForVerification(String encryptedParams) { String decryptedParams = encryptionManager.decryptForExternalUse(encryptedParams); LOGGER.debug("Got verification params: {}", decryptedParams); return new VerifyRegistrationToken(decryptedParams); } @Override public String createMinimalRegistration(Registration registration, boolean usedCaptcha, Locale locale, String ip) { String emailAddress = registration.getEmail().getValue(); try { String orcidId = transactionTemplate.execute(new TransactionCallback<String>() { public String doInTransaction(TransactionStatus status) { if (emailManager.emailExists(emailAddress)) { checkAutoDeprecateIsEnabledForEmail(emailAddress); String unclaimedOrcid = getOrcidIdFromEmail(emailAddress); emailManager.removeEmail(unclaimedOrcid, emailAddress, true); String newUserOrcid = createMinimalProfile(registration, usedCaptcha, locale, ip); ProfileDeprecationRequest result = new ProfileDeprecationRequest(); adminManager.deprecateProfile(result, unclaimedOrcid, newUserOrcid); notificationManager.sendAutoDeprecateNotification(newUserOrcid, unclaimedOrcid); profileEntityCacheManager.remove(unclaimedOrcid); return newUserOrcid; } else { return createMinimalProfile(registration, usedCaptcha, locale, ip); } } }); return orcidId; } catch (Exception e) { throw new InvalidRequestException("Unable to register user due: " + e.getMessage(), e.getCause()); } } /** * Creates a minimal record * * @param orcidProfile * The record to create * @return the new record */ private String createMinimalProfile(Registration registration, boolean usedCaptcha, Locale locale, String ip) { Date now = new Date(); String orcid = orcidGenerationManager.createNewOrcid(); ProfileEntity newRecord = new ProfileEntity(); newRecord.setId(orcid); try { newRecord.setHashedOrcid(encryptionManager.sha256Hash(orcid)); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } newRecord.setOrcidType(OrcidType.USER); newRecord.setDateCreated(now); newRecord.setLastModified(now); newRecord.setSubmissionDate(now); newRecord.setClaimed(true); newRecord.setEnableDeveloperTools(false); newRecord.setRecordLocked(false); newRecord.setReviewed(false); newRecord.setEnableNotifications(DefaultPreferences.NOTIFICATIONS_ENABLED); newRecord.setUsedRecaptchaOnRegistration(usedCaptcha); newRecord.setUserLastIp(ip); if (PojoUtil.isEmpty(registration.getSendEmailFrequencyDays())) { newRecord.setSendEmailFrequencyDays(Float.valueOf(DefaultPreferences.SEND_EMAIL_FREQUENCY_DAYS)); } else { newRecord.setSendEmailFrequencyDays(Float.valueOf(registration.getSendEmailFrequencyDays().getValue())); } if (registration.getSendMemberUpdateRequests() == null) { newRecord.setSendMemberUpdateRequests(DefaultPreferences.SEND_MEMBER_UPDATE_REQUESTS); } else { newRecord.setSendMemberUpdateRequests(registration.getSendMemberUpdateRequests().getValue()); } newRecord.setCreationMethod(PojoUtil.isEmpty(registration.getCreationType()) ? CreationMethod.DIRECT.value() : registration.getCreationType().getValue()); newRecord.setSendChangeNotifications(registration.getSendChangeNotifications().getValue()); newRecord.setSendOrcidNews(registration.getSendOrcidNews().getValue()); newRecord.setLocale(locale == null ? org.orcid.jaxb.model.common_v2.Locale.EN : org.orcid.jaxb.model.common_v2.Locale.fromValue(locale.toString())); // Visibility defaults newRecord.setActivitiesVisibilityDefault(Visibility.fromValue(registration.getActivitiesVisibilityDefault().getVisibility().value())); // Encrypt the password newRecord.setEncryptedPassword(encryptionManager.hashForInternalUse(registration.getPassword().getValue())); // Set the email EmailEntity emailEntity = new EmailEntity(); emailEntity.setId(registration.getEmail().getValue().trim()); emailEntity.setProfile(newRecord); emailEntity.setPrimary(true); emailEntity.setCurrent(true); emailEntity.setVerified(false); // Email is private by default emailEntity.setVisibility(Visibility.PRIVATE); emailEntity.setSourceId(orcid); Set<EmailEntity> emails = new HashSet<>(); emails.add(emailEntity); newRecord.setEmails(emails); // Set the name RecordNameEntity recordNameEntity = new RecordNameEntity(); recordNameEntity.setDateCreated(now); recordNameEntity.setLastModified(now); recordNameEntity.setProfile(newRecord); // Name is public by default recordNameEntity.setVisibility(Visibility.PUBLIC); if (!PojoUtil.isEmpty(registration.getFamilyNames())) { recordNameEntity.setFamilyName(registration.getFamilyNames().getValue().trim()); } if (!PojoUtil.isEmpty(registration.getGivenNames())) { recordNameEntity.setGivenNames(registration.getGivenNames().getValue().trim()); } newRecord.setRecordNameEntity(recordNameEntity); // Set authority OrcidGrantedAuthority authority = new OrcidGrantedAuthority(); authority.setProfileEntity(newRecord); authority.setAuthority(OrcidWebRole.ROLE_USER.getAuthority()); Set<OrcidGrantedAuthority> authorities = new HashSet<OrcidGrantedAuthority>(1); authorities.add(authority); newRecord.setAuthorities(authorities); profileDao.persist(newRecord); profileDao.flush(); return newRecord.getId(); } /** * Validates if the given email address could be auto deprecated * * @param emailAddress * The email we want to check */ private void checkAutoDeprecateIsEnabledForEmail(String emailAddress) throws InvalidRequestException { // If the email doesn't exists, just return if (!emailManager.emailExists(emailAddress)) { return; } // Check the record is not claimed if (profileEntityManager.isProfileClaimedByEmail(emailAddress)) { throw new InvalidRequestException("Email " + emailAddress + " already exists and is claimed, so, it can't be used again"); } // Check the auto deprecate is enabled for this email address if (!emailManager.isAutoDeprecateEnableForEmail(emailAddress)) { throw new InvalidRequestException("Autodeprecate is not enabled for " + emailAddress); } } /** * Returns the orcid id associated with an email address * * @param emailAddress * @return the orcid id associated with the given email address */ private String getOrcidIdFromEmail(String emailAddress) { Map<String, String> emailMap = emailManager.findOricdIdsByCommaSeparatedEmails(emailAddress); String unclaimedOrcid = emailMap == null ? null : emailMap.get(emailAddress); if (PojoUtil.isEmpty(unclaimedOrcid)) { throw new InvalidRequestException("Unable to find orcid id for " + emailAddress); } return unclaimedOrcid; } @Override public boolean passwordIsCommon(String password) { return commonPasswords.contains(password); } @Override public void afterPropertiesSet() throws Exception { LOGGER.info("Building common passwords list..."); commonPasswords = new ArrayList<>(); InputStream inputStream = getClass().getResourceAsStream(COMMON_PASSWORDS_FILENAME); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); try { String line = reader.readLine(); while (line != null) { commonPasswords.add(line.trim()); line = reader.readLine(); } } finally { reader.close(); } LOGGER.info("Built list of " + commonPasswords.size() + " common passwords"); } }