PL-6730 REST API should work in portal module; Some URL configurations from the rest-api-security-spring.xml are extracted to the app properties; jackson dependency added; Portal spring context is now a root web spring context

This commit is contained in:
Maxim Gorbunkov 2016-07-22 12:23:00 +04:00
parent 4d46afca65
commit e34d41f164
15 changed files with 131 additions and 35 deletions

View File

@ -741,6 +741,8 @@ configure(restApiModule) {
compile(group: 'org.springframework', name: 'spring-webmvc', version: springVersion)
compile(group: 'org.springframework', name: 'spring-context-support', version: springVersion)
compile(group: 'org.json', name: 'json', version: '20140107')
compile(group: 'com.fasterxml.jackson.core', name:'jackson-databind', version: '2.8.0')
compile(groovyArtifact)
compile(group: 'org.springframework.security', name: 'spring-security-core', version: springSecurityVersion)

View File

@ -86,7 +86,7 @@ public class AppContextLoader extends AbstractWebAppContextLoader {
}
@Override
protected ClassPathXmlApplicationContext createClassPathXmlApplicationContext(String[] locations) {
protected ClassPathXmlApplicationContext createApplicationContext(String[] locations) {
return new CubaCoreApplicationContext(locations);
}

View File

@ -77,7 +77,7 @@ public class SingleAppCoreContextLoader extends AppContextLoader {
}
@Override
protected ClassPathXmlApplicationContext createClassPathXmlApplicationContext(String[] locations) {
protected ClassPathXmlApplicationContext createApplicationContext(String[] locations) {
return new CubaCoreApplicationContext(locations) {
/**
* Here we create resource resolver which scans only core jars (and avoid putting web beans to core context)

View File

@ -45,7 +45,8 @@
<module name="rest-api" dependsOn="client" blocks="web,portal">
<artifact name="cuba-rest-api" appJar="true"/>
<property name="cuba.dispatcherSpringContextConfig" value="rest-api-security-spring.xml"/>
<property name="cuba.springContextConfig" value="rest-api-security-spring.xml"/>
<property name="cuba.dispatcherSpringContextConfig" value="rest-api-dispatcher-spring.xml"/>
</module>
<module name="gui" dependsOn="client" blocks="web,desktop">

View File

@ -20,6 +20,7 @@ package com.haulmont.cuba.core.sys;
import com.haulmont.cuba.core.sys.persistence.EclipseLinkCustomizer;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.text.StrTokenizer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.ResourceUtils;
@ -52,7 +53,7 @@ public abstract class AbstractAppContextLoader {
String[] locations = tokenizer.getTokenArray();
replaceLocationsFromConf(locations);
ClassPathXmlApplicationContext appContext = createClassPathXmlApplicationContext(locations);
ApplicationContext appContext = createApplicationContext(locations);
AppContext.Internals.setApplicationContext(appContext);
}
@ -74,7 +75,7 @@ public abstract class AbstractAppContextLoader {
}
}
protected ClassPathXmlApplicationContext createClassPathXmlApplicationContext(String[] locations) {
protected ApplicationContext createApplicationContext(String[] locations) {
return new CubaClassPathXmlApplicationContext(locations);
}

View File

@ -20,6 +20,12 @@ package com.haulmont.cuba.portal.sys;
import com.haulmont.cuba.core.global.ClientType;
import com.haulmont.cuba.core.sys.AbstractWebAppContextLoader;
import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.core.sys.CubaXmlWebApplicationContext;
import com.haulmont.cuba.core.sys.ServletContextHolder;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import javax.servlet.ServletContext;
/**
* {@link AppContext} loader of the web portal client application.
@ -38,4 +44,25 @@ public class PortalAppContextLoader extends AbstractWebAppContextLoader {
AppContext.setProperty("cuba.clientType", ClientType.PORTAL.toString());
}
@Override
protected ApplicationContext createApplicationContext(String[] locations) {
CubaXmlWebApplicationContext webContext = new CubaXmlWebApplicationContext();
String[] classPathLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
classPathLocations[i] = "classpath:" + locations[i];
}
webContext.setConfigLocations(classPathLocations);
webContext.setServletContext(ServletContextHolder.getServletContext());
webContext.refresh();
ServletContext servletContext = ServletContextHolder.getServletContext();
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, webContext);
return webContext;
}
}

View File

@ -59,4 +59,19 @@ cuba.mainMessagePack=com.haulmont.cuba.core
cuba.groovyClassPath=
cuba.groovyEvaluatorImport=com.haulmont.cuba.core.global.PersistenceHelper
cuba.passwordEncryptionModule=cuba_Sha1EncryptionModule
cuba.passwordEncryptionModule=cuba_Sha1EncryptionModule
###############################################################################
# REST API #
###############################################################################
cuba.rest.url.oauthPattern=/api/oauth/**
cuba.rest.url.apiPattern=/api/**
cuba.rest.url.oauthToken=/api/oauth/token
# Credentials for REST API client
cuba.rest.client.id=client
cuba.rest.client.secret=secret
# A token expiration time in seconds for the default client (12 hours)
cuba.rest.client.tokenExpirationTimeSec=43200

View File

@ -30,7 +30,6 @@ import javax.inject.Inject;
/**
*/
@Component
public class OAuthTokenRevoker {
private static final Logger log = LoggerFactory.getLogger(OAuthTokenRevoker.class);

View File

@ -32,8 +32,7 @@ import java.util.UUID;
/**
*/
@Component
public class ParseUtils {
public class RestParseUtils {
@Inject
protected EntitySerializationAPI entitySerializationAPI;

View File

@ -42,7 +42,6 @@ import java.util.stream.Collectors;
* Class is used for loading and storing of predefined JPQL queries that are used by the REST API.
* Queries are loaded from configuration files defined by the {@code cuba.rest.queriesConfig} application property.
*/
@Component("cuba_RestQueriesManager")
public class RestQueriesManager {
protected final String CUBA_REST_QUERIES_CONFIG_PROP_NAME = "cuba.rest.queriesConfig";

View File

@ -25,10 +25,9 @@ import com.haulmont.chile.core.datatypes.Datatypes;
import com.haulmont.cuba.core.app.serialization.EntitySerializationAPI;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.restapi.common.ParseUtils;
import com.haulmont.restapi.common.RestParseUtils;
import com.haulmont.restapi.exception.RestAPIException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import javax.inject.Inject;
@ -42,11 +41,10 @@ import java.util.Map;
/**
* Class is used for invoking middleware services from REST API service controller
*/
@Component
public class RestServiceInvoker {
@Inject
protected ParseUtils parseUtils;
protected RestParseUtils restParseUtils;
@Inject
protected EntitySerializationAPI entitySerializationAPI;
@ -86,7 +84,7 @@ public class RestServiceInvoker {
for (int i = 0; i < types.length; i++) {
Class<?> aClass = types[i];
try {
paramValues.add(parseUtils.toObject(aClass, paramValuesStr.get(i)));
paramValues.add(restParseUtils.toObject(aClass, paramValuesStr.get(i)));
} catch (ParseException e) {
throw new RestAPIException("Invalid parameter value",
e.getMessage(),

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2008-2016 Haulmont.
~
~ 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.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.haulmont.restapi.controllers"/>
</beans>

View File

@ -31,10 +31,17 @@
http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.haulmont.restapi"/>
<bean class="com.haulmont.cuba.core.sys.CubaPropertyPlaceholderConfigurer"/>
<!-- Module beans -->
<bean id="cuba_OAuthTokenRevoker" class="com.haulmont.restapi.auth.OAuthTokenRevoker"/>
<bean id="cuba_RestQueriesManager" class="com.haulmont.restapi.query.RestQueriesManager"/>
<bean id="cuba_RestServiceInvoker" class="com.haulmont.restapi.service.RestServiceInvoker"/>
<bean id="cuba_RestParseUtils" class="com.haulmont.restapi.common.RestParseUtils"/>
<!-- According to the spec the token endpoint should be secured by the basic authentication -->
<http pattern="/dispatch/api/oauth/**"
<http pattern="${cuba.rest.url.oauthPattern}"
create-session="stateless"
authentication-manager-ref="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
@ -43,6 +50,8 @@
<csrf disabled="true"/>
</http>
<bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"/>
<!-- Authentication manager that is used by token endpoint. Checks client credentials in http header -->
<security:authentication-manager id="clientAuthenticationManager">
<security:authentication-provider user-service-ref="clientDetailsUserDetailsService"/>
@ -56,26 +65,27 @@
accessing the auth token -->
<oauth2:client-details-service id="clientDetailsService">
<oauth2:client
client-id="client"
secret="secret"
client-id="${cuba.rest.client.id}"
secret="${cuba.rest.client.secret}"
access-token-validity="${cuba.rest.client.tokenExpirationTimeSec}"
authorized-grant-types="password"
scope="rest-api"/>
</oauth2:client-details-service>
<!-- Specifies token endpoint.-->
<oauth2:authorization-server
token-endpoint-url="/api/oauth/token"
token-endpoint-url="${cuba.rest.url.oauthToken}"
client-details-service-ref="clientDetailsService"
token-services-ref="tokenServices" >
<oauth2:password authentication-manager-ref="userAuthenticationManager"/>
</oauth2:authorization-server>
<bean id="userAuthenticationProvider" class="com.haulmont.restapi.auth.CubaUserAuthenticationProvider"/>
<security:authentication-manager alias="userAuthenticationManager">
<security:authentication-provider ref="userAuthenticationProvider"/>
</security:authentication-manager>
<bean id="userAuthenticationProvider" class="com.haulmont.restapi.auth.CubaUserAuthenticationProvider"/>
<!--todo MG try to remove clientDetailsService-->
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore"/>
@ -86,27 +96,24 @@
<oauth2:resource-server id="resourceFilter" token-services-ref="tokenServices" />
<http pattern="/dispatch/api/**"
<!--todo MG remove hasRole check-->
<http pattern="${cuba.rest.url.apiPattern}"
create-session="stateless"
entry-point-ref="oauthAuthenticationEntryPoint"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/dispatch/api/entities/**" access="hasRole('USER')"/>
<intercept-url pattern="/dispatch/api/queries/**" access="hasRole('USER')"/>
<intercept-url pattern="/dispatch/api/services/**" access="hasRole('USER')"/>
<intercept-url pattern="/**" access="hasRole('USER')"/>
<anonymous enabled="false"/>
<csrf disabled="true"/>
<custom-filter ref="resourceFilter" before="PRE_AUTH_FILTER"/>
</http>
<bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"/>
<bean id="clientCredentialsTokenEndpointFilter"
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="clientAuthenticationManager"/>
</bean>
<!--<bean id="clientCredentialsTokenEndpointFilter"-->
<!--class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">-->
<!--<property name="authenticationManager" ref="clientAuthenticationManager"/>-->
<!--</bean>-->
<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="test" />
<property name="realmName" value="rest-api" />
</bean>
</beans>

View File

@ -100,7 +100,7 @@ public class SingleAppWebContextLoader extends WebAppContextLoader {
}
@Override
protected ClassPathXmlApplicationContext createClassPathXmlApplicationContext(String[] locations) {
protected ClassPathXmlApplicationContext createApplicationContext(String[] locations) {
return new CubaClassPathXmlApplicationContext(locations) {
/**
* Here we create resource resolver which scans only web jars (and avoid putting core beans to web context)

View File

@ -89,4 +89,19 @@ cuba.web.loginDialogDefaultPassword=admin
com.vaadin.terminal.gwt.server.productionMode=false
# Enable Testing mode: true or false (by default)
cuba.testMode=false
cuba.testMode=false
###############################################################################
# REST API #
###############################################################################
cuba.rest.url.oauthPattern=/dispatch/api/oauth/**
cuba.rest.url.apiPattern=/dispatch/api/**
cuba.rest.url.oauthToken=/api/oauth/token
# Credentials for REST API client
cuba.rest.client.id=client
cuba.rest.client.secret=secret
# A token expiration time in seconds for the default client (12 hours)
cuba.rest.client.tokenExpirationTimeSec=43200