001/******************************************************************************* 002 * Copyright 2017 The MIT Internet Trust Consortium 003 * 004 * Portions copyright 2011-2013 The MITRE Corporation 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 *******************************************************************************/ 018/** 019 * 020 */ 021package org.mitre.oauth2.token; 022 023import java.util.HashSet; 024import java.util.Set; 025 026import org.mitre.oauth2.model.OAuth2AccessTokenEntity; 027import org.mitre.oauth2.service.ClientDetailsEntityService; 028import org.mitre.oauth2.service.OAuth2TokenEntityService; 029import org.springframework.beans.factory.annotation.Autowired; 030import org.springframework.security.core.AuthenticationException; 031import org.springframework.security.oauth2.common.exceptions.InvalidScopeException; 032import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; 033import org.springframework.security.oauth2.provider.ClientDetails; 034import org.springframework.security.oauth2.provider.OAuth2Authentication; 035import org.springframework.security.oauth2.provider.OAuth2RequestFactory; 036import org.springframework.security.oauth2.provider.TokenRequest; 037import org.springframework.security.oauth2.provider.token.AbstractTokenGranter; 038import org.springframework.stereotype.Component; 039 040import com.google.common.collect.Sets; 041 042/** 043 * @author jricher 044 * 045 */ 046@Component("chainedTokenGranter") 047public class ChainedTokenGranter extends AbstractTokenGranter { 048 049 public static final String GRANT_TYPE = "urn:ietf:params:oauth:grant_type:redelegate"; 050 051 // keep down-cast versions so we can get to the right queries 052 private OAuth2TokenEntityService tokenServices; 053 054 /** 055 * @param tokenServices 056 * @param clientDetailsService 057 * @param GRANT_TYPE 058 */ 059 @Autowired 060 public ChainedTokenGranter(OAuth2TokenEntityService tokenServices, ClientDetailsEntityService clientDetailsService, OAuth2RequestFactory requestFactory) { 061 super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE); 062 this.tokenServices = tokenServices; 063 } 064 065 /* (non-Javadoc) 066 * @see org.springframework.security.oauth2.provider.token.AbstractTokenGranter#getOAuth2Authentication(org.springframework.security.oauth2.provider.AuthorizationRequest) 067 */ 068 @Override 069 protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) throws AuthenticationException, InvalidTokenException { 070 // read and load up the existing token 071 String incomingTokenValue = tokenRequest.getRequestParameters().get("token"); 072 OAuth2AccessTokenEntity incomingToken = tokenServices.readAccessToken(incomingTokenValue); 073 074 // check for scoping in the request, can't up-scope with a chained request 075 Set<String> approvedScopes = incomingToken.getScope(); 076 Set<String> requestedScopes = tokenRequest.getScope(); 077 078 if (requestedScopes == null) { 079 requestedScopes = new HashSet<>(); 080 } 081 082 // do a check on the requested scopes -- if they exactly match the client scopes, they were probably shadowed by the token granter 083 if (client.getScope().equals(requestedScopes)) { 084 requestedScopes = new HashSet<>(); 085 } 086 087 // if our scopes are a valid subset of what's allowed, we can continue 088 if (approvedScopes.containsAll(requestedScopes)) { 089 090 if (requestedScopes.isEmpty()) { 091 // if there are no scopes, inherit the original scopes from the token 092 tokenRequest.setScope(approvedScopes); 093 } else { 094 // if scopes were asked for, give only the subset of scopes requested 095 // this allows safe downscoping 096 tokenRequest.setScope(Sets.intersection(requestedScopes, approvedScopes)); 097 } 098 099 // NOTE: don't revoke the existing access token 100 101 // create a new access token 102 OAuth2Authentication authentication = new OAuth2Authentication(getRequestFactory().createOAuth2Request(client, tokenRequest), incomingToken.getAuthenticationHolder().getAuthentication().getUserAuthentication()); 103 104 return authentication; 105 106 } else { 107 throw new InvalidScopeException("Invalid scope requested in chained request", approvedScopes); 108 } 109 110 } 111 112}