FINERACT-1433: Switch to Java based config for Spring

This commit is contained in:
Petri Tuomola
2021-11-21 04:21:10 +01:00
committed by Aleksandar Vidakovic
parent 2513ee4837
commit 2b57bf379e
31 changed files with 454 additions and 712 deletions
@@ -26,7 +26,6 @@ import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;
/**
* Fineract main() application which launches Fineract in an embedded Tomcat HTTP (using Spring Boot).
@@ -39,13 +38,11 @@ import org.springframework.context.annotation.ImportResource;
*
* It's the old/classic Mifos (non-X) Workspace 2.0 reborn for Fineract! ;-)
*
* @see ServerWithMariaDB4jApplication for an alternative with an embedded DB
*/
public class ServerApplication extends SpringBootServletInitializer {
@Import({ EmbeddedTomcatWithSSLConfiguration.class })
@ImportResource({ "classpath*:META-INF/spring/hikariDataSource.xml" })
private static class Configuration extends AbstractApplicationConfiguration {}
@Override
@@ -1,105 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.fineract;
import java.awt.Desktop;
import java.io.IOException;
import java.net.URI;
import org.apache.fineract.infrastructure.core.boot.AbstractApplicationConfiguration;
import org.apache.fineract.infrastructure.core.boot.ApplicationExitUtil;
import org.apache.fineract.infrastructure.core.boot.EmbeddedTomcatWithSSLConfiguration;
import org.apache.fineract.infrastructure.core.boot.db.MariaDB4jDataSourceConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.Resource;
/**
* Fineract main() application which launches Fineract in an embedded Tomcat HTTP
* server (using Spring Boot), as well as an embedded database (using
* MariaDB4j).
*
* You can easily launch this via Debug as Java Application in your IDE -
* without needing command line Gradle stuff, no need to build and deploy a WAR,
* remote attachment etc.
*
* It's the old/classic Mifos (non-X) Workspace 2.0 reborn for Fineract! ;-)
*
* @see ServerApplication for the same without the embedded MariaDB4j database
*/
public class ServerWithMariaDB4jApplication {
private final static Logger LOG = LoggerFactory.getLogger(ServerWithMariaDB4jApplication.class);
@Import({ MariaDB4jDataSourceConfiguration.class, EmbeddedTomcatWithSSLConfiguration.class })
public static class Configuration extends AbstractApplicationConfiguration { }
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext ctx = SpringApplication.run(Configuration.class, args);
if (!Desktop.isDesktopSupported()) {
LOG.info("Not going to open UI homepage in local web browser, because !Desktop.isDesktopSupported()");
} else {
// apps/community-app/dist/community-app/index.html
Resource distResource = ctx.getResource("file:" + System.getProperty("user.dir") +
System.getProperty("file.separator") + "apps" +
System.getProperty("file.separator") + "community-app" +
System.getProperty("file.separator") + "dist" +
System.getProperty("file.separator") + "community-app" +
System.getProperty("file.separator") + "index.html");
URI distURI = URI.create("https://localhost:8443/fineract-provider" +
"/apps/community-app/index.html?baseApiUrl=https://localhost:8443" +
"&tenantIdentifier=default#/");
// apps/community-app/app/index.html
Resource devResource = ctx.getResource("file:" + System.getProperty("user.dir") +
System.getProperty("file.separator") + "apps" +
System.getProperty("file.separator") + "community-app" +
System.getProperty("file.separator") + "app" +
System.getProperty("file.separator") + "index.html");
URI devURI = URI.create("https://localhost:8443/fineract-provider" +
"/apps/community-app/app/index.html?baseApiUrl=https://localhost:8443" +
"&tenantIdentifier=default#/");
if (distResource.exists()) {
openWebBrowser(distURI);
} else if (devResource.exists()) {
openWebBrowser(devURI);
} else {
LOG.error("Cannot open Fineract UI in browser; not found: " + distResource.toString());
}
}
// TODO Tray Icon stuff; dig out my very own old @see https://github.com/mifos/head/tree/hudsonBuild-MIFOS-5157_Launch4j-EXE_NewDist-squash1/server-jetty/src/main/java/org/mifos/server/tray
ApplicationExitUtil.waitForKeyPressToCleanlyExit(ctx);
}
private static void openWebBrowser(URI uri) {
try {
LOG.info("Opening Fineract UI in browser: " + uri.toString());
Desktop.getDesktop().browse(uri);
} catch (IOException e) {
LOG.error("IOException when opening Fineract UI in browser: " + uri.toString(), e);
}
}
}
@@ -22,18 +22,14 @@ import org.apache.fineract.infrastructure.cache.service.RuntimeDelegatingCacheMa
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@SuppressWarnings("deprecation")
@Configuration
@EnableCaching
public class PlatformCacheConfiguration implements CachingConfigurer {
public class PlatformCacheConfiguration extends CachingConfigurerSupport implements CachingConfigurer {
@Autowired
private RuntimeDelegatingCacheManager delegatingCacheManager;
@@ -43,21 +39,4 @@ public class PlatformCacheConfiguration implements CachingConfigurer {
public CacheManager cacheManager() {
return this.delegatingCacheManager;
}
@Override
public CacheResolver cacheResolver() {
// TODO https://issues.apache.org/jira/browse/FINERACT-705
return null;
}
@Override
public KeyGenerator keyGenerator() {
return new SimpleKeyGenerator();
}
@Override
public CacheErrorHandler errorHandler() {
// TODO https://issues.apache.org/jira/browse/FINERACT-705
return null;
}
}
@@ -47,13 +47,13 @@ public class RuntimeDelegatingCacheManager implements CacheManager {
private static final Logger LOG = LoggerFactory.getLogger(RuntimeDelegatingCacheManager.class);
private final JCacheCacheManager jcacheCacheManager;
private final CacheManager cacheManager;
private final CacheManager noOpCacheManager = new NoOpCacheManager();
private CacheManager currentCacheManager;
@Autowired
public RuntimeDelegatingCacheManager(final JCacheCacheManager jcacheCacheManager) {
this.jcacheCacheManager = jcacheCacheManager;
public RuntimeDelegatingCacheManager(final JCacheCacheManager cacheManager) {
this.cacheManager = cacheManager;
this.currentCacheManager = this.noOpCacheManager;
}
@@ -109,7 +109,7 @@ public class RuntimeDelegatingCacheManager implements CacheManager {
changes.put(CacheApiConstants.cacheTypeParameter, toCacheType.getValue());
clearEhCache();
}
this.currentCacheManager = this.jcacheCacheManager;
this.currentCacheManager = this.cacheManager;
if (this.currentCacheManager.getCacheNames().size() == 0) {
LOG.error("No caches configured for activated CacheManager {}", this.currentCacheManager);
@@ -126,7 +126,6 @@ public class RuntimeDelegatingCacheManager implements CacheManager {
}
private void clearEhCache() {
javax.cache.CacheManager cacheManager = this.jcacheCacheManager.getCacheManager();
Iterable<String> cacheNames = cacheManager.getCacheNames();
for (String cacheName : cacheNames) {
cacheManager.getCache(cacheName).clear();
@@ -18,7 +18,6 @@
*/
package org.apache.fineract.infrastructure.core.boot;
import org.apache.fineract.notification.config.MessagingConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration;
@@ -26,33 +25,24 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.PropertySource;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* Base Spring Configuration with what's common to all Configuration subclasses.
*
* Notably the EnableAutoConfiguration excludes relevant for (and often adjusted when upgrading versions of) Spring
* Boot, the "old" (pre. Spring Boot &amp; MariaDB4j) fineract appContext.xml which all configurations need, and the
* web.xml successor WebXmlConfiguration.
*
* Should NOT include Configuration related to embedded Tomcat, data sources, and MariaDB4j (because those differ in the
* subclasses).
* Base Spring Configuration. Excludes autoconfiguration for those things we want to manually configure.
*/
@Configuration
@Import({ WebXmlConfiguration.class, WebXmlOauthConfiguration.class, WebFrontEndConfiguration.class, MessagingConfiguration.class,
WebTwoFactorXmlConfiguration.class, JerseyConfig.class })
@ImportResource({ "classpath*:META-INF/spring/appContext.xml" })
@PropertySource(value = "classpath:META-INF/spring/jdbc.properties")
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class, FlywayAutoConfiguration.class, GsonAutoConfiguration.class,
JdbcTemplateAutoConfiguration.class })
@EnableWebSecurity
@EnableTransactionManagement
@EnableWebSecurity
@ComponentScan(basePackages = "org.apache.fineract.**")
public abstract class AbstractApplicationConfiguration {
}
@@ -1,36 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.fineract.infrastructure.core.boot;
import org.apache.fineract.infrastructure.security.filter.TwoFactorAuthenticationFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WebTwoFactorXmlConfiguration {
@Bean
public FilterRegistrationBean twoFactorFilterBean(TwoFactorAuthenticationFilter filter) {
FilterRegistrationBean<TwoFactorAuthenticationFilter> registrationBean = new FilterRegistrationBean<TwoFactorAuthenticationFilter>(
filter);
registrationBean.setEnabled(false);
return registrationBean;
}
}
@@ -1,49 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.fineract.infrastructure.core.boot;
import org.apache.fineract.infrastructure.security.filter.TenantAwareBasicAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
/**
* This Configuration replaces what formerly was in web.xml.
*
* @see <a href=
* "http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-convert-an-existing-application-to-spring-boot">#howto-convert-an-existing-application-to-spring-boot</a>
*/
@Configuration
@Profile("basicauth")
public class WebXmlConfiguration {
@Autowired
private TenantAwareBasicAuthenticationFilter basicAuthenticationProcessingFilter;
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean<TenantAwareBasicAuthenticationFilter> filterRegistrationBean = new FilterRegistrationBean<TenantAwareBasicAuthenticationFilter>();
filterRegistrationBean.setFilter(basicAuthenticationProcessingFilter);
filterRegistrationBean.setEnabled(false);
return filterRegistrationBean;
}
}
@@ -1,62 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.fineract.infrastructure.core.boot.db;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import ch.vorburger.mariadb4j.springframework.MariaDB4jSpringService;
@Configuration
public class MariaDB4jDataSourceConfiguration extends DataSourceConfiguration {
@Bean
public MariaDB4jSetupService mariaDB4jSetUp() {
return new MariaDB4jSetupService(mariaDB4j().getDB());
}
@Bean
public MariaDB4jSpringService mariaDB4j() {
MariaDB4jSpringService mariaDB4jSpringService = new MariaDB4jSpringService();
mariaDB4jSpringService.setDefaultBaseDir("build/db/bin");
mariaDB4jSpringService.setDefaultDataDir("build/db/data");
return mariaDB4jSpringService;
}
@Override
// NOT @Bean @Override dataSourceProperties() - doesn't work :(
protected DataSourceProperties getProperties() {
DataSourceProperties p = super.getProperties();
String dbName = mariaDB4jSetUp().getTenantDBName();
// Do not use p.setUrl(mariaDB4j().getConfiguration().getURL(dbName));
// Because TenantDataSourcePortFixService needs separate
// host/port/db/uid/pwd:
// (DataSourceProperties getUrl() creates the correct JDBC URL from it)
// This intentionally overrides any fineract.datasource.* settings, because
// in this configuration, logically the mariaDB4j settings take
// precedence:
p.setHost("localhost");
p.setPort(mariaDB4j().getConfiguration().getPort());
p.setDBName(dbName);
// TODO p.setUsername(mariaDB4j().getConfiguration().getUsername());
// TODO p.setPassword(mariaDB4j().getConfiguration().getPassword());
return p;
}
}
@@ -1,54 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.fineract.infrastructure.core.boot.db;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.beans.factory.annotation.Autowired;
import ch.vorburger.exec.ManagedProcessException;
import ch.vorburger.mariadb4j.DB;
public class MariaDB4jSetupService {
private DB db;
@Autowired
public MariaDB4jSetupService(DB db) {
this.db = db;
}
@PostConstruct
protected void setUpDBs() throws ManagedProcessException {
db.createDB(getTenantDBName());
db.createDB("fineract_default");
// Note that we don't need to initialize the DBs, because
// the TenantDatabaseUpgradeService will do this in just a moment.
}
public String getTenantDBName() {
return "fineract_tenants";
}
@PreDestroy
protected void stop() throws ManagedProcessException {
db = null;
}
}
@@ -0,0 +1,73 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.fineract.infrastructure.core.config;
import java.time.Duration;
import javax.cache.CacheManager;
import javax.cache.Caching;
import javax.cache.spi.CachingProvider;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.ExpiryPolicyBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.jsr107.Eh107Configuration;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CacheConfig {
@Bean
public JCacheCacheManager ehCacheManager() {
JCacheCacheManager jCacheCacheManager = new JCacheCacheManager();
jCacheCacheManager.setCacheManager(getCustomCacheManager());
return jCacheCacheManager;
}
private CacheManager getCustomCacheManager() {
CachingProvider provider = Caching.getCachingProvider();
CacheManager cacheManager = provider.getCacheManager();
javax.cache.configuration.Configuration<Object, Object> defaultTemplate = Eh107Configuration.fromEhcacheCacheConfiguration(
CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder.heap(10000))
.withExpiry(ExpiryPolicyBuilder.noExpiration()).build());
cacheManager.createCache("users", defaultTemplate);
cacheManager.createCache("usersByUsername", defaultTemplate);
cacheManager.createCache("tenantsById", defaultTemplate);
cacheManager.createCache("offices", defaultTemplate);
cacheManager.createCache("officesForDropdown", defaultTemplate);
cacheManager.createCache("officesById", defaultTemplate);
cacheManager.createCache("charges", defaultTemplate);
cacheManager.createCache("funds", defaultTemplate);
cacheManager.createCache("code_values", defaultTemplate);
cacheManager.createCache("codes", defaultTemplate);
cacheManager.createCache("hooks", defaultTemplate);
cacheManager.createCache("tfConfig", defaultTemplate);
javax.cache.configuration.Configuration<Object, Object> accessTokenTemplate = Eh107Configuration.fromEhcacheCacheConfiguration(
CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder.heap(10000))
.withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofHours(2))).build());
cacheManager.createCache("userTFAccessToken", accessTokenTemplate);
return cacheManager;
}
}
@@ -0,0 +1,83 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.fineract.infrastructure.core.config;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.util.Properties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
@Configuration
public class HikariCpConfig {
@Autowired
ApplicationContext context;
@Bean(destroyMethod = "close")
public HikariDataSource hikariTenantDataSource(HikariConfig hc) {
return new HikariDataSource(hc);
}
@Bean
public HikariConfig hikariConfig() {
Environment environment = context.getEnvironment();
HikariConfig hc = new HikariConfig();
hc.setDriverClassName(environment.getProperty("fineract_tenants_driver"));
hc.setJdbcUrl(environment.getProperty("fineract_tenants_url"));
hc.setUsername(environment.getProperty("fineract_tenants_uid"));
hc.setPassword(environment.getProperty("fineract_tenants_pwd"));
hc.setMinimumIdle(3);
hc.setMaximumPoolSize(10);
hc.setIdleTimeout(60000);
hc.setConnectionTestQuery("SELECT 1");
hc.setDataSourceProperties(dataSourceProperties());
return hc;
}
// These are the properties for the all Tenants DB; the same configuration is also (hard-coded) in the
// TomcatJdbcDataSourcePerTenantService class -->
private Properties dataSourceProperties() {
Properties props = new Properties();
props.setProperty("cachePrepStmts", "true");
props.setProperty("prepStmtCacheSize", "250");
props.setProperty("prepStmtCacheSqlLimit", "2048");
props.setProperty("useServerPrepStmts", "true");
props.setProperty("useLocalSessionState", "true");
props.setProperty("rewriteBatchedStatements", "true");
props.setProperty("cacheResultSetMetadata", "true");
props.setProperty("cacheServerConfiguration", "true");
props.setProperty("elideSetAutoCommits", "true");
props.setProperty("maintainTimeStats", "false");
// https://github.com/brettwooldridge/HikariCP/wiki/JDBC-Logging#mysql-connectorj
// TODO FINERACT-890: <prop key="logger">com.mysql.cj.log.Slf4JLogger</prop>
props.setProperty("logSlowQueries", "true");
props.setProperty("dumpQueriesOnException", "true");
return props;
}
}
@@ -17,7 +17,7 @@
* under the License.
*/
package org.apache.fineract.infrastructure.core.boot;
package org.apache.fineract.infrastructure.core.config;
import javax.annotation.PostConstruct;
import javax.ws.rs.ApplicationPath;
@@ -0,0 +1,76 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.fineract.infrastructure.core.config;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.apache.fineract.infrastructure.core.domain.AuditorAwareImpl;
import org.apache.fineract.infrastructure.openjpa.OpenJpaVendorAdapter;
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.support.TransactionTemplate;
@Configuration
@EnableJpaAuditing
@EnableJpaRepositories(basePackages = "org.apache.fineract.**.domain")
public class PersistenceConfig {
@Autowired
DataSource routingDataSource;
@Bean
@DependsOn("tenantDatabaseUpgradeService")
public EntityManagerFactory entityManagerFactory() {
final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(routingDataSource);
em.setJpaVendorAdapter(new OpenJpaVendorAdapter());
em.setPersistenceUnitName("jpa-pu");
em.afterPropertiesSet();
return em.getObject();
}
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager jtm = new JpaTransactionManager();
jtm.setEntityManagerFactory(emf);
return jtm;
}
@Bean
public TransactionTemplate txTemplate(JpaTransactionManager jtm) {
TransactionTemplate tt = new TransactionTemplate();
tt.setTransactionManager(jtm);
return tt;
}
@Bean
public AuditorAware<AppUser> auditorAware() {
return new AuditorAwareImpl();
}
}
@@ -0,0 +1,140 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.fineract.infrastructure.core.config;
import org.apache.fineract.infrastructure.security.filter.TenantAwareBasicAuthenticationFilter;
import org.apache.fineract.infrastructure.security.filter.TwoFactorAuthenticationFilter;
import org.apache.fineract.infrastructure.security.service.TenantAwareJpaPlatformUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
@Configuration
@Profile("basicauth")
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private TenantAwareJpaPlatformUserDetailsService userDetailsService;
@Autowired
private TenantAwareBasicAuthenticationFilter tenantAwareBasicAuthenticationFilter;
@Autowired
private TwoFactorAuthenticationFilter twoFactorAuthenticationFilter;
/**
* The purpose of this method is to exclude the URL's specific to Login, Swagger UI and static files. Any URL that
* should be excluded from the Spring security chain should be added to the ignore list in this method only
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/swagger-ui/**", "/actuator/**", "/api-docs/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**").csrf().disable() //
.authorizeRequests() //
.antMatchers(HttpMethod.POST, "/api/*/echo").permitAll() //
.antMatchers(HttpMethod.POST, "/api/*/authentication").permitAll() //
.antMatchers(HttpMethod.POST, "/api/*/self/authentication").permitAll() //
.antMatchers(HttpMethod.POST, "/api/*/self/registration").permitAll() //
.antMatchers(HttpMethod.POST, "/api/*/self/registration/user").permitAll() //
.antMatchers(HttpMethod.POST, "/api/*/twofactor/validate").fullyAuthenticated() //
.antMatchers("/api/*/twofactor").fullyAuthenticated() //
.antMatchers("/api/**").access("isFullyAuthenticated() and hasAuthority('TWOFACTOR_AUTHENTICATED')").and() //
.httpBasic() //
.authenticationEntryPoint(basicAuthenticationEntryPoint()) //
.and() //
.sessionManagement() //
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) //
.and() //
.addFilterAfter(tenantAwareBasicAuthenticationFilter, SecurityContextPersistenceFilter.class) //
.addFilterAfter(twoFactorAuthenticationFilter, BasicAuthenticationFilter.class) //
.requiresChannel(channel -> channel.antMatchers("/api/**").requiresSecure());
}
@Bean
public BasicAuthenticationEntryPoint basicAuthenticationEntryPoint() {
BasicAuthenticationEntryPoint basicAuthenticationEntryPoint = new BasicAuthenticationEntryPoint();
basicAuthenticationEntryPoint.setRealmName("Fineract Platform API");
return basicAuthenticationEntryPoint;
}
@Bean(name = "customAuthenticationProvider")
public DaoAuthenticationProvider authProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider());
auth.eraseCredentials(false);
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public FilterRegistrationBean<TenantAwareBasicAuthenticationFilter> tenantAwareBasicAuthenticationFilterRegistration() {
FilterRegistrationBean<TenantAwareBasicAuthenticationFilter> registration = new FilterRegistrationBean<TenantAwareBasicAuthenticationFilter>(
tenantAwareBasicAuthenticationFilter);
registration.setEnabled(false);
return registration;
}
@Bean
public FilterRegistrationBean<TwoFactorAuthenticationFilter> twoFactorAuthenticationFilterRegistration() {
FilterRegistrationBean<TwoFactorAuthenticationFilter> registration = new FilterRegistrationBean<TwoFactorAuthenticationFilter>(
twoFactorAuthenticationFilter);
registration.setEnabled(false);
return registration;
}
}
@@ -0,0 +1,49 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.fineract.infrastructure.core.config;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.security.core.context.SecurityContextHolder;
@Configuration
public class SpringConfig {
@Bean
public SimpleApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster saem = new SimpleApplicationEventMulticaster();
saem.setTaskExecutor(new SimpleAsyncTaskExecutor());
return saem;
}
// The application events (for importing) rely on the inheritable thread local security context strategy
// This is NOT compatible with threadpools so if we use threadpools the below will need to be reworked
@Bean
public MethodInvokingFactoryBean methodInvokingFactoryBean() {
MethodInvokingFactoryBean mifb = new MethodInvokingFactoryBean();
mifb.setTargetClass(SecurityContextHolder.class);
mifb.setTargetMethod("setStrategyName");
mifb.setArguments("MODE_INHERITABLETHREADLOCAL");
return mifb;
}
}
@@ -25,7 +25,6 @@ import org.springframework.context.annotation.Scope;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@SuppressWarnings("deprecation")
@Service(value = "applicationPasswordEncoder")
@Scope("singleton")
public class DefaultPlatformPasswordEncoder implements PlatformPasswordEncoder {
@@ -40,7 +40,7 @@ import org.springframework.stereotype.Service;
*
* This filter adds 'TWOFACTOR_AUTHENTICATED' authority to every authenticated platform user.
*/
@Service(value = "twoFactorAuthFilter")
@Service
@Profile("!twofactor")
public class InsecureTwoFactorAuthenticationFilter extends TwoFactorAuthenticationFilter {
@@ -59,7 +59,7 @@ import org.springframework.stereotype.Service;
*
* If multi-tenant and basic auth credentials are invalid, a http error response is returned.
*/
@Service(value = "basicAuthenticationProcessingFilter")
@Service
@Profile("basicauth")
public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFilter {
@@ -54,7 +54,7 @@ import org.springframework.web.filter.GenericFilterBean;
* An authenticated platform user with permission 'BYPASS_TWOFACTOR' will always be granted 'TWOFACTOR_AUTHENTICATED'
* authority regardless of the value of the 'Fineract-Platform-TFA-Token' header.
*/
@Service(value = "twoFactorAuthFilter")
@Service
@Profile("twofactor")
public class TwoFactorAuthenticationFilter extends GenericFilterBean {
@@ -31,7 +31,7 @@ import org.springframework.stereotype.Service;
/**
* Used in securityContext.xml as implementation of spring security's {@link UserDetailsService}.
*/
@Service(value = "userDetailsService")
@Service("userDetailsService")
public class TenantAwareJpaPlatformUserDetailsService implements PlatformUserDetailsService {
@Autowired