Skip to content

Commit

Permalink
Upgrade to Spring 3.1
Browse files Browse the repository at this point in the history
Change-Id: I1d68ef97b46f4906b43fd8748c68e12d8a1e8cc8
  • Loading branch information
dsyer committed Dec 15, 2011
1 parent c2d431a commit 92647e4
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 53 deletions.
26 changes: 21 additions & 5 deletions README.md
Expand Up @@ -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.
Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Expand Up @@ -13,7 +13,7 @@
</modules>

<properties>
<spring.version>3.0.6.RELEASE</spring.version>
<spring.version>3.1.0.RELEASE</spring.version>
<spring.security.version>3.1.0.RELEASE</spring.security.version>
<spring.security.oauth.version>1.0.0.BUILD-SNAPSHOT</spring.security.oauth.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
Expand Down
Expand Up @@ -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
Expand All @@ -35,19 +43,24 @@
* @author Dave Syer
*
*/
public class HandlerAdapterFactoryBean implements FactoryBean<HandlerAdapter> {
public class HandlerAdapterFactoryBean implements FactoryBean<HandlerAdapter>, 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
.<HandlerMethodReturnValueHandler> asList(new ScimEtagHandlerMethodReturnValueHandler(getMessageConverters())));
adapter.afterPropertiesSet();
return adapter;
}

