View Javadoc
1   /*
2    * Copyright 2006-2011 the original author or authors.
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.provider.endpoint;
15  
16  import java.lang.reflect.Method;
17  import java.util.HashMap;
18  import java.util.HashSet;
19  import java.util.Map;
20  import java.util.Set;
21  
22  import org.springframework.core.Ordered;
23  import org.springframework.core.annotation.AnnotationUtils;
24  import org.springframework.security.oauth2.common.util.OAuth2Utils;
25  import org.springframework.util.StringUtils;
26  import org.springframework.web.servlet.mvc.condition.NameValueExpression;
27  import org.springframework.web.servlet.mvc.condition.ParamsRequestCondition;
28  import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
29  import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
30  import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
31  import org.springframework.web.servlet.view.UrlBasedViewResolver;
32  
33  /**
34   * A handler mapping for framework endpoints (those annotated with @FrameworkEndpoint).
35   * 
36   * @author Dave Syer
37   * 
38   */
39  public class FrameworkEndpointHandlerMapping extends RequestMappingHandlerMapping {
40  
41  	private static final String REDIRECT = UrlBasedViewResolver.REDIRECT_URL_PREFIX;
42  
43  	private static final String FORWARD = UrlBasedViewResolver.FORWARD_URL_PREFIX;
44  
45  	private Map<String, String> mappings = new HashMap<String, String>();
46  
47  	private String approvalParameter = OAuth2Utils.USER_OAUTH_APPROVAL;
48  
49  	private Set<String> paths = new HashSet<String>();
50  
51  	private String prefix;
52  
53  	/**
54  	 * @param prefix the prefix to set
55  	 */
56  	public void setPrefix(String prefix) {
57  		if (!StringUtils.hasText(prefix)) {
58  			prefix = "";
59  		}
60  		else
61  			while (prefix.endsWith("/")) {
62  				prefix = prefix.substring(0, prefix.lastIndexOf("/"));
63  			}
64  		this.prefix = prefix;
65  	}
66  
67  	/**
68  	 * Custom mappings for framework endpoint paths. The keys in the map are the default framework endpoint path, e.g.
69  	 * "/oauth/authorize", and the values are the desired runtime paths.
70  	 * 
71  	 * @param patternMap the mappings to set
72  	 */
73  	public void setMappings(Map<String, String> patternMap) {
74  		this.mappings = new HashMap<String, String>(patternMap);
75  		for (String key : mappings.keySet()) {
76  			String result = mappings.get(key);
77  			if (result.startsWith(FORWARD)) {
78  				result = result.substring(FORWARD.length());
79  			}
80  			if (result.startsWith(REDIRECT)) {
81  				result = result.substring(REDIRECT.length());
82  			}
83  			mappings.put(key, result);
84  		}
85  	}
86  
87  	/**
88  	 * @return the mapping from default endpoint paths to custom ones (or the default if no customization is known)
89  	 */
90  	public String getServletPath(String defaultPath) {
91  		return (prefix == null ? "" : prefix) + getPath(defaultPath);
92  	}
93  
94  	/**
95  	 * @return the mapping from default endpoint paths to custom ones (or the default if no customization is known)
96  	 */
97  	public String getPath(String defaultPath) {
98  		String result = defaultPath;
99  		if (mappings.containsKey(defaultPath)) {
100 			result = mappings.get(defaultPath);
101 		}
102 		return result;
103 	}
104 
105 	public Set<String> getPaths() {
106 		return paths;
107 	}
108 
109 	/**
110 	 * The name of the request parameter that distinguishes a call to approve an authorization. Default is
111 	 * {@link OAuth2Utils#USER_OAUTH_APPROVAL}.
112 	 * 
113 	 * @param approvalParameter the approvalParameter to set
114 	 */
115 	public void setApprovalParameter(String approvalParameter) {
116 		this.approvalParameter = approvalParameter;
117 	}
118 
119 	public FrameworkEndpointHandlerMapping() {
120 		// Make sure user-supplied mappings take precedence by default (except the resource mapping)
121 		setOrder(Ordered.LOWEST_PRECEDENCE - 2);
122 	}
123 
124 	/**
125 	 * Detects &#64;FrameworkEndpoint annotations in handler beans.
126 	 * 
127 	 * @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#isHandler(java.lang.Class)
128 	 */
129 	@Override
130 	protected boolean isHandler(Class<?> beanType) {
131 		return AnnotationUtils.findAnnotation(beanType, FrameworkEndpoint.class) != null;
132 	}
133 
134 	@Override
135 	protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
136 
137 		RequestMappingInfo defaultMapping = super.getMappingForMethod(method, handlerType);
138 		if (defaultMapping == null) {
139 			return null;
140 		}
141 
142 		Set<String> defaultPatterns = defaultMapping.getPatternsCondition().getPatterns();
143 		String[] patterns = new String[defaultPatterns.size()];
144 
145 		int i = 0;
146 		for (String pattern : defaultPatterns) {
147 			patterns[i] = getPath(pattern);
148 			paths.add(pattern);
149 			i++;
150 		}
151 		PatternsRequestCondition patternsInfo = new PatternsRequestCondition(patterns, getUrlPathHelper(),
152 				getPathMatcher(), useSuffixPatternMatch(), useTrailingSlashMatch(), getFileExtensions());
153 
154 		ParamsRequestCondition paramsInfo = defaultMapping.getParamsCondition();
155 		if (!approvalParameter.equals(OAuth2Utils.USER_OAUTH_APPROVAL) && defaultPatterns.contains("/oauth/authorize")) {
156 			String[] params = new String[paramsInfo.getExpressions().size()];
157 			Set<NameValueExpression<String>> expressions = paramsInfo.getExpressions();
158 			i = 0;
159 			for (NameValueExpression<String> expression : expressions) {
160 				String param = expression.toString();
161 				if (OAuth2Utils.USER_OAUTH_APPROVAL.equals(param)) {
162 					params[i] = approvalParameter;
163 				}
164 				else {
165 					params[i] = param;
166 				}
167 				i++;
168 			}
169 			paramsInfo = new ParamsRequestCondition(params);
170 		}
171 
172 		RequestMappingInfo mapping = new RequestMappingInfo(patternsInfo, defaultMapping.getMethodsCondition(),
173 				paramsInfo, defaultMapping.getHeadersCondition(), defaultMapping.getConsumesCondition(),
174 				defaultMapping.getProducesCondition(), defaultMapping.getCustomCondition());
175 		return mapping;
176 
177 	}
178 
179 }