FINERACT-1433: Switch to Java based config for Spring
This commit is contained in:
committed by
Aleksandar Vidakovic
parent
2513ee4837
commit
2b57bf379e
@@ -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
|
||||
|
||||
-105
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+2
-23
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-5
@@ -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();
|
||||
|
||||
+5
-15
@@ -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 & 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 {
|
||||
|
||||
}
|
||||
|
||||
-36
@@ -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;
|
||||
}
|
||||
}
|
||||
-49
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-62
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-54
@@ -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;
|
||||
}
|
||||
}
|
||||
+73
@@ -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;
|
||||
}
|
||||
}
|
||||
+83
@@ -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;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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;
|
||||
+76
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
+140
@@ -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;
|
||||
}
|
||||
}
|
||||
+49
@@ -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;
|
||||
}
|
||||
}
|
||||
-1
@@ -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 {
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user