diff --git a/core/src/test/java/feast/core/metrics/CoreMetricsIT.java b/core/src/test/java/feast/core/metrics/CoreMetricsIT.java new file mode 100644 index 0000000000..f195ff37a3 --- /dev/null +++ b/core/src/test/java/feast/core/metrics/CoreMetricsIT.java @@ -0,0 +1,59 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2018-2020 The Feast 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 + * + * https://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 feast.core.metrics; + +import static org.junit.Assert.assertTrue; + +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.Response; +import feast.core.it.BaseIT; +import java.io.IOException; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.web.server.LocalServerPort; + +@SpringBootTest( + webEnvironment = WebEnvironment.RANDOM_PORT, + properties = { + "feast.security.authentication.enabled=true", + }) +public class CoreMetricsIT extends BaseIT { + private static final String METRIC_ENDPOINT = "/metrics"; + private static OkHttpClient httpClient; + @LocalServerPort private int metricsPort; + + @BeforeAll + public static void globalSetUp() { + httpClient = new OkHttpClient(); + } + + /** Test that Feast Core metrics endpoint can be accessed with authentication enabled */ + @Test + public void shouldAllowUnauthenticatedAccessToMetricsEndpoint() throws IOException { + Request request = + new Request.Builder() + .url(String.format("http://localhost:%d%s", metricsPort, METRIC_ENDPOINT)) + .get() + .build(); + Response response = httpClient.newCall(request).execute(); + assertTrue(response.isSuccessful()); + assertTrue(!response.body().string().isEmpty()); + } +} diff --git a/serving/src/main/java/feast/serving/config/WebSecurityConfig.java b/serving/src/main/java/feast/serving/config/WebSecurityConfig.java new file mode 100644 index 0000000000..f7b24a7740 --- /dev/null +++ b/serving/src/main/java/feast/serving/config/WebSecurityConfig.java @@ -0,0 +1,51 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2018-2020 The Feast 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 + * + * https://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 feast.serving.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +/** + * WebSecurityConfig disables auto configuration of Spring HTTP Security and allows security methods + * to be overridden + */ +@Configuration +@EnableWebSecurity +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + + /** + * Allows for custom web security rules to be applied. + * + * @param http {@link HttpSecurity} for configuring web based security + * @throws Exception + */ + @Override + protected void configure(HttpSecurity http) throws Exception { + + // Bypasses security/authentication for the following paths + http.authorizeRequests() + .antMatchers("/actuator/**", "/metrics/**") + .permitAll() + .anyRequest() + .authenticated() + .and() + .csrf() + .disable(); + } +} diff --git a/serving/src/test/java/feast/serving/it/ServingServiceOauthAuthenticationIT.java b/serving/src/test/java/feast/serving/it/ServingServiceOauthAuthenticationIT.java index f1289adc73..e50693f8ac 100644 --- a/serving/src/test/java/feast/serving/it/ServingServiceOauthAuthenticationIT.java +++ b/serving/src/test/java/feast/serving/it/ServingServiceOauthAuthenticationIT.java @@ -20,6 +20,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.testcontainers.containers.wait.strategy.Wait.forHttp; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.Response; import feast.proto.serving.ServingAPIProto.GetOnlineFeaturesRequest; import feast.proto.serving.ServingAPIProto.GetOnlineFeaturesResponse; import feast.proto.serving.ServingServiceGrpc.ServingServiceBlockingStub; @@ -35,6 +38,8 @@ import org.junit.jupiter.api.Test; import org.junit.runners.model.InitializationError; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.web.server.LocalServerPort; import org.springframework.test.context.ActiveProfiles; import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.containers.wait.strategy.Wait; @@ -43,6 +48,7 @@ @ActiveProfiles("it") @SpringBootTest( + webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "feast.core-authentication.enabled=true", "feast.core-authentication.provider=oauth", @@ -55,6 +61,7 @@ public class ServingServiceOauthAuthenticationIT extends BaseAuthIT { static final Map options = new HashMap<>(); static final int FEAST_SERVING_PORT = 6566; + @LocalServerPort private int metricsPort; @ClassRule @Container public static DockerComposeContainer environment = @@ -84,6 +91,19 @@ static void globalSetup() throws IOException, InitializationError, InterruptedEx options.put("grant_type", GRANT_TYPE); } + /** Test that Feast Serving metrics endpoint can be accessed with authentication enabled */ + @Test + public void shouldAllowUnauthenticatedAccessToMetricsEndpoint() throws IOException { + Request request = + new Request.Builder() + .url(String.format("http://localhost:%d/metrics", metricsPort)) + .get() + .build(); + Response response = new OkHttpClient().newCall(request).execute(); + assertTrue(response.isSuccessful()); + assertTrue(!response.body().string().isEmpty()); + } + @Test public void shouldAllowUnauthenticatedGetOnlineFeatures() { // apply feature set