/*
* Copyright 2006-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.springframework.security.oauth2.provider.endpoint;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.mvc.condition.NameValueExpression;
import org.springframework.web.servlet.mvc.condition.ParamsRequestCondition;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.view.UrlBasedViewResolver;
/**
* A handler mapping for framework endpoints (those annotated with @FrameworkEndpoint).
*
* @author Dave Syer
*
*/
public class FrameworkEndpointHandlerMapping extends RequestMappingHandlerMapping {
private static final String REDIRECT = UrlBasedViewResolver.REDIRECT_URL_PREFIX;
private static final String FORWARD = UrlBasedViewResolver.FORWARD_URL_PREFIX;
private Map<String, String> mappings = new HashMap<String, String>();
private String approvalParameter = OAuth2Utils.USER_OAUTH_APPROVAL;
private Set<String> paths = new HashSet<String>();
private String prefix;
/**
* @param prefix the prefix to set
*/
public void setPrefix(String prefix) {
if (!StringUtils.hasText(prefix)) {
prefix = "";
}
else
while (prefix.endsWith("/")) {
prefix = prefix.substring(0, prefix.lastIndexOf("/"));
}
this.prefix = prefix;
}
/**
* Custom mappings for framework endpoint paths. The keys in the map are the default framework endpoint path, e.g.
* "/oauth/authorize", and the values are the desired runtime paths.
*
* @param patternMap the mappings to set
*/
public void setMappings(Map<String, String> patternMap) {
this.mappings = new HashMap<String, String>(patternMap);
for (String key : mappings.keySet()) {
String result = mappings.get(key);
if (result.startsWith(FORWARD)) {
result = result.substring(FORWARD.length());
}
if (result.startsWith(REDIRECT)) {
result = result.substring(REDIRECT.length());
}
mappings.put(key, result);
}
}
/**
* @return the mapping from default endpoint paths to custom ones (or the default if no customization is known)
*/
public String getServletPath(String defaultPath) {
return (prefix == null ? "" : prefix) + getPath(defaultPath);
}
/**
* @return the mapping from default endpoint paths to custom ones (or the default if no customization is known)
*/
public String getPath(String defaultPath) {
String result = defaultPath;
if (mappings.containsKey(defaultPath)) {
result = mappings.get(defaultPath);
}
return result;
}
public Set<String> getPaths() {
return paths;
}
/**
* The name of the request parameter that distinguishes a call to approve an authorization. Default is
* {@link OAuth2Utils#USER_OAUTH_APPROVAL}.
*
* @param approvalParameter the approvalParameter to set
*/
public void setApprovalParameter(String approvalParameter) {
this.approvalParameter = approvalParameter;
}
public FrameworkEndpointHandlerMapping() {
// Make sure user-supplied mappings take precedence by default (except the resource mapping)
setOrder(Ordered.LOWEST_PRECEDENCE - 2);
}
/**
* Detects @FrameworkEndpoint annotations in handler beans.
*
* @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#isHandler(java.lang.Class)
*/
@Override
protected boolean isHandler(Class<?> beanType) {
return AnnotationUtils.findAnnotation(beanType, FrameworkEndpoint.class) != null;
}
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo defaultMapping = super.getMappingForMethod(method, handlerType);
if (defaultMapping == null) {
return null;
}
Set<String> defaultPatterns = defaultMapping.getPatternsCondition().getPatterns();
String[] patterns = new String[defaultPatterns.size()];
int i = 0;
for (String pattern : defaultPatterns) {
patterns[i] = getPath(pattern);
paths.add(pattern);
i++;
}
PatternsRequestCondition patternsInfo = new PatternsRequestCondition(patterns, getUrlPathHelper(),
getPathMatcher(), useSuffixPatternMatch(), useTrailingSlashMatch(), getFileExtensions());
ParamsRequestCondition paramsInfo = defaultMapping.getParamsCondition();
if (!approvalParameter.equals(OAuth2Utils.USER_OAUTH_APPROVAL) && defaultPatterns.contains("/oauth/authorize")) {
String[] params = new String[paramsInfo.getExpressions().size()];
Set<NameValueExpression<String>> expressions = paramsInfo.getExpressions();
i = 0;
for (NameValueExpression<String> expression : expressions) {
String param = expression.toString();
if (OAuth2Utils.USER_OAUTH_APPROVAL.equals(param)) {
params[i] = approvalParameter;
}
else {
params[i] = param;
}
i++;
}
paramsInfo = new ParamsRequestCondition(params);
}
RequestMappingInfo mapping = new RequestMappingInfo(patternsInfo, defaultMapping.getMethodsCondition(),
paramsInfo, defaultMapping.getHeadersCondition(), defaultMapping.getConsumesCondition(),
defaultMapping.getProducesCondition(), defaultMapping.getCustomCondition());
return mapping;
}
}