View Javadoc
1   /*
2    * Copyright 2002-2011 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.security.oauth2.provider.approval;
18  
19  import java.util.HashMap;
20  import java.util.Map;
21  import java.util.Set;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.springframework.beans.factory.InitializingBean;
26  import org.springframework.security.core.Authentication;
27  import org.springframework.security.oauth2.common.OAuth2AccessToken;
28  import org.springframework.security.oauth2.common.util.OAuth2Utils;
29  import org.springframework.security.oauth2.provider.AuthorizationRequest;
30  import org.springframework.security.oauth2.provider.ClientDetails;
31  import org.springframework.security.oauth2.provider.ClientDetailsService;
32  import org.springframework.security.oauth2.provider.ClientRegistrationException;
33  import org.springframework.security.oauth2.provider.OAuth2Authentication;
34  import org.springframework.security.oauth2.provider.OAuth2Request;
35  import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
36  import org.springframework.security.oauth2.provider.token.TokenStore;
37  import org.springframework.util.Assert;
38  
39  /**
40   * A user approval handler that remembers approval decisions by consulting existing tokens.
41   * 
42   * @author Dave Syer
43   * 
44   */
45  public class TokenStoreUserApprovalHandler implements UserApprovalHandler, InitializingBean {
46  
47  	private static Log logger = LogFactory.getLog(TokenStoreUserApprovalHandler.class);
48  
49  	private String approvalParameter = OAuth2Utils.USER_OAUTH_APPROVAL;
50  	
51  	private TokenStore tokenStore;
52  	
53  	private ClientDetailsService clientDetailsService;
54  	
55  	/**
56  	 * Service to load client details (optional) for auto approval checks.
57  	 * 
58  	 * @param clientDetailsService a client details service
59  	 */
60  	public void setClientDetailsService(ClientDetailsService clientDetailsService) {
61  		this.clientDetailsService = clientDetailsService;
62  	}
63  
64  	/**
65  	 * @param approvalParameter the approvalParameter to set
66  	 */
67  	public void setApprovalParameter(String approvalParameter) {
68  		this.approvalParameter = approvalParameter;
69  	}
70  
71  	/**
72  	 * @param tokenStore the token store to set
73  	 */
74  	public void setTokenStore(TokenStore tokenStore) {
75  		this.tokenStore = tokenStore;
76  	}
77  
78  	private OAuth2RequestFactory requestFactory;
79  	
80  	public void setRequestFactory(OAuth2RequestFactory requestFactory) {
81  		this.requestFactory = requestFactory;
82  	}
83  	
84  	@Override
85  	public void afterPropertiesSet() {
86  		Assert.state(tokenStore != null, "TokenStore must be provided");
87  		Assert.state(requestFactory != null, "OAuth2RequestFactory must be provided");
88  	}
89  	
90  	/**
91  	 * Basic implementation just requires the authorization request to be explicitly approved and the user to be
92  	 * authenticated.
93  	 * 
94  	 * @param authorizationRequest The authorization request.
95  	 * @param userAuthentication the current user authentication
96  	 * 
97  	 * @return Whether the specified request has been approved by the current user.
98  	 */
99  	@Override
100 	public boolean isApproved(AuthorizationRequest authorizationRequest, Authentication userAuthentication) {
101 		return authorizationRequest.isApproved();
102 	}
103 
104 	@Override
105 	public AuthorizationRequestringframework/security/oauth2/provider/AuthorizationRequest.html#AuthorizationRequest">AuthorizationRequest checkForPreApproval(AuthorizationRequest authorizationRequest, Authentication userAuthentication) {
106 		
107 		boolean approved = false;
108 		
109 		String clientId = authorizationRequest.getClientId();
110 		Set<String> scopes = authorizationRequest.getScope();
111 		if (clientDetailsService!=null) {
112 			try {
113 				ClientDetails client = clientDetailsService.loadClientByClientId(clientId);
114 				approved = true;
115 				for (String scope : scopes) {
116 					if (!client.isAutoApprove(scope)) {
117 						approved = false;
118 					}
119 				}
120 				if (approved) {
121 					authorizationRequest.setApproved(true);
122 					return authorizationRequest;
123 				}
124 			}
125 			catch (ClientRegistrationException e) {
126 				logger.warn("Client registration problem prevent autoapproval check for client=" + clientId);
127 			}		
128 		}
129 		
130 		OAuth2Request storedOAuth2Request = requestFactory.createOAuth2Request(authorizationRequest);
131 		
132 		OAuth2Authentication authentication = new OAuth2Authentication(storedOAuth2Request, userAuthentication);
133 		if (logger.isDebugEnabled()) {
134 			StringBuilder builder = new StringBuilder("Looking up existing token for ");
135 			builder.append("client_id=" + clientId);
136 			builder.append(", scope=" + scopes);
137 			builder.append(" and username=" + userAuthentication.getName());
138 			logger.debug(builder.toString());
139 		}
140 
141 		OAuth2AccessToken accessToken = tokenStore.getAccessToken(authentication);
142 		if (logger.isDebugEnabled()) {
143 			logger.debug("Existing access token=" + accessToken);
144 		}
145 		if (accessToken != null && !accessToken.isExpired()) {
146 			if (logger.isDebugEnabled()) {
147 				logger.debug("User already approved with token=" + accessToken);
148 			}
149 			// A token was already granted and is still valid, so this is already approved
150 			approved = true;
151 		}
152 		else {
153 			logger.debug("Checking explicit approval");
154 			approved = userAuthentication.isAuthenticated() && approved;
155 		}
156 		
157 		authorizationRequest.setApproved(approved);
158 
159 		return authorizationRequest;
160 	}
161 
162 	@Override
163 	public AuthorizationRequestringframework/security/oauth2/provider/AuthorizationRequest.html#AuthorizationRequest">AuthorizationRequest updateAfterApproval(AuthorizationRequest authorizationRequest, Authentication userAuthentication) {
164 		Map<String, String> approvalParameters = authorizationRequest.getApprovalParameters();
165 		String flag = approvalParameters.get(approvalParameter);
166 		boolean approved = flag != null && flag.toLowerCase().equals("true");
167 		authorizationRequest.setApproved(approved);
168 		return authorizationRequest;
169 	}
170 
171 	@Override
172 	public Map<String, Object> getUserApprovalRequest(AuthorizationRequest authorizationRequest,
173 			Authentication userAuthentication) {
174 		Map<String, Object> model = new HashMap<String, Object>();
175 		// In case of a redirect we might want the request parameters to be included
176 		model.putAll(authorizationRequest.getRequestParameters());
177 		return model;
178 	}
179 }