From 6648478bd91b2d8e0cb3b8400a8f0f85b830f728 Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish <62088117+mdanish98@users.noreply.github.com> Date: Fri, 23 Feb 2024 07:58:59 +0100 Subject: [PATCH] Fixes health check failure after enabling authorization (#229) * Fixes health check failure after enabling authorization Signed-off-by: Mohammad Ghazanfar Ali Danish * Minor refactoring Signed-off-by: Mohammad Ghazanfar Ali Danish --------- Signed-off-by: Mohammad Ghazanfar Ali Danish --- .../TestAuthorizedAasEnvironment.java | 12 ++++ .../resources/authorization/HealthOutput.json | 3 + .../AuthorizedAasRepositoryTestSuite.java | 12 ++++ .../resources/authorization/HealthOutput.json | 3 + .../CommonSecurityConfiguration.java | 60 +++++++++++++++++++ ...ConceptDescriptionRepositoryTestSuite.java | 12 ++++ .../resources/authorization/HealthOutput.json | 3 + ...AuthorizedSubmodelRepositoryTestSuite.java | 11 ++++ .../resources/authorization/HealthOutput.json | 3 + 9 files changed, 119 insertions(+) create mode 100644 basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/resources/authorization/HealthOutput.json create mode 100644 basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/resources/authorization/HealthOutput.json create mode 100644 basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java create mode 100644 basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/resources/authorization/HealthOutput.json create mode 100644 basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/resources/authorization/HealthOutput.json diff --git a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/TestAuthorizedAasEnvironment.java b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/TestAuthorizedAasEnvironment.java index 1ede6cd47..586898f23 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/TestAuthorizedAasEnvironment.java +++ b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/TestAuthorizedAasEnvironment.java @@ -33,6 +33,7 @@ import java.util.Collection; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.message.BasicHeader; import org.eclipse.basyx.digitaltwin.aasenvironment.http.DummyAASEnvironmentComponent; import org.eclipse.basyx.digitaltwin.aasenvironment.http.TestAasEnvironmentHTTP; @@ -69,6 +70,7 @@ public class TestAuthorizedAasEnvironment { private static String clientId = "basyx-client-api"; private static AccessTokenProvider tokenProvider; private static ConfigurableApplicationContext appContext; + private static String healthEndpointUrl = "http://127.0.0.1:8081/actuator/health"; @BeforeClass public static void setUp() throws FileNotFoundException, IOException { @@ -78,6 +80,16 @@ public static void setUp() throws FileNotFoundException, IOException { addDummyElementsToRepositories(); } + + @Test + public void healthEndpointWithoutAuthorization() throws IOException, ParseException { + String expectedHealthEndpointOutput = getStringFromFile("authorization/HealthOutput.json"); + + CloseableHttpResponse healthCheckResponse = BaSyxHttpTestUtils.executeGetOnURL(healthEndpointUrl); + assertEquals(HttpStatus.OK.value(), healthCheckResponse.getCode()); + + BaSyxHttpTestUtils.assertSameJSONContent(expectedHealthEndpointOutput, BaSyxHttpTestUtils.getResponseAsString(healthCheckResponse)); + } @Test public void createSerializationWithCorrectRoleAndPermission() throws IOException { diff --git a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/resources/authorization/HealthOutput.json b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/resources/authorization/HealthOutput.json new file mode 100644 index 000000000..11e93135d --- /dev/null +++ b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/resources/authorization/HealthOutput.json @@ -0,0 +1,3 @@ +{ + "status": "UP" +} \ No newline at end of file diff --git a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AuthorizedAasRepositoryTestSuite.java b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AuthorizedAasRepositoryTestSuite.java index 9868d04dc..1a9786d15 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AuthorizedAasRepositoryTestSuite.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AuthorizedAasRepositoryTestSuite.java @@ -37,6 +37,7 @@ import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.ParseException; import org.eclipse.digitaltwin.basyx.authorization.AccessTokenProvider; import org.eclipse.digitaltwin.basyx.authorization.DummyCredential; import org.eclipse.digitaltwin.basyx.authorization.DummyCredentialStore; @@ -63,6 +64,7 @@ public class AuthorizedAasRepositoryTestSuite { private static final String THUMBNAIL_FILE_PATH = "authorization/" + THUMBNAIL_FILE_NAME; public static String authenticaltionServerTokenEndpoint = "http://localhost:9096/realms/BaSyx/protocol/openid-connect/token"; public static String aasRepositoryBaseUrl = "http://127.0.0.1:8087/shells"; + public static String healthEndpointUrl = "http://127.0.0.1:8087/actuator/health"; public static String clientId = "basyx-client-api"; private static AccessTokenProvider tokenProvider; @@ -72,6 +74,16 @@ public static void setUp() throws FileNotFoundException, IOException { createAasOnRepositoryWithAuthorization(getAasJSONString(AAS_SIMPLE_1_JSON), getAdminAccessToken()); } + + @Test + public void healthEndpointWithoutAuthorization() throws IOException, ParseException { + String expectedHealthEndpointOutput = getAasJSONString("authorization/HealthOutput.json"); + + CloseableHttpResponse healthCheckResponse = BaSyxHttpTestUtils.executeGetOnURL(healthEndpointUrl); + assertEquals(HttpStatus.OK.value(), healthCheckResponse.getCode()); + + BaSyxHttpTestUtils.assertSameJSONContent(expectedHealthEndpointOutput, BaSyxHttpTestUtils.getResponseAsString(healthCheckResponse)); + } @Test public void getAllAasWithCorrectRoleAndPermission() throws IOException { diff --git a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/resources/authorization/HealthOutput.json b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/resources/authorization/HealthOutput.json new file mode 100644 index 000000000..11e93135d --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/resources/authorization/HealthOutput.json @@ -0,0 +1,3 @@ +{ + "status": "UP" +} \ No newline at end of file diff --git a/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java b/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java new file mode 100644 index 000000000..5c4f7e9d1 --- /dev/null +++ b/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (C) 2024 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.authorization; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; +import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; +import org.springframework.security.web.SecurityFilterChain; + +/** + * Common configurations for security + * + * @author danish + */ +@Configuration +@ConditionalOnExpression("#{${" + CommonAuthorizationProperties.ENABLED_PROPERTY_KEY + ":false}}") +public class CommonSecurityConfiguration { + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.authorizeHttpRequests(authorize -> authorize.requestMatchers("/actuator/health/**").permitAll().anyRequest().authenticated()) + .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))); + + return http.build(); + } + + private JwtAuthenticationConverter jwtAuthenticationConverter() { + JwtAuthenticationConverter converter = new JwtAuthenticationConverter(); + converter.setJwtGrantedAuthoritiesConverter(new JwtGrantedAuthoritiesConverter()); + + return converter; + } + +} diff --git a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepositoryTestSuite.java b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepositoryTestSuite.java index f41500470..8d8590a90 100644 --- a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepositoryTestSuite.java +++ b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepositoryTestSuite.java @@ -33,6 +33,7 @@ import java.net.URL; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.ParseException; import org.eclipse.digitaltwin.basyx.authorization.AccessTokenProvider; import org.eclipse.digitaltwin.basyx.authorization.DummyCredential; import org.eclipse.digitaltwin.basyx.authorization.DummyCredentialStore; @@ -56,6 +57,7 @@ public class AuthorizedConceptDescriptionRepositoryTestSuite { private static final String SPECIFIC_CONCEPT_DESCRIPTION_ID = "specificConceptDescriptionId"; public static String authenticaltionServerTokenEndpoint = "http://localhost:9096/realms/BaSyx/protocol/openid-connect/token"; public static String conceptDescriptionRepositoryBaseUrl = "http://127.0.0.1:8089"; + public static String healthEndpointUrl = "http://127.0.0.1:8089/actuator/health"; public static String clientId = "basyx-client-api"; private static AccessTokenProvider tokenProvider; @@ -65,6 +67,16 @@ public static void setUp() throws FileNotFoundException, IOException { createConceptDescriptionOnRepositoryWithAuthorization(getConceptDescriptionJSONString(CONCEPT_DESCRIPTION_SIMPLE_1_JSON), getAdminAccessToken()); } + + @Test + public void healthEndpointWithoutAuthorization() throws IOException, ParseException { + String expectedHealthEndpointOutput = getConceptDescriptionJSONString("authorization/HealthOutput.json"); + + CloseableHttpResponse healthCheckResponse = BaSyxHttpTestUtils.executeGetOnURL(healthEndpointUrl); + assertEquals(HttpStatus.OK.value(), healthCheckResponse.getCode()); + + BaSyxHttpTestUtils.assertSameJSONContent(expectedHealthEndpointOutput, BaSyxHttpTestUtils.getResponseAsString(healthCheckResponse)); + } @Test public void getAllCDWithCorrectRoleAndPermission() throws IOException { diff --git a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/resources/authorization/HealthOutput.json b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/resources/authorization/HealthOutput.json new file mode 100644 index 000000000..11e93135d --- /dev/null +++ b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/resources/authorization/HealthOutput.json @@ -0,0 +1,3 @@ +{ + "status": "UP" +} \ No newline at end of file diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepositoryTestSuite.java b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepositoryTestSuite.java index 783590e76..ce9ed8d2a 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepositoryTestSuite.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepositoryTestSuite.java @@ -75,6 +75,7 @@ public class AuthorizedSubmodelRepositoryTestSuite { public static String authenticaltionServerTokenEndpoint = "http://localhost:9096/realms/BaSyx/protocol/openid-connect/token"; public static String submodelRepositoryBaseUrl = "http://127.0.0.1:8088"; public static String clientId = "basyx-client-api"; + public static String healthEndpointUrl = "http://127.0.0.1:8088/actuator/health"; private static AccessTokenProvider tokenProvider; private static final String FILE_NAME = "BaSyx-Logo.png"; @@ -84,6 +85,16 @@ public static void setUp() throws FileNotFoundException, IOException { createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getSubmodelJSONString(SUBMODEL_SIMPLE_1_JSON), getAdminAccessToken()); } + + @Test + public void healthEndpointWithoutAuthorization() throws IOException, ParseException { + String expectedHealthEndpointOutput = getJSONValueAsString("authorization/HealthOutput.json"); + + CloseableHttpResponse healthCheckResponse = BaSyxHttpTestUtils.executeGetOnURL(healthEndpointUrl); + assertEquals(HttpStatus.OK.value(), healthCheckResponse.getCode()); + + BaSyxHttpTestUtils.assertSameJSONContent(expectedHealthEndpointOutput, BaSyxHttpTestUtils.getResponseAsString(healthCheckResponse)); + } @Test public void getAllSubmodelsWithCorrectRoleAndPermission() throws IOException { diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/resources/authorization/HealthOutput.json b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/resources/authorization/HealthOutput.json new file mode 100644 index 000000000..11e93135d --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/resources/authorization/HealthOutput.json @@ -0,0 +1,3 @@ +{ + "status": "UP" +} \ No newline at end of file