/* * Copyright 2015 herd contributors * * Licensed 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.finra.herd.app.config; import java.util.ArrayList; import java.util.List; import javax.servlet.Filter; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.core.env.Environment; import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.AccessDecisionVoter; import org.springframework.security.access.vote.AffirmativeBased; import org.springframework.security.access.vote.RoleVoter; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.ProviderManager; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; import org.springframework.security.web.FilterChainProxy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider; import org.springframework.security.web.context.SecurityContextPersistenceFilter; import org.finra.herd.app.security.HerdUserDetailsService; import org.finra.herd.app.security.HttpHeaderApplicationUserBuilder; import org.finra.herd.app.security.HttpHeaderAuthenticationFilter; import org.finra.herd.app.security.TrustedApplicationUserBuilder; import org.finra.herd.app.security.TrustedUserAuthenticationFilter; import org.finra.herd.core.ApplicationContextHolder; import org.finra.herd.core.helper.ConfigurationHelper; import org.finra.herd.dao.Log4jOverridableConfigurer; import org.finra.herd.dao.config.DaoSpringModuleConfig; import org.finra.herd.model.dto.ConfigurationValue; import org.finra.herd.model.jpa.ConfigurationEntity; /** * This is the Spring root configuration for the web application. */ @Configuration // Component scan all packages, but exclude the configuration ones since they are explicitly specified. @ComponentScan(value = "org.finra.herd.app", excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = "org\\.finra\\.herd\\.app\\.config\\..*")) @EnableGlobalMethodSecurity(securedEnabled = true) public class AppSpringModuleConfig extends GlobalMethodSecurityConfiguration { @Autowired private ConfigurationHelper configurationHelper; @Autowired private HerdUserDetailsService herdUserDetailsService; /** * The Log4J overridable configurer that will handle our Log4J initialization for the herd application. * <p/> * IMPORTANT: Ensure this method is static since the returned Log4jOverridableConfigurer is a bean factory post processor (BFPP). If it weren't static, * autowiring and injection on this @Configuration class won't work due to lifecycle issues. See "Bootstrapping" comment in @Bean annotation for more * details. * * @return the Log4J overridable configurer. */ @Bean public static Log4jOverridableConfigurer log4jConfigurer() { // Access the environment using the application context holder since we're in a static method that doesn't have access to the environment in any // other way. Environment environment = ApplicationContextHolder.getApplicationContext().getEnvironment(); // Create the Log4J configurer. Log4jOverridableConfigurer log4jConfigurer = new Log4jOverridableConfigurer(); // This is the primary database override location (if present). An entry needs to be present in the database at application startup for this // configuration to be used. log4jConfigurer.setDataSource(DaoSpringModuleConfig.getHerdDataSource()); log4jConfigurer.setTableName(ConfigurationEntity.TABLE_NAME); log4jConfigurer.setSelectColumn(ConfigurationEntity.COLUMN_VALUE_CLOB); log4jConfigurer.setWhereColumn(ConfigurationEntity.COLUMN_KEY); log4jConfigurer.setWhereValue(ConfigurationValue.LOG4J_OVERRIDE_CONFIGURATION.getKey()); // This is the secondary override location (if present and if the primary location isn't present). This resource needs to be present at application // startup for this configuration to be used. log4jConfigurer.setOverrideResourceLocation(ConfigurationHelper.getProperty(ConfigurationValue.LOG4J_OVERRIDE_RESOURCE_LOCATION, environment)); // This is the default fallback location which is bundled in the WAR and should always be present in case the override locations aren't present. // Note that the herd-log4j.xml file must be in the herd-war project so it is accessible on the classpath as a file. Placing it within another // project's JAR will result in the resource not being found as a "file". log4jConfigurer.setDefaultResourceLocation("classpath:herd-log4j.xml"); // Return the Log4J configurer. return log4jConfigurer; } /** * Gets a filter chain proxy. * * @param trustedUserAuthenticationFilter the trusted user authentication filter. * @param httpHeaderAuthenticationFilter the HTTP header authentication filter. * * @return the filter chain proxy. */ @Bean public FilterChainProxy filterChainProxy(final TrustedUserAuthenticationFilter trustedUserAuthenticationFilter, final HttpHeaderAuthenticationFilter httpHeaderAuthenticationFilter) { return new FilterChainProxy(new SecurityFilterChain() { @Override public boolean matches(HttpServletRequest request) { // Match all URLs. return true; } @Override public List<Filter> getFilters() { List<Filter> filters = new ArrayList<>(); // Required filter to store session information between HTTP requests. filters.add(new SecurityContextPersistenceFilter()); // Trusted user filter to bypass security based on SpEL expression environment property. filters.add(trustedUserAuthenticationFilter); // Filter that authenticates based on http headers. if (Boolean.valueOf(configurationHelper.getProperty(ConfigurationValue.SECURITY_HTTP_HEADER_ENABLED))) { filters.add(httpHeaderAuthenticationFilter); } // Anonymous user filter. filters.add(new AnonymousAuthenticationFilter("AnonymousFilterKey")); return filters; } }); } @Bean public TrustedUserAuthenticationFilter trustedUserAuthenticationFilter(AuthenticationManager authenticationManager, TrustedApplicationUserBuilder trustedApplicationUserBuilder) { return new TrustedUserAuthenticationFilter(authenticationManager, trustedApplicationUserBuilder); } @Bean public TrustedApplicationUserBuilder trustedApplicationUserBuilder() { return new TrustedApplicationUserBuilder(); } @Bean public HttpHeaderAuthenticationFilter httpHeaderAuthenticationFilter(AuthenticationManager authenticationManager, HttpHeaderApplicationUserBuilder httpHeaderApplicationUserBuilder) { return new HttpHeaderAuthenticationFilter(authenticationManager, httpHeaderApplicationUserBuilder); } @Bean public HttpHeaderApplicationUserBuilder httpHeaderApplicationUserBuilder() { return new HttpHeaderApplicationUserBuilder(); } @Bean @Override public AuthenticationManager authenticationManager() { PreAuthenticatedAuthenticationProvider authenticationProvider = new PreAuthenticatedAuthenticationProvider(); authenticationProvider.setPreAuthenticatedUserDetailsService(herdUserDetailsService); List<AuthenticationProvider> providers = new ArrayList<>(); providers.add(authenticationProvider); return new ProviderManager(providers); } /** * Overridden to remove role prefix for the role voter. The application does not require any other access decision voters in the default configuration. */ /* * rawtypes must be suppressed because AffirmativeBased constructor takes in a raw typed list of AccessDecisionVoters */ @SuppressWarnings("rawtypes") @Override protected AccessDecisionManager accessDecisionManager() { List<AccessDecisionVoter<?>> decisionVoters = new ArrayList<>(); RoleVoter decisionVoter = new RoleVoter(); decisionVoter.setRolePrefix(""); decisionVoters.add(decisionVoter); return new AffirmativeBased(decisionVoters); } }