Commit 0470bb37 authored by Matija Obreza's avatar Matija Obreza

Genesys Server reconfiguration

- WebInitializer
- No more src/main/resources/spring/spring-**.xml
- Moved /spring/spring.properties to /applicaiton.properties
parent ca218337
......@@ -58,7 +58,7 @@
<snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
<junit.version>4.12</junit.version>
<application.blocks.version>1.2</application.blocks.version>
<application.blocks.version>1.3-SNAPSHOT</application.blocks.version>
<commons.beanutils.version>1.9.2</commons.beanutils.version>
<commons.collections.version>3.2.1</commons.collections.version>
<commons.fileupload.version>1.3.1</commons.fileupload.version>
......@@ -75,7 +75,7 @@
<spring-data-jpa.version>1.10.4.RELEASE</spring-data-jpa.version>
<spring.data.release-train>Hopper-SR1</spring.data.release-train>
<spring.security.version>4.1.3.RELEASE</spring.security.version>
<spring.security.oauth2.version>1.0.5.RELEASE</spring.security.oauth2.version>
<spring.security.oauth2.version>2.0.14.RELEASE</spring.security.oauth2.version>
<org.springframework.social-version>1.1.4.RELEASE</org.springframework.social-version>
<org.springframework.social-google-version>1.0.0.RELEASE</org.springframework.social-google-version>
<querydsl.version>4.1.4</querydsl.version>
......@@ -302,6 +302,11 @@
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<!-- App blocks -->
<dependency>
......
......@@ -59,7 +59,7 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Service(value = "userService")
@Transactional(readOnly = true)
public class UserServiceImpl extends BasicUserServiceImpl<UserRole, User> implements UserService {
......
......@@ -16,19 +16,31 @@
package org.genesys2.spring.config;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.genesys2.brapi.service.BrAPIService;
import org.genesys2.brapi.service.impl.BrAPIServiceImpl;
import org.genesys2.transifex.client.TransifexService;
import org.genesys2.transifex.client.TransifexServiceImpl;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
@Configuration
@Import({ SpringProperties.class, SpringCommonConfig.class, SpringAclConfig.class, SpringSchedulerConfig.class, SpringDataBaseConfig.class,
SpringMailConfig.class, SpringSecurityOauthConfig.class, SpringSecurityConfig.class, SpringCacheConfig.class, ElasticsearchConfig.class, FileRepositoryConfig.class })
@Import({ SpringCommonConfig.class, SpringSchedulerConfig.class, SpringDataBaseConfig.class,
SpringMailConfig.class, OAuth2ServerConfig.class, SecurityConfig.class, SpringCacheConfig.class, ElasticsearchConfig.class, FileRepositoryConfig.class, WebConfiguration.class })
public class ApplicationConfig {
public static final Log LOG = LogFactory.getLog(ApplicationConfig.class);
......@@ -41,4 +53,60 @@ public class ApplicationConfig {
public BrAPIService brapiService() {
return new BrAPIServiceImpl();
}
@Bean
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
final PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer();
propertyPlaceholderConfigurer.setIgnoreResourceNotFound(false);
propertyPlaceholderConfigurer.setSystemPropertiesMode(PropertyPlaceholderConfigurer.SYSTEM_PROPERTIES_MODE_OVERRIDE);
propertyPlaceholderConfigurer.setFileEncoding("utf-8");
final List<Resource> locations = new ArrayList<>();
locations.add(new ClassPathResource("application.properties"));
locations.add(new ClassPathResource("genesys.properties"));
final String extraConfigFile = System.getenv("config.file");
if (StringUtils.isNotBlank(extraConfigFile)) {
final File f = new File(extraConfigFile);
if (f.exists() && f.canRead()) {
System.err.println("Including properties from config.file=" + extraConfigFile);
locations.add(new FileSystemResource(extraConfigFile));
} else {
System.err.println("Cannot read properties from config.file=" + extraConfigFile);
}
}
propertyPlaceholderConfigurer.setLocations(locations.toArray(new Resource[] {}));
return propertyPlaceholderConfigurer;
}
@Bean
public ResourceBundleMessageSource messageSource() {
final ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setBasename("content/language");
source.setDefaultEncoding("UTF-8");
source.setUseCodeAsDefaultMessage(true);
return source;
}
/**
* localeURLFilter in web.xml: en es de fr fa ar ru zh pt
*/
@Bean
public static Set<String> supportedLocales() {
final Set<String> supportedLocales = new HashSet<String>();
supportedLocales.add("en");
supportedLocales.add("es");
supportedLocales.add("de");
supportedLocales.add("fr");
supportedLocales.add("fa");
supportedLocales.add("ar");
supportedLocales.add("ru");
supportedLocales.add("zh");
supportedLocales.add("pt");
return supportedLocales;
}
}
/*
* Copyright 2017 Global Crop Diversity Trust
*
* 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.genesys2.spring.config;
import org.genesys.blocks.oauth.service.OAuthServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
@Import(SwaggerConfig.class)
public class OAuth2ServerConfig {
private static final String APPLICATION_RESOURCE_ID = "genesys";
@Bean
public OAuthServiceImpl oauthService() {
return new OAuthServiceImpl();
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
@Configuration
@EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(final ResourceServerSecurityConfigurer resources) {
resources.resourceId(APPLICATION_RESOURCE_ID).stateless(true);
}
@Override
public void configure(final HttpSecurity http) throws Exception {
http
// Since we want the protected resources to be accessible in the UI as well we
// need session creation to be allowed (it's disabled by default in 2.0.6)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)
// no CSRF
.and().csrf().disable()
// cors
.cors()
// API info can be accessed anonymously
.and().authorizeRequests().antMatchers("/api/v0/info/version").permitAll()
.and().requestMatchers().antMatchers("/api/**", "/token")
.and().authorizeRequests().antMatchers("/api/**", "/token").authenticated()
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
private TokenStore tokenStore;
@Autowired
@Qualifier("userService")
private UserDetailsService userDetailsService;
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Autowired
@Qualifier("oauthService")
private ClientDetailsService clientDetailsService;
@Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
@Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.userDetailsService(userDetailsService).tokenStore(tokenStore).authenticationManager(authenticationManager);
}
@Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.allowFormAuthenticationForClients().checkTokenAccess("permitAll()").realm(APPLICATION_RESOURCE_ID + "/client");
}
}
@Configuration
protected static class Stuff {
@Autowired
@Qualifier("oauthService")
private TokenStore tokenStore;
@Bean
public ApprovalStore approvalStore() throws Exception {
final TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore);
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
}
}
/**
* Copyright 2014 Global Crop Diversity Trust
/*
* Copyright 2017 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -12,28 +12,49 @@
* 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.genesys2.spring.config;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.genesys.blocks.security.component.AclAssignerAspect;
import org.genesys.blocks.security.component.Slf4jLogAuditLogger;
import org.genesys.blocks.security.lockout.AccountLockoutConfig;
import org.genesys.blocks.security.service.CustomAclService;
import org.genesys.blocks.security.service.PasswordPolicy;
import org.genesys.blocks.security.service.impl.CustomAclServiceImpl;
import org.genesys.blocks.security.service.impl.SimplePasswordPolicy;
import org.genesys2.server.cache.HazelcastAclCache;
import org.genesys2.server.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.acls.AclPermissionEvaluator;
import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl;
import org.springframework.security.acls.domain.AuditLogger;
import org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy;
import org.springframework.security.acls.jdbc.BasicLookupStrategy;
import org.springframework.security.acls.jdbc.JdbcMutableAclService;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SpringAclConfig {
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true, securedEnabled=true)
@Import({ AccountLockoutConfig.class })
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userDetailsService;
@Autowired
private DataSource dataSource;
......@@ -56,11 +77,6 @@ public class SpringAclConfig {
return new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ADMINISTRATOR"));
}
@Bean
public AuditLogger auditLogger() {
return new Slf4jLogAuditLogger();
}
@Bean
public DefaultPermissionGrantingStrategy permissionGrantingStrategy() {
return new DefaultPermissionGrantingStrategy(auditLogger());
......@@ -68,17 +84,78 @@ public class SpringAclConfig {
@Bean
public HazelcastAclCache aclCache() {
return new HazelcastAclCache(aclCacheCache(), permissionGrantingStrategy(), aclAuthorizationStrategy());
return new HazelcastAclCache(cacheManager.getCache("aclcache"), permissionGrantingStrategy(), aclAuthorizationStrategy());
}
@Bean
public Cache aclCacheCache() {
return cacheManager.getCache("aclcache");
public BasicLookupStrategy lookupStrategy() {
return new BasicLookupStrategy(dataSource, aclCache(), aclAuthorizationStrategy(), permissionGrantingStrategy());
}
@Bean
public BasicLookupStrategy lookupStrategy() {
return new BasicLookupStrategy(dataSource, aclCache(), aclAuthorizationStrategy(), permissionGrantingStrategy());
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* Ensure that AclAware models have permissions assigned and removed
*/
@Bean
public AclAssignerAspect aclAssignerAspect() {
return new AclAssignerAspect();
}
@Bean
public CustomAclService customAclService() {
return new CustomAclServiceImpl();
}
@Bean
public AuditLogger auditLogger() {
return new Slf4jLogAuditLogger();
}
@Bean
public PasswordPolicy passwordPolicy() {
SimplePasswordPolicy passwordPolicy = new SimplePasswordPolicy();
return passwordPolicy;
}
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
http
// No JSESSIONID in URL
.sessionManagement().enableSessionUrlRewriting(false).sessionFixation().migrateSession()
// Authorizations
.and().authorizeRequests()
.antMatchers("/admin/**", "/1/admin/**").hasRole("ADMINISTRATOR")
.antMatchers("/profile**", "/oauth/authorize", "/swagger-**").fullyAuthenticated()
// access denied
.and().exceptionHandling().accessDeniedPage("/access-denied")
// CSRF
.and().csrf()
// Logout and login
.and().logout().logoutUrl("/logout").logoutSuccessUrl("/")
// Login form
.and().formLogin().permitAll().loginPage("/login").failureUrl("/login?error=1").loginProcessingUrl("/login-attempt").defaultSuccessUrl("/");
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
......@@ -27,7 +27,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.ImportResource;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.orm.jpa.JpaTransactionManager;
......@@ -37,12 +37,12 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
import liquibase.integration.spring.SpringLiquibase;
@EnableJpaRepositories(basePackages = { "org.genesys.blocks.persistence", "org.genesys.blocks.security.persistence", "org.genesys2.server.persistence.domain", "org.genesys2.server.persistence.acl",
@EnableJpaRepositories(basePackages = { "org.genesys.blocks.persistence", "org.genesys.blocks.security.persistence", "org.genesys.blocks.oauth.persistence",
"org.genesys2.server.persistence.domain", "org.genesys2.server.persistence.acl",
"org.genesys.filerepository.persistence" }, entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager", repositoryImplementationPostfix = "CustomImpl")
// @EnableJpaAuditing(auditorAwareRef = "auditorAware")
@EnableJpaAuditing(auditorAwareRef = "auditorAware")
@EnableTransactionManagement
@Configuration
@ImportResource("classpath:/spring/spring-db.xml")
public class SpringDataBaseConfig {
@Value("${db.url}")
......@@ -67,7 +67,8 @@ public class SpringDataBaseConfig {
private String hibernateDialect;
/*
* db.pool.initialSize=1 db.pool.maxIdle=10 db.pool.maxActive=20 db.pool.maxIdleTime=15
* db.pool.initialSize=1 db.pool.maxIdle=10 db.pool.maxActive=20
* db.pool.maxIdleTime=15
*/
@Value("${db.pool.initialSize}")
private int initialSize;
......@@ -112,7 +113,8 @@ public class SpringDataBaseConfig {
@Bean(name = "databaseMigration")
public SpringLiquibase databaseMigration() {
SpringLiquibase liquibase = new SpringLiquibase();
// Run controlled database migration only when not generating schema automatically
// Run controlled database migration only when not generating schema
// automatically
liquibase.setShouldRun(!dbGenerateDdl);
liquibase.setChangeLog("classpath:liquibase/liquibase-changeLog.yml");
liquibase.setDataSource(dataSource());
......@@ -132,7 +134,8 @@ public class SpringDataBaseConfig {
System.err.println("JPA: " + key + " = " + jpaProperties.get(key));
}
entityManager.setJpaProperties(jpaProperties);
entityManager.setPackagesToScan("org.genesys.blocks.model", "org.genesys.blocks.security.model", "org.genesys2.server.model", "org.genesys.filerepository.model");
entityManager.setPackagesToScan("org.genesys.blocks.model", "org.genesys.blocks.security.model", "org.genesys.blocks.oauth.model", "org.genesys2.server.model",
"org.genesys.filerepository.model");
return entityManager;
}
......@@ -147,7 +150,7 @@ public class SpringDataBaseConfig {
private Properties jpaProperties() throws Exception {
Properties jpaProp = new Properties();
jpaProp.load(getClass().getResourceAsStream("/spring/hibernate.properties"));
jpaProp.load(getClass().getResourceAsStream("/hibernate.properties"));
jpaProp.put("hibernate.dialect", hibernateDialect);
return jpaProp;
}
......
/**
* Copyright 2014 Global Crop Diversity Trust
*
* 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.genesys2.spring.config;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
@Configuration
public class SpringProperties {
@Bean
public PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
for (String envvar : System.getenv().keySet()) {
System.err.println("ENV " + envvar + "=" + System.getenv(envvar));
}
for (Object prop : System.getProperties().keySet()) {
System.err.println("PROP " + prop + "=" + System.getProperty(prop.toString()));
}
final PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer();
// Need to ignore "genesys.properties" if not found
propertyPlaceholderConfigurer.setIgnoreResourceNotFound(true);
propertyPlaceholderConfigurer.setSystemPropertiesMode(PropertyPlaceholderConfigurer.SYSTEM_PROPERTIES_MODE_OVERRIDE);
propertyPlaceholderConfigurer.setFileEncoding("utf-8");
List<Resource> locations = new ArrayList<>();
locations.add(new ClassPathResource("application.properties"));
locations.add(new ClassPathResource("spring/spring.properties"));
// Include genesys.properties
locations.add(new ClassPathResource("genesys.properties"));
String extraConfigFile = System.getenv("config.file");
if (StringUtils.isNotBlank(extraConfigFile)) {
File f = new File(extraConfigFile);
System.err.println("Attempting to read properties from config.file=" + extraConfigFile + " at " + f.getAbsolutePath());
if (f.exists() && f.canRead()) {
System.err.println("Including properties from config.file=" + f.getAbsolutePath());
locations.add(new FileSystemResource(extraConfigFile));
} else {
System.err.println("Cannot read properties from config.file=" + f.getAbsolutePath());
}
}
propertyPlaceholderConfigurer.setLocations(locations.toArray(new Resource[] {}));
return propertyPlaceholderConfigurer;
}
/**
* localeURLFilter in web.xml: en es de fr fa ar ru zh pt
*/
@Bean
public static Set<String> supportedLocales() {
final Set<String> supportedLocales = new HashSet<String>();
supportedLocales.add("en");
supportedLocales.add("es");
supportedLocales.add("de");
supportedLocales.add("fr");
supportedLocales.add("fa");
supportedLocales.add("ar");
supportedLocales.add("ru");
supportedLocales.add("zh");
supportedLocales.add("pt");
return supportedLocales;
}
}
/*
* Copyright 2017 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");