/** * ============================================================================= * * 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; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.annotation.Resource; import org.apache.commons.lang.StringUtils; import org.orcid.core.constants.OrcidOauth2Constants; import org.orcid.core.manager.ClientDetailsEntityCacheManager; import org.orcid.core.oauth.service.OrcidOAuth2RequestValidator; import org.orcid.jaxb.model.message.ScopePathType; import org.orcid.persistence.jpa.entities.ClientDetailsEntity; import org.orcid.pojo.ajaxForm.PojoUtil; import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; import org.springframework.security.oauth2.common.exceptions.InvalidScopeException; import org.springframework.security.oauth2.common.util.OAuth2Utils; import org.springframework.security.oauth2.provider.AuthorizationRequest; import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.oauth2.provider.OAuth2RequestFactory; import org.springframework.security.oauth2.provider.TokenRequest; /** * @author Declan Newman (declan) Date: 10/05/2012 */ public class OrcidClientCredentialsChecker { @Resource private ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; @Resource private OrcidOAuth2RequestValidator orcidOAuth2RequestValidator; private final OAuth2RequestFactory oAuth2RequestFactory; public void setOrcidOAuth2RequestValidator(OrcidOAuth2RequestValidator orcidOAuth2RequestValidator) { this.orcidOAuth2RequestValidator = orcidOAuth2RequestValidator; } public OrcidClientCredentialsChecker(OAuth2RequestFactory oAuth2RequestFactory) { this.oAuth2RequestFactory = oAuth2RequestFactory; } public void setClientDetailsEntityCacheManager(ClientDetailsEntityCacheManager clientDetailsEntityCacheManager) { this.clientDetailsEntityCacheManager = clientDetailsEntityCacheManager; } public OAuth2Request validateCredentials(String grantType, TokenRequest tokenRequest) { String clientId = tokenRequest.getClientId(); String scopesString = tokenRequest.getRequestParameters().get(OrcidOauth2Constants.SCOPE_PARAM); Set<String> scopes = new HashSet<String>(); if(!PojoUtil.isEmpty(scopesString)) { scopes = OAuth2Utils.parseParameterList(scopesString); } ClientDetailsEntity clientDetails = clientDetailsEntityCacheManager.retrieve(clientId); orcidOAuth2RequestValidator.validateClientIsEnabled(clientDetails); validateGrantType(grantType, clientDetails); if (scopes != null) { validateScope(clientDetails, scopes); } Map<String, String> authorizationParams = new HashMap<String, String>(); authorizationParams.putAll(tokenRequest.getRequestParameters()); authorizationParams.put(OrcidOauth2Constants.GRANT_TYPE, grantType); authorizationParams.put(OAuth2Utils.SCOPE, StringUtils.join(scopes, ' ')); authorizationParams.put(OAuth2Utils.CLIENT_ID, clientId); AuthorizationRequest authorizationRequest = oAuth2RequestFactory.createAuthorizationRequest(authorizationParams); authorizationRequest.setAuthorities(clientDetails.getAuthorities()); authorizationRequest.setResourceIds(clientDetails.getResourceIds()); authorizationRequest.setApproved(true); return oAuth2RequestFactory.createOAuth2Request(authorizationRequest); } private void validateScope(ClientDetails clientDetails, Set<String> scopes) { if (clientDetails.isScoped()) { Set<String> validScope = clientDetails.getScope(); if (scopes.isEmpty()) { throw new InvalidScopeException("Invalid scope (none)"); } else if (!containsAny(validScope, ScopePathType.ORCID_PROFILE_CREATE, ScopePathType.WEBHOOK, ScopePathType.PREMIUM_NOTIFICATION, ScopePathType.GROUP_ID_RECORD_READ, ScopePathType.GROUP_ID_RECORD_UPDATE) && !scopes.contains(ScopePathType.READ_PUBLIC.value()) && scopes.size() == 1) { throw new InvalidScopeException("Invalid scope" + (scopes != null && scopes.size() > 1 ? "s: " : ": " + "") + OAuth2Utils.formatParameterList(scopes), validScope); } // The Read public does not have to be granted. It's the implied // read level. We let this through, regardless if (scopes.size() == 1 && scopes.iterator().next().equals(ScopePathType.READ_PUBLIC.value())) { return; } for (String scope : scopes) { if (!validScope.contains(scope)) { throw new InvalidScopeException("Invalid scope: " + scope, validScope); } } } } private boolean containsAny(Set<String> scopes, ScopePathType... scopePathTypes) { for (ScopePathType scopePathType : scopePathTypes) { if (scopes.contains(scopePathType.value())) { return true; } } return false; } private void validateGrantType(String grantType, ClientDetails clientDetails) { Collection<String> authorizedGrantTypes = clientDetails.getAuthorizedGrantTypes(); if (authorizedGrantTypes != null && !authorizedGrantTypes.isEmpty() && !authorizedGrantTypes.contains(grantType)) { throw new InvalidGrantException("Unauthorized grant type: " + grantType); } } }