[DMAAP-DR] Remove AAF/TLS phase 1
[dmaap/datarouter.git] / datarouter-node / src / test / java / org / onap / dmaap / datarouter / node / NodeServletTest.java
1 /*******************************************************************************
2  * ============LICENSE_START==================================================
3  * * org.onap.dmaap
4  * * ===========================================================================
5  * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
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
10  * *
11  *  *      http://www.apache.org/licenses/LICENSE-2.0
12  * *
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.
18  * * ============LICENSE_END====================================================
19  * *
20  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
21  * *
22  ******************************************************************************/
23 package org.onap.dmaap.datarouter.node;
24
25 import static org.junit.Assert.assertEquals;
26 import static org.mockito.ArgumentMatchers.any;
27 import static org.mockito.ArgumentMatchers.anyInt;
28 import static org.mockito.ArgumentMatchers.anyObject;
29 import static org.mockito.ArgumentMatchers.eq;
30 import static org.mockito.Mockito.anyString;
31 import static org.mockito.Mockito.doNothing;
32 import static org.mockito.Mockito.mock;
33 import static org.mockito.Mockito.verify;
34 import static org.mockito.Mockito.when;
35
36 import ch.qos.logback.classic.Logger;
37 import ch.qos.logback.classic.spi.ILoggingEvent;
38 import ch.qos.logback.core.read.ListAppender;
39 import java.io.File;
40 import java.io.IOException;
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.Collections;
44 import java.util.Enumeration;
45 import java.util.List;
46 import jakarta.servlet.http.HttpServletRequest;
47 import jakarta.servlet.http.HttpServletResponse;
48 import org.apache.commons.lang3.reflect.FieldUtils;
49 import org.junit.AfterClass;
50 import org.junit.Before;
51 import org.junit.Test;
52 import org.junit.runner.RunWith;
53 import org.mockito.BDDMockito;
54 import org.mockito.Mock;
55 import org.mockito.Mockito;
56 import org.mockito.internal.matchers.Any;
57 import org.powermock.api.mockito.PowerMockito;
58 import org.powermock.core.classloader.annotations.PowerMockIgnore;
59 import org.powermock.core.classloader.annotations.PrepareForTest;
60 import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor;
61 import org.powermock.modules.junit4.PowerMockRunner;
62 import org.slf4j.LoggerFactory;
63
64 @RunWith(PowerMockRunner.class)
65 @SuppressStaticInitializationFor("org.onap.dmaap.datarouter.node.NodeConfigManager")
66 @PrepareForTest(NodeServer.class)
67 @PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.w3c.*"})
68 public class NodeServletTest {
69
70     private NodeServlet nodeServlet;
71     private Delivery delivery;
72
73     @Mock
74     private HttpServletRequest request;
75
76     @Mock
77     private HttpServletResponse response;
78
79     private ListAppender<ILoggingEvent> listAppender;
80
81     private NodeConfigManager config = mock(NodeConfigManager.class);
82
83     @Before
84     public void setUp() throws Exception {
85         listAppender = setTestLogger();
86         setBehalfHeader("Stub_Value");
87         when(request.getPathInfo()).thenReturn("2");
88         when(request.isSecure()).thenReturn(true);
89         createFilesAndDirectories();
90         setUpConfig();
91         setUpNodeMainDelivery();
92         delivery = mock(Delivery.class);
93         when(delivery.markTaskSuccess("spool/s/0/1", "dmaap-dr-node.1234567")).thenReturn(true);
94         PowerMockito.mockStatic(NodeServer.class);
95         nodeServlet = new NodeServlet(delivery, config);
96         when(request.getHeader("Authorization")).thenReturn("User1");
97         when(request.getHeader("X-DMAAP-DR-PUBLISH-ID")).thenReturn("User1");
98     }
99
100     @AfterClass
101     public static void tearDown() {
102         deleteCreatedDirectories();
103     }
104
105     @Test
106     public void Given_Request_Is_HTTP_GET_And_Config_Is_Down_Then_Service_Unavailable_Response_Is_Generated() throws Exception {
107         setNodeConfigManagerIsConfiguredToReturnFalse();
108         nodeServlet.doGet(request, response);
109         verify(response).sendError(eq(HttpServletResponse.SC_SERVICE_UNAVAILABLE));
110         verifyEnteringExitCalled(listAppender);
111     }
112
113     @Test
114     public void Given_Request_Is_HTTP_GET_And_Endpoint_Is_Internal_FetchProv_Then_No_Content_Response_Is_Generated() {
115         when(request.getPathInfo()).thenReturn("/internal/fetchProv");
116         nodeServlet.doGet(request, response);
117         verify(response).setStatus(eq(HttpServletResponse.SC_NO_CONTENT));
118         verifyEnteringExitCalled(listAppender);
119     }
120
121     @Test
122     public void Given_Request_Is_HTTP_GET_And_Endpoint_Is_ResetSubscription_Then_No_Content_Response_Is_Generated() {
123         when(request.getPathInfo()).thenReturn("/internal/resetSubscription/1");
124         nodeServlet.doGet(request, response);
125         verify(response).setStatus(eq(HttpServletResponse.SC_NO_CONTENT));
126         verifyEnteringExitCalled(listAppender);
127     }
128
129     @Test
130     public void Given_Request_Is_HTTP_GET_To_Invalid_Endpoint_Then_Not_Found_Response_Is_Generated() throws Exception {
131         when(request.getPathInfo()).thenReturn("/incorrect");
132         nodeServlet.doGet(request, response);
133         verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND));
134         verifyEnteringExitCalled(listAppender);
135     }
136
137     @Test
138     public void Given_Request_Is_HTTP_PUT_And_Config_Is_Down_Then_Service_Unavailable_Response_Is_Generated() throws Exception {
139         setNodeConfigManagerIsConfiguredToReturnFalse();
140         nodeServlet.doPut(request, response);
141         verify(response).sendError(eq(HttpServletResponse.SC_SERVICE_UNAVAILABLE));
142         verifyEnteringExitCalled(listAppender);
143     }
144
145     @Test
146     public void Given_Request_Is_HTTP_PUT_And_Endpoint_Is_Incorrect_Then_Not_Found_Response_Is_Generated() throws Exception {
147         when(request.getPathInfo()).thenReturn("/incorrect/");
148         nodeServlet.doPut(request, response);
149         verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
150         verifyEnteringExitCalled(listAppender);
151     }
152
153     @Test
154     public void Given_Request_Is_HTTP_PUT_And_Request_Is_Not_Secure_And_TLS_Enabled_Then_Forbidden_Response_Is_Generated() throws Exception {
155         when(request.isSecure()).thenReturn(false);
156         when(config.isTlsEnabled()).thenReturn(true);
157         nodeServlet.doPut(request, response);
158         verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), anyString());
159         verifyEnteringExitCalled(listAppender);
160     }
161
162     @Test
163     public void Given_Request_Is_HTTP_PUT_And_File_Id_Is_Null_Then_Not_Found_Response_Is_Generated() throws Exception {
164         when(request.getPathInfo()).thenReturn(null);
165         nodeServlet.doPut(request, response);
166         verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
167         verifyEnteringExitCalled(listAppender);
168     }
169
170     @Test
171     public void Given_Request_Is_HTTP_PUT_And_Authorization_Is_Null_Then_Forbidden_Response_Is_Generated() throws Exception {
172         when(request.getHeader("Authorization")).thenReturn(null);
173         nodeServlet.doPut(request, response);
174         verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), anyString());
175         verifyEnteringExitCalled(listAppender);
176     }
177
178     @Test
179     public void Given_Request_Is_HTTP_PUT_And_Publish_Does_Not_Include_File_Id_Then_Not_Found_Response_Is_Generated() throws Exception {
180         when(request.getPathInfo()).thenReturn("/publish/");
181         nodeServlet.doPut(request, response);
182         verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
183         verifyEnteringExitCalled(listAppender);
184     }
185
186     @Test
187     public void Given_Request_Is_HTTP_PUT_And_Publish_Not_Permitted_Then_Forbidden_Response_Is_Generated() throws Exception {
188         when(request.getPathInfo()).thenReturn("/publish/1/fileName");
189         when(request.getRemoteAddr()).thenReturn("1.2.3.4");
190         setNodeConfigManagerIsPublishPermittedToReturnAReason();
191         nodeServlet.doPut(request, response);
192         verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), anyString());
193         verifyEnteringExitCalled(listAppender);
194     }
195
196     @Test
197     public void Given_Request_Is_HTTP_PUT_And_Internal_Publish_On_Same_Node_Then_Forbidden_Response_Is_Generated() throws Exception {
198         when(request.getPathInfo()).thenReturn("/internal/publish/1/fileName");
199         setNodeConfigManagerIsPublishPermittedToReturnAReason();
200         nodeServlet.doPut(request, response);
201         verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN));
202         verifyEnteringExitCalled(listAppender);
203     }
204
205     @Test
206     public void Given_Request_Is_HTTP_PUT_And_Internal_Publish_But_Invalid_File_Id_Then_Not_Found_Response_Is_Generated() throws Exception {
207         when(request.getPathInfo()).thenReturn("/internal/publish/1/blah");
208         when(request.getRemoteAddr()).thenReturn("1.2.3.4");
209         when(config.isAnotherNode(anyString(), anyString())).thenReturn(true);
210         nodeServlet.doPut(request, response);
211         verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
212         verifyEnteringExitCalled(listAppender);
213     }
214
215     @Test
216     public void Given_Request_Is_HTTP_PUT_On_Publish_And_Ingress_Node_Is_Provided_Then_Request_Is_Redirected() throws Exception {
217         setNodeConfigManagerToAllowRedirectOnIngressNode();
218         when(request.getPathInfo()).thenReturn("/publish/1/fileName");
219         when(request.getRemoteAddr()).thenReturn("1.2.3.4");
220         nodeServlet.doPut(request, response);
221         verify(response).sendRedirect(anyString());
222         verifyEnteringExitCalled(listAppender);
223     }
224
225     @Test
226     public void Given_Request_Is_HTTP_PUT_On_Publish_With_Meta_Data_Too_Long_Then_Bad_Request_Response_Is_Generated() throws Exception {
227         when(request.getPathInfo()).thenReturn("/publish/1/fileName");
228         setHeadersForValidRequest(true);
229         nodeServlet.doPut(request, response);
230         verify(response).sendError(eq(HttpServletResponse.SC_BAD_REQUEST), anyString());
231     }
232
233     @Test
234     public void Given_Request_Is_HTTP_PUT_On_Publish_With_Meta_Data_Malformed_Then_Bad_Request_Response_Is_Generated() throws Exception {
235         when(request.getPathInfo()).thenReturn("/publish/1/fileName");
236         setHeadersForValidRequest(false);
237         nodeServlet.doPut(request, response);
238         verify(response).sendError(eq(HttpServletResponse.SC_BAD_REQUEST), anyString());
239     }
240
241     @Test
242     public void Given_Request_Is_HTTP_PUT_On_Publish_On_AAF_Feed_And_Cadi_Enabled_And_No_Permissions_Then_Forbidden_Response_Is_Generated() throws Exception {
243         when(config.getCadiEnabled()).thenReturn(true);
244         when(config.getAafInstance("1")).thenReturn("*");
245         when(request.getPathInfo()).thenReturn("/publish/1/fileName");
246         setHeadersForValidRequest(true);
247         nodeServlet.doPut(request, response);
248         verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), anyString());
249         verifyEnteringExitCalled(listAppender);
250     }
251
252     @Test
253     public void Given_Request_Is_HTTP_DELETE_On_Publish_With_Meta_Data_Malformed_Then_Bad_Request_Response_Is_Generated() throws Exception {
254         when(request.getPathInfo()).thenReturn("/publish/1/fileName");
255         setHeadersForValidRequest(false);
256         nodeServlet.doDelete(request, response);
257         verify(response).sendError(eq(HttpServletResponse.SC_BAD_REQUEST), anyString());
258     }
259
260     @Test
261     public void Given_Request_Is_HTTP_DELETE_File_With_Invalid_Endpoint_Then_Not_Found_Response_Is_Generated() throws Exception {
262         when(request.getPathInfo()).thenReturn("/delete/1");
263         nodeServlet.doDelete(request, response);
264         verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
265         verifyEnteringExitCalled(listAppender);
266     }
267
268     @Test
269     public void Given_Request_Is_HTTP_DELETE_File_And_Is_Not_Privileged_Subscription_Then_Not_Found_Response_Is_Generated() throws Exception {
270         when(request.getPathInfo()).thenReturn("/delete/1/dmaap-dr-node.1234567");
271         setUpConfigToReturnUnprivilegedSubscriber();
272         nodeServlet.doDelete(request, response);
273         verify(response).sendError(eq(HttpServletResponse.SC_UNAUTHORIZED));
274         verifyEnteringExitCalled(listAppender);
275     }
276
277     @Test
278     public void Given_Request_Is_HTTP_DELETE_File_And_Subscription_Does_Not_Exist_Then_Not_Found_Response_Is_Generated() throws Exception {
279         when(request.getPathInfo()).thenReturn("/delete/1/dmaap-dr-node.1234567");
280         setUpConfigToReturnNullOnIsDeletePermitted();
281         nodeServlet.doDelete(request, response);
282         verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND));
283         verifyEnteringExitCalled(listAppender);
284     }
285
286     @Test
287     public void Given_Request_Is_HTTP_DELETE_File_Then_Request_Succeeds() throws Exception {
288         when(request.getPathInfo()).thenReturn("/delete/1/dmaap-dr-node.1234567");
289         createFilesAndDirectories();
290         nodeServlet.doDelete(request, response);
291         verify(response).setStatus(eq(HttpServletResponse.SC_OK));
292         verifyEnteringExitCalled(listAppender);
293     }
294
295     @Test
296     public void Given_Request_Is_HTTP_DELETE_File_And_Request_Is_Not_Secure_But_TLS_Disabled_Then_Request_Succeeds() throws Exception {
297         when(request.isSecure()).thenReturn(false);
298         when(config.isTlsEnabled()).thenReturn(false);
299         when(request.getPathInfo()).thenReturn("/delete/1/dmaap-dr-node.1234567");
300         createFilesAndDirectories();
301         nodeServlet.doDelete(request, response);
302         verify(response).setStatus(eq(HttpServletResponse.SC_OK));
303         verifyEnteringExitCalled(listAppender);
304     }
305
306     @Test
307     public void Given_Request_Is_HTTP_DELETE_File_And_File_Does_Not_Exist_Then_Not_Found_Response_Is_Generated() throws IOException {
308         when(request.getPathInfo()).thenReturn("/delete/1/nonExistingFile");
309         nodeServlet.doDelete(request, response);
310         verify(response).sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString());
311         verifyEnteringExitCalled(listAppender);
312     }
313
314     private void setBehalfHeader(String headerValue) {
315         when(request.getHeader("X-DMAAP-DR-ON-BEHALF-OF")).thenReturn(headerValue);
316     }
317
318     private ListAppender<ILoggingEvent> setTestLogger() {
319         Logger Logger = (Logger) LoggerFactory.getLogger(NodeServlet.class);
320         ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
321         listAppender.start();
322         Logger.addAppender(listAppender);
323         return listAppender;
324     }
325
326     private void verifyEnteringExitCalled(ListAppender<ILoggingEvent> listAppender) {
327         assertEquals("EELF0004I  Entering data router node component with RequestId and InvocationId", listAppender.list.get(0).getMessage());
328         assertEquals("EELF0005I  Exiting data router node component with RequestId and InvocationId", listAppender.list.get(listAppender.list.size() -1).getMessage());
329     }
330
331     private void setUpConfig() throws IllegalAccessException {
332         PowerMockito.mockStatic(NodeConfigManager.class);
333         when(config.isShutdown()).thenReturn(false);
334         when(config.isConfigured()).thenReturn(true);
335         when(config.getSpoolDir()).thenReturn("spool/f");
336         when(config.getSpoolBase()).thenReturn("spool");
337         when(config.getLogDir()).thenReturn("log/dir");
338         when(config.getPublishId()).thenReturn("User1");
339         when(config.isAnotherNode(anyString(), anyString())).thenReturn(false);
340         when(config.getEventLogInterval()).thenReturn("40");
341         when(config.isDeletePermitted("1")).thenReturn(true);
342         when(config.getAllDests()).thenReturn(new DestInfo[0]);
343         FieldUtils.writeDeclaredStaticField(NodeConfigManager.class, "base", config, true);
344     }
345
346     private void setUpConfigToReturnUnprivilegedSubscriber() throws IllegalAccessException {
347         PowerMockito.mockStatic(NodeConfigManager.class);
348         when(config.isShutdown()).thenReturn(false);
349         when(config.isConfigured()).thenReturn(true);
350         when(config.isDeletePermitted("1")).thenReturn(false);
351         FieldUtils.writeDeclaredStaticField(NodeConfigManager.class, "base", config, true);
352     }
353
354     private void setUpConfigToReturnNullOnIsDeletePermitted() throws IllegalAccessException {
355         PowerMockito.mockStatic(NodeConfigManager.class);
356         when(config.isShutdown()).thenReturn(false);
357         when(config.isConfigured()).thenReturn(true);
358         when(config.isDeletePermitted("1")).thenThrow(new NullPointerException());
359         FieldUtils.writeDeclaredStaticField(NodeConfigManager.class, "base", config, true);
360     }
361
362     private void setUpNodeMainDelivery() throws IllegalAccessException{
363         Delivery delivery = mock(Delivery.class);
364         doNothing().when(delivery).resetQueue(anyObject());
365         FieldUtils.writeDeclaredStaticField(NodeServer.class, "delivery", delivery, true);
366     }
367
368     private void setNodeConfigManagerIsConfiguredToReturnFalse() throws IllegalAccessException {
369         when(config.isConfigured()).thenReturn(false);
370         FieldUtils.writeDeclaredStaticField(NodeConfigManager.class, "base", config, true);
371     }
372
373     private void setNodeConfigManagerIsPublishPermittedToReturnAReason() throws IllegalAccessException{
374         when(config.isShutdown()).thenReturn(false);
375         when(config.getMyName()).thenReturn("dmaap-dr-node");
376         when(config.isConfigured()).thenReturn(true);
377         when(config.getSpoolDir()).thenReturn("spool/dir");
378         when(config.getLogDir()).thenReturn("log/dir");
379         when(config.isPublishPermitted(anyString(), anyString(), anyString())).thenReturn("Publisher not permitted for this feed");
380         when(config.isAnotherNode(anyString(), anyString())).thenReturn(false);
381         FieldUtils.writeDeclaredStaticField(NodeConfigManager.class, "base", config, true);
382     }
383
384     private void setNodeConfigManagerToAllowRedirectOnIngressNode() {
385         when(config.isShutdown()).thenReturn(false);
386         when(config.isConfigured()).thenReturn(true);
387         when(config.getSpoolDir()).thenReturn("spool/dir");
388         when(config.getLogDir()).thenReturn("log/dir");
389         when(config.getPublishId()).thenReturn("User1");
390         when(config.isAnotherNode(anyString(), anyString())).thenReturn(true);
391         when(config.getAuthUser(anyString(), anyString())).thenReturn("User1");
392         when(config.getIngressNode(anyString(), anyString(), anyString())).thenReturn("NewNode");
393         when(config.getExtHttpsPort()).thenReturn(8080);
394     }
395
396     private String createLargeMetaDataString() {
397         StringBuilder myString = new StringBuilder("meta");
398         for (int i = 0; i <= 4098; ++i) {
399             myString.append('x');
400         }
401         return myString.toString();
402     }
403
404     private void setHeadersForValidRequest(boolean isMetaTooLong) {
405         String metaDataString;
406         if (isMetaTooLong) {
407             metaDataString = createLargeMetaDataString();
408         } else {
409             metaDataString = "?#@><";
410         }
411         List<String> headers = new ArrayList<>();
412         headers.add("Content-Type");
413         headers.add("X-DMAAP-DR-ON-BEHALF-OF");
414         headers.add("X-DMAAP-DR-META");
415         Enumeration<String> headerNames = Collections.enumeration(headers);
416         when(request.getHeaderNames()).thenReturn(headerNames);
417         Enumeration<String> contentTypeHeader = Collections.enumeration(Arrays.asList("text/plain"));
418         Enumeration<String> behalfHeader = Collections.enumeration(Arrays.asList("User1"));
419         Enumeration<String> metaDataHeader = Collections.enumeration(Arrays.asList(metaDataString));
420         when(request.getHeaders("Content-Type")).thenReturn(contentTypeHeader);
421         when(request.getHeaders("X-DMAAP-DR-ON-BEHALF-OF")).thenReturn(behalfHeader);
422         when(request.getHeaders("X-DMAAP-DR-META")).thenReturn(metaDataHeader);
423     }
424
425     private void createFilesAndDirectories() throws IOException {
426         File nodeDir = new File("spool/n/172.0.0.1");
427         File spoolDir = new File("spool/s/0/1");
428         File dataFile = new File("spool/s/0/1/dmaap-dr-node.1234567");
429         File metaDataFile = new File("spool/s/0/1/dmaap-dr-node.1234567.M");
430         nodeDir.mkdirs();
431         spoolDir.mkdirs();
432         dataFile.createNewFile();
433         metaDataFile.createNewFile();
434     }
435
436     private static void deleteCreatedDirectories() {
437         File spoolDir = new File("spool");
438         delete(spoolDir);
439     }
440
441     private static void delete(File file) {
442         if (file.isDirectory()) {
443             for (File f: file.listFiles()) {
444                 delete(f);
445             }
446         }
447         if (!file.delete()) {
448             System.out.println("Failed to delete: " + file);
449         }
450     }
451 }