diff --git a/plugin-nifi/pom.xml b/plugin-nifi/pom.xml index 43bb1980115..d1751ad70d6 100644 --- a/plugin-nifi/pom.xml +++ b/plugin-nifi/pom.xml @@ -170,5 +170,11 @@ ${mockito.version} test + + org.mockito + mockito-junit-jupiter + ${mockito.version} + test + diff --git a/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/TestRangerServiceNiFi.java b/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/TestRangerServiceNiFi.java new file mode 100644 index 00000000000..b770a7db11a --- /dev/null +++ b/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/TestRangerServiceNiFi.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.ranger.services.nifi; + +import org.apache.ranger.plugin.service.ResourceLookupContext; +import org.apache.ranger.services.nifi.client.NiFiClient; +import org.apache.ranger.services.nifi.client.NiFiConfigs; +import org.apache.ranger.services.nifi.client.NiFiConnectionMgr; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; + +/** + * @generated by Cursor + * @description : Unit Test cases for RangerServiceNiFi + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +class TestRangerServiceNiFi { + @Test + void validateConfig_throwsWhenConfigsNull() { + RangerServiceNiFi service = new RangerServiceNiFi(); + service.setConfigs(null); + + assertThrows(IllegalStateException.class, service::validateConfig); + } + + @Test + void validateConfig_propagatesWhenConnectionTestThrows() { + RangerServiceNiFi service = new RangerServiceNiFi(); + service.setServiceName("nifi-svc"); + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "http://localhost:8080/nifi-api/resources"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, "NONE"); + service.setConfigs(configs); + + try (MockedStatic mgr = Mockito.mockStatic(NiFiConnectionMgr.class)) { + mgr.when(() -> NiFiConnectionMgr.connectionTest(anyString(), any())) + .thenThrow(new RuntimeException("connection test failure")); + + assertThrows(RuntimeException.class, service::validateConfig); + } + } + + @Test + void lookupResource_returnsResourcesFromClient() throws Exception { + RangerServiceNiFi service = new RangerServiceNiFi(); + service.setServiceName("nifi-svc"); + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "http://localhost:8080/nifi-api/resources"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, "NONE"); + service.setConfigs(configs); + + ResourceLookupContext context = Mockito.mock(ResourceLookupContext.class); + NiFiClient client = Mockito.mock(NiFiClient.class); + Mockito.when(client.getResources(context)).thenReturn(List.of("/flow", "/system")); + + try (MockedStatic mgr = Mockito.mockStatic(NiFiConnectionMgr.class)) { + mgr.when(() -> NiFiConnectionMgr.getNiFiClient("nifi-svc", configs)).thenReturn(client); + + List resources = service.lookupResource(context); + + assertEquals(2, resources.size()); + assertTrue(resources.contains("/flow")); + } + } + + @Test + void lookupResource_propagatesExceptionFromClient() throws Exception { + RangerServiceNiFi service = new RangerServiceNiFi(); + service.setServiceName("nifi-svc"); + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "http://localhost:8080/nifi-api/resources"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, "NONE"); + service.setConfigs(configs); + + ResourceLookupContext context = Mockito.mock(ResourceLookupContext.class); + NiFiClient client = Mockito.mock(NiFiClient.class); + Mockito.when(client.getResources(context)).thenThrow(new Exception("lookup failed")); + + try (MockedStatic mgr = Mockito.mockStatic(NiFiConnectionMgr.class)) { + mgr.when(() -> NiFiConnectionMgr.getNiFiClient("nifi-svc", configs)).thenReturn(client); + + Exception ex = assertThrows(Exception.class, () -> service.lookupResource(context)); + assertTrue(ex.getMessage().contains("lookup failed")); + } + } + + @Test + void validateConfig_successPathWhenConnectionTestReturnsSuccessMap() { + RangerServiceNiFi service = new RangerServiceNiFi(); + service.setServiceName("nifi-svc"); + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "http://localhost:8080/nifi-api/resources"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, "NONE"); + service.setConfigs(configs); + + HashMap expected = new HashMap<>(); + expected.put("connectivityStatus", true); + expected.put("message", "ok"); + + try (MockedStatic mgr = Mockito.mockStatic(NiFiConnectionMgr.class)) { + mgr.when(() -> NiFiConnectionMgr.connectionTest(anyString(), any())).thenReturn(expected); + + Map result = service.validateConfig(); + + assertTrue((Boolean) result.get("connectivityStatus")); + assertEquals("ok", result.get("message")); + } + } + + @Test + void validateConfig_returnsFailureMapWhenConnectionTestReturnsFailureWithoutThrowing() { + RangerServiceNiFi service = new RangerServiceNiFi(); + service.setServiceName("nifi-svc"); + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "http://localhost:8080/nifi-api/resources"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, "NONE"); + service.setConfigs(configs); + + HashMap expected = new HashMap<>(); + expected.put("connectivityStatus", false); + expected.put("message", "Error creating NiFi client"); + + try (MockedStatic mgr = Mockito.mockStatic(NiFiConnectionMgr.class)) { + mgr.when(() -> NiFiConnectionMgr.connectionTest(anyString(), any())).thenReturn(expected); + + Map result = service.validateConfig(); + + assertFalse((Boolean) result.get("connectivityStatus")); + assertEquals("Error creating NiFi client", result.get("message")); + } + } +} diff --git a/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiAuthType.java b/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiAuthType.java new file mode 100644 index 00000000000..7edae2ad288 --- /dev/null +++ b/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiAuthType.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.ranger.services.nifi.client; + +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * @generated by Cursor + * @description : Unit Test cases for NiFiAuthType + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +class TestNiFiAuthType { + @Test + void valueOfReturnsConstantForValidName() { + assertEquals(NiFiAuthType.NONE, NiFiAuthType.valueOf("NONE")); + assertEquals(NiFiAuthType.SSL, NiFiAuthType.valueOf("SSL")); + } + + @Test + void valueOfThrowsForInvalidName() { + assertThrows(IllegalArgumentException.class, () -> NiFiAuthType.valueOf("UNKNOWN")); + } +} diff --git a/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiClient.java b/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiClient.java index 589ea29ef67..015bee424b1 100644 --- a/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiClient.java +++ b/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiClient.java @@ -20,11 +20,17 @@ import org.apache.ranger.plugin.service.ResourceLookupContext; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.stubbing.OngoingStubbing; +import javax.net.ssl.SSLContext; +import javax.ws.rs.ProcessingException; +import javax.ws.rs.WebApplicationException; import javax.ws.rs.client.Client; import javax.ws.rs.client.Invocation; import javax.ws.rs.client.WebTarget; @@ -42,7 +48,13 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; -public class TestNiFiClient { +/** + * @generated by Cursor + * @description : Unit Test cases for NiFiClient + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +class TestNiFiClient { private static final String RESOURCES_RESPONSE = "{\n" + " \"revision\": {\n" + " \"clientId\": \"0daac173-025c-4aa7-b644-97f7b10435d2\"\n" + @@ -77,18 +89,18 @@ public class TestNiFiClient { private NiFiClient niFiClient; - @BeforeEach - public void setup() { - niFiClient = new NiFiClient("http://localhost:8080/nifi-api/resources", null) { + private static NiFiClient newNiFiClientWithMockResourcesResponse(boolean stubReadEntityForOkStatus) { + return new NiFiClient("http://localhost:8080/nifi-api/resources", null) { @Override protected Client buildClient() { - return new MockNiFiClient(RESOURCES_RESPONSE, 200).buildClient(); + return new MockNiFiClient(RESOURCES_RESPONSE, 200, stubReadEntityForOkStatus).buildClient(); } }; } @Test - public void testGetResourcesNoUserInput() throws Exception { + void testGetResourcesNoUserInput() throws Exception { + niFiClient = newNiFiClientWithMockResourcesResponse(true); ResourceLookupContext resourceLookupContext = Mockito.mock(ResourceLookupContext.class); when(resourceLookupContext.getUserInput()).thenReturn(""); @@ -109,7 +121,8 @@ public void testGetResourcesNoUserInput() throws Exception { } @Test - public void testGetResourcesWithUserInputBeginning() throws Exception { + void testGetResourcesWithUserInputBeginning() throws Exception { + niFiClient = newNiFiClientWithMockResourcesResponse(true); ResourceLookupContext resourceLookupContext = Mockito.mock(ResourceLookupContext.class); when(resourceLookupContext.getUserInput()).thenReturn("/pr"); @@ -126,7 +139,8 @@ public void testGetResourcesWithUserInputBeginning() throws Exception { } @Test - public void testGetResourcesWithUserInputAnywhere() throws Exception { + void testGetResourcesWithUserInputAnywhere() throws Exception { + niFiClient = newNiFiClientWithMockResourcesResponse(true); ResourceLookupContext resourceLookupContext = Mockito.mock(ResourceLookupContext.class); when(resourceLookupContext.getUserInput()).thenReturn("trol"); @@ -142,7 +156,7 @@ public void testGetResourcesWithUserInputAnywhere() throws Exception { } @Test - public void testGetResourcesErrorResponse() throws NoSuchAlgorithmException, KeyManagementException { + void testGetResourcesErrorResponse() throws NoSuchAlgorithmException, KeyManagementException { final String errorMsg = "unknown error"; niFiClient = new NiFiClient("http://localhost:8080/nifi-api/resources", null) { @@ -153,7 +167,6 @@ protected Client buildClient() { }; ResourceLookupContext resourceLookupContext = Mockito.mock(ResourceLookupContext.class); - when(resourceLookupContext.getUserInput()).thenReturn(""); try { niFiClient.getResources(resourceLookupContext); @@ -164,14 +177,15 @@ protected Client buildClient() { } @Test - public void testConnectionTestSuccess() { + void testConnectionTestSuccess() { + niFiClient = newNiFiClientWithMockResourcesResponse(false); HashMap ret = niFiClient.connectionTest(); Assertions.assertNotNull(ret); Assertions.assertEquals(NiFiClient.SUCCESS_MSG, ret.get("message")); } @Test - public void testConnectionTestFailure() { + void testConnectionTestFailure() { final String errorMsg = "unknown error"; niFiClient = new NiFiClient("http://localhost:8080/nifi-api/resources", null) { @@ -186,19 +200,136 @@ protected Client buildClient() { Assertions.assertEquals(NiFiClient.FAILURE_MSG, ret.get("message")); } + @Test + void testConnectionTestProcessingExceptionMessageNullUsesUnknownError() { + NiFiClient client = new NiFiClient("http://localhost:8080/nifi-api/resources", null) { + @Override + protected Client buildClient() { + Client mockClient = Mockito.mock(Client.class); + WebTarget mockWebTarget = Mockito.mock(WebTarget.class); + Invocation.Builder mockBuilder = Mockito.mock(Invocation.Builder.class); + when(mockClient.target(anyString())).thenReturn(mockWebTarget); + when(mockWebTarget.request(any(String.class))).thenReturn(mockBuilder); + ProcessingException processingException = Mockito.mock(ProcessingException.class); + Mockito.when(processingException.getMessage()).thenReturn(null); + when(mockBuilder.get()).thenThrow(processingException); + return mockClient; + } + }; + + HashMap ret = client.connectionTest(); + Assertions.assertNotNull(ret); + Assertions.assertEquals(NiFiClient.FAILURE_MSG, ret.get("message")); + Assertions.assertTrue(ret.get("description").toString().contains("Unknown error")); + } + + @Test + void testConnectionTestWebApplicationException() { + NiFiClient client = new NiFiClient("http://localhost:8080/nifi-api/resources", null) { + @Override + protected Client buildClient() { + Client mockClient = Mockito.mock(Client.class); + WebTarget mockWebTarget = Mockito.mock(WebTarget.class); + Invocation.Builder mockBuilder = Mockito.mock(Invocation.Builder.class); + when(mockClient.target(anyString())).thenReturn(mockWebTarget); + when(mockWebTarget.request(any(String.class))).thenReturn(mockBuilder); + when(mockBuilder.get()).thenThrow(new WebApplicationException("bad gateway")); + return mockClient; + } + }; + + HashMap ret = client.connectionTest(); + Assertions.assertFalse((Boolean) ret.get("connectivityStatus")); + Assertions.assertTrue(ret.get("description").toString().contains("bad gateway")); + } + + @Test + void testConnectionTestGenericExceptionFromGet() { + NiFiClient client = new NiFiClient("http://localhost:8080/nifi-api/resources", null) { + @Override + protected Client buildClient() { + Client mockClient = Mockito.mock(Client.class); + WebTarget mockWebTarget = Mockito.mock(WebTarget.class); + Invocation.Builder mockBuilder = Mockito.mock(Invocation.Builder.class); + when(mockClient.target(anyString())).thenReturn(mockWebTarget); + when(mockWebTarget.request(any(String.class))).thenReturn(mockBuilder); + when(mockBuilder.get()).thenThrow(new IllegalStateException("unexpected")); + return mockClient; + } + }; + + HashMap ret = client.connectionTest(); + Assertions.assertFalse((Boolean) ret.get("connectivityStatus")); + Assertions.assertTrue(ret.get("description").toString().contains("unexpected")); + } + + @Test + void testGetResourcesBlankWhitespaceUserInputReturnsAllIdentifiers() throws Exception { + NiFiClient client = newNiFiClientWithMockResourcesResponse(true); + + ResourceLookupContext resourceLookupContext = Mockito.mock(ResourceLookupContext.class); + when(resourceLookupContext.getUserInput()).thenReturn(" "); + + List resources = client.getResources(resourceLookupContext); + Assertions.assertEquals(6, resources.size()); + } + + @Test + void testGetResourcesMalformedJsonThrows() { + NiFiClient client = new NiFiClient("http://localhost:8080/nifi-api/resources", null) { + @Override + protected Client buildClient() { + return new MockNiFiClient("not-json", 200).buildClient(); + } + }; + + ResourceLookupContext resourceLookupContext = Mockito.mock(ResourceLookupContext.class); + + Assertions.assertThrows(Exception.class, () -> client.getResources(resourceLookupContext)); + } + + @Test + void testGetSslContextHostnameVerifierAndUrlWithSslContext() throws Exception { + SSLContext sslContext = SSLContext.getDefault(); + NiFiClient client = new NiFiClient("https://localhost:8443/nifi-api/resources", sslContext) { + @Override + protected Client buildClient() { + return Mockito.mock(Client.class); + } + }; + + Assertions.assertEquals("https://localhost:8443/nifi-api/resources", client.getUrl()); + Assertions.assertSame(sslContext, client.getSslContext()); + Assertions.assertNotNull(client.getHostnameVerifier()); + } + + @Test + void testBuildClientInvokesSecureBuilderWhenSslContextNonNull() throws Exception { + SSLContext sslContext = SSLContext.getDefault(); + NiFiClient client = new NiFiClient("https://localhost:8443/nifi-api/resources", sslContext); + Assertions.assertNotNull(client); + Assertions.assertSame(sslContext, client.getSslContext()); + } + /** * Extend NiFiClient to return mock responses. */ private static final class MockNiFiClient { - private final int statusCode; - private final String responseEntity; + private final int statusCode; + private final String responseEntity; + private final boolean stubReadEntityForOkStatus; + + MockNiFiClient(String responseEntity, int statusCode) { + this(responseEntity, statusCode, true); + } - public MockNiFiClient(String responseEntity, int statusCode) { - this.statusCode = statusCode; - this.responseEntity = responseEntity; + MockNiFiClient(String responseEntity, int statusCode, boolean stubReadEntityForOkStatus) { + this.statusCode = statusCode; + this.responseEntity = responseEntity; + this.stubReadEntityForOkStatus = stubReadEntityForOkStatus; } - public Client buildClient() { + Client buildClient() { Client mockClient = Mockito.mock(Client.class); WebTarget mockWebTarget = Mockito.mock(WebTarget.class); Invocation.Builder mockBuilder = Mockito.mock(Invocation.Builder.class); @@ -213,7 +344,10 @@ public Client buildClient() { if (statusCode == 200) { ongoingStubbing.thenReturn(mockResponse); when(mockResponse.getStatus()).thenReturn(statusCode); - when(mockResponse.readEntity(any(Class.class))).thenReturn(new ByteArrayInputStream(responseEntity.getBytes(StandardCharsets.UTF_8))); + if (stubReadEntityForOkStatus) { + when(mockResponse.readEntity(any(Class.class))).thenReturn( + new ByteArrayInputStream(responseEntity.getBytes(StandardCharsets.UTF_8))); + } } else { ongoingStubbing.thenReturn(mockResponse); when(mockResponse.getStatus()).thenReturn(statusCode); diff --git a/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiClientAdditionalScenarios.java b/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiClientAdditionalScenarios.java new file mode 100644 index 00000000000..9c278af45e6 --- /dev/null +++ b/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiClientAdditionalScenarios.java @@ -0,0 +1,268 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.ranger.services.nifi.client; + +import org.apache.ranger.plugin.service.ResourceLookupContext; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.Invocation; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Response; + +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import java.security.cert.Certificate; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +/** + * @generated by Cursor + * @description : Unit Test cases for NiFiConfigs + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +class TestNiFiClientAdditionalScenarios { + @Test + void connectionTest_nonOkResponse_appendsErrorBodyReadAsInputStream() { + NiFiClient client = new NiFiClient("http://localhost:8080/nifi-api/resources", null) { + @Override + protected Client buildClient() { + Client mockClient = Mockito.mock(Client.class); + WebTarget mockWebTarget = Mockito.mock(WebTarget.class); + Invocation.Builder mockBuilder = Mockito.mock(Invocation.Builder.class); + Response mockResponse = Mockito.mock(Response.class); + when(mockClient.target(anyString())).thenReturn(mockWebTarget); + when(mockWebTarget.request(any(String.class))).thenReturn(mockBuilder); + when(mockBuilder.get()).thenReturn(mockResponse); + when(mockResponse.getStatus()).thenReturn(502); + when(mockResponse.readEntity(java.io.InputStream.class)).thenReturn( + new ByteArrayInputStream("upstream failure".getBytes(StandardCharsets.UTF_8))); + return mockClient; + } + }; + + HashMap ret = client.connectionTest(); + + assertFalse((Boolean) ret.get("connectivityStatus")); + assertTrue(ret.get("description").toString().contains("502")); + assertTrue(ret.get("description").toString().contains("upstream failure")); + } + + @Test + void connectionTest_nonOkWhenReadingErrorBodyThrows_stillReportsFailure() { + NiFiClient client = new NiFiClient("http://localhost:8080/nifi-api/resources", null) { + @Override + protected Client buildClient() { + Client mockClient = Mockito.mock(Client.class); + WebTarget mockWebTarget = Mockito.mock(WebTarget.class); + Invocation.Builder mockBuilder = Mockito.mock(Invocation.Builder.class); + Response mockResponse = Mockito.mock(Response.class); + when(mockClient.target(anyString())).thenReturn(mockWebTarget); + when(mockWebTarget.request(any(String.class))).thenReturn(mockBuilder); + when(mockBuilder.get()).thenReturn(mockResponse); + when(mockResponse.getStatus()).thenReturn(500); + when(mockResponse.readEntity(java.io.InputStream.class)).thenThrow(new IllegalStateException("broken stream")); + return mockClient; + } + }; + + HashMap ret = client.connectionTest(); + + assertFalse((Boolean) ret.get("connectivityStatus")); + assertTrue(ret.get("description").toString().contains("broken stream")); + } + + @Test + void getResources_jsonMissingResourcesNode_throws() throws Exception { + String body = "{\"revision\":{\"clientId\":\"x\"}}"; + NiFiClient client = new NiFiClient("http://localhost:8080/nifi-api/resources", null) { + @Override + protected Client buildClient() { + return mockClientReturningJson(body, 200); + } + }; + + ResourceLookupContext ctx = Mockito.mock(ResourceLookupContext.class); + + assertThrows(Throwable.class, () -> client.getResources(ctx)); + } + + @Test + void hostnameVerifier_firstCertificateNotX509_returnsFalse() throws Exception { + HostnameVerifier verifier = newHostnameVerifier(); + SSLSession session = Mockito.mock(SSLSession.class); + Certificate plain = Mockito.mock(Certificate.class); + when(session.getPeerCertificates()).thenReturn(new Certificate[] {plain}); + + assertFalse(verifier.verify("my-host", session)); + } + + @Test + void hostnameVerifier_peerCertificatesEmpty_returnsFalse() throws Exception { + HostnameVerifier verifier = newHostnameVerifier(); + SSLSession session = Mockito.mock(SSLSession.class); + when(session.getPeerCertificates()).thenReturn(new Certificate[] {}); + + assertFalse(verifier.verify("my-host", session)); + } + + @Test + void hostnameVerifier_peerCertificatesNull_returnsFalse() throws Exception { + HostnameVerifier verifier = newHostnameVerifier(); + SSLSession session = Mockito.mock(SSLSession.class); + when(session.getPeerCertificates()).thenReturn(null); + + assertFalse(verifier.verify("my-host", session)); + } + + @Test + void hostnameVerifier_sslPeerUnverified_returnsFalse() throws Exception { + HostnameVerifier verifier = newHostnameVerifier(); + SSLSession session = Mockito.mock(SSLSession.class); + when(session.getPeerCertificates()).thenThrow(new SSLPeerUnverifiedException("peer")); + + assertFalse(verifier.verify("my-host", session)); + } + + @Test + void hostnameVerifier_subjectAltParsingFails_returnsFalse() throws Exception { + HostnameVerifier verifier = newHostnameVerifier(); + SSLSession session = Mockito.mock(SSLSession.class); + X509Certificate cert = Mockito.mock(X509Certificate.class); + when(cert.getSubjectAlternativeNames()).thenThrow(new CertificateParsingException("bad san")); + when(session.getPeerCertificates()).thenReturn(new Certificate[] {cert}); + + assertFalse(verifier.verify("my-host", session)); + } + + @Test + void hostnameVerifier_subjectAltListExcludesHostname_returnsFalse() throws Exception { + HostnameVerifier verifier = newHostnameVerifier(); + SSLSession session = Mockito.mock(SSLSession.class); + X509Certificate cert = Mockito.mock(X509Certificate.class); + List> sans = new ArrayList<>(); + sans.add(List.of(2, "other.example.com")); + when(cert.getSubjectAlternativeNames()).thenReturn(sans); + when(session.getPeerCertificates()).thenReturn(new Certificate[] {cert}); + + assertFalse(verifier.verify("my-host", session)); + } + + @Test + void hostnameVerifier_subjectAltNullList_hostnameDoesNotMatch_returnsFalse() throws Exception { + HostnameVerifier verifier = newHostnameVerifier(); + SSLSession session = Mockito.mock(SSLSession.class); + X509Certificate cert = Mockito.mock(X509Certificate.class); + when(cert.getSubjectAlternativeNames()).thenReturn(null); + when(session.getPeerCertificates()).thenReturn(new Certificate[] {cert}); + + assertFalse(verifier.verify("my-host", session)); + } + + @Test + void hostnameVerifier_subjectAltNameMatches_returnsTrue() throws Exception { + HostnameVerifier verifier = newHostnameVerifier(); + SSLSession session = Mockito.mock(SSLSession.class); + X509Certificate cert = Mockito.mock(X509Certificate.class); + List> sans = new ArrayList<>(); + sans.add(List.of(2, "NiFi.example.COM")); + when(cert.getSubjectAlternativeNames()).thenReturn(sans); + when(session.getPeerCertificates()).thenReturn(new Certificate[] {cert}); + + assertTrue(verifier.verify("nifi.example.com", session)); + } + + @Test + void hostnameVerifier_skipsGeneralNameWhenSecondElementNotString() throws Exception { + HostnameVerifier verifier = newHostnameVerifier(); + SSLSession session = Mockito.mock(SSLSession.class); + X509Certificate cert = Mockito.mock(X509Certificate.class); + List> sans = new ArrayList<>(); + List entry = new ArrayList<>(); + entry.add(2); + entry.add(new byte[] {1, 2, 3}); + sans.add(entry); + when(cert.getSubjectAlternativeNames()).thenReturn(sans); + when(session.getPeerCertificates()).thenReturn(new Certificate[] {cert}); + + assertFalse(verifier.verify("my-host", session)); + } + + @Test + void hostnameVerifier_skipsGeneralNameWithTooFewElements() throws Exception { + HostnameVerifier verifier = newHostnameVerifier(); + SSLSession session = Mockito.mock(SSLSession.class); + X509Certificate cert = Mockito.mock(X509Certificate.class); + List> sans = new ArrayList<>(); + sans.add(Collections.singletonList(2)); + when(cert.getSubjectAlternativeNames()).thenReturn(sans); + when(session.getPeerCertificates()).thenReturn(new Certificate[] {cert}); + + assertFalse(verifier.verify("my-host", session)); + } + + private static HostnameVerifier newHostnameVerifier() throws Exception { + NiFiClient client = new NiFiClient("https://localhost:8443/nifi-api/resources", SSLContext.getDefault()) { + @Override + protected Client buildClient() { + return Mockito.mock(Client.class); + } + }; + return client.getHostnameVerifier(); + } + + private static Client mockClientReturningJson(String jsonBody, int status) { + Client mockClient = Mockito.mock(Client.class); + WebTarget mockWebTarget = Mockito.mock(WebTarget.class); + Invocation.Builder mockBuilder = Mockito.mock(Invocation.Builder.class); + Response mockResponse = Mockito.mock(Response.class); + when(mockClient.target(anyString())).thenReturn(mockWebTarget); + when(mockWebTarget.request(any(String.class))).thenReturn(mockBuilder); + when(mockBuilder.get()).thenReturn(mockResponse); + when(mockResponse.getStatus()).thenReturn(status); + if (status == 200) { + when(mockResponse.readEntity(any(Class.class))).thenReturn( + new ByteArrayInputStream(jsonBody.getBytes(StandardCharsets.UTF_8))); + } else { + when(mockResponse.readEntity(String.class)).thenReturn("err"); + } + return mockClient; + } +} diff --git a/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiConfigs.java b/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiConfigs.java new file mode 100644 index 00000000000..9d89105df79 --- /dev/null +++ b/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiConfigs.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.ranger.services.nifi.client; + +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +/** + * @generated by Cursor + * @description : Unit Test cases for NiFiConfigs + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +class TestNiFiConfigs { + @Test + void constantsMatchServiceDefinitionKeys() { + assertEquals("nifi.url", NiFiConfigs.NIFI_URL); + assertEquals("nifi.authentication", NiFiConfigs.NIFI_AUTHENTICATION_TYPE); + assertEquals("nifi.ssl.keystore", NiFiConfigs.NIFI_SSL_KEYSTORE); + assertEquals("nifi.ssl.keystoreType", NiFiConfigs.NIFI_SSL_KEYSTORE_TYPE); + assertEquals("nifi.ssl.keystorePassword", NiFiConfigs.NIFI_SSL_KEYSTORE_PASSWORD); + assertEquals("nifi.ssl.truststore", NiFiConfigs.NIFI_SSL_TRUSTSTORE); + assertEquals("nifi.ssl.truststoreType", NiFiConfigs.NIFI_SSL_TRUSTSTORE_TYPE); + assertEquals("nifi.ssl.truststorePassword", NiFiConfigs.NIFI_SSL_TRUSTSTORE_PASSWORD); + assertEquals("nifi.ssl.use.default.context", NiFiConfigs.NIFI_SSL_USER_DEFAULT_CONTEXT); + } + + @Test + void constantsAreNonBlank() { + assertFalse(NiFiConfigs.NIFI_URL.isEmpty()); + assertFalse(NiFiConfigs.NIFI_AUTHENTICATION_TYPE.isEmpty()); + } +} diff --git a/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiConnectionMgr.java b/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiConnectionMgr.java index a4bda9032e2..18f73b428f7 100644 --- a/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiConnectionMgr.java +++ b/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiConnectionMgr.java @@ -18,18 +18,30 @@ */ package org.apache.ranger.services.nifi.client; -import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; import java.io.FileNotFoundException; import java.util.HashMap; import java.util.Map; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; -public class TestNiFiConnectionMgr { +/** + * @generated by Cursor + * @description : Unit Test cases for NiFiConnectionMgr + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +class TestNiFiConnectionMgr { @Test - public void testValidURLWithWrongEndPoint() throws Exception { + void testValidURLWithWrongEndPoint() throws Exception { final String nifiUrl = "http://localhost:8080/nifi"; Map configs = new HashMap<>(); @@ -40,7 +52,7 @@ public void testValidURLWithWrongEndPoint() throws Exception { } @Test - public void testInvalidURL() throws Exception { + void testInvalidURL() throws Exception { final String nifiUrl = "not a url"; Map configs = new HashMap<>(); @@ -51,7 +63,7 @@ public void testInvalidURL() throws Exception { } @Test - public void testAuthTypeNone() throws Exception { + void testAuthTypeNone() throws Exception { final String nifiUrl = "http://localhost:8080/nifi-api/resources"; Map configs = new HashMap<>(); @@ -59,13 +71,13 @@ public void testAuthTypeNone() throws Exception { configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, NiFiAuthType.NONE.name()); NiFiClient client = NiFiConnectionMgr.getNiFiClient("nifi", configs); - Assertions.assertNotNull(client); - Assertions.assertEquals(nifiUrl, client.getUrl()); - Assertions.assertNull(client.getSslContext()); + assertNotNull(client); + assertEquals(nifiUrl, client.getUrl()); + assertNull(client.getSslContext()); } @Test - public void testAuthTypeNoneMissingURL() throws Exception { + void testAuthTypeNoneMissingURL() throws Exception { Map configs = new HashMap<>(); configs.put(NiFiConfigs.NIFI_URL, null); configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, NiFiAuthType.NONE.name()); @@ -74,7 +86,7 @@ public void testAuthTypeNoneMissingURL() throws Exception { } @Test - public void testAuthTypeSSL() throws Exception { + void testAuthTypeSSL() throws Exception { final String nifiUrl = "https://localhost:8080/nifi-api/resources"; Map configs = new HashMap<>(); @@ -93,7 +105,7 @@ public void testAuthTypeSSL() throws Exception { } @Test - public void testAuthTypeSSLWithNonHttpsUrl() throws Exception { + void testAuthTypeSSLWithNonHttpsUrl() throws Exception { final String nifiUrl = "http://localhost:8080/nifi-api/resources"; Map configs = new HashMap<>(); @@ -112,7 +124,7 @@ public void testAuthTypeSSLWithNonHttpsUrl() throws Exception { } @Test - public void testAuthTypeSSLMissingConfigs() throws Exception { + void testAuthTypeSSLMissingConfigs() throws Exception { final String nifiUrl = "http://localhost:8080/nifi"; Map configs = new HashMap<>(); @@ -121,4 +133,85 @@ public void testAuthTypeSSLMissingConfigs() throws Exception { assertThrows(IllegalArgumentException.class, () -> NiFiConnectionMgr.getNiFiClient("nifi", configs)); } + + @Test + void testAuthTypeInvalidEnumValue() { + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "http://localhost:8080/nifi-api/resources"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, "NOT_A_VALID_AUTH"); + + assertThrows(IllegalArgumentException.class, () -> NiFiConnectionMgr.getNiFiClient("nifi", configs)); + } + + @Test + void testAuthTypeMissingAuthenticationType() { + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "http://localhost:8080/nifi-api/resources"); + + assertThrows(IllegalArgumentException.class, () -> NiFiConnectionMgr.getNiFiClient("nifi", configs)); + } + + @Test + void testConnectionTestReturnsErrorMapWhenClientCannotBeCreated() { + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "http://localhost:8080/nifi-api"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, NiFiAuthType.NONE.name()); + + HashMap ret = NiFiConnectionMgr.connectionTest("nifi", configs); + + assertNotNull(ret); + assertEquals(false, ret.get("connectivityStatus")); + assertEquals("Error creating NiFi client", ret.get("message")); + } + + @Test + void testGetNiFiClientSslUseDefaultContext() throws Exception { + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "https://localhost:8443/nifi-api/resources"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, NiFiAuthType.SSL.name()); + configs.put(NiFiConfigs.NIFI_SSL_USER_DEFAULT_CONTEXT, "true"); + + NiFiClient client = NiFiConnectionMgr.getNiFiClient("nifi", configs); + assertNotNull(client.getSslContext()); + } + + @Test + void testGetNiFiClientSslUseDefaultContextRejectsAdditionalKeystoreConfig() { + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "https://localhost:8443/nifi-api/resources"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, NiFiAuthType.SSL.name()); + configs.put(NiFiConfigs.NIFI_SSL_USER_DEFAULT_CONTEXT, "true"); + configs.put(NiFiConfigs.NIFI_SSL_KEYSTORE, "/tmp/any.jks"); + + assertThrows(IllegalArgumentException.class, () -> NiFiConnectionMgr.getNiFiClient("nifi", configs)); + } + + @Test + void testSslMissingTruststoreType() { + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "https://localhost:8443/nifi-api/resources"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, NiFiAuthType.SSL.name()); + configs.put(NiFiConfigs.NIFI_SSL_KEYSTORE, "src/test/resources/missing.jks"); + configs.put(NiFiConfigs.NIFI_SSL_KEYSTORE_TYPE, "JKS"); + configs.put(NiFiConfigs.NIFI_SSL_KEYSTORE_PASSWORD, "password"); + configs.put(NiFiConfigs.NIFI_SSL_TRUSTSTORE, "src/test/resources/missing.jks"); + configs.put(NiFiConfigs.NIFI_SSL_TRUSTSTORE_PASSWORD, "password"); + + assertThrows(IllegalArgumentException.class, () -> NiFiConnectionMgr.getNiFiClient("nifi", configs)); + } + + @Test + void testSslMissingKeystorePasswordAfterTrim() { + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "https://localhost:8443/nifi-api/resources"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, NiFiAuthType.SSL.name()); + configs.put(NiFiConfigs.NIFI_SSL_KEYSTORE, "src/test/resources/missing.jks"); + configs.put(NiFiConfigs.NIFI_SSL_KEYSTORE_TYPE, "JKS"); + configs.put(NiFiConfigs.NIFI_SSL_KEYSTORE_PASSWORD, " "); + configs.put(NiFiConfigs.NIFI_SSL_TRUSTSTORE, "src/test/resources/missing.jks"); + configs.put(NiFiConfigs.NIFI_SSL_TRUSTSTORE_PASSWORD, "password"); + configs.put(NiFiConfigs.NIFI_SSL_TRUSTSTORE_TYPE, "JKS"); + + assertThrows(IllegalArgumentException.class, () -> NiFiConnectionMgr.getNiFiClient("nifi", configs)); + } } diff --git a/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiConnectionMgrAdditionalScenarios.java b/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiConnectionMgrAdditionalScenarios.java new file mode 100644 index 00000000000..b0d53ff55a7 --- /dev/null +++ b/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiConnectionMgrAdditionalScenarios.java @@ -0,0 +1,186 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.ranger.services.nifi.client; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +/** + * @generated by Cursor + * @description : Unit Test cases for NiFiConnectionMgrAdditionalScenarios + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +class TestNiFiConnectionMgrAdditionalScenarios { + @Test + void connectionTest_success_returnsClientConnectionResult() { + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "http://localhost:8080/nifi-api/resources"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, NiFiAuthType.NONE.name()); + + NiFiClient mockClient = Mockito.mock(NiFiClient.class); + HashMap expected = new HashMap<>(); + expected.put("connectivityStatus", true); + expected.put("msgKey", "from-client"); + + when(mockClient.connectionTest()).thenReturn(expected); + + try (MockedStatic mgr = Mockito.mockStatic(NiFiConnectionMgr.class)) { + mgr.when(() -> NiFiConnectionMgr.getNiFiClient(anyString(), any())).thenReturn(mockClient); + mgr.when(() -> NiFiConnectionMgr.connectionTest(anyString(), any())) + .thenCallRealMethod(); + + HashMap ret = NiFiConnectionMgr.connectionTest("svc", configs); + + assertEquals(expected, ret); + } + } + + @Test + void getNiFiClient_sslDefaultContextRejectsKeystoreTypeOnly() { + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "https://localhost:8443/nifi-api/resources"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, NiFiAuthType.SSL.name()); + configs.put(NiFiConfigs.NIFI_SSL_USER_DEFAULT_CONTEXT, "true"); + configs.put(NiFiConfigs.NIFI_SSL_KEYSTORE_TYPE, "JKS"); + + assertThrows(IllegalArgumentException.class, () -> NiFiConnectionMgr.getNiFiClient("svc", configs)); + } + + @Test + void getNiFiClient_urlHasWhitespaceRejectedByValidateUrlBeforeTrim() { + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, " http://localhost:8080/nifi-api/resources "); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, NiFiAuthType.NONE.name()); + + assertThrows(IllegalArgumentException.class, () -> NiFiConnectionMgr.getNiFiClient("svc", configs)); + } + + @Test + void getNiFiClient_blankAuthenticationTypeAfterTrim_throws() { + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "http://localhost:8080/nifi-api/resources"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, " "); + + assertThrows(IllegalArgumentException.class, () -> NiFiConnectionMgr.getNiFiClient("svc", configs)); + } + + @Test + void getNiFiClient_blankUrlAfterTrim_throws() { + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, " "); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, NiFiAuthType.NONE.name()); + + assertThrows(IllegalArgumentException.class, () -> NiFiConnectionMgr.getNiFiClient("svc", configs)); + } + + @Test + void getNiFiClient_sslDefaultContextTrueCaseInsensitive_buildsClient() throws Exception { + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "https://localhost:8443/nifi-api/resources"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, NiFiAuthType.SSL.name()); + configs.put(NiFiConfigs.NIFI_SSL_USER_DEFAULT_CONTEXT, "TRUe"); + + NiFiClient client = NiFiConnectionMgr.getNiFiClient("svc", configs); + + assertNotNull(client.getSslContext()); + } + + @Test + void getNiFiClient_sslDefaultContextRejectsNonBooleanTruststorePasswordOnly() { + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "https://localhost:8443/nifi-api/resources"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, NiFiAuthType.SSL.name()); + configs.put(NiFiConfigs.NIFI_SSL_USER_DEFAULT_CONTEXT, "true"); + configs.put(NiFiConfigs.NIFI_SSL_TRUSTSTORE_PASSWORD, "secret"); + + assertThrows(IllegalArgumentException.class, () -> NiFiConnectionMgr.getNiFiClient("svc", configs)); + } + + @Test + void getNiFiClient_sslMissingKeystore_throwsBeforeFileIo() { + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "https://localhost:8443/nifi-api/resources"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, NiFiAuthType.SSL.name()); + configs.put(NiFiConfigs.NIFI_SSL_KEYSTORE_TYPE, "JKS"); + configs.put(NiFiConfigs.NIFI_SSL_KEYSTORE_PASSWORD, "password"); + configs.put(NiFiConfigs.NIFI_SSL_TRUSTSTORE, "src/test/resources/missing.jks"); + configs.put(NiFiConfigs.NIFI_SSL_TRUSTSTORE_PASSWORD, "password"); + configs.put(NiFiConfigs.NIFI_SSL_TRUSTSTORE_TYPE, "JKS"); + + assertThrows(IllegalArgumentException.class, () -> NiFiConnectionMgr.getNiFiClient("svc", configs)); + } + + @Test + void getNiFiClient_validUrlAllowsQueryOnResourcesPath() throws Exception { + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "http://localhost:8080/nifi-api/resources?version=1"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, NiFiAuthType.NONE.name()); + + NiFiClient client = NiFiConnectionMgr.getNiFiClient("svc", configs); + + assertNotNull(client); + assertEquals("http://localhost:8080/nifi-api/resources?version=1", client.getUrl()); + } + + @Test + void connectionTest_mapsValidationFailureFromGetNiFiClient() { + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "http://localhost:8080/nifi-api"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, NiFiAuthType.NONE.name()); + + HashMap ret = NiFiConnectionMgr.connectionTest("svc", configs); + + Assertions.assertEquals(Boolean.FALSE, ret.get("connectivityStatus")); + Assertions.assertEquals("Error creating NiFi client", ret.get("message")); + Object description = ret.get("description"); + Assertions.assertNotNull(description); + Assertions.assertTrue(description.toString().contains(NiFiConnectionMgr.INVALID_URL_MSG)); + } + + @Test + void getNiFiClient_sslMissingKeystoreType_throws() { + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "https://localhost:8443/nifi-api/resources"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, NiFiAuthType.SSL.name()); + configs.put(NiFiConfigs.NIFI_SSL_KEYSTORE, "src/test/resources/missing.jks"); + configs.put(NiFiConfigs.NIFI_SSL_KEYSTORE_PASSWORD, "password"); + configs.put(NiFiConfigs.NIFI_SSL_TRUSTSTORE, "src/test/resources/missing.jks"); + configs.put(NiFiConfigs.NIFI_SSL_TRUSTSTORE_PASSWORD, "password"); + configs.put(NiFiConfigs.NIFI_SSL_TRUSTSTORE_TYPE, "JKS"); + + assertThrows(IllegalArgumentException.class, () -> NiFiConnectionMgr.getNiFiClient("svc", configs)); + } +} diff --git a/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiConnectionMgrSslKeystoreLoad.java b/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiConnectionMgrSslKeystoreLoad.java new file mode 100644 index 00000000000..5d32ea3c94d --- /dev/null +++ b/plugin-nifi/src/test/java/org/apache/ranger/services/nifi/client/TestNiFiConnectionMgrSslKeystoreLoad.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.ranger.services.nifi.client; + +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * @generated by Cursor + * @description : Unit Test cases for NiFiConnectionMgrSslKeystoreLoad + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +class TestNiFiConnectionMgrSslKeystoreLoad { + @Test + void getNiFiClient_ssl_loadsKeystoresCreatedWithKeytool(@TempDir Path tempDir) throws Exception { + Path keytool = Path.of(System.getProperty("java.home"), "bin", "keytool"); + Assumptions.assumeTrue(Files.exists(keytool), "JDK keytool not found at " + keytool); + + Path keystore = tempDir.resolve("keystore.jks"); + Path truststore = tempDir.resolve("truststore.jks"); + Path exportedCert = tempDir.resolve("nifi.cer"); + String password = "changeit"; + + Process gen = new ProcessBuilder( + keytool.toString(), + "-genkeypair", + "-alias", + "nifi", + "-keyalg", + "RSA", + "-keysize", + "2048", + "-storetype", + "JKS", + "-keystore", + keystore.toString(), + "-storepass", + password, + "-keypass", + password, + "-dname", + "CN=localhost", + "-validity", + "365") + .redirectError(ProcessBuilder.Redirect.PIPE) + .start(); + Assumptions.assumeTrue(gen.waitFor(120, TimeUnit.SECONDS), "keytool genkeypair timed out"); + Assumptions.assumeTrue(gen.exitValue() == 0, "keytool genkeypair failed"); + + Process exp = new ProcessBuilder( + keytool.toString(), + "-exportcert", + "-alias", + "nifi", + "-keystore", + keystore.toString(), + "-storepass", + password, + "-file", + exportedCert.toString()) + .redirectError(ProcessBuilder.Redirect.PIPE) + .start(); + Assumptions.assumeTrue(exp.waitFor(60, TimeUnit.SECONDS), "keytool exportcert timed out"); + Assumptions.assumeTrue(exp.exitValue() == 0, "keytool exportcert failed"); + + Process imp = new ProcessBuilder( + keytool.toString(), + "-importcert", + "-noprompt", + "-alias", + "nifi", + "-file", + exportedCert.toString(), + "-keystore", + truststore.toString(), + "-storepass", + password, + "-storetype", + "JKS") + .redirectError(ProcessBuilder.Redirect.PIPE) + .start(); + Assumptions.assumeTrue(imp.waitFor(60, TimeUnit.SECONDS), "keytool importcert timed out"); + Assumptions.assumeTrue(imp.exitValue() == 0, "keytool importcert failed"); + + Map configs = new HashMap<>(); + configs.put(NiFiConfigs.NIFI_URL, "https://localhost:8443/nifi-api/resources"); + configs.put(NiFiConfigs.NIFI_AUTHENTICATION_TYPE, NiFiAuthType.SSL.name()); + configs.put(NiFiConfigs.NIFI_SSL_KEYSTORE, keystore.toString()); + configs.put(NiFiConfigs.NIFI_SSL_KEYSTORE_TYPE, "JKS"); + configs.put(NiFiConfigs.NIFI_SSL_KEYSTORE_PASSWORD, password); + configs.put(NiFiConfigs.NIFI_SSL_TRUSTSTORE, truststore.toString()); + configs.put(NiFiConfigs.NIFI_SSL_TRUSTSTORE_TYPE, "JKS"); + configs.put(NiFiConfigs.NIFI_SSL_TRUSTSTORE_PASSWORD, password); + + NiFiClient client = NiFiConnectionMgr.getNiFiClient("svc", configs); + + assertNotNull(client.getSslContext()); + assertEquals("https://localhost:8443/nifi-api/resources", client.getUrl()); + } +}