Java tutorial
/** * ============================================================================= * * 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.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; import javax.annotation.Resource; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.validator.routines.UrlValidator; import org.orcid.core.locale.LocaleManager; import org.orcid.core.manager.EmailManager; import org.orcid.core.manager.InternalSSOManager; import org.orcid.core.manager.OrcidProfileManager; import org.orcid.core.manager.ProfileEntityManager; import org.orcid.core.manager.SourceManager; import org.orcid.core.manager.impl.OrcidUrlManager; import org.orcid.core.manager.impl.StatisticsCacheManager; import org.orcid.core.oauth.OrcidProfileUserDetails; import org.orcid.core.utils.JsonUtils; import org.orcid.frontend.web.forms.LoginForm; import org.orcid.frontend.web.forms.validate.OrcidUrlValidator; import org.orcid.jaxb.model.message.Email; import org.orcid.jaxb.model.message.OrcidProfile; import org.orcid.jaxb.model.message.SendEmailFrequency; import org.orcid.jaxb.model.message.Visibility; import org.orcid.persistence.constants.SiteConstants; import org.orcid.pojo.ajaxForm.ErrorsInterface; import org.orcid.pojo.ajaxForm.PojoUtil; import org.orcid.pojo.ajaxForm.Text; import org.orcid.utils.OrcidStringUtils; import org.orcid.utils.UTF8Control; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.annotation.Cacheable; import org.springframework.core.io.ClassPathResource; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.security.web.csrf.CsrfTokenRepository; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.servlet.support.RequestContextUtils; import com.fasterxml.jackson.databind.ObjectMapper; public class BaseController { String[] urlValschemes = { "http", "https", "ftp" }; // DEFAULT schemes = // "http", "https", // "ftp" UrlValidator urlValidator = new OrcidUrlValidator(urlValschemes); private String devSandboxUrl; private String aboutUri; private String knowledgeBaseUri; private boolean reducedFunctionalityMode; private String maintenanceMessage; private URL maintenanceHeaderUrl; private String googleAnalyticsTrackingId; protected List<String> domainsAllowingRobots; protected static final String STATIC_FOLDER_PATH = "/static"; private static final Logger LOGGER = LoggerFactory.getLogger(BaseController.class); private Date startupDate = new Date(); private String staticContentPath; private String staticCdnPath; @Resource private String cdnConfigFile; @Resource private LocaleManager localeManager; @Resource protected OrcidProfileManager orcidProfileManager; @Resource protected EmailManager emailManager; @Resource private StatisticsCacheManager statisticsCacheManager; @Resource protected OrcidUrlManager orcidUrlManager; @Resource protected SourceManager sourceManager; @Resource private ProfileEntityManager profileEntityManager; @Resource private InternalSSOManager internalSSOManager; @Resource protected CsrfTokenRepository csrfTokenRepository; protected static final String EMPTY = "empty"; @Value("${org.orcid.recaptcha.web_site_key:}") private String recaptchaWebKey; @ModelAttribute("recaptchaWebKey") public String getRecaptchaWebKey() { return recaptchaWebKey; } public void setRecaptchaWebKey(String recaptchaWebKey) { this.recaptchaWebKey = recaptchaWebKey; } public LocaleManager getLocaleManager() { return localeManager; } public void setLocaleManager(LocaleManager localeManager) { this.localeManager = localeManager; } public OrcidProfileManager getOrcidProfileManager() { return orcidProfileManager; } public void setOrcidProfileManager(OrcidProfileManager orcidProfileManager) { this.orcidProfileManager = orcidProfileManager; } @ModelAttribute("devSandboxUrl") public String getDevSandboxUrl() { return devSandboxUrl; } @Value("${org.orcid.frontend.web.devSandboxUrl:}") public void setDevSandboxUrl(String devSandboxUrl) { this.devSandboxUrl = devSandboxUrl; } @ModelAttribute("aboutUri") public String getAboutUri() { return aboutUri; } @ModelAttribute("knowledgeBaseUri") public String getKnowledgeBaseUri() { return knowledgeBaseUri; } @Value("${org.orcid.core.knowledgeBaseUri:http://support.orcid.org/knowledgebase}") public void setKnowledgeBaseUri(String knowledgeBaseUri) { this.knowledgeBaseUri = knowledgeBaseUri; } @Value("${org.orcid.core.aboutUri:http://about.orcid.org}") public void setAboutUri(String aboutUri) { this.aboutUri = aboutUri; } @ModelAttribute("reducedFunctionalityMode") public boolean isReducedFunctionalityMode() { return reducedFunctionalityMode; } @Value("${org.orcid.frontend.web.reducedFunctionalityMode:false}") public void setReducedFunctionalityMode(boolean reducedFunctionalityMode) { this.reducedFunctionalityMode = reducedFunctionalityMode; } @ModelAttribute("googleAnalyticsTrackingId") public String getGoogleAnalyticsTrackingId() { return googleAnalyticsTrackingId; } @Value("${org.orcid.frontend.web.googleAnalyticsTrackingId:}") public void setGoogleAnalyticsTrackingId(String googleAnalyticsTrackingId) { this.googleAnalyticsTrackingId = googleAnalyticsTrackingId; } @ModelAttribute("maintenanceMessage") public String getMaintenanceMessage() { if (maintenanceHeaderUrl != null) { try { String maintenanceHeader = IOUtils.toString(maintenanceHeaderUrl); if (StringUtils.isNotBlank(maintenanceHeader)) { return maintenanceHeader; } } catch (IOException e) { LOGGER.debug("Error reading maintenance header", e); } } return maintenanceMessage; } @ModelAttribute("sendEmailFrequencies") public Map<String, String> retrieveRolesAsMap() { Map<String, String> map = new LinkedHashMap<>(); for (SendEmailFrequency freq : SendEmailFrequency.values()) { map.put(String.valueOf(freq.value()), getMessage(buildInternationalizationKey(SendEmailFrequency.class, freq.name()))); } return map; } /** * Use maintenanceHeaderUrl instead */ @Deprecated @Value("${org.orcid.frontend.web.maintenanceMessage:}") public void setMaintenanceMessage(String maintenanceMessage) { this.maintenanceMessage = maintenanceMessage; } public URL getMaintenanceHeaderUrl() { return maintenanceHeaderUrl; } @Value("${org.orcid.frontend.web.maintenanceHeaderUrl:}") public void setMaintenanceHeaderUrl(URL maintenanceHeaderUrl) { this.maintenanceHeaderUrl = maintenanceHeaderUrl; } @Value("${org.orcid.frontend.web.domainsAllowingRobotsAsWhiteSpaceSeparatedList:orcid.org}") public void setDomainsAllowingRobots(String whitespaceSeparatedDomains) { domainsAllowingRobots = Arrays.asList(whitespaceSeparatedDomains.split("\\s")); } public void setDomainsAllowingRobots(List<String> domainsAllowingRobots) { this.domainsAllowingRobots = domainsAllowingRobots; } @ModelAttribute("visibilities") public Map<String, String> retrieveVisibilitiesAsMap() { Map<String, String> visibilities = new LinkedHashMap<String, String>(); visibilities.put(Visibility.PUBLIC.value(), "Public"); visibilities.put(Visibility.LIMITED.value(), "Limited"); visibilities.put(Visibility.PRIVATE.value(), "Private"); return visibilities; } @ModelAttribute("startupDate") public Date getStartupDate() { // If the cdn config file is missing, we are in development env and we // need to refresh the cache ClassPathResource configFile = new ClassPathResource(this.cdnConfigFile); if (!configFile.exists()) { return new Date(); } return startupDate; } protected OrcidProfileUserDetails getCurrentUser() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if ((authentication instanceof UsernamePasswordAuthenticationToken || authentication instanceof PreAuthenticatedAuthenticationToken) && authentication.getPrincipal() instanceof OrcidProfileUserDetails) { return ((OrcidProfileUserDetails) authentication.getPrincipal()); } else { return null; } } protected String getCurrentUserOrcid() { return getEffectiveUserOrcid(); } protected void logoutCurrentUser(HttpServletRequest request, HttpServletResponse response) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (internalSSOManager.enableCookie()) { Cookie[] cookies = request.getCookies(); // Delete cookie and token associated with that cookie if (cookies != null) { for (Cookie cookie : cookies) { if (InternalSSOManager.COOKIE_NAME.equals(cookie.getName())) { try { // If it is a valid cookie, extract the orcid value // and // remove the token and the cookie @SuppressWarnings("unchecked") HashMap<String, String> cookieValues = JsonUtils .readObjectFromJsonString(cookie.getValue(), HashMap.class); if (cookieValues.containsKey(InternalSSOManager.COOKIE_KEY_ORCID) && !PojoUtil.isEmpty(cookieValues.get(InternalSSOManager.COOKIE_KEY_ORCID))) { internalSSOManager.deleteToken( cookieValues.get(InternalSSOManager.COOKIE_KEY_ORCID), request, response); } else { // If it is not valid, just remove the cookie cookie.setValue(StringUtils.EMPTY); cookie.setMaxAge(0); response.addCookie(cookie); } } catch (RuntimeException re) { // If any exception happens, but, the cookie exists, // remove the cookie cookie.setValue(StringUtils.EMPTY); cookie.setMaxAge(0); response.addCookie(cookie); } break; } } } // Delete token if exists if (authentication != null && !PojoUtil.isEmpty(authentication.getName())) { internalSSOManager.deleteToken(authentication.getName()); } } if (authentication != null && authentication.isAuthenticated()) { new SecurityContextLogoutHandler().logout(request, response, authentication); } CsrfToken token = csrfTokenRepository.generateToken(request); csrfTokenRepository.saveToken(token, request, response); request.setAttribute("_csrf", token); } protected boolean isEmailOkForCurrentUser(String decryptedEmail) { OrcidProfileUserDetails userDetails = getCurrentUser(); if (userDetails == null) { return true; } OrcidProfile orcidProfile = getEffectiveProfile(); if (orcidProfile == null) { return true; } List<Email> emails = orcidProfile.getOrcidBio().getContactDetails().getEmail(); for (Email email : emails) { if (decryptedEmail.equalsIgnoreCase(email.getValue())) { return true; } } return false; } @ModelAttribute("realUserOrcid") public String getRealUserOrcid() { return sourceManager.retrieveRealUserOrcid(); } @ModelAttribute("effectiveUserOrcid") public String getEffectiveUserOrcid() { OrcidProfileUserDetails currentUser = getCurrentUser(); if (currentUser == null) { return null; } return currentUser.getOrcid(); } @ModelAttribute("inDelegationMode") public boolean isInDelegationMode() { return sourceManager.isInDelegationMode(); } @ModelAttribute("isDelegatedByAdmin") public boolean isDelegatedByAdmin() { return sourceManager.isDelegatedByAnAdmin(); } @ModelAttribute("request") public HttpServletRequest getRequest(HttpServletRequest request) { return request; } @ModelAttribute("loginForm") public LoginForm getLoginForm() { return new LoginForm(); } @ModelAttribute("jsMessagesJson") public String getJavascriptMessages(HttpServletRequest request) { ObjectMapper mapper = new ObjectMapper(); Locale locale = RequestContextUtils.getLocale(request); org.orcid.pojo.Local lPojo = new org.orcid.pojo.Local(); lPojo.setLocale(locale.toString()); ResourceBundle resources = ResourceBundle.getBundle("i18n/javascript", locale, new UTF8Control()); lPojo.setMessages(OrcidStringUtils.resourceBundleToMap(resources)); String messages = ""; try { messages = StringEscapeUtils.escapeEcmaScript(mapper.writeValueAsString(lPojo)); } catch (IOException e) { LOGGER.error("getJavascriptMessages error:" + e.toString(), e); } return messages; } protected void validateEmailAddress(String email, HttpServletRequest request, BindingResult bindingResult) { validateEmailAddress(email, true, false, request, bindingResult); } protected void validateEmailAddressOnRegister(String email, HttpServletRequest request, BindingResult bindingResult) { validateEmailAddress(email, true, true, request, bindingResult); } protected void validateEmailAddress(String email, boolean ignoreCurrentUser, boolean isRegisterRequest, HttpServletRequest request, BindingResult bindingResult) { if (StringUtils.isNotBlank(email)) { if (!validateEmailAddress(email)) { String[] codes = { "Email.personalInfoForm.email" }; String[] args = { email }; bindingResult.addError(new FieldError("email", "email", email, false, codes, args, "Not vaild")); } if (!(ignoreCurrentUser && emailMatchesCurrentUser(email)) && emailManager.emailExists(email)) { OrcidProfile orcidProfile = orcidProfileManager.retrieveOrcidProfileByEmail(email); if (orcidProfile.getOrcidHistory().isClaimed()) { String[] codes = null; if (isRegisterRequest) { codes = new String[] { "orcid.frontend.verify.duplicate_email" }; } else { codes = new String[] { "orcid.frontend.verify.claimed_email" }; } String[] args = { email }; bindingResult.addError( new FieldError("email", "email", email, false, codes, args, "Email already exists")); } else { String resendUrl = createResendClaimUrl(email, request); String[] codes = { "orcid.frontend.verify.unclaimed_email" }; String[] args = { email, resendUrl }; bindingResult.addError( new FieldError("email", "email", email, false, codes, args, "Unclaimed record exists")); } } } } /** * Validates if the provided string matches an email address pattern. * * @param email * The string to evaluate * @return true if the provided string matches an email address pattern, * false otherwise. * */ protected boolean validateEmailAddress(String email) { if (StringUtils.isNotBlank(email)) { try { InternetAddress addr = new InternetAddress(email); addr.validate(); return true; } catch (AddressException ex) { } } return false; } private String createResendClaimUrl(String email, HttpServletRequest request) { String urlEncodedEmail = null; try { urlEncodedEmail = URLEncoder.encode(email, "UTF-8"); } catch (UnsupportedEncodingException e) { LOGGER.debug("Unable to url encode email address: {}", email, e); } StringBuilder resendUrl = new StringBuilder(); resendUrl.append(orcidUrlManager.getServerStringWithContextPath(request)); resendUrl.append("/resend-claim"); if (urlEncodedEmail != null) { resendUrl.append("?email="); resendUrl.append(urlEncodedEmail); } return resendUrl.toString(); } private boolean emailMatchesCurrentUser(String email) { OrcidProfileUserDetails currentUser = getCurrentUser(); if (currentUser == null) { return false; } boolean match = false; for (Email cuEmail : getEffectiveProfile().getOrcidBio().getContactDetails().getEmail()) { if (cuEmail.getValue() != null && cuEmail.getValue().equalsIgnoreCase(email)) match = true; } return match; } public OrcidProfile getEffectiveProfile() { String effectiveOrcid = getEffectiveUserOrcid(); return effectiveOrcid == null ? null : orcidProfileManager.retrieveOrcidProfile(effectiveOrcid); } public OrcidProfile getRealProfile() { String realOrcid = getRealUserOrcid(); return realOrcid == null ? null : orcidProfileManager.retrieveOrcidProfile(realOrcid); } public String getMessage(String messageCode, Object... messageParams) { return localeManager.resolveMessage(messageCode, messageParams); } @ModelAttribute("locale") public String getLocaleAsString() { return localeManager.getLocale().toString(); } public Locale getLocale() { return localeManager.getLocale(); } @ModelAttribute("liveIds") public String getLiveIds() { return statisticsCacheManager.retrieveLiveIds(localeManager.getLocale()); } @ModelAttribute("baseUri") public String getBaseUri() { return orcidUrlManager.getBaseUrl(); } @ModelAttribute("pubBaseUri") public String getPubBaseUri() { return orcidUrlManager.getPubBaseUrl(); } /** * * CDN Configuration * * */ public String getCdnConfigFile() { return this.cdnConfigFile; } public void setCdnConfigFile(String cdnConfigFile) { this.cdnConfigFile = cdnConfigFile; } /** * @return the path to the static content on local project * */ @ModelAttribute("staticLoc") public String getStaticContentPath() { if (StringUtils.isBlank(this.staticContentPath)) { this.staticContentPath = orcidUrlManager.getBaseUrl() + STATIC_FOLDER_PATH; this.staticContentPath = this.staticContentPath.replace("https:", ""); this.staticContentPath = this.staticContentPath.replace("http:", ""); } return this.staticContentPath; } /** * Return the path where the static content will be. If there is a cdn path * configured, it will return the cdn path; if it is not a cdn path it will * return a reference to the static folder "/static" * * @return the path to the CDN or the path to the local static content * */ @ModelAttribute("staticCdn") @Cacheable("staticContent") public String getStaticCdnPath() { if (StringUtils.isEmpty(this.cdnConfigFile)) { return getStaticContentPath(); } ClassPathResource configFile = new ClassPathResource(this.cdnConfigFile); if (configFile.exists()) { try (InputStream is = configFile.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is))) { String uri = br.readLine(); if (uri != null) this.staticCdnPath = uri; } catch (IOException e) { e.printStackTrace(); } } if (StringUtils.isBlank(this.staticCdnPath)) this.staticCdnPath = this.getStaticContentPath(); return staticCdnPath; } @ModelAttribute("baseDomainRmProtocall") public String getBaseDomainRmProtocall() { return orcidUrlManager.getBaseDomainRmProtocall(); } @ModelAttribute("baseUriHttp") public String getBaseUriHttp() { return orcidUrlManager.getBaseUriHttp(); } @ModelAttribute("basePath") public String getBasePath() { return orcidUrlManager.getBasePath(); } /** * A method that will help us to internationalize enums For each enum, the * value can be internationalized by adding a key of the form * full.class.name.key and then using this method to build the string for * that key. * * @param theClass * @param key * @return a String of the form full.class.name.with.package.key * */ @SuppressWarnings("rawtypes") protected String buildInternationalizationKey(Class theClass, String key) { return theClass.getName() + '.' + key; } protected static void copyErrors(ErrorsInterface from, ErrorsInterface into) { for (String s : from.getErrors()) { into.getErrors().add(s); } } protected void setError(ErrorsInterface ei, String msg) { ei.getErrors().add(getMessage(msg)); } protected void setError(ErrorsInterface ei, String msg, Object... messageParams) { ei.getErrors().add(getMessage(msg, messageParams)); } protected void validateBiography(Text text) { text.setErrors(new ArrayList<String>()); if (!PojoUtil.isEmpty(text.getValue())) { // trim if required if (!text.getValue().equals(text.getValue().trim())) text.setValue(text.getValue().trim()); // check length if (text.getValue().length() > 5000) setError(text, "Length.changePersonalInfoForm.biography"); } } protected void validateUrl(Text url) { validateUrl(url, SiteConstants.URL_MAX_LENGTH); } protected void validateUrl(Text url, int maxLength) { url.setErrors(new ArrayList<String>()); if (!PojoUtil.isEmpty(url.getValue())) { // trim if required if (!url.getValue().equals(url.getValue().trim())) url.setValue(url.getValue().trim()); // check length validateNoLongerThan(maxLength, url); // add protocall if missing if (!urlValidator.isValid(url.getValue())) { String tempUrl = "http://" + url.getValue(); // test validity again if (urlValidator.isValid(tempUrl)) url.setValue("http://" + url.getValue()); else setError(url, "common.invalid_url"); } } } protected void validateNoLongerThan(int maxLength, Text text) { if (PojoUtil.isEmpty(text)) { return; } if (text.getValue().length() > maxLength) { setError(text, "manualWork.length_less_X", maxLength); } } void givenNameValidate(Text givenName) { // validate given name isn't blank givenName.setErrors(new ArrayList<String>()); if (givenName.getValue() == null || givenName.getValue().trim().isEmpty()) { setError(givenName, "NotBlank.registrationForm.givenNames"); } } @ModelAttribute("searchBaseUrl") protected String createSearchBaseUrl() { String baseUrlWithCorrectedProtocol = orcidUrlManager.getBaseUrl().replaceAll("^https?:", ""); String baseUrlWithCorrectedContext = baseUrlWithCorrectedProtocol.replaceAll("/orcid-web$", "/orcid-pub-web"); return baseUrlWithCorrectedContext + "/v1.2/search/orcid-bio/"; } @ModelAttribute("locked") public boolean isLocked() { OrcidProfile profile = getEffectiveProfile(); if (profile == null) return false; return profile.isLocked(); } }