/* * Copyright 2012-2017 the original author or authors. * * 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.springframework.boot.actuate.cloudfoundry; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.Set; import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints; import org.springframework.boot.actuate.endpoint.mvc.NamedMvcEndpoint; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.security.IgnoredRequestCustomizer; import org.springframework.boot.cloud.CloudPlatform; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.servlet.HandlerInterceptor; /** * {@link EnableAutoConfiguration Auto-configuration} to expose actuator endpoints for * cloud foundry to use. * * @author Madhura Bhave * @since 1.5.0 */ @Configuration @ConditionalOnProperty(prefix = "management.cloudfoundry", name = "enabled", matchIfMissing = true) @ConditionalOnBean(MvcEndpoints.class) @AutoConfigureAfter(EndpointWebMvcAutoConfiguration.class) @ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY) public class CloudFoundryActuatorAutoConfiguration { @Bean public CloudFoundryEndpointHandlerMapping cloudFoundryEndpointHandlerMapping( MvcEndpoints mvcEndpoints, RestTemplateBuilder restTemplateBuilder, Environment environment) { Set<NamedMvcEndpoint> endpoints = new LinkedHashSet<>( mvcEndpoints.getEndpoints(NamedMvcEndpoint.class)); HandlerInterceptor securityInterceptor = getSecurityInterceptor( restTemplateBuilder, environment); CorsConfiguration corsConfiguration = getCorsConfiguration(); CloudFoundryEndpointHandlerMapping mapping = new CloudFoundryEndpointHandlerMapping( endpoints, corsConfiguration, securityInterceptor); mapping.setPrefix("/cloudfoundryapplication"); return mapping; } private HandlerInterceptor getSecurityInterceptor( RestTemplateBuilder restTemplateBuilder, Environment environment) { CloudFoundrySecurityService cloudfoundrySecurityService = getCloudFoundrySecurityService( restTemplateBuilder, environment); TokenValidator tokenValidator = new TokenValidator(cloudfoundrySecurityService); HandlerInterceptor securityInterceptor = new CloudFoundrySecurityInterceptor( tokenValidator, cloudfoundrySecurityService, environment.getProperty("vcap.application.application_id")); return securityInterceptor; } private CloudFoundrySecurityService getCloudFoundrySecurityService( RestTemplateBuilder restTemplateBuilder, Environment environment) { String cloudControllerUrl = environment.getProperty("vcap.application.cf_api"); boolean skipSslValidation = environment.getProperty( "management.cloudfoundry.skip-ssl-validation", Boolean.class, false); return cloudControllerUrl == null ? null : new CloudFoundrySecurityService(restTemplateBuilder, cloudControllerUrl, skipSslValidation); } private CorsConfiguration getCorsConfiguration() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.addAllowedOrigin(CorsConfiguration.ALL); corsConfiguration.setAllowedMethods( Arrays.asList(HttpMethod.GET.name(), HttpMethod.POST.name())); corsConfiguration.setAllowedHeaders( Arrays.asList("Authorization", "X-Cf-App-Instance", "Content-Type")); return corsConfiguration; } /** * Nested configuration for ignored requests if Spring Security is present. */ @ConditionalOnClass(WebSecurity.class) static class CloudFoundryIgnoredRequestConfiguration { @Bean public IgnoredRequestCustomizer cloudFoundryIgnoredRequestCustomizer() { return new CloudFoundryIgnoredRequestCustomizer(); } private static class CloudFoundryIgnoredRequestCustomizer implements IgnoredRequestCustomizer { @Override public void customize(WebSecurity.IgnoredRequestConfigurer configurer) { configurer.requestMatchers( new AntPathRequestMatcher("/cloudfoundryapplication/**")); } } } }