private HttpMessageConverter<?>[] getMessageConverters() {
return new RestTemplate().getMessageConverters().toArray(new HttpMessageConverter[0]);
private List<HttpMessageConverter<?>> getMessageConverters() {
return new RestTemplate().getMessageConverters();
}

@Override
Expand All @@ -60,17 +73,32 @@ public boolean isSingleton() {
return true;
}

private static class ScimEtagModelAndViewResolver implements ModelAndViewResolver {
private static class ScimEtagHandlerMethodReturnValueHandler extends RequestResponseBodyMethodProcessor {

public ScimEtagHandlerMethodReturnValueHandler(List<HttpMessageConverter<?>> 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;
}
}
Expand Up @@ -31,7 +31,7 @@ public String getIdentity(HttpSession session) {
}

@RequestMapping("/oauth/confirm_access")
public String confirm(UnconfirmedAuthorizationCodeClientToken clientAuth, Map<String, Object> model, final HttpServletRequest request)
public String confirm(@ModelAttribute UnconfirmedAuthorizationCodeClientToken clientAuth, Map<String, Object> model, final HttpServletRequest request)
throws Exception {

if (clientAuth == null) {
Expand Down
26 changes: 19 additions & 7 deletions uaa/src/main/webapp/WEB-INF/spring-data-source.xml
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.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">
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">

<context:property-placeholder properties-ref="applicationProperties" />

Expand All @@ -14,13 +14,13 @@
<property name="resource" value="classpath:/uaa.yml" />
<property name="keyReplacements">
<map>
<entry key="databases.#{systemProperties['PLATFORM']?:'hsqldb'}" value="database" />
<entry key="databases.#{@platform}" value="database" />
</map>
</property>
</bean>
<bean class="org.cloudfoundry.identity.uaa.config.YamlPropertiesFactoryBean">
<property name="resource" value="${CLOUD_FOUNDRY_CONFIG_PATH:config}/uaa.yml" />
<property name="ignoreResourceNotFound" value="true"/>
<property name="ignoreResourceNotFound" value="true" />
</bean>
</list>
</property>
Expand All @@ -32,9 +32,21 @@
<property name="username" value="${database.username}" />
<property name="password" value="${database.password}" />
</bean>

<jdbc:initialize-database ignore-failures="ALL">
<jdbc:script location="classpath:org/cloudfoundry/identity/uaa/scim/schema-${PLATFORM:hsqldb}.sql"/>
<jdbc:script location="classpath:org/cloudfoundry/identity/uaa/scim/schema-#{@platform}.sql" />
</jdbc:initialize-database>

<beans profile="default,hsqldb">
<bean id="platform" class="java.lang.String">
<constructor-arg value="hsqldb" />
</bean>
</beans>

<beans profile="postgresql">
<bean id="platform" class="java.lang.String">
<constructor-arg value="postgresql" />
</bean>
</beans>

</beans>
70 changes: 45 additions & 25 deletions uaa/src/main/webapp/WEB-INF/spring-servlet.xml
Expand Up @@ -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">

<import resource="spring-data-source.xml"/>
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">

<sec:debug />

Expand Down Expand Up @@ -101,26 +99,6 @@
<constructor-arg ref="userDatabase" />
</bean>

<bean id="userDatabase" class="org.cloudfoundry.identity.uaa.user.InMemoryUaaUserDatabase">
<constructor-arg ref="userData" />
</bean>

<bean id="scimUserProvisioning" class="org.cloudfoundry.identity.uaa.scim.InMemoryScimUserProvisioning">
<constructor-arg ref="userData" />
</bean>

<bean id="userData" class="org.cloudfoundry.identity.uaa.config.UaaUserMapFactoryBean">
<constructor-arg>
<list value-type="org.cloudfoundry.identity.uaa.user.UaaUser">
<value>dale|$2a$10$HoWPAUn9zqmmb0b.2TBZWe6cjQcxyo8TDwTX.5G46PBL347N3/0zO|olds@vmware.com|Dale|Olds</value>
<value>joel|$2a$10$HoWPAUn9zqmmb0b.2TBZWe6cjQcxyo8TDwTX.5G46PBL347N3/0zO|jdsa@vmware.com|Joel|D'Sa</value>
<value>dave|$2a$10$HoWPAUn9zqmmb0b.2TBZWe6cjQcxyo8TDwTX.5G46PBL347N3/0zO|dsyer@vmware.com|Dave|Syer</value>
<value>luke|$2a$10$HoWPAUn9zqmmb0b.2TBZWe6cjQcxyo8TDwTX.5G46PBL347N3/0zO|ltaylor@vmware.com|Luke|Taylor</value>
<value>marissa|$2a$10$ikFXo9IFG6zbMbhGcssySOhjDsGPpqzKwsdVOeCvJ7JoWjSQxyfs6|marissa@test.org|Marissa|Bloggs</value>
</list>
</constructor-arg>
</bean>

<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.RandomValueTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
Expand Down Expand Up @@ -213,4 +191,46 @@

<bean id="homeController" class="org.cloudfoundry.identity.uaa.home.HomeController" />

<beans profile="default,dev">

<bean id="userDatabase" class="org.cloudfoundry.identity.uaa.user.InMemoryUaaUserDatabase">
<constructor-arg ref="userData" />
</bean>

<bean id="scimUserProvisioning" class="org.cloudfoundry.identity.uaa.scim.InMemoryScimUserProvisioning">
<constructor-arg ref="userData" />
</bean>

<bean id="userData" class="org.cloudfoundry.identity.uaa.config.UaaUserMapFactoryBean">
<constructor-arg>
<list value-type="org.cloudfoundry.identity.uaa.user.UaaUser">
<value>dale|$2a$10$HoWPAUn9zqmmb0b.2TBZWe6cjQcxyo8TDwTX.5G46PBL347N3/0zO|olds@vmware.com|Dale|Olds</value>
<value>joel|$2a$10$HoWPAUn9zqmmb0b.2TBZWe6cjQcxyo8TDwTX.5G46PBL347N3/0zO|jdsa@vmware.com|Joel|D'Sa</value>
<value>dave|$2a$10$HoWPAUn9zqmmb0b.2TBZWe6cjQcxyo8TDwTX.5G46PBL347N3/0zO|dsyer@vmware.com|Dave|Syer</value>
<value>luke|$2a$10$HoWPAUn9zqmmb0b.2TBZWe6cjQcxyo8TDwTX.5G46PBL347N3/0zO|ltaylor@vmware.com|Luke|Taylor</value>
<value>marissa|$2a$10$ikFXo9IFG6zbMbhGcssySOhjDsGPpqzKwsdVOeCvJ7JoWjSQxyfs6|marissa@test.org|Marissa|Bloggs</value>
</list>
</constructor-arg>
</bean>

</beans>

<beans profile="jdbc">

<import resource="spring-data-source.xml" />

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>

<bean id="userDatabase" class="org.cloudfoundry.identity.uaa.user.JdbcUaaUserDatabase">
<constructor-arg ref="jdbcTemplate" />
</bean>

<bean id="scimUserProvisioning" class="org.cloudfoundry.identity.uaa.scim.JdbcScimUserProvisioning">
<constructor-arg ref="jdbcTemplate" />
</bean>

</beans>

</beans>
Expand Up @@ -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;
Expand All @@ -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();
}

Expand Down

0 comments on commit 92647e4

Please sign in to comment.