2 * ============LICENSE_START==========================================
4 * ===================================================================
5 * Copyright (c) 2017 AT&T Intellectual Property
6 * ===================================================================
7 * Modifications Copyright (C) 2019 IBM
8 * ===================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
21 * ============LICENSE_END=============================================
22 * ====================================================================
24 package org.onap.music.eelf.logging;
26 import java.io.IOException;
27 import java.util.Enumeration;
28 import java.util.HashMap;
30 import java.util.stream.Collectors;
32 import javax.servlet.Filter;
33 import javax.servlet.FilterChain;
34 import javax.servlet.FilterConfig;
35 import javax.servlet.ServletException;
36 import javax.servlet.ServletRequest;
37 import javax.servlet.ServletResponse;
38 import javax.servlet.http.HttpServletRequest;
39 import javax.servlet.http.HttpServletResponse;
41 import org.onap.music.authentication.AuthorizationError;
42 import org.onap.music.main.MusicUtil;
44 import com.fasterxml.jackson.databind.ObjectMapper;
48 * This is the first filter in the chain to be executed before cadi
49 * authentication. The priority has been set in <code>MusicApplication</code>
50 * through filter registration bean
52 * The responsibility of this filter is to validate header values as per
53 * contract and write it to MDC and http response header back.
60 public class MusicLoggingServletFilter implements Filter {
62 private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MusicLoggingServletFilter.class);
63 // client transaction id, specific to client system, set in properties
64 public static final String CONVERSATION_ID = MusicUtil.getConversationIdPrefix() + "ConversationId";
66 // can be used as correlation-id in case of callback, also this can be passed to
67 // other services for tracking.
68 public static final String MESSAGE_ID = MusicUtil.getMessageIdPrefix() + "MessageId";
70 // client id would be the unique client source-system-id, i;e VALET or CONDUCTOR
72 public static final String CLIENT_ID = MusicUtil.getClientIdPrefix() + "ClientId";
74 // unique transaction of the source system
75 private static final String TRANSACTION_ID = MusicUtil.getTransIdPrefix() + "Transaction-Id";
77 public MusicLoggingServletFilter() throws ServletException {
82 public void init(FilterConfig filterConfig) throws ServletException {
87 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
88 throws IOException, ServletException {
90 logger.info(EELFLoggerDelegate.securityLogger,
91 "In MusicLogginServletFilter doFilter start() :: [\"+MusicUtil.getTransIdRequired()+\",\"+MusicUtil.getConversationIdRequired()+\",\"+MusicUtil.getClientIdRequired()+\",\"+MusicUtil.getMessageIdRequired()");
93 HttpServletRequest httpRequest = null;
94 HttpServletResponse httpResponse = null;
95 Map<String, String> headerMap = null;
96 Map<String, String> upperCaseHeaderMap = null;
98 if (null != request && null != response) {
99 httpRequest = (HttpServletRequest) request;
100 httpResponse = (HttpServletResponse) response;
102 headerMap = getHeadersInfo(httpRequest);
104 // The custom header values automatically converted into lower case, not sure
105 // why ? So i had to covert all keys to upper case
106 // The response header back to client will have all custom header values as
108 upperCaseHeaderMap = headerMap.entrySet().stream()
109 .collect(Collectors.toMap(entry -> entry.getKey().toUpperCase(), entry -> entry.getValue()));
110 // Enable/disable keys are present in /opt/app/music/etc/music.properties
112 if (MusicUtil.getTransIdRequired()
113 && !upperCaseHeaderMap.containsKey(TRANSACTION_ID.toUpperCase())) {
114 populateError(httpResponse, "Transaction id '" + TRANSACTION_ID
115 + "' required on http header");
118 populateMDCAndResponseHeader(upperCaseHeaderMap, TRANSACTION_ID, "transactionId",
119 MusicUtil.getTransIdRequired(), httpResponse);
122 if (MusicUtil.getConversationIdRequired()
123 && !upperCaseHeaderMap.containsKey(CONVERSATION_ID.toUpperCase())) {
124 populateError(httpResponse, "Conversation Id '" + CONVERSATION_ID
125 + "' required on http header");
128 populateMDCAndResponseHeader(upperCaseHeaderMap, CONVERSATION_ID, "conversationId",
129 MusicUtil.getConversationIdRequired(), httpResponse);
132 if (MusicUtil.getMessageIdRequired()
133 && !upperCaseHeaderMap.containsKey(MESSAGE_ID.toUpperCase())) {
134 populateError(httpResponse, "Message Id '" + MESSAGE_ID
135 + "' required on http header");
138 populateMDCAndResponseHeader(upperCaseHeaderMap, MESSAGE_ID, "messageId",
139 MusicUtil.getMessageIdRequired(), httpResponse);
142 if (MusicUtil.getClientIdRequired()
143 && !upperCaseHeaderMap.containsKey(CLIENT_ID.toUpperCase())) {
144 populateError(httpResponse, "Client Id '" + CLIENT_ID
145 + "' required on http header");
148 populateMDCAndResponseHeader(upperCaseHeaderMap, CLIENT_ID, "clientId",
149 MusicUtil.getClientIdRequired(), httpResponse);
154 logger.info(EELFLoggerDelegate.securityLogger,
155 "In MusicLogginServletFilter doFilter. Header values validated sucessfully");
157 chain.doFilter(request, response);
160 private void populateError(HttpServletResponse httpResponse, String errMsg) throws IOException {
161 AuthorizationError authError = new AuthorizationError();
162 authError.setResponseCode(HttpServletResponse.SC_BAD_REQUEST);
163 authError.setResponseMessage(errMsg);
165 byte[] responseToSend = restResponseBytes(authError);
166 httpResponse.setHeader("Content-Type", "application/json");
168 // ideally the http response code should be 200, as this is a biz validation
169 // failure. For now, keeping it consistent with other places.
170 httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);
171 httpResponse.getOutputStream().write(responseToSend);
174 private void populateMDCAndResponseHeader(Map<String, String> headerMap, String idKey, String mdcKey,
175 boolean isRequired, HttpServletResponse httpResponse) throws IOException {
177 idKey = idKey.trim().toUpperCase();
179 // 1. setting the keys & value in MDC for future use 2.setting the values in
180 // http response header back to client.
181 if (isRequired && (headerMap.containsKey(idKey))) {
182 EELFLoggerDelegate.mdcPut(mdcKey, headerMap.get(idKey));
183 httpResponse.addHeader(idKey, headerMap.get(idKey));
189 private Map<String, String> getHeadersInfo(HttpServletRequest request) {
191 Map<String, String> map = new HashMap<String, String>();
193 Enumeration<String> headerNames = request.getHeaderNames();
194 while (headerNames.hasMoreElements()) {
195 String key = (String) headerNames.nextElement();
196 String value = request.getHeader(key);
203 private byte[] restResponseBytes(AuthorizationError eErrorResponse) throws IOException {
204 String serialized = new ObjectMapper().writeValueAsString(eErrorResponse);
205 return serialized.getBytes();