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 {
