View Javadoc
1   /*
2    * Copyright 2008-2009 Web Cohesion
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5    * the License. You may obtain a copy of the License at
6    * 
7    * https://www.apache.org/licenses/LICENSE-2.0
8    * 
9    * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10   * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11   * specific language governing permissions and limitations under the License.
12   */
13  
14  package org.springframework.security.oauth2.config.xml;
15  
16  import org.springframework.beans.BeanMetadataElement;
17  import org.springframework.beans.factory.config.RuntimeBeanReference;
18  import org.springframework.beans.factory.config.TypedStringValue;
19  import org.springframework.beans.factory.support.AbstractBeanDefinition;
20  import org.springframework.beans.factory.support.BeanDefinitionBuilder;
21  import org.springframework.beans.factory.support.ManagedList;
22  import org.springframework.beans.factory.support.ManagedMap;
23  import org.springframework.beans.factory.xml.ParserContext;
24  import org.springframework.security.config.BeanIds;
25  import org.springframework.security.oauth2.provider.CompositeTokenGranter;
26  import org.springframework.security.oauth2.provider.approval.DefaultUserApprovalHandler;
27  import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter;
28  import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter;
29  import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
30  import org.springframework.security.oauth2.provider.endpoint.*;
31  import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter;
32  import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
33  import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter;
34  import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
35  import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
36  import org.springframework.util.StringUtils;
37  import org.springframework.util.xml.DomUtils;
38  import org.w3c.dom.Element;
39  
40  import java.util.List;
41  
42  /**
43   * Parser for the OAuth "provider" element.
44   * 
45   * @author Ryan Heaton
46   * @author Dave Syer
47   */
48  public class AuthorizationServerBeanDefinitionParser
49  		extends ProviderBeanDefinitionParser {
50  
51  	@Override
52  	protected AbstractBeanDefinition parseEndpointAndReturnFilter(Element element,
53  			ParserContext parserContext, String tokenServicesRef, String serializerRef) {
54  
55  		String clientDetailsRef = element.getAttribute("client-details-service-ref");
56  		String oAuth2RequestFactoryRef = element
57  				.getAttribute("authorization-request-manager-ref");
58  		String tokenEndpointUrl = element.getAttribute("token-endpoint-url");
59  		String checkTokenUrl = element.getAttribute("check-token-endpoint-url");
60  		String enableCheckToken = element.getAttribute("check-token-enabled");
61  		String authorizationEndpointUrl = element
62  				.getAttribute("authorization-endpoint-url");
63  		String tokenGranterRef = element.getAttribute("token-granter-ref");
64  		String redirectStrategyRef = element.getAttribute("redirect-strategy-ref");
65  		String userApprovalHandlerRef = element.getAttribute("user-approval-handler-ref");
66  
67  		String approvalPage = element.getAttribute("user-approval-page");
68  		String errorPage = element.getAttribute("error-page");
69  		String approvalParameter = element.getAttribute("approval-parameter-name");
70  		String redirectResolverRef = element.getAttribute("redirect-resolver-ref");
71  
72  		String oAuth2RequestValidatorRef = element.getAttribute("request-validator-ref");
73  
74  		// Create a bean definition speculatively for the auth endpoint
75  		BeanDefinitionBuilder authorizationEndpointBean = BeanDefinitionBuilder
76  				.rootBeanDefinition(AuthorizationEndpoint.class);
77  
78  		if (!StringUtils.hasText(clientDetailsRef)) {
79  			parserContext.getReaderContext()
80  					.error("ClientDetailsService must be provided", element);
81  			return null;
82  		}
83  
84  		if (!StringUtils.hasText(oAuth2RequestValidatorRef)) {
85  			oAuth2RequestValidatorRef = "defaultOAuth2RequestValidator";
86  			BeanDefinitionBuilder oAuth2RequestValidator = BeanDefinitionBuilder
87  					.rootBeanDefinition(DefaultOAuth2RequestValidator.class);
88  			parserContext.getRegistry().registerBeanDefinition(oAuth2RequestValidatorRef,
89  					oAuth2RequestValidator.getBeanDefinition());
90  		}
91  		authorizationEndpointBean.addPropertyReference("oAuth2RequestValidator",
92  				oAuth2RequestValidatorRef);
93  
94  		if (!StringUtils.hasText(oAuth2RequestFactoryRef)) {
95  			oAuth2RequestFactoryRef = "oAuth2AuthorizationRequestManager";
96  			BeanDefinitionBuilder oAuth2RequestManager = BeanDefinitionBuilder
97  					.rootBeanDefinition(DefaultOAuth2RequestFactory.class);
98  			oAuth2RequestManager.addConstructorArgReference(clientDetailsRef);
99  			parserContext.getRegistry().registerBeanDefinition(oAuth2RequestFactoryRef,
100 					oAuth2RequestManager.getBeanDefinition());
101 		}
102 
103 		ManagedList<BeanMetadataElement> tokenGranters = null;
104 		if (!StringUtils.hasText(tokenGranterRef)) {
105 			tokenGranterRef = "oauth2TokenGranter";
106 			BeanDefinitionBuilder tokenGranterBean = BeanDefinitionBuilder
107 					.rootBeanDefinition(CompositeTokenGranter.class);
108 			parserContext.getRegistry().registerBeanDefinition(tokenGranterRef,
109 					tokenGranterBean.getBeanDefinition());
110 			tokenGranters = new ManagedList<BeanMetadataElement>();
111 			tokenGranterBean.addConstructorArgValue(tokenGranters);
112 		}
113 		authorizationEndpointBean.addPropertyReference("tokenGranter", tokenGranterRef);
114 
115 		boolean registerAuthorizationEndpoint = false;
116 
117 		Element authorizationCodeElement = DomUtils.getChildElementByTagName(element,
118 				"authorization-code");
119 
120 		if (authorizationCodeElement != null && !"true"
121 				.equalsIgnoreCase(authorizationCodeElement.getAttribute("disabled"))) {
122 			// authorization code grant configuration.
123 			String authorizationCodeServices = authorizationCodeElement
124 					.getAttribute("authorization-code-services-ref");
125 			String clientTokenCacheRef = authorizationCodeElement
126 					.getAttribute("client-token-cache-ref");
127 
128 			BeanDefinitionBuilder authorizationCodeTokenGranterBean = BeanDefinitionBuilder
129 					.rootBeanDefinition(AuthorizationCodeTokenGranter.class);
130 
131 			if (StringUtils.hasText(tokenServicesRef)) {
132 				authorizationCodeTokenGranterBean
133 						.addConstructorArgReference(tokenServicesRef);
134 			}
135 
136 			if (!StringUtils.hasText(authorizationCodeServices)) {
137 				authorizationCodeServices = "oauth2AuthorizationCodeServices";
138 				BeanDefinitionBuilder authorizationCodeServicesBean = BeanDefinitionBuilder
139 						.rootBeanDefinition(InMemoryAuthorizationCodeServices.class);
140 				parserContext.getRegistry().registerBeanDefinition(
141 						authorizationCodeServices,
142 						authorizationCodeServicesBean.getBeanDefinition());
143 			}
144 
145 			authorizationEndpointBean.addPropertyReference("authorizationCodeServices",
146 					authorizationCodeServices);
147 			authorizationCodeTokenGranterBean
148 					.addConstructorArgReference(authorizationCodeServices);
149 			authorizationCodeTokenGranterBean
150 					.addConstructorArgReference(clientDetailsRef);
151 			authorizationCodeTokenGranterBean
152 					.addConstructorArgReference(oAuth2RequestFactoryRef);
153 
154 			if (StringUtils.hasText(clientTokenCacheRef)) {
155 				authorizationEndpointBean.addPropertyReference("clientTokenCache",
156 						clientTokenCacheRef);
157 			}
158 			if (StringUtils.hasText(oAuth2RequestFactoryRef)) {
159 				authorizationEndpointBean.addPropertyReference("oAuth2RequestFactory",
160 						oAuth2RequestFactoryRef);
161 			}
162 
163 			if (tokenGranters != null) {
164 				tokenGranters.add(authorizationCodeTokenGranterBean.getBeanDefinition());
165 			}
166 			// end authorization code provider configuration.
167 			registerAuthorizationEndpoint = true;
168 		}
169 
170 		if (tokenGranters != null) {
171 			Element refreshTokenElement = DomUtils.getChildElementByTagName(element,
172 					"refresh-token");
173 
174 			if (refreshTokenElement != null && !"true"
175 					.equalsIgnoreCase(refreshTokenElement.getAttribute("disabled"))) {
176 				BeanDefinitionBuilder refreshTokenGranterBean = BeanDefinitionBuilder
177 						.rootBeanDefinition(RefreshTokenGranter.class);
178 				refreshTokenGranterBean.addConstructorArgReference(tokenServicesRef);
179 				refreshTokenGranterBean.addConstructorArgReference(clientDetailsRef);
180 				refreshTokenGranterBean
181 						.addConstructorArgReference(oAuth2RequestFactoryRef);
182 				tokenGranters.add(refreshTokenGranterBean.getBeanDefinition());
183 			}
184 			Element implicitElement = DomUtils.getChildElementByTagName(element,
185 					"implicit");
186 			if (implicitElement != null && !"true"
187 					.equalsIgnoreCase(implicitElement.getAttribute("disabled"))) {
188 				BeanDefinitionBuilder implicitGranterBean = BeanDefinitionBuilder
189 						.rootBeanDefinition(ImplicitTokenGranter.class);
190 				implicitGranterBean.addConstructorArgReference(tokenServicesRef);
191 				implicitGranterBean.addConstructorArgReference(clientDetailsRef);
192 				implicitGranterBean.addConstructorArgReference(oAuth2RequestFactoryRef);
193 				tokenGranters.add(implicitGranterBean.getBeanDefinition());
194 				registerAuthorizationEndpoint = true;
195 			}
196 			Element clientCredentialsElement = DomUtils.getChildElementByTagName(element,
197 					"client-credentials");
198 			if (clientCredentialsElement != null && !"true".equalsIgnoreCase(
199 					clientCredentialsElement.getAttribute("disabled"))) {
200 				BeanDefinitionBuilder clientCredentialsGranterBean = BeanDefinitionBuilder
201 						.rootBeanDefinition(ClientCredentialsTokenGranter.class);
202 				clientCredentialsGranterBean.addConstructorArgReference(tokenServicesRef);
203 				clientCredentialsGranterBean.addConstructorArgReference(clientDetailsRef);
204 				clientCredentialsGranterBean
205 						.addConstructorArgReference(oAuth2RequestFactoryRef);
206 				tokenGranters.add(clientCredentialsGranterBean.getBeanDefinition());
207 			}
208 			Element clientPasswordElement = DomUtils.getChildElementByTagName(element,
209 					"password");
210 			if (clientPasswordElement != null && !"true"
211 					.equalsIgnoreCase(clientPasswordElement.getAttribute("disabled"))) {
212 				BeanDefinitionBuilder clientPasswordTokenGranter = BeanDefinitionBuilder
213 						.rootBeanDefinition(ResourceOwnerPasswordTokenGranter.class);
214 				String authenticationManagerRef = clientPasswordElement
215 						.getAttribute("authentication-manager-ref");
216 				if (!StringUtils.hasText(authenticationManagerRef)) {
217 					authenticationManagerRef = BeanIds.AUTHENTICATION_MANAGER;
218 				}
219 				clientPasswordTokenGranter
220 						.addConstructorArgReference(authenticationManagerRef);
221 				clientPasswordTokenGranter.addConstructorArgReference(tokenServicesRef);
222 				clientPasswordTokenGranter.addConstructorArgReference(clientDetailsRef);
223 				clientPasswordTokenGranter
224 						.addConstructorArgReference(oAuth2RequestFactoryRef);
225 				tokenGranters.add(clientPasswordTokenGranter.getBeanDefinition());
226 			}
227 			List<Element> customGrantElements = DomUtils
228 					.getChildElementsByTagName(element, "custom-grant");
229 			for (Element customGrantElement : customGrantElements) {
230 				if (!"true"
231 						.equalsIgnoreCase(customGrantElement.getAttribute("disabled"))) {
232 					String customGranterRef = customGrantElement
233 							.getAttribute("token-granter-ref");
234 					tokenGranters.add(new RuntimeBeanReference(customGranterRef));
235 				}
236 			}
237 		}
238 
239 		if (registerAuthorizationEndpoint) {
240 
241 			BeanDefinitionBuilder approvalEndpointBean = BeanDefinitionBuilder
242 					.rootBeanDefinition(WhitelabelApprovalEndpoint.class);
243 			parserContext.getRegistry().registerBeanDefinition("oauth2ApprovalEndpoint",
244 					approvalEndpointBean.getBeanDefinition());
245 
246 			if (!StringUtils.hasText(clientDetailsRef)) {
247 				parserContext.getReaderContext()
248 						.error("A client details service is mandatory", element);
249 			}
250 
251 			if (StringUtils.hasText(redirectStrategyRef)) {
252 				authorizationEndpointBean.addPropertyReference("redirectStrategy",
253 						redirectStrategyRef);
254 			}
255 
256 			if (StringUtils.hasText(userApprovalHandlerRef)) {
257 				authorizationEndpointBean.addPropertyReference("userApprovalHandler",
258 						userApprovalHandlerRef);
259 			}
260 
261 			authorizationEndpointBean.addPropertyReference("clientDetailsService",
262 					clientDetailsRef);
263 			if (StringUtils.hasText(redirectResolverRef)) {
264 				authorizationEndpointBean.addPropertyReference("redirectResolver",
265 						redirectResolverRef);
266 			}
267 			if (StringUtils.hasText(approvalPage)) {
268 				authorizationEndpointBean.addPropertyValue("userApprovalPage",
269 						approvalPage);
270 			}
271 			if (StringUtils.hasText(errorPage)) {
272 				authorizationEndpointBean.addPropertyValue("errorPage", errorPage);
273 			}
274 
275 			parserContext.getRegistry().registerBeanDefinition(
276 					"oauth2AuthorizationEndpoint",
277 					authorizationEndpointBean.getBeanDefinition());
278 		}
279 
280 		// configure the token endpoint
281 		BeanDefinitionBuilder tokenEndpointBean = BeanDefinitionBuilder
282 				.rootBeanDefinition(TokenEndpoint.class);
283 		tokenEndpointBean.addPropertyReference("clientDetailsService", clientDetailsRef);
284 		tokenEndpointBean.addPropertyReference("tokenGranter", tokenGranterRef);
285 		authorizationEndpointBean.addPropertyReference("oAuth2RequestValidator",
286 				oAuth2RequestValidatorRef);
287 		parserContext.getRegistry().registerBeanDefinition("oauth2TokenEndpoint",
288 				tokenEndpointBean.getBeanDefinition());
289 		if (StringUtils.hasText(oAuth2RequestFactoryRef)) {
290 			tokenEndpointBean.addPropertyReference("oAuth2RequestFactory",
291 					oAuth2RequestFactoryRef);
292 		}
293 		if (StringUtils.hasText(oAuth2RequestValidatorRef)) {
294 			tokenEndpointBean.addPropertyReference("oAuth2RequestValidator",
295 					oAuth2RequestValidatorRef);
296 		}
297 
298 		// Register a handler mapping that can detect the auth server endpoints
299 		BeanDefinitionBuilder handlerMappingBean = BeanDefinitionBuilder
300 				.rootBeanDefinition(FrameworkEndpointHandlerMapping.class);
301 		ManagedMap<String, TypedStringValue> mappings = new ManagedMap<String, TypedStringValue>();
302 		if (StringUtils.hasText(tokenEndpointUrl)
303 				|| StringUtils.hasText(authorizationEndpointUrl)) {
304 			if (StringUtils.hasText(tokenEndpointUrl)) {
305 				mappings.put("/oauth/token",
306 						new TypedStringValue(tokenEndpointUrl, String.class));
307 			}
308 			if (StringUtils.hasText(authorizationEndpointUrl)) {
309 				mappings.put("/oauth/authorize",
310 						new TypedStringValue(authorizationEndpointUrl, String.class));
311 			}
312 			if (StringUtils.hasText(approvalPage)) {
313 				mappings.put("/oauth/confirm_access",
314 						new TypedStringValue(approvalPage, String.class));
315 			}
316 		}
317 		if (StringUtils.hasText(enableCheckToken) && enableCheckToken.equals("true")) {
318 			// configure the check token endpoint
319 			BeanDefinitionBuilder checkTokenEndpointBean = BeanDefinitionBuilder
320 					.rootBeanDefinition(CheckTokenEndpoint.class);
321 			checkTokenEndpointBean.addConstructorArgReference(tokenServicesRef);
322 			parserContext.getRegistry().registerBeanDefinition("oauth2CheckTokenEndpoint",
323 					checkTokenEndpointBean.getBeanDefinition());
324 
325 			if (StringUtils.hasText(checkTokenUrl)) {
326 				mappings.put("/oauth/check_token",
327 						new TypedStringValue(checkTokenUrl, String.class));
328 			}
329 		}
330 		if (!mappings.isEmpty()) {
331 			handlerMappingBean.addPropertyValue("mappings", mappings);
332 		}
333 
334 		if (StringUtils.hasText(approvalParameter) && registerAuthorizationEndpoint) {
335 			if (!StringUtils.hasText(userApprovalHandlerRef)) {
336 				BeanDefinitionBuilder userApprovalHandler = BeanDefinitionBuilder
337 						.rootBeanDefinition(DefaultUserApprovalHandler.class);
338 				userApprovalHandler.addPropertyValue("approvalParameter",
339 						new TypedStringValue(approvalParameter, String.class));
340 				authorizationEndpointBean.addPropertyValue("userApprovalHandler",
341 						userApprovalHandler.getBeanDefinition());
342 			}
343 			handlerMappingBean.addPropertyValue("approvalParameter", approvalParameter);
344 		}
345 
346 		parserContext.getRegistry().registerBeanDefinition("oauth2HandlerMapping",
347 				handlerMappingBean.getBeanDefinition());
348 
349 		// We aren't defining a filter...
350 		return null;
351 
352 	}
353 
354 }