You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ar...@apache.org on 2023/06/06 09:45:32 UTC
[fineract] 15/15: FINERACT-1724: Web security adjustments
This is an automated email from the ASF dual-hosted git repository.
arnold pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
commit d87d48366573acb24726d732aa26225d591567fd
Author: Arnold Galovics <ga...@gmail.com>
AuthorDate: Mon Jun 5 21:27:22 2023 +0200
FINERACT-1724: Web security adjustments
---
.../core/config/OAuth2SecurityConfig.java | 59 ++++++++++++----------
.../infrastructure/core/config/SecurityConfig.java | 53 +++++++++----------
.../TenantAwareBasicAuthenticationFilter.java | 1 +
.../filter/TenantAwareTenantIdentifierFilter.java | 4 --
4 files changed, 55 insertions(+), 62 deletions(-)
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/OAuth2SecurityConfig.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/OAuth2SecurityConfig.java
index 070f91968..7411069de 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/OAuth2SecurityConfig.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/OAuth2SecurityConfig.java
@@ -23,6 +23,7 @@ import static org.apache.fineract.infrastructure.security.vote.SelfServiceUserAu
import static org.springframework.security.authorization.AuthenticatedAuthorizationManager.fullyAuthenticated;
import static org.springframework.security.authorization.AuthorityAuthorizationManager.hasAuthority;
import static org.springframework.security.authorization.AuthorizationManagers.allOf;
+import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
import java.util.Collection;
import org.apache.fineract.infrastructure.businessdate.service.BusinessDateReadPlatformService;
@@ -32,6 +33,7 @@ import org.apache.fineract.infrastructure.core.exceptionmapper.OAuth2ExceptionEn
import org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer;
import org.apache.fineract.infrastructure.security.data.FineractJwtAuthenticationToken;
import org.apache.fineract.infrastructure.security.data.PlatformRequestLog;
+import org.apache.fineract.infrastructure.security.filter.InsecureTwoFactorAuthenticationFilter;
import org.apache.fineract.infrastructure.security.filter.TenantAwareTenantIdentifierFilter;
import org.apache.fineract.infrastructure.security.filter.TwoFactorAuthenticationFilter;
import org.apache.fineract.infrastructure.security.service.BasicAuthTenantDetailsService;
@@ -40,6 +42,7 @@ import org.apache.fineract.infrastructure.security.service.TwoFactorService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
@@ -73,7 +76,7 @@ public class OAuth2SecurityConfig {
private ServerProperties serverProperties;
@Autowired
- private TwoFactorService twoFactorService;
+ private FineractProperties fineractProperties;
@Autowired
private BasicAuthTenantDetailsService basicAuthTenantDetailsService;
@@ -89,45 +92,40 @@ public class OAuth2SecurityConfig {
@Autowired
private BusinessDateReadPlatformService businessDateReadPlatformService;
+ @Autowired
+ private ApplicationContext applicationContext;
private static final JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
- @Bean
- public SecurityFilterChain authorizationFilterChain(HttpSecurity http) throws Exception {
- http //
- .securityMatcher("/api/**").authorizeHttpRequests((auth) -> {
- auth.requestMatchers(HttpMethod.OPTIONS, "/api/**").permitAll() //
- .requestMatchers(HttpMethod.POST, "/api/*/echo").permitAll() //
- .requestMatchers(HttpMethod.POST, "/api/*/authentication").permitAll() //
- .requestMatchers(HttpMethod.POST, "/api/*/self/authentication").permitAll() //
- .requestMatchers(HttpMethod.POST, "/api/*/self/registration").permitAll() //
- .requestMatchers(HttpMethod.POST, "/api/*/self/registration/user").permitAll() //
- .requestMatchers(HttpMethod.POST, "/api/*/twofactor/validate").fullyAuthenticated() //
- .requestMatchers("/api/*/twofactor").fullyAuthenticated() //
- .requestMatchers("/api/**")
- .access(allOf(fullyAuthenticated(), hasAuthority("TWOFACTOR_AUTHENTICATED"), selfServiceUserAuthManager())); //
- });
-
- if (serverProperties.getSsl().isEnabled()) {
- http.requiresChannel(channel -> channel.requestMatchers("/api/**").requiresSecure());
- }
-
- return http.build();
- }
-
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http //
- .csrf((csrf) -> csrf.disable()) // NOSONAR only creating a service that is used by non-browser clients
+ .securityMatcher(antMatcher("/api/**")).authorizeHttpRequests((auth) -> {
+ auth.requestMatchers(antMatcher(HttpMethod.OPTIONS, "/api/**")).permitAll() //
+ .requestMatchers(antMatcher(HttpMethod.POST, "/api/*/echo")).permitAll() //
+ .requestMatchers(antMatcher(HttpMethod.POST, "/api/*/authentication")).permitAll() //
+ .requestMatchers(antMatcher(HttpMethod.POST, "/api/*/self/authentication")).permitAll() //
+ .requestMatchers(antMatcher(HttpMethod.POST, "/api/*/self/registration")).permitAll() //
+ .requestMatchers(antMatcher(HttpMethod.POST, "/api/*/self/registration/user")).permitAll() //
+ .requestMatchers(antMatcher(HttpMethod.POST, "/api/*/twofactor/validate")).fullyAuthenticated() //
+ .requestMatchers(antMatcher("/api/*/twofactor")).fullyAuthenticated() //
+ .requestMatchers(antMatcher("/api/**"))
+ .access(allOf(fullyAuthenticated(), hasAuthority("TWOFACTOR_AUTHENTICATED"), selfServiceUserAuthManager())); //
+ }).csrf((csrf) -> csrf.disable()) // NOSONAR only creating a service that is used by non-browser clients
.exceptionHandling((ehc) -> ehc.authenticationEntryPoint(new OAuth2ExceptionEntryPoint()))
.oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(authenticationConverter()))
.authenticationEntryPoint(new OAuth2ExceptionEntryPoint())) //
.sessionManagement((smc) -> smc.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) //
- .addFilterAfter(tenantAwareTenantIdentifierFilter(), SecurityContextHolderFilter.class) //
- .addFilterAfter(twoFactorAuthenticationFilter(), BasicAuthenticationFilter.class); //
+ .addFilterAfter(tenantAwareTenantIdentifierFilter(), SecurityContextHolderFilter.class);
+
+ if (fineractProperties.getSecurity().getTwoFactor().isEnabled()) {
+ http.addFilterAfter(twoFactorAuthenticationFilter(), BasicAuthenticationFilter.class);
+ } else {
+ http.addFilterAfter(insecureTwoFactorAuthenticationFilter(), BasicAuthenticationFilter.class);
+ }
if (serverProperties.getSsl().isEnabled()) {
- http.requiresChannel(channel -> channel.requestMatchers("/api/**").requiresSecure());
+ http.requiresChannel(channel -> channel.requestMatchers(antMatcher("/api/**")).requiresSecure());
}
return http.build();
@@ -139,9 +137,14 @@ public class OAuth2SecurityConfig {
}
public TwoFactorAuthenticationFilter twoFactorAuthenticationFilter() {
+ TwoFactorService twoFactorService = applicationContext.getBean(TwoFactorService.class);
return new TwoFactorAuthenticationFilter(twoFactorService);
}
+ public InsecureTwoFactorAuthenticationFilter insecureTwoFactorAuthenticationFilter() {
+ return new InsecureTwoFactorAuthenticationFilter();
+ }
+
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java
index 564b96307..4fc460085 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java
@@ -22,6 +22,7 @@ package org.apache.fineract.infrastructure.core.config;
import static org.springframework.security.authorization.AuthenticatedAuthorizationManager.fullyAuthenticated;
import static org.springframework.security.authorization.AuthorityAuthorizationManager.hasAuthority;
import static org.springframework.security.authorization.AuthorizationManagers.allOf;
+import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
import org.apache.fineract.commands.domain.CommandSourceRepository;
import org.apache.fineract.commands.service.CommandSourceService;
@@ -67,7 +68,6 @@ import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.security.web.context.SecurityContextHolderFilter;
-import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@Configuration
@ConditionalOnProperty("fineract.security.basicauth.enabled")
@@ -114,48 +114,41 @@ public class SecurityConfig {
@Autowired
private IdempotencyStoreHelper idempotencyStoreHelper;
- @Bean
- public SecurityFilterChain authorizationFilterChain(HttpSecurity http) throws Exception {
- http //
- .securityMatcher("/api/**").authorizeHttpRequests((auth) -> {
- auth.requestMatchers(HttpMethod.OPTIONS, "/api/**").permitAll() //
- .requestMatchers(HttpMethod.POST, "/api/*/echo").permitAll() //
- .requestMatchers(HttpMethod.POST, "/api/*/authentication").permitAll() //
- .requestMatchers(HttpMethod.POST, "/api/*/self/authentication").permitAll() //
- .requestMatchers(HttpMethod.POST, "/api/*/self/registration").permitAll() //
- .requestMatchers(HttpMethod.POST, "/api/*/self/registration/user").permitAll() //
- .requestMatchers(HttpMethod.PUT, "/api/*/instance-mode").permitAll() //
- .requestMatchers(HttpMethod.POST, "/api/*/twofactor/validate").fullyAuthenticated() //
- .requestMatchers("/api/*/twofactor").fullyAuthenticated() //
- .requestMatchers("/api/**").access(allOf(fullyAuthenticated(), hasAuthority("TWOFACTOR_AUTHENTICATED"))); //
- }) //
- .httpBasic((httpBasic) -> httpBasic.authenticationEntryPoint(basicAuthenticationEntryPoint()));
-
- if (serverProperties.getSsl().isEnabled()) {
- http.requiresChannel(channel -> channel.requestMatchers("/api/**").requiresSecure());
- }
- return http.build();
- }
-
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http //
+ .securityMatcher(antMatcher("/api/**")).authorizeHttpRequests((auth) -> {
+ auth.requestMatchers(antMatcher(HttpMethod.OPTIONS, "/api/**")).permitAll() //
+ .requestMatchers(antMatcher(HttpMethod.POST, "/api/*/echo")).permitAll() //
+ .requestMatchers(antMatcher(HttpMethod.POST, "/api/*/authentication")).permitAll() //
+ .requestMatchers(antMatcher(HttpMethod.POST, "/api/*/self/authentication")).permitAll() //
+ .requestMatchers(antMatcher(HttpMethod.POST, "/api/*/self/registration")).permitAll() //
+ .requestMatchers(antMatcher(HttpMethod.POST, "/api/*/self/registration/user")).permitAll() //
+ .requestMatchers(antMatcher(HttpMethod.PUT, "/api/*/instance-mode")).permitAll() //
+ .requestMatchers(antMatcher(HttpMethod.POST, "/api/*/twofactor/validate")).fullyAuthenticated() //
+ .requestMatchers(antMatcher("/api/*/twofactor")).fullyAuthenticated() //
+ .requestMatchers(antMatcher("/api/**"))
+ .access(allOf(fullyAuthenticated(), hasAuthority("TWOFACTOR_AUTHENTICATED"))); //
+ }).httpBasic((httpBasic) -> httpBasic.authenticationEntryPoint(basicAuthenticationEntryPoint())) //
.csrf((csrf) -> csrf.disable()) // NOSONAR only creating a service that is used by non-browser clients
.sessionManagement((smc) -> smc.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) //
- .addFilterBefore(tenantAwareBasicAuthenticationFilter(), SecurityContextHolderFilter.class)
- .addFilterAfter(requestResponseFilter(), ExceptionTranslationFilter.class)
- .addFilterAfter(correlationHeaderFilter(), RequestResponseFilter.class)
+ .addFilterBefore(tenantAwareBasicAuthenticationFilter(), SecurityContextHolderFilter.class) //
+ .addFilterAfter(requestResponseFilter(), ExceptionTranslationFilter.class) //
+ .addFilterAfter(correlationHeaderFilter(), RequestResponseFilter.class) //
.addFilterAfter(responseCorsFilter(), CorrelationHeaderFilter.class) //
.addFilterAfter(fineractInstanceModeApiFilter(), ResponseCorsFilter.class) //
.addFilterAfter(loanCOBApiFilter(), FineractInstanceModeApiFilter.class) //
.addFilterAfter(idempotencyStoreFilter(), LoanCOBApiFilter.class); //
if (fineractProperties.getSecurity().getTwoFactor().isEnabled()) {
- http.addFilterAfter(twoFactorAuthenticationFilter(), FineractInstanceModeApiFilter.class);
+ http.addFilterAfter(twoFactorAuthenticationFilter(), ResponseCorsFilter.class);
} else {
- http.addFilterAfter(insecureTwoFactorAuthenticationFilter(), FineractInstanceModeApiFilter.class);
+ http.addFilterAfter(insecureTwoFactorAuthenticationFilter(), ResponseCorsFilter.class);
}
+ if (serverProperties.getSsl().isEnabled()) {
+ http.requiresChannel(channel -> channel.requestMatchers(antMatcher("/api/**")).requiresSecure());
+ }
return http.build();
}
@@ -196,7 +189,7 @@ public class SecurityConfig {
TenantAwareBasicAuthenticationFilter filter = new TenantAwareBasicAuthenticationFilter(authenticationManagerBean(),
basicAuthenticationEntryPoint(), toApiJsonSerializer, configurationDomainService, cacheWritePlatformService,
userNotificationService, basicAuthTenantDetailsService, businessDateReadPlatformService);
- filter.setRequestMatcher(AntPathRequestMatcher.antMatcher("/api/**"));
+ filter.setRequestMatcher(antMatcher("/api/**"));
return filter;
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java
index 527715343..99059c16a 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java
@@ -106,6 +106,7 @@ public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFil
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
// ignore to allow 'preflight' requests from AJAX applications
// in different origin (domain name)
+ filterChain.doFilter(request, response);
} else {
if (requestMatcher.matches(request)) {
String tenantIdentifier = request.getHeader(this.tenantRequestHeader);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareTenantIdentifierFilter.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareTenantIdentifierFilter.java
index aa09aa266..4ea5301ec 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareTenantIdentifierFilter.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareTenantIdentifierFilter.java
@@ -42,9 +42,7 @@ import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.security.data.PlatformRequestLog;
import org.apache.fineract.infrastructure.security.exception.InvalidTenantIdentifierException;
import org.apache.fineract.infrastructure.security.service.BasicAuthTenantDetailsService;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.stereotype.Service;
import org.springframework.web.filter.GenericFilterBean;
/**
@@ -58,8 +56,6 @@ import org.springframework.web.filter.GenericFilterBean;
*
* Used to support Oauth2 authentication and the service is loaded only when "oauth" profile is active.
*/
-@Service
-@ConditionalOnProperty("fineract.security.oauth.enabled")
@RequiredArgsConstructor
@Slf4j
public class TenantAwareTenantIdentifierFilter extends GenericFilterBean {