FINERACT-2421: fix(security): migrate request matchers to PathPatternRequestMatcher (no behavior change)
This commit is contained in:
+68
-64
@@ -23,11 +23,9 @@ import static org.apache.fineract.infrastructure.security.vote.SelfServiceUserAu
|
|||||||
import static org.springframework.security.authorization.AuthenticatedAuthorizationManager.fullyAuthenticated;
|
import static org.springframework.security.authorization.AuthenticatedAuthorizationManager.fullyAuthenticated;
|
||||||
import static org.springframework.security.authorization.AuthorityAuthorizationManager.hasAuthority;
|
import static org.springframework.security.authorization.AuthorityAuthorizationManager.hasAuthority;
|
||||||
import static org.springframework.security.authorization.AuthorizationManagers.allOf;
|
import static org.springframework.security.authorization.AuthorizationManagers.allOf;
|
||||||
import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import org.apache.fineract.infrastructure.businessdate.service.BusinessDateReadPlatformService;
|
import org.apache.fineract.infrastructure.businessdate.service.BusinessDateReadPlatformService;
|
||||||
import org.apache.fineract.infrastructure.cache.service.CacheWritePlatformService;
|
import org.apache.fineract.infrastructure.cache.service.CacheWritePlatformService;
|
||||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||||
@@ -72,6 +70,7 @@ import org.springframework.security.web.access.ExceptionTranslationFilter;
|
|||||||
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
||||||
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
|
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
|
||||||
import org.springframework.security.web.context.SecurityContextHolderFilter;
|
import org.springframework.security.web.context.SecurityContextHolderFilter;
|
||||||
|
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
import org.springframework.web.cors.CorsConfigurationSource;
|
import org.springframework.web.cors.CorsConfigurationSource;
|
||||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
@@ -81,18 +80,19 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
|||||||
@EnableMethodSecurity
|
@EnableMethodSecurity
|
||||||
public class SecurityConfig {
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
private static final PathPatternRequestMatcher.Builder API_MATCHER = PathPatternRequestMatcher.withDefaults();
|
||||||
|
private static final String ALL_FUNCTIONS = "ALL_FUNCTIONS";
|
||||||
|
private static final String ALL_FUNCTIONS_READ = "ALL_FUNCTIONS_READ";
|
||||||
|
private static final String ALL_FUNCTIONS_WRITE = "ALL_FUNCTIONS_WRITE";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ApplicationContext applicationContext;
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private TenantAwareJpaPlatformUserDetailsService userDetailsService;
|
private TenantAwareJpaPlatformUserDetailsService userDetailsService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private FineractProperties fineractProperties;
|
private FineractProperties fineractProperties;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ServerProperties serverProperties;
|
private ServerProperties serverProperties;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ToApiJsonSerializer<PlatformRequestLog> toApiJsonSerializer;
|
private ToApiJsonSerializer<PlatformRequestLog> toApiJsonSerializer;
|
||||||
@Autowired
|
@Autowired
|
||||||
@@ -109,7 +109,6 @@ public class SecurityConfig {
|
|||||||
private MDCWrapper mdcWrapper;
|
private MDCWrapper mdcWrapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
private FineractRequestContextHolder fineractRequestContextHolder;
|
private FineractRequestContextHolder fineractRequestContextHolder;
|
||||||
|
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
private LoanCOBFilterHelper loanCOBFilterHelper;
|
private LoanCOBFilterHelper loanCOBFilterHelper;
|
||||||
@Autowired
|
@Autowired
|
||||||
@@ -117,62 +116,64 @@ public class SecurityConfig {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
http //
|
|
||||||
.securityMatcher(antMatcher("/api/**")).authorizeHttpRequests((auth) -> {
|
http.securityMatcher(API_MATCHER.matcher("/api/**")).authorizeHttpRequests(auth -> {
|
||||||
List<AuthorizationManager<RequestAuthorizationContext>> authorizationManagers = new ArrayList<>();
|
|
||||||
authorizationManagers.add(fullyAuthenticated());
|
List<AuthorizationManager<RequestAuthorizationContext>> authorizationManagers = new ArrayList<>();
|
||||||
if (fineractProperties.getSecurity().getTwoFactor().isEnabled()) {
|
authorizationManagers.add(fullyAuthenticated());
|
||||||
authorizationManagers.add(hasAuthority("TWOFACTOR_AUTHENTICATED"));
|
|
||||||
}
|
if (fineractProperties.getSecurity().getTwoFactor().isEnabled()) {
|
||||||
if (fineractProperties.getModule().getSelfService().isEnabled()) {
|
authorizationManagers.add(hasAuthority("TWOFACTOR_AUTHENTICATED"));
|
||||||
auth.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(); //
|
if (fineractProperties.getModule().getSelfService().isEnabled()) {
|
||||||
authorizationManagers.add(selfServiceUserAuthManager());
|
auth.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/self/authentication")).permitAll()
|
||||||
}
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/self/registration")).permitAll()
|
||||||
auth.requestMatchers(antMatcher(HttpMethod.OPTIONS, "/api/**")).permitAll() //
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/self/registration/user")).permitAll();
|
||||||
.requestMatchers(antMatcher(HttpMethod.POST, "/api/*/echo")).permitAll() //
|
authorizationManagers.add(selfServiceUserAuthManager());
|
||||||
.requestMatchers(antMatcher(HttpMethod.POST, "/api/*/authentication")).permitAll() //
|
}
|
||||||
.requestMatchers(antMatcher(HttpMethod.PUT, "/api/*/instance-mode")).permitAll() //
|
|
||||||
// businessdate
|
auth.requestMatchers(API_MATCHER.matcher(HttpMethod.OPTIONS, "/api/**")).permitAll()
|
||||||
.requestMatchers(antMatcher(HttpMethod.GET, "/api/*/businessdate/*"))
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/echo")).permitAll()
|
||||||
.hasAnyAuthority("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", "READ_BUSINESS_DATE")
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/authentication")).permitAll()
|
||||||
.requestMatchers(antMatcher(HttpMethod.POST, "/api/*/businessdate"))
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.PUT, "/api/*/instance-mode")).permitAll()
|
||||||
.hasAnyAuthority("ALL_FUNCTIONS", "ALL_FUNCTIONS_WRITE", "UPDATE_BUSINESS_DATE")
|
// businessdate
|
||||||
// external
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.GET, "/api/*/businessdate/*"))
|
||||||
.requestMatchers(antMatcher(HttpMethod.GET, "/api/*/externalevents/configuration"))
|
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_READ, "READ_BUSINESS_DATE")
|
||||||
.hasAnyAuthority("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", "READ_EXTERNAL_EVENT_CONFIGURATION")
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/businessdate"))
|
||||||
.requestMatchers(antMatcher(HttpMethod.PUT, "/api/*/externalevents/configuration"))
|
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "UPDATE_BUSINESS_DATE")
|
||||||
.hasAnyAuthority("ALL_FUNCTIONS", "ALL_FUNCTIONS_WRITE", "UPDATE_EXTERNAL_EVENT_CONFIGURATION")
|
// external
|
||||||
// cache
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.GET, "/api/*/externalevents/configuration"))
|
||||||
.requestMatchers(antMatcher(HttpMethod.GET, "/api/*/caches"))
|
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_READ, "READ_EXTERNAL_EVENT_CONFIGURATION")
|
||||||
.hasAnyAuthority("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", "READ_CACHE")
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.PUT, "/api/*/externalevents/configuration"))
|
||||||
.requestMatchers(antMatcher(HttpMethod.PUT, "/api/*/caches"))
|
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "UPDATE_EXTERNAL_EVENT_CONFIGURATION")
|
||||||
.hasAnyAuthority("ALL_FUNCTIONS", "ALL_FUNCTIONS_WRITE", "UPDATE_CACHE")
|
// cache
|
||||||
// currency
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.GET, "/api/*/caches"))
|
||||||
.requestMatchers(antMatcher(HttpMethod.GET, "/api/*/currencies"))
|
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_READ, "READ_CACHE")
|
||||||
.hasAnyAuthority("ALL_FUNCTIONS", "ALL_FUNCTIONS_READ", "READ_CURRENCY")
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.PUT, "/api/*/caches"))
|
||||||
.requestMatchers(antMatcher(HttpMethod.POST, "/api/*/currencies"))
|
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "UPDATE_CACHE")
|
||||||
.hasAnyAuthority("ALL_FUNCTIONS", "ALL_FUNCTIONS_WRITE", "UPDATE_CURRENCY")
|
// currency
|
||||||
// ...
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.GET, "/api/*/currencies"))
|
||||||
.requestMatchers(antMatcher(HttpMethod.POST, "/api/*/twofactor/validate")).fullyAuthenticated() //
|
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_READ, "READ_CURRENCY")
|
||||||
.requestMatchers(antMatcher("/api/*/twofactor")).fullyAuthenticated() //
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/currencies"))
|
||||||
.requestMatchers(antMatcher("/api/**"))
|
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "UPDATE_CURRENCY")
|
||||||
.access(allOf(authorizationManagers.toArray(new AuthorizationManager[0]))); //
|
|
||||||
}).httpBasic((httpBasic) -> httpBasic.authenticationEntryPoint(basicAuthenticationEntryPoint())) //
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/twofactor/validate")).fullyAuthenticated()
|
||||||
.csrf(AbstractHttpConfigurer::disable) // NOSONAR only creating a
|
.requestMatchers(API_MATCHER.matcher("/api/*/twofactor")).fullyAuthenticated()
|
||||||
// service that
|
.requestMatchers(API_MATCHER.matcher("/api/**"))
|
||||||
// is used by non-browser clients
|
.access(allOf(authorizationManagers.toArray(new AuthorizationManager[0])));
|
||||||
.sessionManagement((smc) -> smc.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) //
|
}).httpBasic(hb -> hb.authenticationEntryPoint(basicAuthenticationEntryPoint())).csrf(AbstractHttpConfigurer::disable)
|
||||||
.addFilterBefore(tenantAwareBasicAuthenticationFilter(), SecurityContextHolderFilter.class) //
|
.sessionManagement(smc -> smc.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||||
.addFilterAfter(requestResponseFilter(), ExceptionTranslationFilter.class) //
|
.addFilterBefore(tenantAwareBasicAuthenticationFilter(), SecurityContextHolderFilter.class)
|
||||||
.addFilterAfter(correlationHeaderFilter(), RequestResponseFilter.class) //
|
.addFilterAfter(requestResponseFilter(), ExceptionTranslationFilter.class)
|
||||||
.addFilterAfter(fineractInstanceModeApiFilter(), CorrelationHeaderFilter.class); //
|
.addFilterAfter(correlationHeaderFilter(), RequestResponseFilter.class)
|
||||||
if (!Objects.isNull(loanCOBFilterHelper)) {
|
.addFilterAfter(fineractInstanceModeApiFilter(), CorrelationHeaderFilter.class);
|
||||||
http.addFilterAfter(loanCOBApiFilter(), FineractInstanceModeApiFilter.class) //
|
|
||||||
.addFilterAfter(idempotencyStoreFilter(), LoanCOBApiFilter.class); //
|
if (loanCOBFilterHelper != null) {
|
||||||
|
http.addFilterAfter(loanCOBApiFilter(), FineractInstanceModeApiFilter.class).addFilterAfter(idempotencyStoreFilter(),
|
||||||
|
LoanCOBApiFilter.class);
|
||||||
} else {
|
} else {
|
||||||
http.addFilterAfter(idempotencyStoreFilter(), FineractInstanceModeApiFilter.class); //
|
http.addFilterAfter(idempotencyStoreFilter(), FineractInstanceModeApiFilter.class);
|
||||||
}
|
}
|
||||||
if (fineractProperties.getIpTracking().isEnabled()) {
|
if (fineractProperties.getIpTracking().isEnabled()) {
|
||||||
http.addFilterAfter(callerIpTrackingFilter(), RequestResponseFilter.class);
|
http.addFilterAfter(callerIpTrackingFilter(), RequestResponseFilter.class);
|
||||||
@@ -182,16 +183,18 @@ public class SecurityConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (serverProperties.getSsl().isEnabled()) {
|
if (serverProperties.getSsl().isEnabled()) {
|
||||||
http.requiresChannel(channel -> channel.requestMatchers(antMatcher("/api/**")).requiresSecure());
|
http.requiresChannel(channel -> channel.requestMatchers(API_MATCHER.matcher("/api/**")).requiresSecure());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fineractProperties.getSecurity().getHsts().isEnabled()) {
|
if (fineractProperties.getSecurity().getHsts().isEnabled()) {
|
||||||
http.requiresChannel(channel -> channel.anyRequest().requiresSecure()).headers(
|
http.requiresChannel(channel -> channel.anyRequest().requiresSecure()).headers(
|
||||||
headers -> headers.httpStrictTransportSecurity(hsts -> hsts.includeSubDomains(true).maxAgeInSeconds(31536000)));
|
headers -> headers.httpStrictTransportSecurity(hsts -> hsts.includeSubDomains(true).maxAgeInSeconds(31536000)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fineractProperties.getSecurity().getCors().isEnabled()) {
|
if (fineractProperties.getSecurity().getCors().isEnabled()) {
|
||||||
http.cors(Customizer.withDefaults());
|
http.cors(Customizer.withDefaults());
|
||||||
}
|
}
|
||||||
|
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +231,8 @@ public class SecurityConfig {
|
|||||||
TenantAwareBasicAuthenticationFilter filter = new TenantAwareBasicAuthenticationFilter(authenticationManagerBean(),
|
TenantAwareBasicAuthenticationFilter filter = new TenantAwareBasicAuthenticationFilter(authenticationManagerBean(),
|
||||||
basicAuthenticationEntryPoint(), toApiJsonSerializer, configurationDomainService, cacheWritePlatformService,
|
basicAuthenticationEntryPoint(), toApiJsonSerializer, configurationDomainService, cacheWritePlatformService,
|
||||||
userNotificationService, basicAuthTenantDetailsService, businessDateReadPlatformService);
|
userNotificationService, basicAuthTenantDetailsService, businessDateReadPlatformService);
|
||||||
filter.setRequestMatcher(antMatcher("/api/**"));
|
|
||||||
|
filter.setRequestMatcher(API_MATCHER.matcher("/api/**"));
|
||||||
return filter;
|
return filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user