2 * ============LICENSE_START==========================================
4 * ===================================================================
5 * Copyright (c) 2017 AT&T Intellectual Property
6 * ===================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 * ============LICENSE_END=============================================
20 * ====================================================================
22 package org.onap.music.eelf.logging;
24 import java.io.IOException;
25 import java.util.Enumeration;
26 import java.util.HashMap;
28 import java.util.stream.Collectors;
30 import javax.servlet.Filter;
31 import javax.servlet.FilterChain;
32 import javax.servlet.FilterConfig;
33 import javax.servlet.ServletException;
34 import javax.servlet.ServletRequest;
35 import javax.servlet.ServletResponse;
36 import javax.servlet.http.HttpServletRequest;
37 import javax.servlet.http.HttpServletResponse;
39 import org.onap.music.authentication.AuthorizationError;
40 import org.onap.music.main.MusicUtil;
42 import com.fasterxml.jackson.databind.ObjectMapper;
46 * This is the first filter in the chain to be executed before cadi
47 * authentication. The priority has been set in <code>MusicApplication</code>
48 * through filter registration bean
50 * The responsibility of this filter is to validate header values as per
51 * contract and write it to MDC and http response header back.
58 public class MusicLoggingServletFilter implements Filter {
60 private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MusicLoggingServletFilter.class);
61 // client transaction id, specific to client system, set in properties
62 public static final String CONVERSATION_ID = MusicUtil.getConversationIdPrefix() + "ConversationId";
64 // can be used as correlation-id in case of callback, also this can be passed to
65 // other services for tracking.
66 public static final String MESSAGE_ID = MusicUtil.getMessageIdPrefix() + "MessageId";
68 // client id would be the unique client source-system-id, i;e VALET or CONDUCTOR
70 public static final String CLIENT_ID = MusicUtil.getClientIdPrefix() + "ClientId";
72 // unique transaction of the source system
73 private static final String TRANSACTION_ID = MusicUtil.getTransIdPrefix() + "Transaction-Id";
75 public MusicLoggingServletFilter() throws ServletException {
80 public void init(FilterConfig filterConfig) throws ServletException {
85 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
86 throws IOException, ServletException {
88 logger.info(EELFLoggerDelegate.applicationLogger,
89 "In MusicLogginServletFilter doFilter start() ::::::::::::::::::::::: [\"+MusicUtil.getTransIdRequired()+\",\"+MusicUtil.getConversationIdRequired()+\",\"+MusicUtil.getClientIdRequired()+\",\"+MusicUtil.getMessageIdRequired()");
91 HttpServletRequest httpRequest = null;
92 HttpServletResponse httpResponse = null;
93 Map<String, String> headerMap = null;
94 Map<String, String> upperCaseHeaderMap = null;
96 if (null != request && null != response) {
97 httpRequest = (HttpServletRequest) request;
98 httpResponse = (HttpServletResponse) response;
100 headerMap = getHeadersInfo(httpRequest);
102 // The custom header values automatically converted into lower case, not sure
103 // why ? So i had to covert all keys to upper case
104 // The response header back to client will have all custom header values as
106 upperCaseHeaderMap = headerMap.entrySet().stream()
107 .collect(Collectors.toMap(entry -> entry.getKey().toUpperCase(), entry -> entry.getValue()));
108 // Enable/disable keys are present in /opt/app/music/etc/music.properties
110 if (Boolean.valueOf(MusicUtil.getTransIdRequired())
111 && !upperCaseHeaderMap.containsKey(TRANSACTION_ID.toUpperCase())) {
112 populateError(httpResponse, "Transaction id '" + TRANSACTION_ID
113 + "' required on http header");
116 populateMDCAndResponseHeader(upperCaseHeaderMap, TRANSACTION_ID, "transactionId",
117 Boolean.valueOf(MusicUtil.getTransIdRequired()), httpResponse);
120 if (Boolean.valueOf(MusicUtil.getConversationIdRequired())
121 && !upperCaseHeaderMap.containsKey(CONVERSATION_ID.toUpperCase())) {
122 populateError(httpResponse, "Conversation Id '" + CONVERSATION_ID
123 + "' required on http header");
126 populateMDCAndResponseHeader(upperCaseHeaderMap, CONVERSATION_ID, "conversationId",
127 Boolean.valueOf(MusicUtil.getConversationIdRequired()), httpResponse);
130 if (Boolean.valueOf(MusicUtil.getMessageIdRequired())
131 && !upperCaseHeaderMap.containsKey(MESSAGE_ID.toUpperCase())) {
132 populateError(httpResponse, "Message Id '" + MESSAGE_ID
133 + "' required on http header");
136 populateMDCAndResponseHeader(upperCaseHeaderMap, MESSAGE_ID, "messageId",
137 Boolean.valueOf(MusicUtil.getMessageIdRequired()), httpResponse);
140 if (Boolean.valueOf(MusicUtil.getClientIdRequired())
141 && !upperCaseHeaderMap.containsKey(CLIENT_ID.toUpperCase())) {
142 populateError(httpResponse, "Client Id '" + CLIENT_ID
143 + "' required on http header");
146 populateMDCAndResponseHeader(upperCaseHeaderMap, CLIENT_ID, "clientId",
147 Boolean.valueOf(MusicUtil.getClientIdRequired()), httpResponse);
152 logger.info(EELFLoggerDelegate.applicationLogger,
153 "In MusicLogginServletFilter doFilter. Header values validated sucessfully");
155 chain.doFilter(request, response);
158 private void populateError(HttpServletResponse httpResponse, String errMsg) throws IOException {
159 AuthorizationError authError = new AuthorizationError();
160 authError.setResponseCode(HttpServletResponse.SC_BAD_REQUEST);
161 authError.setResponseMessage(errMsg);
163 byte[] responseToSend = restResponseBytes(authError);
164 httpResponse.setHeader("Content-Type", "application/json");
166 // ideally the http response code should be 200, as this is a biz validation
167 // failure. For now, keeping it consistent with other places.
168 httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);
169 httpResponse.getOutputStream().write(responseToSend);
172 private void populateMDCAndResponseHeader(Map<String, String> headerMap, String idKey, String mdcKey,
173 boolean isRequired, HttpServletResponse httpResponse) throws ServletException, IOException {
175 idKey = idKey.trim().toUpperCase();
177 // 1. setting the keys & value in MDC for future use 2.setting the values in
178 // http response header back to client.
179 if (isRequired && (headerMap.containsKey(idKey))) {
180 EELFLoggerDelegate.mdcPut(mdcKey, headerMap.get(idKey));
181 httpResponse.addHeader(idKey, headerMap.get(idKey));
187 private Map<String, String> getHeadersInfo(HttpServletRequest request) {
189 Map<String, String> map = new HashMap<String, String>();
191 Enumeration<String> headerNames = request.getHeaderNames();
192 while (headerNames.hasMoreElements()) {
193 String key = (String) headerNames.nextElement();
194 String value = request.getHeader(key);
201 private byte[] restResponseBytes(AuthorizationError eErrorResponse) throws IOException {
202 String serialized = new ObjectMapper().writeValueAsString(eErrorResponse);
203 return serialized.getBytes();