From 92647e44a60f07c7bc0eebcaafc6945d12641fe0 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Thu, 15 Dec 2011 08:43:39 +0000 Subject: [PATCH] Upgrade to Spring 3.1 Change-Id: I1d68ef97b46f4906b43fd8748c68e12d8a1e8cc8 --- README.md | 26 +++++-- pom.xml | 2 +- .../uaa/config/HandlerAdapterFactoryBean.java | 54 ++++++++++---- .../identity/uaa/oauth/AccessController.java | 2 +- .../webapp/WEB-INF/spring-data-source.xml | 26 +++++-- .../main/webapp/WEB-INF/spring-servlet.xml | 70 ++++++++++++------- .../identity/uaa/BootstrapTests.java | 22 +++++- 7 files changed, 149 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 52156a1354b..bf504999d4a 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,8 @@ In CloudFoundry terms webapp that needs single sign on and access to the `api` service on behalf of users. +## UAA Server + The authentication service is `uaa`. It's a plain Spring MVC webapp. Deploy as normal in Tomcat or your container of choice, or execute `mvn tomcat:run` to run it directly from `uaa` directory in the source tree. @@ -95,16 +97,30 @@ It supports the APIs defined in the UAA-APIs document. To summarise: 3. A /check_token endpoint, to allow resource servers to obtain information about an access token submitted by an OAuth2 client. -4. SCIM user provisioning endpoints (todo) +4. SCIM user provisioning endpoint -5. OpenID connect endpoints to support authentication /userinfo and /check_id -(todo). Implemented roughly enough to get it working (so /app authenticates -here), but not to meet the spec. +5. OpenID connect endpoints to support authentication /userinfo and +/check_id (todo). Implemented roughly enough to get it working (so +/app authenticates here), but not to meet the spec. Authentication can be performed by command line clients by submitting credentials directly to the `/authorize` endpoint (as described in UAA-API doc). There is an `ImplicitAccessTokenProvider` in Spring -Security OAuth that can do the heavy lifting. +Security OAuth that can do the heavy lifting if your client is Java. + +By default `uaa` will launch with a context root `/uaa`. There is a +Maven profile `vcap` to launch with context root `/`. + +### User Account Data + +The default is to use an in-memory, hash-based user store that is +pre-populated with some test users: e.g. `dale` has password +`password` and `marissa` has password `koala`. + +To use a RDBMS for user data activate the Spring profiles `jdbc` and +one of `hsqldb` or `postgresql`. The `hsqldb` profile will start up +with an in-memory RDBMS by default. Warning: the database will start +empty, so no users can log in until the first account is created. ## The API Application diff --git a/pom.xml b/pom.xml index e5aee815dc7..29ad861b5ac 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ - 3.0.6.RELEASE + 3.1.0.RELEASE 3.1.0.RELEASE 1.0.0.BUILD-SNAPSHOT UTF-8 diff --git a/uaa/src/main/java/org/cloudfoundry/identity/uaa/config/HandlerAdapterFactoryBean.java b/uaa/src/main/java/org/cloudfoundry/identity/uaa/config/HandlerAdapterFactoryBean.java index dc4f5940ce5..8f88f976de4 100644 --- a/uaa/src/main/java/org/cloudfoundry/identity/uaa/config/HandlerAdapterFactoryBean.java +++ b/uaa/src/main/java/org/cloudfoundry/identity/uaa/config/HandlerAdapterFactoryBean.java @@ -12,21 +12,29 @@ */ package org.cloudfoundry.identity.uaa.config; -import java.lang.reflect.Method; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; import javax.servlet.http.HttpServletResponse; import org.cloudfoundry.identity.uaa.scim.ScimUser; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.FactoryBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.core.MethodParameter; import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.ui.ExtendedModelMap; +import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.client.RestTemplate; import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodReturnValueHandler; +import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.servlet.HandlerAdapter; -import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter; -import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; +import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor; /** * Factory for a handler adapter that sniffs the results from {@link RequestMapping} method executions and adds an ETag @@ -35,19 +43,24 @@ * @author Dave Syer * */ -public class HandlerAdapterFactoryBean implements FactoryBean { +public class HandlerAdapterFactoryBean implements FactoryBean, ApplicationContextAware { + + private ApplicationContext applicationContext; @Override public HandlerAdapter getObject() throws Exception { - AnnotationMethodHandlerAdapter adapter = new AnnotationMethodHandlerAdapter(); + RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter(); + adapter.setApplicationContext(applicationContext); adapter.setMessageConverters(getMessageConverters()); adapter.setOrder(0); - adapter.setCustomModelAndViewResolver(new ScimEtagModelAndViewResolver()); + adapter.setReturnValueHandlers(Arrays + . asList(new ScimEtagHandlerMethodReturnValueHandler(getMessageConverters()))); + adapter.afterPropertiesSet(); return adapter; } - private HttpMessageConverter[] getMessageConverters() { - return new RestTemplate().getMessageConverters().toArray(new HttpMessageConverter[0]); + private List> getMessageConverters() { + return new RestTemplate().getMessageConverters(); } @Override @@ -60,17 +73,32 @@ public boolean isSingleton() { return true; } - private static class ScimEtagModelAndViewResolver implements ModelAndViewResolver { + private static class ScimEtagHandlerMethodReturnValueHandler extends RequestResponseBodyMethodProcessor { + + public ScimEtagHandlerMethodReturnValueHandler(List> messageConverters) { + super(messageConverters); + } @Override - public ModelAndView resolveModelAndView(Method handlerMethod, @SuppressWarnings("rawtypes") Class handlerType, - Object returnValue, ExtendedModelMap implicitModel, NativeWebRequest webRequest) { + public boolean supportsReturnType(MethodParameter returnType) { + return ScimUser.class.isAssignableFrom(returnType.getMethod().getReturnType()); + } + + @Override + public void handleReturnValue(Object returnValue, MethodParameter returnType, + ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, + HttpMediaTypeNotAcceptableException { if (returnValue instanceof ScimUser) { HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class); response.addHeader("ETag", "\"" + ((ScimUser) returnValue).getVersion() + "\""); } - return ModelAndViewResolver.UNRESOLVED; + super.handleReturnValue(returnValue, returnType, mavContainer, webRequest); } } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } } diff --git a/uaa/src/main/java/org/cloudfoundry/identity/uaa/oauth/AccessController.java b/uaa/src/main/java/org/cloudfoundry/identity/uaa/oauth/AccessController.java index e5586aa6283..f704b713f9c 100755 --- a/uaa/src/main/java/org/cloudfoundry/identity/uaa/oauth/AccessController.java +++ b/uaa/src/main/java/org/cloudfoundry/identity/uaa/oauth/AccessController.java @@ -31,7 +31,7 @@ public String getIdentity(HttpSession session) { } @RequestMapping("/oauth/confirm_access") - public String confirm(UnconfirmedAuthorizationCodeClientToken clientAuth, Map model, final HttpServletRequest request) + public String confirm(@ModelAttribute UnconfirmedAuthorizationCodeClientToken clientAuth, Map model, final HttpServletRequest request) throws Exception { if (clientAuth == null) { diff --git a/uaa/src/main/webapp/WEB-INF/spring-data-source.xml b/uaa/src/main/webapp/WEB-INF/spring-data-source.xml index 86a68c5c332..f6a68eb8e4f 100755 --- a/uaa/src/main/webapp/WEB-INF/spring-data-source.xml +++ b/uaa/src/main/webapp/WEB-INF/spring-data-source.xml @@ -1,9 +1,9 @@ + xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> @@ -14,13 +14,13 @@ - + - + @@ -32,9 +32,21 @@ - + - + + + + + + + + + + + + + diff --git a/uaa/src/main/webapp/WEB-INF/spring-servlet.xml b/uaa/src/main/webapp/WEB-INF/spring-servlet.xml index ded77e61afb..ce7efd7cca4 100755 --- a/uaa/src/main/webapp/WEB-INF/spring-servlet.xml +++ b/uaa/src/main/webapp/WEB-INF/spring-servlet.xml @@ -3,12 +3,10 @@ xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:sec="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> - - + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> @@ -101,26 +99,6 @@ - - - - - - - - - - - - dale|$2a$10$HoWPAUn9zqmmb0b.2TBZWe6cjQcxyo8TDwTX.5G46PBL347N3/0zO|olds@vmware.com|Dale|Olds - joel|$2a$10$HoWPAUn9zqmmb0b.2TBZWe6cjQcxyo8TDwTX.5G46PBL347N3/0zO|jdsa@vmware.com|Joel|D'Sa - dave|$2a$10$HoWPAUn9zqmmb0b.2TBZWe6cjQcxyo8TDwTX.5G46PBL347N3/0zO|dsyer@vmware.com|Dave|Syer - luke|$2a$10$HoWPAUn9zqmmb0b.2TBZWe6cjQcxyo8TDwTX.5G46PBL347N3/0zO|ltaylor@vmware.com|Luke|Taylor - marissa|$2a$10$ikFXo9IFG6zbMbhGcssySOhjDsGPpqzKwsdVOeCvJ7JoWjSQxyfs6|marissa@test.org|Marissa|Bloggs - - - - @@ -213,4 +191,46 @@ + + + + + + + + + + + + + + dale|$2a$10$HoWPAUn9zqmmb0b.2TBZWe6cjQcxyo8TDwTX.5G46PBL347N3/0zO|olds@vmware.com|Dale|Olds + joel|$2a$10$HoWPAUn9zqmmb0b.2TBZWe6cjQcxyo8TDwTX.5G46PBL347N3/0zO|jdsa@vmware.com|Joel|D'Sa + dave|$2a$10$HoWPAUn9zqmmb0b.2TBZWe6cjQcxyo8TDwTX.5G46PBL347N3/0zO|dsyer@vmware.com|Dave|Syer + luke|$2a$10$HoWPAUn9zqmmb0b.2TBZWe6cjQcxyo8TDwTX.5G46PBL347N3/0zO|ltaylor@vmware.com|Luke|Taylor + marissa|$2a$10$ikFXo9IFG6zbMbhGcssySOhjDsGPpqzKwsdVOeCvJ7JoWjSQxyfs6|marissa@test.org|Marissa|Bloggs + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/BootstrapTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/BootstrapTests.java index ca485d396ee..bc54e62e9d5 100755 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/BootstrapTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/BootstrapTests.java @@ -13,6 +13,11 @@ package org.cloudfoundry.identity.uaa; +import static org.junit.Assert.assertNotNull; + +import org.cloudfoundry.identity.uaa.user.InMemoryUaaUserDatabase; +import org.cloudfoundry.identity.uaa.user.JdbcUaaUserDatabase; +import org.junit.After; import org.junit.Test; import org.springframework.context.support.GenericXmlApplicationContext; import org.springframework.core.io.FileSystemResource; @@ -22,10 +27,25 @@ * */ public class BootstrapTests { + + + @After + public void cleanup() { + System.clearProperty("spring.profiles.active"); + } + + @Test + public void testRootContextWithJdbcUsers() throws Exception { + System.setProperty("spring.profiles.active", "jdbc,hsqldb"); + GenericXmlApplicationContext context = new GenericXmlApplicationContext(new FileSystemResource("src/main/webapp/WEB-INF/spring-servlet.xml")); + assertNotNull(context.getBean("userDatabase", JdbcUaaUserDatabase.class)); + context.close(); + } @Test - public void testRootContext() throws Exception { + public void testRootContextWithDevUsers() throws Exception { GenericXmlApplicationContext context = new GenericXmlApplicationContext(new FileSystemResource("src/main/webapp/WEB-INF/spring-servlet.xml")); + assertNotNull(context.getBean("userDatabase", InMemoryUaaUserDatabase.class)); context.close(); }