diff options
-rw-r--r-- | ms/neng/pom.xml | 1 | ||||
-rw-r--r-- | ms/pom.xml | 1 | ||||
-rw-r--r-- | ms/sliboot/pom.xml | 34 | ||||
-rw-r--r-- | ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/SlibootApp.java | 2 | ||||
-rw-r--r-- | services/pom.xml | 3 | ||||
-rw-r--r-- | services/src/main/java/org/onap/ccsdk/apps/filters/AuditLogFilter.java | 43 | ||||
-rw-r--r-- | services/src/main/java/org/onap/ccsdk/apps/filters/ContentTypeFilter.java | 81 | ||||
-rw-r--r-- | services/src/main/java/org/onap/ccsdk/apps/filters/PayloadLoggingFilter.java | 322 | ||||
-rw-r--r-- | services/src/main/java/org/onap/ccsdk/apps/services/SvcLogicFactory.java | 1 |
9 files changed, 472 insertions, 16 deletions
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========================================================= --> - <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> @@ -18,7 +18,6 @@ * limitations under the License. * ============LICENSE_END========================================================= --> - <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> 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 @@ <version>${aaf.cadi.version}</version> <scope>runtime</scope> </dependency> + <!-- Needed by logging-analytics payload logging filter --> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-spring-boot-starter-jaxrs</artifactId> + <version>3.4.4</version> + </dependency> + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-cadi-client</artifactId> + <version>${aaf.cadi.version}</version> + </dependency> + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-cadi-core</artifactId> + <version>${aaf.cadi.version}</version> + </dependency> + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-auth-client</artifactId> + <version>${aaf.cadi.version}</version> + <scope>runtime</scope> + </dependency> + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-misc-env</artifactId> + <version>${aaf.cadi.version}</version> + <scope>runtime</scope> + </dependency> + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-misc-rosetta</artifactId> + <version>${aaf.cadi.version}</version> + <scope>runtime</scope> + </dependency> <dependency> <groupId>${project.groupId}</groupId> <artifactId>services</artifactId> 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 @@ <ccsdk.project.version>${project.version}</ccsdk.project.version> <ccsdk.build.timestamp>${maven.build.timestamp}</ccsdk.build.timestamp> <maven.build.timestamp.format>yyyyMMdd'T'HHmmss'Z'</maven.build.timestamp.format> + <logging.analytics.version>1.6.9</logging.analytics.version> </properties> <dependencies> @@ -90,7 +91,7 @@ <dependency> <groupId>org.onap.logging-analytics</groupId> <artifactId>logging-filter-spring</artifactId> - <version>1.6.6</version> + <version>${logging.analytics.version}</version> </dependency> <dependency> <groupId>javax.ws.rs</groupId> 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<String> acceptList; List<String> 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<String>(); Enumeration<String> 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<String> 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<String>(); + for (Enumeration<String> 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<String> getHeaders(String name) { + if ("Accept".equalsIgnoreCase(name) && (acceptList != null)) { + return Collections.enumeration(acceptList); + } else { + return super.getHeaders(name); + } + } + @Override public Enumeration<String> 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<String, Object> headers; + public Map<String, Object> 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<String, Object> 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<String> headerNames = getHeaderNames(); + while (headerNames.hasMoreElements()) { + String name = headerNames.nextElement(); + + if (name.equalsIgnoreCase("authorization")) { + r.headers.put(name, "***REDACTED***"); + continue; + } + + Enumeration<String> values = getHeaders(name); + List<String> 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<String, Object> getParamMap() { + Map<String, String[]> parameterMap = getParameterMap(); + Map<String, Object> paramMap = new HashMap<>(); + if (parameterMap != null) { + for (Entry<String, String[]> 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.<String> 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<String> headerNames = getHeaderNames(); + for (String name : headerNames) { + + if (name.equalsIgnoreCase("authorization")) { + r.headers.put(name, "***REDACTED***"); + continue; + } + + Collection<String> values = getHeaders(name); + List<String> 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<String, Object> m) { + if (m != null) { + for (Entry<String, Object> 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();
}
-
}
|