/** * ============================================================================= * * 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.oauth.service; import java.net.URI; import java.net.URISyntaxException; import java.security.Principal; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.orcid.jaxb.model.message.ScopePathType; import org.springframework.security.oauth2.common.exceptions.ClientAuthenticationException; import org.springframework.security.oauth2.common.exceptions.InvalidScopeException; import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; import org.springframework.security.oauth2.common.exceptions.RedirectMismatchException; import org.springframework.security.oauth2.common.util.OAuth2Utils; import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint; import org.springframework.web.HttpSessionRequiredException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.support.SessionStatus; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.view.RedirectView; @RequestMapping(value = "/oauth/authorize") public class OrcidAuthorizationEndpoint extends AuthorizationEndpoint { private String redirectUriError = "forward:/oauth/error/redirect-uri-mismatch"; private String oauthError = "forward:/oauth/error"; private OrcidOAuth2RequestValidator orcidOAuth2RequestValidator; @Override @ExceptionHandler(HttpSessionRequiredException.class) public ModelAndView handleHttpSessionRequiredException(HttpSessionRequiredException e, ServletWebRequest webRequest) throws Exception { return new ModelAndView("redirect:" + buildRedirectUri(webRequest).toString()); } @Override @ExceptionHandler(OAuth2Exception.class) public ModelAndView handleOAuth2Exception(OAuth2Exception e, ServletWebRequest webRequest) throws Exception { logger.info("Handling OAuth2 error: " + e.getSummary()); if (e instanceof RedirectMismatchException) { return new ModelAndView(redirectUriError); } else if (e instanceof ClientAuthenticationException) { return new ModelAndView(oauthError); } return super.handleOAuth2Exception(e, webRequest); } @Override @RequestMapping public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<String, String> requestParameters, SessionStatus sessionStatus, Principal principal) { try { trimRequestParameters(requestParameters); } catch (InvalidScopeException ise) { String redirectUri = requestParameters.get("redirect_uri"); String redirectUriWithParams = ""; if(redirectUri != null) { redirectUriWithParams = redirectUri; } redirectUriWithParams += "?error=invalid_scope&error_description=" + ise.getMessage(); RedirectView rView = new RedirectView(redirectUriWithParams); ModelAndView error = new ModelAndView(); error.setView(rView); return error; } return super.authorize(model, requestParameters, sessionStatus, principal); } private void trimRequestParameters(Map<String, String> requestParameters) throws InvalidScopeException { for(Map.Entry<String,String> entry : requestParameters.entrySet()) { requestParameters.put(entry.getKey(), entry.getValue().trim()); } String scopes = requestParameters.get(OAuth2Utils.SCOPE); if(scopes != null) { requestParameters.put(OAuth2Utils.SCOPE, trimClientCredentialScopes(scopes.trim().replaceAll(" +", " "))); } } /** * Validate if the given client have the defined scope * @param scopes a space or comma separated list of scopes * @param clientDetails * @throws InvalidScopeException in case the given client doesnt have any of the given scopes * */ public void validateScope(String scopes, ClientDetails clientDetails) throws InvalidScopeException { Map<String, String> parameters = new HashMap<String, String>(); parameters.put(OAuth2Utils.SCOPE, scopes); //Check the user have permissions to the other scopes orcidOAuth2RequestValidator.validateParameters(parameters, clientDetails); } private URI buildRedirectUri(ServletWebRequest webRequest) throws URISyntaxException { String[] referers = webRequest.getHeaderValues("referer"); if (referers != null && referers.length > 0) { return new URI(referers[0]); } String uri = "/session-expired"; String contextPath = webRequest.getContextPath(); if (contextPath != null) { uri = contextPath + uri; } return new URI(uri); } private String trimClientCredentialScopes(String scopes) throws InvalidScopeException { String result = scopes; for (String scope : OAuth2Utils.parseParameterList(scopes)) { if(StringUtils.isNotBlank(scope)) { ScopePathType scopeType = null; try { scopeType = ScopePathType.fromValue(scope); } catch(Exception e) { throw new InvalidScopeException("Invalid scope: " + scope); } if (scopeType.isClientCreditalScope()) { if(scopes.contains(ScopePathType.ORCID_PROFILE_CREATE.getContent())) result = scopes.replaceAll(ScopePathType.ORCID_PROFILE_CREATE.getContent(), ""); else if(scopes.contains(ScopePathType.READ_PUBLIC.getContent())) result = scopes.replaceAll(ScopePathType.READ_PUBLIC.getContent(), ""); else if(scopes.contains(ScopePathType.WEBHOOK.getContent())) result = scopes.replaceAll(ScopePathType.WEBHOOK.getContent(), ""); } } } return result; } public OrcidOAuth2RequestValidator getOrcidOAuth2RequestValidator() { return orcidOAuth2RequestValidator; } public void setOrcidOAuth2RequestValidator(OrcidOAuth2RequestValidator orcidOAuth2RequestValidator) { this.orcidOAuth2RequestValidator = orcidOAuth2RequestValidator; } }