/** * ============================================================================= * * 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.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang.NotImplementedException; import org.apache.commons.lang.StringUtils; import org.apache.commons.validator.routines.UrlValidator; import org.orcid.core.manager.ClientDetailsManager; import org.orcid.core.manager.LoadOptions; import org.orcid.core.manager.OrcidSSOManager; import org.orcid.core.manager.ProfileEntityCacheManager; import org.orcid.core.manager.ProfileEntityManager; import org.orcid.jaxb.model.clientgroup.RedirectUriType; import org.orcid.jaxb.model.message.OrcidProfile; import org.orcid.jaxb.model.message.OrcidType; import org.orcid.persistence.jpa.entities.ClientDetailsEntity; import org.orcid.persistence.jpa.entities.ProfileEntity; import org.orcid.pojo.ajaxForm.PojoUtil; import org.orcid.pojo.ajaxForm.RedirectUri; import org.orcid.pojo.ajaxForm.SSOCredentials; import org.orcid.pojo.ajaxForm.Text; import org.orcid.utils.OrcidStringUtils; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ModelAttribute; 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.ResponseBody; import org.springframework.web.servlet.ModelAndView; @Controller("developerToolsController") @RequestMapping(value = { "/developer-tools" }) @PreAuthorize("!@sourceManager.isInDelegationMode() OR @sourceManager.isDelegatedByAnAdmin()") public class DeveloperToolsController extends BaseWorkspaceController { private static int CLIENT_NAME_LENGTH = 255; @Resource private OrcidSSOManager orcidSSOManager; @Resource private ProfileEntityManager profileEntityManager; @Resource private ClientDetailsManager clientDetailsManager; @Resource(name = "profileEntityCacheManager") ProfileEntityCacheManager profileEntityCacheManager; @RequestMapping public ModelAndView manageDeveloperTools() { ModelAndView mav = new ModelAndView("developer_tools"); OrcidProfile profile = orcidProfileManager.retrieveOrcidProfile(getCurrentUserOrcid(), LoadOptions.BIO_AND_INTERNAL_ONLY); mav.addObject("profile", profile); try { if (!profile.getOrcidInternal().getPreferences().getDeveloperToolsEnabled().isValue()) { if (OrcidType.USER.equals(profile.getType())) { mav.addObject("error", getMessage("manage.developer_tools.user.error.enable_developer_tools")); } else { mav.addObject("error", getMessage("manage.developer_tools.user.error.invalid_user_type")); } } } catch (NullPointerException npe) { } return mav; } @RequestMapping(value = "/get-empty-redirect-uri.json", method = RequestMethod.GET) public @ResponseBody RedirectUri getEmptyRedirectUri(HttpServletRequest request) { RedirectUri result = new RedirectUri(); result.setValue(new Text()); result.setActType(Text.valueOf("")); result.setGeoArea(Text.valueOf("")); result.setType(Text.valueOf(RedirectUriType.DEFAULT.name())); return result; } @RequestMapping(value = "/get-empty-sso-credential.json", method = RequestMethod.GET) public @ResponseBody SSOCredentials getEmptySSOCredentials(HttpServletRequest request) { SSOCredentials emptyObject = new SSOCredentials(); emptyObject.setClientSecret(Text.valueOf(StringUtils.EMPTY)); RedirectUri redirectUri = new RedirectUri(); redirectUri.setValue(new Text()); redirectUri.setType(Text.valueOf(RedirectUriType.DEFAULT.name())); Set<RedirectUri> set = new HashSet<RedirectUri>(); set.add(redirectUri); emptyObject.setRedirectUris(set); return emptyObject; } @RequestMapping(value = "/generate-sso-credentials.json", method = RequestMethod.POST) public @ResponseBody SSOCredentials generateSSOCredentialsJson(@RequestBody SSOCredentials ssoCredentials) { boolean hasErrors = validateSSOCredentials(ssoCredentials); if (!hasErrors) { OrcidProfile profile = getEffectiveProfile(); String orcid = profile.getOrcidIdentifier().getPath(); Set<String> redirectUriStrings = new HashSet<String>(); for (RedirectUri redirectUri : ssoCredentials.getRedirectUris()) { redirectUriStrings.add(redirectUri.getValue().getValue()); } String clientName = ssoCredentials.getClientName().getValue(); String clientDescription = ssoCredentials.getClientDescription().getValue(); String clientWebsite = ssoCredentials.getClientWebsite().getValue(); ClientDetailsEntity clientDetails = orcidSSOManager.grantSSOAccess(orcid, clientName, clientDescription, clientWebsite, redirectUriStrings); ssoCredentials = SSOCredentials.toSSOCredentials(clientDetails); } else { List<String> errors = ssoCredentials.getErrors(); if (errors == null) errors = new ArrayList<String>(); if (ssoCredentials.getClientName().getErrors() != null && !ssoCredentials.getClientName().getErrors().isEmpty()) errors.addAll(ssoCredentials.getClientName().getErrors()); if (ssoCredentials.getClientDescription().getErrors() != null && !ssoCredentials.getClientDescription().getErrors().isEmpty()) errors.addAll(ssoCredentials.getClientDescription().getErrors()); if (ssoCredentials.getClientWebsite().getErrors() != null && !ssoCredentials.getClientWebsite().getErrors().isEmpty()) errors.addAll(ssoCredentials.getClientWebsite().getErrors()); if (ssoCredentials.getRedirectUris() != null) { for (RedirectUri redirectUri : ssoCredentials.getRedirectUris()) { if (redirectUri.getErrors() != null && !redirectUri.getErrors().isEmpty()) errors.addAll(redirectUri.getErrors()); } } ssoCredentials.setErrors(errors); } return ssoCredentials; } @RequestMapping(value = "/update-user-credentials.json", method = RequestMethod.POST) public @ResponseBody SSOCredentials updateUserCredentials(@RequestBody SSOCredentials ssoCredentials) { boolean hasErrors = validateSSOCredentials(ssoCredentials); if (!hasErrors) { OrcidProfile profile = getEffectiveProfile(); String orcid = profile.getOrcidIdentifier().getPath(); Set<String> redirectUriStrings = new HashSet<String>(); for (RedirectUri redirectUri : ssoCredentials.getRedirectUris()) { redirectUriStrings.add(redirectUri.getValue().getValue()); } String clientName = ssoCredentials.getClientName().getValue(); String clientDescription = ssoCredentials.getClientDescription().getValue(); String clientWebsite = ssoCredentials.getClientWebsite().getValue(); ClientDetailsEntity clientDetails = orcidSSOManager.updateUserCredentials(orcid, clientName, clientDescription, clientWebsite, redirectUriStrings); ssoCredentials = SSOCredentials.toSSOCredentials(clientDetails); ssoCredentials.setClientWebsite(Text.valueOf(clientWebsite)); } else { List<String> errors = ssoCredentials.getErrors(); if (errors == null) errors = new ArrayList<String>(); if (ssoCredentials.getClientName().getErrors() != null && !ssoCredentials.getClientName().getErrors().isEmpty()) errors.addAll(ssoCredentials.getClientName().getErrors()); if (ssoCredentials.getClientDescription().getErrors() != null && !ssoCredentials.getClientDescription().getErrors().isEmpty()) errors.addAll(ssoCredentials.getClientDescription().getErrors()); if (ssoCredentials.getClientWebsite().getErrors() != null && !ssoCredentials.getClientWebsite().getErrors().isEmpty()) errors.addAll(ssoCredentials.getClientWebsite().getErrors()); for (RedirectUri redirectUri : ssoCredentials.getRedirectUris()) { if (redirectUri.getErrors() != null && !redirectUri.getErrors().isEmpty()) errors.addAll(redirectUri.getErrors()); } ssoCredentials.setErrors(errors); } return ssoCredentials; } @RequestMapping(value = "/reset-client-secret", method = RequestMethod.POST) public @ResponseBody boolean resetClientSecret(@RequestBody String clientId) { //Verify this client belongs to the user ClientDetailsEntity clientDetails = clientDetailsManager.findByClientId(clientId); if(clientDetails == null) return false; ProfileEntity groupProfile = profileEntityCacheManager.retrieve(clientDetails.getGroupProfileId()); if(groupProfile == null) return false; if(!groupProfile.getId().equals(getCurrentUserOrcid())) return false; return orcidSSOManager.resetClientSecret(clientId); } @RequestMapping(value = "/get-sso-credentials.json", method = RequestMethod.GET) public @ResponseBody SSOCredentials getSSOCredentialsJson() { SSOCredentials credentials = new SSOCredentials(); String userOrcid = getEffectiveUserOrcid(); ClientDetailsEntity existingClientDetails = orcidSSOManager.getUserCredentials(userOrcid); if (existingClientDetails != null) credentials = SSOCredentials.toSSOCredentials(existingClientDetails); return credentials; } @RequestMapping(value = "/revoke-sso-credentials.json", method = RequestMethod.POST) public @ResponseBody SSOCredentials revokeSSOCredentials(HttpServletRequest request) { throw new NotImplementedException(); } /** * Validates the ssoCredentials object * * @param ssoCredentials * @return true if any error is found in the ssoCredentials object * */ private boolean validateSSOCredentials(SSOCredentials ssoCredentials) { boolean hasErrors = false; Set<RedirectUri> redirectUris = ssoCredentials.getRedirectUris(); if (PojoUtil.isEmpty(ssoCredentials.getClientName())) { if (ssoCredentials.getClientName() == null) { ssoCredentials.setClientName(new Text()); } ssoCredentials.getClientName().setErrors(Arrays.asList(getMessage("manage.developer_tools.name_not_empty"))); hasErrors = true; } else if (ssoCredentials.getClientName().getValue().length() > CLIENT_NAME_LENGTH) { ssoCredentials.getClientName().setErrors(Arrays.asList(getMessage("manage.developer_tools.name_too_long"))); hasErrors = true; } else if(OrcidStringUtils.hasHtml(ssoCredentials.getClientName().getValue())){ ssoCredentials.getClientName().setErrors(Arrays.asList(getMessage("manage.developer_tools.name.html"))); hasErrors = true; } else { ssoCredentials.getClientName().setErrors(new ArrayList<String>()); } if (PojoUtil.isEmpty(ssoCredentials.getClientDescription())) { if (ssoCredentials.getClientDescription() == null) { ssoCredentials.setClientDescription(new Text()); } ssoCredentials.getClientDescription().setErrors(Arrays.asList(getMessage("manage.developer_tools.description_not_empty"))); hasErrors = true; } else if(OrcidStringUtils.hasHtml(ssoCredentials.getClientDescription().getValue())) { ssoCredentials.getClientDescription().setErrors(Arrays.asList(getMessage("manage.developer_tools.description.html"))); hasErrors = true; } else { ssoCredentials.getClientDescription().setErrors(new ArrayList<String>()); } if (PojoUtil.isEmpty(ssoCredentials.getClientWebsite())) { if (ssoCredentials.getClientWebsite() == null) { ssoCredentials.setClientWebsite(new Text()); } ssoCredentials.getClientWebsite().setErrors(Arrays.asList(getMessage("manage.developer_tools.website_not_empty"))); hasErrors = true; } else { List<String> errors = new ArrayList<String>(); String[] schemes = { "http", "https", "ftp" }; UrlValidator urlValidator = new UrlValidator(schemes); String websiteString = ssoCredentials.getClientWebsite().getValue(); if (!urlValidator.isValid(websiteString)) websiteString = "http://" + websiteString; // test validity again if (!urlValidator.isValid(websiteString)) { errors.add(getMessage("manage.developer_tools.invalid_website")); } ssoCredentials.getClientWebsite().setErrors(errors); } if (redirectUris == null || redirectUris.isEmpty()) { List<String> errors = new ArrayList<String>(); errors.add(getMessage("manage.developer_tools.at_least_one")); ssoCredentials.setErrors(errors); hasErrors = true; } else { for (RedirectUri redirectUri : redirectUris) { List<String> errors = validateRedirectUri(redirectUri); if (errors != null) { redirectUri.setErrors(errors); hasErrors = true; } } } return hasErrors; } /** * Checks if a redirect uri contains a valid URI associated to it * * @param redirectUri * @return null if there are no errors, an List of strings containing error * messages if any error happens * */ private List<String> validateRedirectUri(RedirectUri redirectUri) { List<String> errors = null; String[] schemes = { "http", "https" }; UrlValidator urlValidator = new UrlValidator(schemes, UrlValidator.ALLOW_LOCAL_URLS); if (!PojoUtil.isEmpty(redirectUri.getValue())) { try { String redirectUriString = redirectUri.getValue().getValue(); if (!urlValidator.isValid(redirectUriString)) { errors = new ArrayList<String>(); errors.add(getMessage("manage.developer_tools.invalid_redirect_uri")); } } catch (NullPointerException npe) { errors = new ArrayList<String>(); errors.add(getMessage("manage.developer_tools.empty_redirect_uri")); } } else { errors = new ArrayList<String>(); errors.add(getMessage("manage.developer_tools.empty_redirect_uri")); } return errors; } /** * Enable developer tools on the current profile * * @return true if the developer tools where enabled on the profile * */ @RequestMapping(value = "/enable-developer-tools.json", method = RequestMethod.POST) public @ResponseBody boolean enableDeveloperTools(HttpServletRequest request) { OrcidProfile profile = getEffectiveProfile(); boolean updated = true; if (profile.getOrcidInternal() != null && profile.getOrcidInternal().getPreferences() != null && profile.getOrcidInternal().getPreferences().getDeveloperToolsEnabled() != null && !profile.getOrcidInternal().getPreferences().getDeveloperToolsEnabled().isValue()) { updated = profileEntityManager.enableDeveloperTools(profile); } return updated; } /** * Disable developer tools on the current profile * * @return true if the developer tools were disabled on the profile * */ @RequestMapping(value = "/disable-developer-tools.json", method = RequestMethod.POST) public @ResponseBody boolean disableDeveloperTools(HttpServletRequest request) { OrcidProfile profile = getEffectiveProfile(); boolean updated = true; if (profile.getOrcidInternal() != null && profile.getOrcidInternal().getPreferences() != null && profile.getOrcidInternal().getPreferences().getDeveloperToolsEnabled() != null && profile.getOrcidInternal().getPreferences().getDeveloperToolsEnabled().isValue()) { // Disable the developer tools updated = profileEntityManager.disableDeveloperTools(profile); // Disable the sso access orcidSSOManager.revokeSSOAccess(profile.getOrcidIdentifier().getPath()); } return updated; } @ModelAttribute("hasVerifiedEmail") public boolean hasVerifiedEmail() { OrcidProfile profile = getEffectiveProfile(); if (profile == null || profile.getOrcidBio() == null || profile.getOrcidBio().getContactDetails() == null) return false; return profile.getOrcidBio().getContactDetails().anyEmailVerified(); } }