From: Dan Timoney Date: Mon, 22 Mar 2021 13:45:16 +0000 (-0400) Subject: Carry forward honolulu fixes X-Git-Tag: 1.2.0~1 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=ad0b39dc92ef67ed6f2df714771bed1053d4125d;p=ccsdk%2Fapps.git Carry forward honolulu fixes This change carries forward the following fixes from Honolulu to master Update to allow ranges for ccsdk.sli.version Update to allow ccsdk.sli.version to be passed with a range as a value Change-Id: Ibb37082bf8162300ae2de37f33b0e4672b796293 Issue-ID: CCSDK-3209 Signed-off-by: Dan Timoney (cherry picked from commit c3e800149c947ecc9b76a2e3b990664fc5d41ce1) Minor Refactoring of sliboot pom Issue-ID: CCSDK-3343 Change-Id: I66d4e2840ff86e092a23453384d02ce49b7048ac Signed-off-by: Singal, Kapil (ks220y) Use CadiFilter instead of shiro Microservices should use CadiFilter rather than shiro to integrate with AAF Change-Id: I95b9a844b7ac868f864134de7345013001357352 Issue-ID: SDNC-1523 Signed-off-by: Dan Timoney (cherry picked from commit a90eecf70419ec4acba6f5a8425300eef7f45290) Minor Refactoring of sliboot pom Updating docker-maven-plugin to 0.34.0 and removing docker image build goal (it was there twice) Issue-ID: CCSDK-3343 Signed-off-by: Singal, Kapil (ks220y) Change-Id: I26401befaf235ddfe694c6206cbd547cbc56cc03 (cherry picked from commit 108a13c20757f4a437aea22880f019e6e6043841) missing logs Add filters for audit and request/response log Add filters for audit and request/response log Issue-ID: CCSDK-3367 Signed-off-by: lalena.aria Change-Id: Id292c1f5109f0c3846a0357d0618f607011825a4 Add code to handle content type plain/text Add code to handle content type plain/text Issue-ID: CCSDK-3378 Signed-off-by: lalena.aria Change-Id: Id4a30ad5ca81db0e7e3a328cc5d2496c42c9e150 Signed-off-by: Dan Timoney 1201 sanity test Add bean for IpAddressTool plugin Add missing import Add bean for IpAddressTool plugin Add missing import Fx typo (text/plain instead of plain/text) Issue-ID: CCSDK-3379 Signed-off-by: lalena.aria Change-Id: I208a6eaf462f86c2a3015af9bf9026ad11efa14b Signed-off-by: Dan Timoney Use version 1.1.8 of parent Use version 1.1.8 of parent in order to use APL-licensed version of liquibase Issue-ID: CCSDK-3423 Signed-off-by: Dan Timoney Change-Id: I1db475fa8991d617d22536ce38d51f96c7806f85 --- diff --git a/ms/neng/pom.xml b/ms/neng/pom.xml index b9a16ffa..84b9cc51 100644 --- a/ms/neng/pom.xml +++ b/ms/neng/pom.xml @@ -18,7 +18,6 @@ * limitations under the License. * ============LICENSE_END========================================================= --> - 4.0.0 diff --git a/ms/pom.xml b/ms/pom.xml index 836a4b56..94cd841e 100644 --- a/ms/pom.xml +++ b/ms/pom.xml @@ -18,7 +18,6 @@ * limitations under the License. * ============LICENSE_END========================================================= --> - 4.0.0 diff --git a/ms/sliboot/pom.xml b/ms/sliboot/pom.xml index a9ffec0b..783e7fd3 100644 --- a/ms/sliboot/pom.xml +++ b/ms/sliboot/pom.xml @@ -105,6 +105,40 @@ ${aaf.cadi.version} runtime + + + org.apache.cxf + cxf-spring-boot-starter-jaxrs + 3.4.4 + + + org.onap.aaf.authz + aaf-cadi-client + ${aaf.cadi.version} + + + org.onap.aaf.authz + aaf-cadi-core + ${aaf.cadi.version} + + + org.onap.aaf.authz + aaf-auth-client + ${aaf.cadi.version} + runtime + + + org.onap.aaf.authz + aaf-misc-env + ${aaf.cadi.version} + runtime + + + org.onap.aaf.authz + aaf-misc-rosetta + ${aaf.cadi.version} + runtime + ${project.groupId} services diff --git a/ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/SlibootApp.java b/ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/SlibootApp.java index beab2569..2bca1dbc 100644 --- a/ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/SlibootApp.java +++ b/ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/SlibootApp.java @@ -50,8 +50,6 @@ public class SlibootApp { SpringApplication.run(SlibootApp.class, args); } - - @Bean @ConditionalOnProperty("cadi.properties.path") @Order(1) diff --git a/services/pom.xml b/services/pom.xml index 5328b55f..c3f1e91f 100644 --- a/services/pom.xml +++ b/services/pom.xml @@ -22,6 +22,7 @@ ${project.version} ${maven.build.timestamp} yyyyMMdd'T'HHmmss'Z' + 1.6.9 @@ -90,7 +91,7 @@ org.onap.logging-analytics logging-filter-spring - 1.6.6 + ${logging.analytics.version} javax.ws.rs diff --git a/services/src/main/java/org/onap/ccsdk/apps/filters/AuditLogFilter.java b/services/src/main/java/org/onap/ccsdk/apps/filters/AuditLogFilter.java new file mode 100644 index 00000000..b6d52c52 --- /dev/null +++ b/services/src/main/java/org/onap/ccsdk/apps/filters/AuditLogFilter.java @@ -0,0 +1,43 @@ +package org.onap.ccsdk.apps.filters +; + +import javax.servlet.http.HttpServletRequest; +import org.onap.logging.filter.base.AuditLogServletFilter; +import org.onap.logging.ref.slf4j.ONAPLogConstants; +import org.slf4j.MDC; +import org.springframework.stereotype.Component; + +@Component +public class AuditLogFilter extends AuditLogServletFilter { + private static final String MDC_HTTP_METHOD_KEY = "HttpMethod"; + + @Override + protected void additionalPreHandling(HttpServletRequest httpServletRequest) { + // Don't overwrite service instance id if it was set outside of this automated method + if (MDC.get(ONAPLogConstants.MDCs.SERVICE_INSTANCE_ID) == null) { + String serviceInstanceId = getServiceInstanceId(httpServletRequest.getRequestURI()); + if (serviceInstanceId != null) { + MDC.put(ONAPLogConstants.MDCs.SERVICE_INSTANCE_ID, serviceInstanceId); + } + } + MDC.put(MDC_HTTP_METHOD_KEY, httpServletRequest.getMethod()); + } + + // restconf URLs follow a pattern, this method attempts to extract the service instance id according to that pattern + protected String getServiceInstanceId(String path) { + int idx = path.indexOf("service-list"); + if (idx != -1) { + // chomp off service-list/ + String str = path.substring(idx + 13); + idx = str.indexOf("/"); + //if there is another forward slash with more information chomp it off + if (idx != -1) { + return str.substring(0, idx); + } else { + return str; + } + } + return null; + } + +} \ No newline at end of file diff --git a/services/src/main/java/org/onap/ccsdk/apps/filters/ContentTypeFilter.java b/services/src/main/java/org/onap/ccsdk/apps/filters/ContentTypeFilter.java index 41c71a45..20f9ec47 100644 --- a/services/src/main/java/org/onap/ccsdk/apps/filters/ContentTypeFilter.java +++ b/services/src/main/java/org/onap/ccsdk/apps/filters/ContentTypeFilter.java @@ -3,6 +3,7 @@ package org.onap.ccsdk.apps.filters; import java.io.IOException; import java.util.Collections; import java.util.Enumeration; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -14,6 +15,8 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @@ -24,33 +27,84 @@ public class ContentTypeFilter implements Filter { String DEFAULT_CONTENT_TYPE = "application/json"; + @Override public void doFilter(ServletRequest httpReq, ServletResponse httpResp, FilterChain chain) throws IOException, ServletException { String defaultContentType = System.getProperty("ccsdk.defaults.content-type", DEFAULT_CONTENT_TYPE); - chain.doFilter(new DefaultContentTypeHttpRequest((HttpServletRequest) httpReq, defaultContentType), httpResp); } private class DefaultContentTypeHttpRequest extends HttpServletRequestWrapper { HttpServletRequest httpRequest; - String defaultContentType; - boolean hasContentType; + String contentType; + List acceptList; List headerNames; public DefaultContentTypeHttpRequest(HttpServletRequest httpRequest, String defaultContentType) { super(httpRequest); + this.httpRequest = httpRequest; - this.defaultContentType = defaultContentType; + this.contentType = defaultContentType; + this.acceptList = null; + + boolean hasContentType = false; headerNames = new LinkedList(); Enumeration headerNameEnum = httpRequest.getHeaderNames(); - hasContentType = false; while (headerNameEnum.hasMoreElements()) { String curHeaderName = headerNameEnum.nextElement(); if ("Content-Type".equalsIgnoreCase(curHeaderName)) { hasContentType = true; + contentType = super.getContentType(); + if ("application/yang-data+json".equalsIgnoreCase(contentType)) { + contentType = "application/json"; + } else if ("application/yang-data+xml".equalsIgnoreCase(contentType)) { + contentType = "application/xml"; + } else if (contentType.startsWith("text/plain")) { + // Use Accept header, if present, to determine content type. + boolean acceptsXml = false; + boolean acceptsJson = false; + for (Enumeration e = getHeaders("Accept") ; e.hasMoreElements() ;) { + String curAcceptValue = e.nextElement(); + if ("application/json".equalsIgnoreCase(curAcceptValue)) { + acceptsJson = true; + } else if ("application/yang-data+json".equalsIgnoreCase(curAcceptValue)) { + acceptsJson = true; + } else if ("application/xml".equalsIgnoreCase(curAcceptValue)) { + acceptsXml = true; + } else if ("application/yang-data+xml".equalsIgnoreCase(curAcceptValue)) { + acceptsXml = true; + } + } + if (acceptsJson) { + contentType = "application/json"; + } else if (acceptsXml) { + contentType = "application/xml"; + } else { + // If Accept does not specify XML or JSON (could be Accept is missing), use default content type + contentType = defaultContentType; + } + } + } else if ("Accept".equalsIgnoreCase(curHeaderName)) { + acceptList = new LinkedList(); + for (Enumeration e = getHeaders("Accept") ; e.hasMoreElements() ;) { + String acceptValue = e.nextElement(); + if ("application/yang-data+json".equalsIgnoreCase(acceptValue)) { + if (!acceptList.contains("application/json")) { + acceptList.add("application/json"); + } + } else if ("application/yang-data+xml".equalsIgnoreCase(acceptValue)) { + if (!acceptList.contains("application/xml")) { + acceptList.add("application/xml"); + } + } else { + if (!acceptList.contains(acceptValue)) { + acceptList.add(acceptValue); + } + } + } } headerNames.add(curHeaderName); } @@ -69,6 +123,17 @@ public class ContentTypeFilter implements Filter { } } + + + @Override + public Enumeration getHeaders(String name) { + if ("Accept".equalsIgnoreCase(name) && (acceptList != null)) { + return Collections.enumeration(acceptList); + } else { + return super.getHeaders(name); + } + } + @Override public Enumeration getHeaderNames() { return(Collections.enumeration(headerNames)); @@ -76,11 +141,7 @@ public class ContentTypeFilter implements Filter { @Override public String getContentType() { - if (hasContentType) { - return super.getContentType(); - } else { - return defaultContentType; - } + return contentType; } } diff --git a/services/src/main/java/org/onap/ccsdk/apps/filters/PayloadLoggingFilter.java b/services/src/main/java/org/onap/ccsdk/apps/filters/PayloadLoggingFilter.java new file mode 100644 index 00000000..e53c50a7 --- /dev/null +++ b/services/src/main/java/org/onap/ccsdk/apps/filters/PayloadLoggingFilter.java @@ -0,0 +1,322 @@ +package org.onap.ccsdk.apps.filters; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.CharArrayWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ReadListener; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletOutputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.WriteListener; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; + +import org.onap.logging.filter.base.AbstractServletFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +@Component +public class PayloadLoggingFilter extends AbstractServletFilter implements Filter { + + private static final Logger log = LoggerFactory.getLogger(PayloadLoggingFilter.class); + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + @Override + public void destroy() { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + RequestWrapper req = new RequestWrapper((HttpServletRequest) request); + Request requestData = req.getMessageRequest(); + + StringBuilder requestHeaders = new StringBuilder("REQUEST|"); + requestHeaders.append(requestData.method); + requestHeaders.append(":"); + requestHeaders.append(requestData.uri); + requestHeaders.append("|"); + mapstr(requestHeaders, requestData.headers); + + log.info(requestHeaders.toString()); + log.info("REQUEST BODY|{}", requestData.body); + + ResponseWrapper res = new ResponseWrapper((HttpServletResponse) response); + + chain.doFilter(req, res); + + Response responseData = res.getMessageResponse(); + + StringBuilder responseHeaders = new StringBuilder(); + responseHeaders.append("RESPONSE HEADERS|"); + mapstr(responseHeaders, responseData.headers); + responseHeaders.append("Status:").append(responseData.code); + responseHeaders.append(";IsCommitted:").append(res.isCommitted()); + + log.info(responseHeaders.toString()); + log.info("RESPONSE BODY|{}", responseData.body); + + res.writeBody(); + } + + private static class Request { + + public String method; + public String uri; + public Map headers; + public Map param; + public String body; + + @Override + public String toString() { + StringBuilder ss = new StringBuilder(); + ss.append("REQUEST|").append(method).append(":").append(uri).append("|"); + ss.append("Headers: "); + mapstr(ss, headers); + if (param != null && !param.isEmpty()) { + ss.append("Parameters: "); + mapstr(ss, param); + } + ss.append("REQUEST BODY|\n"); + ss.append(body); + return ss.toString(); + } + } + + private static class Response { + + public int code; + public String message; + public Map headers; + public String body; + + @Override + public String toString() { + StringBuilder ss = new StringBuilder(); + ss.append("HTTP Response: ").append(code).append(" ").append(message).append("\n"); + ss.append("Headers:\n"); + mapstr(ss, headers); + ss.append("Body:\n"); + ss.append(body); + return ss.toString(); + } + } + + private static class RequestWrapper extends HttpServletRequestWrapper { + + private final String body; + + public RequestWrapper(HttpServletRequest request) throws IOException { + super(request); + + StringBuilder stringBuilder = new StringBuilder(); + InputStream inputStream = request.getInputStream(); + if (inputStream != null) { + try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) { + char[] charBuffer = new char[128]; + int bytesRead = -1; + while ((bytesRead = bufferedReader.read(charBuffer)) > 0) { + stringBuilder.append(charBuffer, 0, bytesRead); + } + } + } + body = stringBuilder.toString(); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes()); + ServletInputStream servletInputStream = new ServletInputStream() { + + @Override + public int read() throws IOException { + return byteArrayInputStream.read(); + } + + @Override + public boolean isFinished() { + return byteArrayInputStream.available() == 0; + } + + @Override + public boolean isReady() { + return true; + } + + @Override + public void setReadListener(ReadListener listener) { + } + }; + return servletInputStream; + } + + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + public String getBody() { + return body; + } + + public Request getMessageRequest() { + Request r = new Request(); + r.method = getMethod(); + r.uri = getRequestURI(); + r.param = getParamMap(); + + r.headers = new HashMap<>(); + Enumeration headerNames = getHeaderNames(); + while (headerNames.hasMoreElements()) { + String name = headerNames.nextElement(); + + if (name.equalsIgnoreCase("authorization")) { + r.headers.put(name, "***REDACTED***"); + continue; + } + + Enumeration values = getHeaders(name); + List valueList = new ArrayList<>(); + while (values.hasMoreElements()) { + valueList.add(values.nextElement()); + } + if (valueList.size() > 1) { + r.headers.put(name, valueList); + } else if (valueList.size() > 0) { + r.headers.put(name, valueList.get(0)); + } + } + + r.body = getBody(); + + return r; + } + + private Map getParamMap() { + Map parameterMap = getParameterMap(); + Map paramMap = new HashMap<>(); + if (parameterMap != null) { + for (Entry entry : parameterMap.entrySet()) { + String name = entry.getKey(); + String[] values = entry.getValue(); + if (values != null && values.length > 0) { + if (values.length == 1) { + paramMap.put(name, values[0]); + } else { + paramMap.put(name, Arrays. asList(values)); + } + } + } + } + return paramMap; + } + } + + public class ResponseWrapper extends HttpServletResponseWrapper { + + private CharArrayWriter writer = new CharArrayWriter(); + + private String statusMessage; + + public ResponseWrapper(HttpServletResponse response) { + super(response); + } + + @Override + public PrintWriter getWriter() { + return new PrintWriter(writer); + } + + @Override + public ServletOutputStream getOutputStream() { + return new ServletOutputStream() { + + @Override + public void write(int b) throws IOException { + writer.write(b); + } + + @Override + public void setWriteListener(WriteListener listener) { + } + + @Override + public boolean isReady() { + return true; + } + }; + } + + @SuppressWarnings("deprecation") + @Override + public void setStatus(int sc, String sm) { + super.setStatus(sc, sm); + statusMessage = sm; + } + + public Response getMessageResponse() { + Response r = new Response(); + r.code = getStatus(); + r.message = statusMessage == null ? "" : statusMessage; + + r.headers = new HashMap<>(); + Collection headerNames = getHeaderNames(); + for (String name : headerNames) { + + if (name.equalsIgnoreCase("authorization")) { + r.headers.put(name, "***REDACTED***"); + continue; + } + + Collection values = getHeaders(name); + List valueList = new ArrayList<>(values); + if (valueList.size() > 1) { + r.headers.put(name, valueList); + } else { + r.headers.put(name, valueList.get(0)); + } + } + + r.body = writer.toString(); + + return r; + } + + public void writeBody() throws IOException { + String body = writer.toString(); + setContentLength(body.length()); + super.getWriter().write(body); + } + } + + private static void mapstr(StringBuilder ss, Map m) { + if (m != null) { + for (Entry entry : m.entrySet()) { + ss.append(entry.getKey()).append(": ").append(entry.getValue()).append(";"); + } + } + } +} \ No newline at end of file diff --git a/services/src/main/java/org/onap/ccsdk/apps/services/SvcLogicFactory.java b/services/src/main/java/org/onap/ccsdk/apps/services/SvcLogicFactory.java index 44c56af3..9ab5656c 100644 --- a/services/src/main/java/org/onap/ccsdk/apps/services/SvcLogicFactory.java +++ b/services/src/main/java/org/onap/ccsdk/apps/services/SvcLogicFactory.java @@ -209,6 +209,5 @@ public class SvcLogicFactory { return new PropertiesNode(); } - }