diff options
author | Fiete Ostkamp <Fiete.Ostkamp@telekom.de> | 2024-08-27 07:57:29 +0200 |
---|---|---|
committer | Fiete Ostkamp <Fiete.Ostkamp@telekom.de> | 2024-08-29 07:46:38 +0200 |
commit | 222f8efa881e940a4a3529706e067d0cefb6db99 (patch) | |
tree | db74c86336ddb2835a1917ede99b94372661bca3 /aai-resources/src | |
parent | 87502d28e8b2edd55dabf0186a8e2ac233388886 (diff) |
Create request interceptor class to remove namespace attribute from xml request bodies
- add interceptor that removes xmlns attribute from xml bodies
- update eclipse persistence (2.6.2 -> 2.7.7)
- use spring-boot-starter-test instead of spring framework's spring-test
- bump snapshot version to 1.14.7-SNAPSHOT
- remove unused imports
Issue-ID: AAI-3976
Change-Id: Iac7103ae003cb7bb7269ada983af97e003be155c
Signed-off-by: Fiete Ostkamp <Fiete.Ostkamp@telekom.de>
Diffstat (limited to 'aai-resources/src')
9 files changed, 149 insertions, 15 deletions
diff --git a/aai-resources/src/main/java/org/onap/aai/interceptors/pre/NamespaceInterceptor.java b/aai-resources/src/main/java/org/onap/aai/interceptors/pre/NamespaceInterceptor.java new file mode 100644 index 0000000..c97e475 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/interceptors/pre/NamespaceInterceptor.java @@ -0,0 +1,128 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2024 Deutsche Telekom. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.interceptors.pre; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; + +import javax.annotation.Priority; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.ext.ReaderInterceptor; +import javax.ws.rs.ext.ReaderInterceptorContext; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.Source; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.onap.aai.IncreaseNodesTool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; + +/** + * With newer versions of EclipseLink Moxy, the xmlns attribute cannot be included + * in the xml structure anymore. + * Changes to the model-loader and babel would be required to 'properly' remove + * it for the model-distribution. + * Since the impact of such a change is hard to judge, this workaround is taken + * that is less invasive and with a lower risk of breaking model distribution. + * + * @deprecated This is only meant as a temporary compatibility layer and will be removed in the future + * once all clients have been updated to not include the xmlns attribute. + * + */ +@Deprecated +@Component +@Priority(AAIRequestFilterPriority.REQUEST_MODIFICATION) +@ConditionalOnProperty(value = "aai.remove-xmlns.enabled", havingValue = "true", matchIfMissing = true) +public class NamespaceInterceptor implements ReaderInterceptor { + + private static final Logger log = LoggerFactory.getLogger(IncreaseNodesTool.class); + private static final String xslStr = String.join("\n", + "<xsl:transform xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">", + "<xsl:output version=\"1.0\" encoding=\"UTF-8\" indent=\"no\"/>", + "<xsl:strip-space elements=\"*\"/>", + " <xsl:template match=\"@*|node()\">", + " <xsl:element name=\"{local-name()}\">", + " <xsl:apply-templates select=\"@*|node()\"/>", + " </xsl:element>", + " </xsl:template>", + " <xsl:template match=\"text()\">", + " <xsl:copy/>", + " </xsl:template>", + "</xsl:transform>"); + + @Override + public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException { + if(MediaType.APPLICATION_XML.equalsIgnoreCase(context.getMediaType().toString())) { + Reader xmlReader = new InputStreamReader(context.getInputStream()); + try { + ByteArrayInputStream inputStream = removeNameSpace(xmlReader); + context.setInputStream(inputStream); + return context.proceed(); + } catch (Exception e) { + log.error("Could not remove namespace from model payload: " + e.getMessage()); + return context.proceed(); + } + } + return context.proceed(); + } + + /** + * Temporary solution to removing the xmlns attribute from the model payload. + * The payload is coming from babel and removing it there would be some larger effort. + * As such, this workaround is applied. + * Taken from: https://stackoverflow.com/questions/37354605/how-to-remove-xmlns-attribute-from-the-root-element-in-xml-and-java#answer-37357777 + * @throws Exception + */ + public ByteArrayInputStream removeNameSpace(Reader xmlReader) throws Exception { + // Parse XML and Build Document + DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + InputSource is = new InputSource(); + is.setCharacterStream(xmlReader); + Document doc = db.parse(is); + + // Parse XSLT and Configure Transformer + Source xslt = new StreamSource(new StringReader(xslStr)); + Transformer tf = TransformerFactory.newInstance().newTransformer(xslt); + tf.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + + // Output Result to String + DOMSource source = new DOMSource(doc); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + StreamResult strresult = new StreamResult(outStream); + tf.transform(source, strresult); + + return new ByteArrayInputStream(outStream.toByteArray()); + } +} diff --git a/aai-resources/src/main/java/org/onap/aai/web/JerseyConfiguration.java b/aai-resources/src/main/java/org/onap/aai/web/JerseyConfiguration.java index 41ec8e1..8e68e8e 100644 --- a/aai-resources/src/main/java/org/onap/aai/web/JerseyConfiguration.java +++ b/aai-resources/src/main/java/org/onap/aai/web/JerseyConfiguration.java @@ -35,6 +35,7 @@ import java.util.logging.Logger; import javax.annotation.Priority; import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.ext.ReaderInterceptor; import org.glassfish.jersey.server.ResourceConfig; import org.onap.aai.rest.BulkAddConsumer; @@ -80,6 +81,7 @@ public class JerseyConfiguration { LegacyMoxyConsumer.class, URLFromVertexIdConsumer.class); resourceConfig.registerClasses(classes); registerFiltersForClasses(resourceConfig, ContainerRequestFilter.class, ContainerResponseFilter.class, + ReaderInterceptor.class, AuditLogContainerFilter.class); if (isLoggingEnabled()) { diff --git a/aai-resources/src/test/java/org/onap/aai/rest/BulkProcessorTestAbstraction.java b/aai-resources/src/test/java/org/onap/aai/rest/BulkProcessorTestAbstraction.java index 706da69..f734360 100644 --- a/aai-resources/src/test/java/org/onap/aai/rest/BulkProcessorTestAbstraction.java +++ b/aai-resources/src/test/java/org/onap/aai/rest/BulkProcessorTestAbstraction.java @@ -37,7 +37,6 @@ import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.mockito.Mockito; import org.onap.aai.AAISetup; diff --git a/aai-resources/src/test/java/org/onap/aai/rest/ConfigurationTest.java b/aai-resources/src/test/java/org/onap/aai/rest/ConfigurationTest.java index 21f2fb4..b780d90 100644 --- a/aai-resources/src/test/java/org/onap/aai/rest/ConfigurationTest.java +++ b/aai-resources/src/test/java/org/onap/aai/rest/ConfigurationTest.java @@ -41,6 +41,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; import org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration; +import org.springframework.boot.test.autoconfigure.actuate.metrics.AutoConfigureMetrics; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.context.annotation.Import; @@ -58,6 +59,7 @@ import org.springframework.web.client.RestTemplate; * Test REST requests against configuration resource */ +@AutoConfigureMetrics @TestPropertySource(locations = "classpath:application-test.properties") @ContextConfiguration(initializers = PropertyPasswordConfiguration.class, classes = {SpringContextAware.class}) @EnableAutoConfiguration(exclude={CassandraDataAutoConfiguration.class, CassandraAutoConfiguration.class}) // there is no running cassandra instance for the test @@ -66,6 +68,7 @@ import org.springframework.web.client.RestTemplate; webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {SpringContextAware.class, ResourcesApp.class}) public class ConfigurationTest extends AbstractSpringRestTest { + @Autowired RestTemplate restTemplate; @@ -92,7 +95,7 @@ public class ConfigurationTest extends AbstractSpringRestTest { headersGet.add("X-FromAppId", "JUNIT"); headersGet.add("X-TransactionId", "JUNIT"); - headersGet.setBasicAuth("AAI","AAI"); + headersGet.setBasicAuth("AAI", "AAI"); headersPutPatch = new HttpHeaders(); headersPutPatch.putAll(headersGet); @@ -112,7 +115,8 @@ public class ConfigurationTest extends AbstractSpringRestTest { responseEntity = restTemplate.exchange(baseUrl + endpoint, HttpMethod.GET, httpEntityGet, String.class); assertEquals(HttpStatus.NOT_FOUND, responseEntity.getStatusCode()); - // String putBody = " configuration-id, configuration-type configuration-sub-type"; + // String putBody = " configuration-id, configuration-type + // configuration-sub-type"; String putBody = "{" + "\"configuration-id\": \"" + cid + "\"," + "\"configuration-type\": \"type1\"," + "\"configuration-sub-type\": \"subtype1\", " + "\"operational-status\": \"example1\", " + "\"orchestration-status\": \"example1\", " + "\"configuration-selflink\": \"example1\", " @@ -171,29 +175,31 @@ public class ConfigurationTest extends AbstractSpringRestTest { ResponseEntity<String> responseEntity = null; String responseBody = null; - // set Accept as text/plain in order to get access of endpoint "/actuator/prometheus" + // set Accept as text/plain in order to get access of endpoint + // "/actuator/prometheus" headersGet.set("Accept", "text/plain"); headersGet.setAccept(Arrays.asList(MediaType.TEXT_PLAIN)); httpEntityGet = new HttpEntity<String>(headersGet); - responseEntity = - restTemplate.exchange(actuatorurl + "/actuator/prometheus", HttpMethod.GET, httpEntityGet, String.class); + responseEntity = restTemplate.exchange(actuatorurl + "/actuator/prometheus", HttpMethod.GET, httpEntityGet, + String.class); responseBody = (String) responseEntity.getBody(); assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); assertTrue(responseBody.contains("group_id")); assertTrue(responseBody.contains("aai_uri")); - // Set Accept as MediaType.APPLICATION_JSON in order to get access of endpoint "/actuator/info" and + // Set Accept as MediaType.APPLICATION_JSON in order to get access of endpoint + // "/actuator/info" and // "/actuator/health" headersGet.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); httpEntityGet = new HttpEntity<String>(headersGet); - responseEntity = - restTemplate.exchange(actuatorurl + "/actuator/info", HttpMethod.GET, httpEntityGet, String.class); + responseEntity = restTemplate.exchange(actuatorurl + "/actuator/info", HttpMethod.GET, httpEntityGet, + String.class); responseBody = (String) responseEntity.getBody(); assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); assertTrue(responseBody.contains("aai-resources")); - responseEntity = - restTemplate.exchange(actuatorurl + "/actuator/health", HttpMethod.GET, httpEntityGet, String.class); + responseEntity = restTemplate.exchange(actuatorurl + "/actuator/health", HttpMethod.GET, httpEntityGet, + String.class); responseBody = (String) responseEntity.getBody(); assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); assertTrue(responseBody.contains("UP")); diff --git a/aai-resources/src/test/java/org/onap/aai/rest/ExampleConsumerTest.java b/aai-resources/src/test/java/org/onap/aai/rest/ExampleConsumerTest.java index 73eed51..b48cdb2 100644 --- a/aai-resources/src/test/java/org/onap/aai/rest/ExampleConsumerTest.java +++ b/aai-resources/src/test/java/org/onap/aai/rest/ExampleConsumerTest.java @@ -38,7 +38,6 @@ import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; diff --git a/aai-resources/src/test/java/org/onap/aai/rest/URLFromVertexIdConsumerTest.java b/aai-resources/src/test/java/org/onap/aai/rest/URLFromVertexIdConsumerTest.java index e515098..e97a43a 100644 --- a/aai-resources/src/test/java/org/onap/aai/rest/URLFromVertexIdConsumerTest.java +++ b/aai-resources/src/test/java/org/onap/aai/rest/URLFromVertexIdConsumerTest.java @@ -41,7 +41,6 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import org.json.JSONException; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; diff --git a/aai-resources/src/test/java/org/onap/aai/rest/VertexIdConsumerTest.java b/aai-resources/src/test/java/org/onap/aai/rest/VertexIdConsumerTest.java index 8c62950..d0455b1 100644 --- a/aai-resources/src/test/java/org/onap/aai/rest/VertexIdConsumerTest.java +++ b/aai-resources/src/test/java/org/onap/aai/rest/VertexIdConsumerTest.java @@ -41,7 +41,6 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import org.json.JSONException; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; diff --git a/aai-resources/src/test/resources/application-test.properties b/aai-resources/src/test/resources/application-test.properties index 253a191..b4cd460 100644 --- a/aai-resources/src/test/resources/application-test.properties +++ b/aai-resources/src/test/resources/application-test.properties @@ -76,6 +76,8 @@ schema.translator.list=config #To expose the Prometheus scraping endpoint in unit test management.server.port=0 +management.endpoint.metrics.enabled=true +management.endpoint.prometheus.enabled=true management.endpoints.enabled-by-default=true management.endpoints.web.exposure.include=info, health, prometheus management.metrics.web.server.request.autotime.enabled=false diff --git a/aai-resources/src/test/resources/logback.xml b/aai-resources/src/test/resources/logback.xml index a550336..80050ca 100644 --- a/aai-resources/src/test/resources/logback.xml +++ b/aai-resources/src/test/resources/logback.xml @@ -269,7 +269,7 @@ <logger name="org.testcontainers" level="INFO"/> <logger name="com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.wire" level="OFF"/> - <root level="DEBUG"> + <root level="INFO"> <appender-ref ref="external" /> <appender-ref ref="STDOUT" /> </root> |