Junit test case coverage for some classes in music-rest
[music.git] / music-rest / src / main / java / org / onap / music / eelf / logging / MusicLoggingServletFilter.java
1 /*
2  * ============LICENSE_START==========================================
3  * org.onap.music
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
12  * 
13  *     http://www.apache.org/licenses/LICENSE-2.0
14  * 
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.
20  * 
21  * ============LICENSE_END=============================================
22  * ====================================================================
23  */
24 package org.onap.music.eelf.logging;
25
26 import java.io.IOException;
27 import java.util.Enumeration;
28 import java.util.HashMap;
29 import java.util.Map;
30 import java.util.stream.Collectors;
31
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;
40
41 import org.onap.music.authentication.AuthorizationError;
42 import org.onap.music.main.MusicUtil;
43
44 import com.fasterxml.jackson.databind.ObjectMapper;
45
46 /**
47  * 
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
51  * 
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.
54  * 
55  * 
56  * @author sp931a
57  *
58  */
59
60 public class MusicLoggingServletFilter implements Filter {
61
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";
65
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";
69
70     // client id would be the unique client source-system-id, i;e VALET or CONDUCTOR
71     // etc
72     public static final String CLIENT_ID = MusicUtil.getClientIdPrefix() + "ClientId";
73
74     // unique transaction of the source system
75     private static final String TRANSACTION_ID = MusicUtil.getTransIdPrefix() + "Transaction-Id";
76
77     public MusicLoggingServletFilter() throws ServletException {
78         super();
79     }
80
81     @Override
82     public void init(FilterConfig filterConfig) throws ServletException {
83
84     }
85     
86     @Override
87     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
88             throws IOException, ServletException {
89
90         logger.info(EELFLoggerDelegate.securityLogger,
91                 "In MusicLogginServletFilter doFilter start() :: [\"+MusicUtil.getTransIdRequired()+\",\"+MusicUtil.getConversationIdRequired()+\",\"+MusicUtil.getClientIdRequired()+\",\"+MusicUtil.getMessageIdRequired()");
92
93         HttpServletRequest httpRequest = null;
94         HttpServletResponse httpResponse = null;
95         Map<String, String> headerMap = null;
96         Map<String, String> upperCaseHeaderMap = null;
97
98         if (null != request && null != response) {
99             httpRequest = (HttpServletRequest) request;
100             httpResponse = (HttpServletResponse) response;
101
102             headerMap = getHeadersInfo(httpRequest);
103
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
107             // upper case.
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
111
112             if (MusicUtil.getTransIdRequired()
113                     && !upperCaseHeaderMap.containsKey(TRANSACTION_ID.toUpperCase())) {
114                 populateError(httpResponse, "Transaction id '" + TRANSACTION_ID 
115                     + "' required on http header");
116                 return;
117             } else {
118                 populateMDCAndResponseHeader(upperCaseHeaderMap, TRANSACTION_ID, "transactionId",
119                     MusicUtil.getTransIdRequired(), httpResponse);
120             }
121
122             if (MusicUtil.getConversationIdRequired()
123                 && !upperCaseHeaderMap.containsKey(CONVERSATION_ID.toUpperCase())) {
124                 populateError(httpResponse, "Conversation Id '" + CONVERSATION_ID 
125                     + "' required on http header");
126                 return;
127             } else {
128                 populateMDCAndResponseHeader(upperCaseHeaderMap, CONVERSATION_ID, "conversationId",
129                     MusicUtil.getConversationIdRequired(), httpResponse);
130             }
131
132             if (MusicUtil.getMessageIdRequired()
133                 && !upperCaseHeaderMap.containsKey(MESSAGE_ID.toUpperCase())) {
134                 populateError(httpResponse, "Message Id '" + MESSAGE_ID 
135                     + "' required on http header");
136                 return;
137             } else {
138                 populateMDCAndResponseHeader(upperCaseHeaderMap, MESSAGE_ID, "messageId",
139                     MusicUtil.getMessageIdRequired(), httpResponse);
140             }
141
142             if (MusicUtil.getClientIdRequired()
143                 && !upperCaseHeaderMap.containsKey(CLIENT_ID.toUpperCase())) {
144                 populateError(httpResponse, "Client Id '" + CLIENT_ID 
145                     + "' required on http header");
146                 return;
147             } else {
148                 populateMDCAndResponseHeader(upperCaseHeaderMap, CLIENT_ID, "clientId",
149                     MusicUtil.getClientIdRequired(), httpResponse);
150             }
151
152         }
153
154         logger.info(EELFLoggerDelegate.securityLogger,
155                 "In MusicLogginServletFilter doFilter. Header values validated sucessfully");
156
157         chain.doFilter(request, response);
158     }
159
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);
164
165         byte[] responseToSend = restResponseBytes(authError);
166         httpResponse.setHeader("Content-Type", "application/json");
167
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);
172     }
173
174     private void populateMDCAndResponseHeader(Map<String, String> headerMap, String idKey, String mdcKey,
175             boolean isRequired, HttpServletResponse httpResponse) {
176
177         idKey = idKey.trim().toUpperCase();
178
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));
184         } else {
185             // do nothing
186         }
187     }
188
189     private Map<String, String> getHeadersInfo(HttpServletRequest request) {
190
191         Map<String, String> map = new HashMap<String, String>();
192
193         Enumeration<String> headerNames = request.getHeaderNames();
194         while (headerNames.hasMoreElements()) {
195             String key = (String) headerNames.nextElement();
196             String value = request.getHeader(key);
197             map.put(key, value);
198         }
199
200         return map;
201     }
202
203     private byte[] restResponseBytes(AuthorizationError eErrorResponse) throws IOException {
204         String serialized = new ObjectMapper().writeValueAsString(eErrorResponse);
205         return serialized.getBytes();
206     }
207 }