/* * Copyright 2008 Web Cohesion * * 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.config.xml; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.config.TypedStringValue; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails; import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; import org.springframework.security.oauth2.client.token.grant.implicit.ImplicitResourceDetails; import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails; import org.springframework.security.oauth2.common.AuthenticationScheme; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.util.StringUtils; import org.w3c.dom.Element; /** * @author Ryan Heaton */ public class ResourceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override protected Class<?> getBeanClass(Element element) { String type = element.getAttribute("type"); if ("authorization_code".equals(type)) { return AuthorizationCodeResourceDetails.class; } if ("implicit".equals(type)) { return ImplicitResourceDetails.class; } if ("client_credentials".equals(type)) { return ClientCredentialsResourceDetails.class; } if ("password".equals(type)) { return ResourceOwnerPasswordResourceDetails.class; } return BaseOAuth2ProtectedResourceDetails.class; } @Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { String id = element.getAttribute("id"); if (!StringUtils.hasText(id)) { parserContext.getReaderContext().error("An id must be supplied on a resource element.", element); } builder.addPropertyValue("id", id); String type = element.getAttribute("type"); if (!StringUtils.hasText(type)) { type = "client_credentials"; } builder.addPropertyValue("grantType", type); String accessTokenUri = element.getAttribute("access-token-uri"); if (!StringUtils.hasText(accessTokenUri) && !"implicit".equals(type)) { parserContext.getReaderContext() .error("An accessTokenUri must be supplied on a resource element of type " + type, element); } builder.addPropertyValue("accessTokenUri", accessTokenUri); String clientId = element.getAttribute("client-id"); if (!StringUtils.hasText(clientId)) { parserContext.getReaderContext().error("An clientId must be supplied on a resource element", element); } builder.addPropertyValue("clientId", clientId); String clientSecret = element.getAttribute("client-secret"); if (StringUtils.hasText(clientSecret)) { builder.addPropertyValue("clientSecret", clientSecret); } String clientAuthenticationScheme = element.getAttribute("client-authentication-scheme"); if (StringUtils.hasText(clientAuthenticationScheme)) { builder.addPropertyValue("clientAuthenticationScheme", clientAuthenticationScheme); } String userAuthorizationUri = element.getAttribute("user-authorization-uri"); if (StringUtils.hasText(userAuthorizationUri)) { if (needsUserAuthorizationUri(type)) { builder.addPropertyValue("userAuthorizationUri", userAuthorizationUri); } else { parserContext.getReaderContext().error("The " + type + " grant type does not accept an authorization URI", element); } } else { if (needsUserAuthorizationUri(type)) { parserContext.getReaderContext().error("An authorization URI must be supplied for a resource of type " + type, element); } } String preEstablishedRedirectUri = element.getAttribute("pre-established-redirect-uri"); if (StringUtils.hasText(preEstablishedRedirectUri)) { builder.addPropertyValue("preEstablishedRedirectUri", preEstablishedRedirectUri); } String requireImmediateAuthorization = element.getAttribute("require-immediate-authorization"); if (StringUtils.hasText(requireImmediateAuthorization)) { builder.addPropertyValue("requireImmediateAuthorization", requireImmediateAuthorization); } String useCurrentUri = element.getAttribute("use-current-uri"); if (StringUtils.hasText(useCurrentUri)) { builder.addPropertyValue("useCurrentUri", useCurrentUri); } String scope = element.getAttribute("scope"); if (StringUtils.hasText(scope)) { BeanDefinitionBuilder scopesBuilder = BeanDefinitionBuilder .genericBeanDefinition(StringListFactoryBean.class); scopesBuilder.addConstructorArgValue(new TypedStringValue(scope)); builder.addPropertyValue("scope", scopesBuilder.getBeanDefinition()); } AuthenticationScheme btm = AuthenticationScheme.header; String bearerTokenMethod = element.getAttribute("authentication-scheme"); if (StringUtils.hasText(bearerTokenMethod)) { btm = AuthenticationScheme.valueOf(bearerTokenMethod); } builder.addPropertyValue("authenticationScheme", btm); String bearerTokenName = element.getAttribute("token-name"); if (!StringUtils.hasText(bearerTokenName)) { bearerTokenName = OAuth2AccessToken.ACCESS_TOKEN; } builder.addPropertyValue("tokenName", bearerTokenName); if (type.equals("password")) { String[] attributeNames = {"username", "password"}; for (String attributeName : attributeNames) { String attribute = element.getAttribute(attributeName); if (StringUtils.hasText(attribute)) { builder.addPropertyValue(attributeName, attribute); } else { parserContext.getReaderContext().error("A " + attributeName + " must be supplied on a resource element of type " + type, element); } } } } /** * Convenience factory bean for enabling comma-separated lists to be specified either as literals or externalized as * expressions or placeholders. N.B. this would not be necessary if Spring used its ConversionService by default * (users have to register one). */ public static class StringListFactoryBean implements FactoryBean<List<String>> { private final String commaSeparatedList; public StringListFactoryBean(String commaSeparatedList) { this.commaSeparatedList = commaSeparatedList; } public List<String> getObject() throws Exception { return new ArrayList<String>(Arrays.asList(StringUtils.commaDelimitedListToStringArray(commaSeparatedList))); } public Class<?> getObjectType() { return List.class; } public boolean isSingleton() { return true; } } private boolean needsUserAuthorizationUri(String type) { return type.equals("authorization_code") || type.equals("implicit"); } }