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 <[email protected]>
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 {

Reply via email to