diff --git a/knox-agent/pom.xml b/knox-agent/pom.xml
index b55acb9ba1..396f5e4751 100644
--- a/knox-agent/pom.xml
+++ b/knox-agent/pom.xml
@@ -317,6 +317,18 @@
${junit.jupiter.version}
test
+
+ org.mockito
+ mockito-inline
+ ${mockito.version}
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ ${mockito.version}
+ test
+
${basedir}/src/main/java
diff --git a/knox-agent/src/test/java/org/apache/ranger/admin/client/TestRangerAdminJersey2RESTClient.java b/knox-agent/src/test/java/org/apache/ranger/admin/client/TestRangerAdminJersey2RESTClient.java
new file mode 100644
index 0000000000..d36e5d943a
--- /dev/null
+++ b/knox-agent/src/test/java/org/apache/ranger/admin/client/TestRangerAdminJersey2RESTClient.java
@@ -0,0 +1,780 @@
+/*
+ * 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.admin.client;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonPrimitive;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.AccessControlException;
+import org.apache.ranger.admin.client.RangerAdminJersey2RESTClient.GsonUnixDateDeserializer;
+import org.apache.ranger.plugin.util.RangerRESTUtils;
+import org.apache.ranger.plugin.util.RangerRoles;
+import org.apache.ranger.plugin.util.RangerServiceNotFoundException;
+import org.apache.ranger.plugin.util.RangerUserStore;
+import org.apache.ranger.plugin.util.ServicePolicies;
+import org.apache.ranger.plugin.util.ServiceTags;
+import org.glassfish.jersey.client.ClientProperties;
+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.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.NewCookie;
+import javax.ws.rs.core.Response;
+
+import java.util.Date;
+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;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+* @generated by Cursor
+* @description
+*/
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestRangerAdminJersey2RESTClient {
+ private static final String PREFIX = "ranger.plugin.test";
+ private static final String SERVICE = "svc";
+ private static final String APPID = "app";
+ private static final String BASE_URL = "http://localhost:6080";
+ private static final String HTTPS_URL = "https://localhost:6182";
+ private static final String COOKIE_NAME = "RANGERADMINSESSION";
+
+ private Configuration buildBaseConfig(String baseUrl) {
+ Configuration conf = new Configuration();
+ conf.set(PREFIX + ".policy.rest.url", baseUrl);
+ conf.setInt(PREFIX + ".policy.rest.client.connection.timeoutMs", 111);
+ conf.setInt(PREFIX + ".policy.rest.client.read.timeoutMs", 222);
+ conf.setInt(PREFIX + ".policy.rest.client.max.retry.attempts", 2);
+ conf.setInt(PREFIX + ".policy.rest.client.retry.interval.ms", 0);
+ conf.set(PREFIX + ".access.cluster.name", "");
+ conf.setBoolean(PREFIX + ".policy.rest.client.cookie.enabled", true);
+ conf.set(PREFIX + ".policy.rest.client.session.cookie.name", COOKIE_NAME);
+ conf.setBoolean(PREFIX + ".forceNonKerberos", true);
+ return conf;
+ }
+
+ private void initWithMockClient(RangerAdminJersey2RESTClient clientUnderTest, Client clientMock,
+ Configuration conf) {
+ clientUnderTest.client = clientMock;
+ clientUnderTest.init(SERVICE, APPID, PREFIX, conf);
+ }
+
+ @Test
+ public void test01_init_sets_fields_and_timeouts() {
+ Client clientMock = mock(Client.class);
+ when(clientMock.property(ArgumentMatchers.anyString(), ArgumentMatchers.any())).thenReturn(clientMock);
+
+ Configuration conf = buildBaseConfig(HTTPS_URL);
+ RangerAdminJersey2RESTClient underTest = new RangerAdminJersey2RESTClient();
+
+ initWithMockClient(underTest, clientMock, conf);
+
+ assertEquals(true, underTest.isSSL);
+ assertEquals(111, underTest.restClientConnTimeOutMs);
+ assertEquals(222, underTest.restClientReadTimeOutMs);
+ assertEquals(2, underTest.restClientMaxRetryAttempts);
+ assertEquals(0, underTest.restClientRetryIntervalMs);
+ assertEquals(SERVICE, underTest.serviceName);
+
+ verify(clientMock, times(1)).property(ClientProperties.CONNECT_TIMEOUT, 111);
+ verify(clientMock, times(1)).property(ClientProperties.READ_TIMEOUT, 222);
+ }
+
+ @Test
+ public void test02_getServicePolicies_credential200_then_cookie304() throws Exception {
+ Client clientMock = mock(Client.class);
+ WebTarget targetMock = mock(WebTarget.class);
+ Invocation.Builder builderMock = mock(Invocation.Builder.class);
+ Response response200 = mock(Response.class);
+ Response response304 = mock(Response.class);
+
+ Configuration conf = buildBaseConfig(BASE_URL);
+ RangerAdminJersey2RESTClient underTest = new RangerAdminJersey2RESTClient();
+ initWithMockClient(underTest, clientMock, conf);
+
+ Map cookies = new HashMap<>();
+ cookies.put(COOKIE_NAME, new NewCookie(COOKIE_NAME, "v"));
+
+ when(response200.getStatus()).thenReturn(200);
+ when(response200.readEntity(String.class)).thenReturn("{}");
+ when(response200.getCookies()).thenReturn(cookies);
+ when(response304.getStatus()).thenReturn(304);
+ when(response304.getCookies()).thenReturn(cookies);
+
+ when(clientMock.target(ArgumentMatchers.anyString())).thenReturn(targetMock);
+ when(targetMock.queryParam(ArgumentMatchers.anyString(), ArgumentMatchers.any())).thenReturn(targetMock);
+ when(targetMock.request(MediaType.APPLICATION_JSON_TYPE)).thenReturn(builderMock);
+ when(builderMock.cookie(ArgumentMatchers.nullable(Cookie.class))).thenReturn(builderMock);
+ when(builderMock.get()).thenReturn(response200, response304);
+
+ ServicePolicies first = underTest.getServicePoliciesIfUpdated(1L, 2L);
+ assertNotNull(first);
+ ServicePolicies second = underTest.getServicePoliciesIfUpdated(1L, 2L);
+ assertNull(second);
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(Cookie.class);
+ verify(builderMock, times(2)).cookie(captor.capture());
+ Cookie firstCookie = captor.getAllValues().get(0);
+ Cookie secondCookie = captor.getAllValues().get(1);
+ assertNull(firstCookie);
+ assertNotNull(secondCookie);
+ }
+
+ @Test
+ public void test03_getServicePolicies_404_and_default_and_null() throws Exception {
+ Client clientMock = mock(Client.class);
+ WebTarget targetMock = mock(WebTarget.class);
+ Invocation.Builder builderMock = mock(Invocation.Builder.class);
+ Response response404 = mock(Response.class);
+ Response response500 = mock(Response.class);
+
+ Configuration conf = buildBaseConfig(BASE_URL);
+ RangerAdminJersey2RESTClient underTest = new RangerAdminJersey2RESTClient();
+ initWithMockClient(underTest, clientMock, conf);
+
+ Map cookies = new HashMap<>();
+ cookies.put(COOKIE_NAME, new NewCookie(COOKIE_NAME, "v"));
+
+ when(response404.getStatus()).thenReturn(404);
+ when(response404.hasEntity()).thenReturn(true);
+ when(response404.readEntity(String.class)).thenReturn("");
+ when(response500.getStatus()).thenReturn(500);
+ when(response500.readEntity(String.class)).thenReturn("ERR");
+
+ when(clientMock.target(ArgumentMatchers.anyString())).thenReturn(targetMock);
+ when(targetMock.queryParam(ArgumentMatchers.anyString(), ArgumentMatchers.any())).thenReturn(targetMock);
+ when(targetMock.request(MediaType.APPLICATION_JSON_TYPE)).thenReturn(builderMock);
+ when(builderMock.cookie(ArgumentMatchers.nullable(Cookie.class))).thenReturn(builderMock);
+ when(builderMock.get()).thenReturn(response404, response500, null);
+
+ ServicePolicies on404 = underTest.getServicePoliciesIfUpdated(1L, 2L);
+ assertNull(on404);
+ ServicePolicies on500 = underTest.getServicePoliciesIfUpdated(1L, 2L);
+ assertNull(on500);
+ ServicePolicies onNull = underTest.getServicePoliciesIfUpdated(1L, 2L);
+ assertNull(onNull);
+ }
+
+ @Test
+ public void test04_getServiceTags_flows() throws Exception {
+ Client clientMock = mock(Client.class);
+ WebTarget targetMock = mock(WebTarget.class);
+ Invocation.Builder builderMock = mock(Invocation.Builder.class);
+ Response response200 = mock(Response.class);
+ Response response304 = mock(Response.class);
+ Response response404 = mock(Response.class);
+ Response response500 = mock(Response.class);
+
+ Configuration conf = buildBaseConfig(BASE_URL);
+ RangerAdminJersey2RESTClient underTest = new RangerAdminJersey2RESTClient();
+ initWithMockClient(underTest, clientMock, conf);
+
+ Map cookies = new HashMap<>();
+ cookies.put(COOKIE_NAME, new NewCookie(COOKIE_NAME, "v"));
+
+ when(response200.getStatus()).thenReturn(200);
+ when(response200.readEntity(String.class)).thenReturn("{}");
+ when(response200.getCookies()).thenReturn(cookies);
+ when(response304.getStatus()).thenReturn(304);
+ when(response304.getCookies()).thenReturn(cookies);
+ when(response404.getStatus()).thenReturn(404);
+ when(response404.hasEntity()).thenReturn(true);
+ when(response404.readEntity(String.class)).thenReturn("");
+ when(response500.getStatus()).thenReturn(500);
+ when(response500.readEntity(String.class)).thenReturn("ERR");
+
+ when(clientMock.target(ArgumentMatchers.anyString())).thenReturn(targetMock);
+ when(targetMock.queryParam(ArgumentMatchers.anyString(), ArgumentMatchers.any())).thenReturn(targetMock);
+ when(targetMock.request(MediaType.APPLICATION_JSON_TYPE)).thenReturn(builderMock);
+ when(builderMock.cookie(ArgumentMatchers.nullable(Cookie.class))).thenReturn(builderMock);
+ when(builderMock.get()).thenReturn(response200, response304, response404, response500, null);
+
+ ServiceTags t200 = underTest.getServiceTagsIfUpdated(1L, 2L);
+ assertNotNull(t200);
+ ServiceTags t304 = underTest.getServiceTagsIfUpdated(1L, 2L);
+ assertNull(t304);
+ ServiceTags t404 = underTest.getServiceTagsIfUpdated(1L, 2L);
+ assertNull(t404);
+ ServiceTags t500 = underTest.getServiceTagsIfUpdated(1L, 2L);
+ assertNull(t500);
+ ServiceTags tNull = underTest.getServiceTagsIfUpdated(1L, 2L);
+ assertNull(tNull);
+ }
+
+ @Test
+ public void test05_getRoles_flows() throws Exception {
+ Client clientMock = mock(Client.class);
+ WebTarget targetMock = mock(WebTarget.class);
+ Invocation.Builder builderMock = mock(Invocation.Builder.class);
+ Response response200 = mock(Response.class);
+ Response response304 = mock(Response.class);
+ Response response404 = mock(Response.class);
+ Response response500 = mock(Response.class);
+
+ Configuration conf = buildBaseConfig(BASE_URL);
+ RangerAdminJersey2RESTClient underTest = new RangerAdminJersey2RESTClient();
+ initWithMockClient(underTest, clientMock, conf);
+
+ Map cookies = new HashMap<>();
+ cookies.put(COOKIE_NAME, new NewCookie(COOKIE_NAME, "v"));
+
+ when(response200.getStatus()).thenReturn(200);
+ when(response200.readEntity(String.class)).thenReturn("{}");
+ when(response200.getCookies()).thenReturn(cookies);
+ when(response304.getStatus()).thenReturn(304);
+ when(response304.getCookies()).thenReturn(cookies);
+ when(response404.getStatus()).thenReturn(404);
+ when(response404.hasEntity()).thenReturn(true);
+ when(response404.readEntity(String.class)).thenReturn("");
+ when(response500.getStatus()).thenReturn(500);
+ when(response500.readEntity(String.class)).thenReturn("ERR");
+
+ when(clientMock.target(ArgumentMatchers.anyString())).thenReturn(targetMock);
+ when(targetMock.queryParam(ArgumentMatchers.anyString(), ArgumentMatchers.any())).thenReturn(targetMock);
+ when(targetMock.request(MediaType.APPLICATION_JSON_TYPE)).thenReturn(builderMock);
+ when(builderMock.cookie(ArgumentMatchers.nullable(Cookie.class))).thenReturn(builderMock);
+ when(builderMock.get()).thenReturn(response200, response304, response404, response500, null);
+
+ RangerRoles r200 = underTest.getRolesIfUpdated(1L, 2L);
+ assertNotNull(r200);
+ RangerRoles r304 = underTest.getRolesIfUpdated(1L, 2L);
+ assertNull(r304);
+ RangerRoles r404 = underTest.getRolesIfUpdated(1L, 2L);
+ assertNull(r404);
+ RangerRoles r500 = underTest.getRolesIfUpdated(1L, 2L);
+ assertNull(r500);
+ RangerRoles rNull = underTest.getRolesIfUpdated(1L, 2L);
+ assertNull(rNull);
+ }
+
+ @Test
+ public void test06_grant_and_revoke_access_paths() throws Exception {
+ Client clientMock = mock(Client.class);
+ WebTarget targetMock = mock(WebTarget.class);
+ Invocation.Builder builderMock = mock(Invocation.Builder.class);
+ Response response200 = mock(Response.class);
+ Response response401 = mock(Response.class);
+ Response response500 = mock(Response.class);
+
+ Configuration conf = buildBaseConfig(BASE_URL);
+ RangerAdminJersey2RESTClient underTest = new RangerAdminJersey2RESTClient();
+ initWithMockClient(underTest, clientMock, conf);
+
+ when(response200.getStatus()).thenReturn(200);
+ when(response401.getStatus()).thenReturn(401);
+ when(response500.getStatus()).thenReturn(500);
+ when(response500.readEntity(String.class)).thenReturn("ERR");
+
+ when(clientMock.target(ArgumentMatchers.anyString())).thenReturn(targetMock);
+ when(targetMock.queryParam(ArgumentMatchers.anyString(), ArgumentMatchers.any())).thenReturn(targetMock);
+ when(targetMock.request(MediaType.APPLICATION_JSON_TYPE)).thenReturn(builderMock);
+ when(builderMock.get()).thenReturn(response200, response401, response500, null);
+
+ underTest.grantAccess(null);
+ assertThrows(AccessControlException.class, () -> underTest.grantAccess(null));
+ assertThrows(Exception.class, () -> underTest.grantAccess(null));
+ assertThrows(Exception.class, () -> underTest.grantAccess(null));
+
+ when(builderMock.get()).thenReturn(response200, response401, response500, null);
+ underTest.revokeAccess(null);
+ assertThrows(AccessControlException.class, () -> underTest.revokeAccess(null));
+ assertThrows(Exception.class, () -> underTest.revokeAccess(null));
+ assertThrows(Exception.class, () -> underTest.revokeAccess(null));
+ }
+
+ @Test
+ public void test07_getUserStore_paths_non_secure() throws Exception {
+ Client clientMock = mock(Client.class);
+ WebTarget targetMock = mock(WebTarget.class);
+ Invocation.Builder builderMock = mock(Invocation.Builder.class);
+ Response response200 = mock(Response.class);
+ Response response304 = mock(Response.class);
+ Response response404 = mock(Response.class);
+ Response response500 = mock(Response.class);
+
+ Configuration conf = buildBaseConfig(BASE_URL);
+ conf.setBoolean(PREFIX + ".forceNonKerberos", true);
+ RangerAdminJersey2RESTClient underTest = new RangerAdminJersey2RESTClient();
+ initWithMockClient(underTest, clientMock, conf);
+
+ when(response200.getStatus()).thenReturn(200);
+ when(response200.readEntity(String.class)).thenReturn("{}");
+ when(response304.getStatus()).thenReturn(304);
+ when(response404.getStatus()).thenReturn(404);
+ when(response404.hasEntity()).thenReturn(false);
+ when(response500.getStatus()).thenReturn(500);
+ when(response500.hasEntity()).thenReturn(true);
+ when(response500.readEntity(String.class)).thenReturn("ERR");
+
+ when(clientMock.target(ArgumentMatchers.anyString())).thenReturn(targetMock);
+ when(targetMock.queryParam(ArgumentMatchers.anyString(), ArgumentMatchers.any())).thenReturn(targetMock);
+ when(targetMock.request(MediaType.APPLICATION_JSON_TYPE)).thenReturn(builderMock);
+ when(builderMock.get()).thenReturn(response200, response304, response404, response500);
+
+ RangerUserStore u200 = underTest.getUserStoreIfUpdated(1L, 2L);
+ assertNotNull(u200);
+ RangerUserStore u304 = underTest.getUserStoreIfUpdated(1L, 2L);
+ assertNull(u304);
+ RangerUserStore u404 = underTest.getUserStoreIfUpdated(1L, 2L);
+ assertNull(u404);
+ RangerUserStore u500 = underTest.getUserStoreIfUpdated(1L, 2L);
+ assertNull(u500);
+ }
+
+ @Test
+ public void test08_shouldRetry_and_exception_path() {
+ Client clientMock = mock(Client.class);
+ Configuration conf = buildBaseConfig(BASE_URL);
+ RangerAdminJersey2RESTClient underTest = new RangerAdminJersey2RESTClient();
+ initWithMockClient(underTest, clientMock, conf);
+
+ boolean first = underTest.shouldRetry(BASE_URL, 0, 0, new ProcessingException("e"));
+ assertEquals(true, first);
+ assertThrows(ProcessingException.class,
+ () -> underTest.shouldRetry(BASE_URL, 0, 2, new ProcessingException("e")));
+ }
+
+ @Test
+ public void test09_getClient_lazy_build_and_singleton() {
+ RangerAdminJersey2RESTClient underTest = new RangerAdminJersey2RESTClient();
+ underTest.isSSL = false;
+ underTest.client = null;
+ Client c1 = underTest.getClient();
+ Client c2 = underTest.getClient();
+ assertNotNull(c1);
+ assertEquals(c1, c2);
+ }
+
+ @Test
+ public void test10_gson_unix_date_deserializer() {
+ GsonUnixDateDeserializer deser = new GsonUnixDateDeserializer();
+ long ts = 123456789L;
+ JsonElement el = new JsonPrimitive(ts);
+ Date d = deser.deserialize(el, Date.class, null);
+ assertEquals(ts, d.getTime());
+ }
+
+ @Test
+ public void test11_getTagTypes_not_implemented() {
+ Configuration conf = buildBaseConfig(BASE_URL);
+ RangerAdminJersey2RESTClient underTest = new RangerAdminJersey2RESTClient();
+ // use a real client here only to init; we won't make network calls
+ underTest.client = mock(Client.class);
+ underTest.init(SERVICE, APPID, PREFIX, conf);
+
+ assertThrows(Exception.class, () -> underTest.getTagTypes("pat"));
+ }
+
+ @Test
+ public void test12_policies_secure_mode_uses_secure_url() throws Exception {
+ Client clientMock = mock(Client.class);
+ WebTarget targetMock = mock(WebTarget.class);
+ Invocation.Builder builderMock = mock(Invocation.Builder.class);
+ Response response200 = mock(Response.class);
+
+ Configuration conf = buildBaseConfig(BASE_URL);
+ conf.setBoolean(PREFIX + ".forceNonKerberos", false);
+ RangerAdminJersey2RESTClient underTest = spy(new RangerAdminJersey2RESTClient());
+ underTest.client = clientMock;
+ underTest.init(SERVICE, APPID, PREFIX, conf);
+ doReturn(true).when(underTest).isKerberosEnabled(ArgumentMatchers.any());
+
+ Map cookies = new HashMap<>();
+ cookies.put(COOKIE_NAME, new NewCookie(COOKIE_NAME, "v"));
+
+ when(response200.getStatus()).thenReturn(200);
+ when(response200.readEntity(String.class)).thenReturn("{}");
+ when(response200.getCookies()).thenReturn(cookies);
+
+ ArgumentCaptor urlCaptor = ArgumentCaptor.forClass(String.class);
+
+ when(clientMock.target(urlCaptor.capture())).thenReturn(targetMock);
+ when(targetMock.queryParam(ArgumentMatchers.anyString(), ArgumentMatchers.any())).thenReturn(targetMock);
+ when(targetMock.request(MediaType.APPLICATION_JSON_TYPE)).thenReturn(builderMock);
+ when(builderMock.cookie(ArgumentMatchers.nullable(Cookie.class))).thenReturn(builderMock);
+ when(builderMock.get()).thenReturn(response200);
+
+ ServicePolicies ret = underTest.getServicePoliciesIfUpdated(1L, 2L);
+ assertNotNull(ret);
+
+ String calledUrl = urlCaptor.getValue();
+ assertNotNull(calledUrl);
+ String expectedPrefix = RangerRESTUtils.REST_URL_POLICY_GET_FOR_SECURE_SERVICE_IF_UPDATED + SERVICE;
+ // expect full URL to contain secure relative path
+ Assertions.assertTrue(calledUrl.contains(expectedPrefix));
+ }
+
+ @Test
+ public void test13_tags_secure_mode_uses_secure_url() throws Exception {
+ Client clientMock = mock(Client.class);
+ WebTarget targetMock = mock(WebTarget.class);
+ Invocation.Builder builderMock = mock(Invocation.Builder.class);
+ Response response200 = mock(Response.class);
+
+ Configuration conf = buildBaseConfig(BASE_URL);
+ conf.setBoolean(PREFIX + ".forceNonKerberos", false);
+ RangerAdminJersey2RESTClient underTest = spy(new RangerAdminJersey2RESTClient());
+ underTest.client = clientMock;
+ underTest.init(SERVICE, APPID, PREFIX, conf);
+ doReturn(true).when(underTest).isKerberosEnabled(ArgumentMatchers.any());
+
+ Map cookies = new HashMap<>();
+ cookies.put(COOKIE_NAME, new NewCookie(COOKIE_NAME, "v"));
+
+ when(response200.getStatus()).thenReturn(200);
+ when(response200.readEntity(String.class)).thenReturn("{}");
+ when(response200.getCookies()).thenReturn(cookies);
+
+ ArgumentCaptor urlCaptor = ArgumentCaptor.forClass(String.class);
+
+ when(clientMock.target(urlCaptor.capture())).thenReturn(targetMock);
+ when(targetMock.queryParam(ArgumentMatchers.anyString(), ArgumentMatchers.any())).thenReturn(targetMock);
+ when(targetMock.request(MediaType.APPLICATION_JSON_TYPE)).thenReturn(builderMock);
+ when(builderMock.cookie(ArgumentMatchers.nullable(Cookie.class))).thenReturn(builderMock);
+ when(builderMock.get()).thenReturn(response200);
+
+ ServiceTags ret = underTest.getServiceTagsIfUpdated(1L, 2L);
+ assertNotNull(ret);
+
+ String calledUrl = urlCaptor.getValue();
+ assertNotNull(calledUrl);
+ String expectedPrefix = RangerRESTUtils.REST_URL_GET_SECURE_SERVICE_TAGS_IF_UPDATED + SERVICE;
+ Assertions.assertTrue(calledUrl.contains(expectedPrefix));
+ }
+
+ @Test
+ public void test14_roles_secure_mode_uses_secure_url() throws Exception {
+ Client clientMock = mock(Client.class);
+ WebTarget targetMock = mock(WebTarget.class);
+ Invocation.Builder builderMock = mock(Invocation.Builder.class);
+ Response response200 = mock(Response.class);
+
+ Configuration conf = buildBaseConfig(BASE_URL);
+ conf.setBoolean(PREFIX + ".forceNonKerberos", false);
+ RangerAdminJersey2RESTClient underTest = spy(new RangerAdminJersey2RESTClient());
+ underTest.client = clientMock;
+ underTest.init(SERVICE, APPID, PREFIX, conf);
+ doReturn(true).when(underTest).isKerberosEnabled(ArgumentMatchers.any());
+
+ Map cookies = new HashMap<>();
+ cookies.put(COOKIE_NAME, new NewCookie(COOKIE_NAME, "v"));
+
+ when(response200.getStatus()).thenReturn(200);
+ when(response200.readEntity(String.class)).thenReturn("{}");
+ when(response200.getCookies()).thenReturn(cookies);
+
+ ArgumentCaptor urlCaptor = ArgumentCaptor.forClass(String.class);
+
+ when(clientMock.target(urlCaptor.capture())).thenReturn(targetMock);
+ when(targetMock.queryParam(ArgumentMatchers.anyString(), ArgumentMatchers.any())).thenReturn(targetMock);
+ when(targetMock.request(MediaType.APPLICATION_JSON_TYPE)).thenReturn(builderMock);
+ when(builderMock.cookie(ArgumentMatchers.nullable(Cookie.class))).thenReturn(builderMock);
+ when(builderMock.get()).thenReturn(response200);
+
+ RangerRoles ret = underTest.getRolesIfUpdated(1L, 2L);
+ assertNotNull(ret);
+
+ String calledUrl = urlCaptor.getValue();
+ assertNotNull(calledUrl);
+ String expectedPrefix = RangerRESTUtils.REST_URL_SERVICE_SERCURE_GET_USER_GROUP_ROLES + SERVICE;
+ Assertions.assertTrue(calledUrl.contains(expectedPrefix));
+ }
+
+ @Test
+ public void test15_userstore_secure_mode_uses_secure_url() throws Exception {
+ Client clientMock = mock(Client.class);
+ WebTarget targetMock = mock(WebTarget.class);
+ Invocation.Builder builderMock = mock(Invocation.Builder.class);
+ Response response200 = mock(Response.class);
+
+ Configuration conf = buildBaseConfig(BASE_URL);
+ conf.setBoolean(PREFIX + ".forceNonKerberos", false);
+ RangerAdminJersey2RESTClient underTest = spy(new RangerAdminJersey2RESTClient());
+ underTest.client = clientMock;
+ underTest.init(SERVICE, APPID, PREFIX, conf);
+ doReturn(true).when(underTest).isKerberosEnabled(ArgumentMatchers.any());
+
+ when(response200.getStatus()).thenReturn(200);
+ when(response200.readEntity(String.class)).thenReturn("{}");
+
+ ArgumentCaptor urlCaptor = ArgumentCaptor.forClass(String.class);
+
+ when(clientMock.target(urlCaptor.capture())).thenReturn(targetMock);
+ when(targetMock.queryParam(ArgumentMatchers.anyString(), ArgumentMatchers.any())).thenReturn(targetMock);
+ when(targetMock.request(MediaType.APPLICATION_JSON_TYPE)).thenReturn(builderMock);
+ when(builderMock.get()).thenReturn(response200);
+
+ RangerUserStore ret = underTest.getUserStoreIfUpdated(1L, 2L);
+ assertNotNull(ret);
+
+ String calledUrl = urlCaptor.getValue();
+ assertNotNull(calledUrl);
+ String expectedPrefix = RangerRESTUtils.REST_URL_SERVICE_SERCURE_GET_USERSTORE + SERVICE;
+ Assertions.assertTrue(calledUrl.contains(expectedPrefix));
+ }
+
+ @Test
+ public void test16_buildClient_ssl_path() throws Exception {
+ RangerAdminJersey2RESTClient underTest = new RangerAdminJersey2RESTClient();
+ underTest.isSSL = true;
+ underTest.sslContext = SSLContext.getInstance("TLS");
+ underTest.sslContext.init(null, null, null);
+ HostnameVerifier verifier = (h, s) -> true;
+ underTest.hv = verifier;
+ underTest.client = null;
+
+ Client c = underTest.buildClient();
+ assertNotNull(c);
+ }
+
+ @Test
+ public void test17_policies_cookie_404_throws_service_not_found() throws Exception {
+ Client clientMock = mock(Client.class);
+ WebTarget targetMock = mock(WebTarget.class);
+ Invocation.Builder builderMock = mock(Invocation.Builder.class);
+ Response response200 = mock(Response.class);
+ Response response404 = mock(Response.class);
+
+ Configuration conf = buildBaseConfig(BASE_URL);
+ RangerAdminJersey2RESTClient underTest = new RangerAdminJersey2RESTClient();
+ initWithMockClient(underTest, clientMock, conf);
+
+ Map cookies = new HashMap<>();
+ cookies.put(COOKIE_NAME, new NewCookie(COOKIE_NAME, "v"));
+
+ when(response200.getStatus()).thenReturn(200);
+ when(response200.readEntity(String.class)).thenReturn("{}");
+ when(response200.getCookies()).thenReturn(cookies);
+
+ when(response404.getStatus()).thenReturn(404);
+ when(response404.hasEntity()).thenReturn(true);
+ when(response404.readEntity(String.class)).thenReturn(RangerServiceNotFoundException.buildExceptionMsg(SERVICE));
+
+ when(clientMock.target(ArgumentMatchers.anyString())).thenReturn(targetMock);
+ when(targetMock.queryParam(ArgumentMatchers.anyString(), ArgumentMatchers.any())).thenReturn(targetMock);
+ when(targetMock.request(MediaType.APPLICATION_JSON_TYPE)).thenReturn(builderMock);
+ when(builderMock.cookie(ArgumentMatchers.nullable(Cookie.class))).thenReturn(builderMock);
+ when(builderMock.get()).thenReturn(response200, response404);
+
+ ServicePolicies first = underTest.getServicePoliciesIfUpdated(1L, 2L);
+ assertNotNull(first);
+ assertThrows(RangerServiceNotFoundException.class, () -> underTest.getServicePoliciesIfUpdated(1L, 2L));
+ }
+
+ @Test
+ public void test18_policies_cookie_default_and_minus1() throws Exception {
+ Client clientMock = mock(Client.class);
+ WebTarget targetMock = mock(WebTarget.class);
+ Invocation.Builder builderMock = mock(Invocation.Builder.class);
+ Response response200 = mock(Response.class);
+ Response response500 = mock(Response.class);
+
+ Configuration conf = buildBaseConfig(BASE_URL);
+ RangerAdminJersey2RESTClient underTest = new RangerAdminJersey2RESTClient();
+ initWithMockClient(underTest, clientMock, conf);
+
+ Map cookies = new HashMap<>();
+ cookies.put(COOKIE_NAME, new NewCookie(COOKIE_NAME, "v"));
+
+ when(response200.getStatus()).thenReturn(200);
+ when(response200.readEntity(String.class)).thenReturn("{}");
+ when(response200.getCookies()).thenReturn(cookies);
+
+ when(response500.getStatus()).thenReturn(500);
+ when(response500.readEntity(String.class)).thenReturn("ERR");
+
+ when(clientMock.target(ArgumentMatchers.anyString())).thenReturn(targetMock);
+ when(targetMock.queryParam(ArgumentMatchers.anyString(), ArgumentMatchers.any())).thenReturn(targetMock);
+ when(targetMock.request(MediaType.APPLICATION_JSON_TYPE)).thenReturn(builderMock);
+ when(builderMock.cookie(ArgumentMatchers.nullable(Cookie.class))).thenReturn(builderMock);
+ when(builderMock.get()).thenReturn(response200, response500, null);
+
+ ServicePolicies first = underTest.getServicePoliciesIfUpdated(1L, 2L);
+ assertNotNull(first);
+ ServicePolicies second = underTest.getServicePoliciesIfUpdated(1L, 2L);
+ assertNull(second);
+ ServicePolicies third = underTest.getServicePoliciesIfUpdated(1L, 2L);
+ assertNull(third);
+ }
+
+ @Test
+ public void test19_roles_cookie_404_throws_service_not_found() throws Exception {
+ Client clientMock = mock(Client.class);
+ WebTarget targetMock = mock(WebTarget.class);
+ Invocation.Builder builderMock = mock(Invocation.Builder.class);
+ Response response200 = mock(Response.class);
+ Response response404 = mock(Response.class);
+
+ Configuration conf = buildBaseConfig(BASE_URL);
+ RangerAdminJersey2RESTClient underTest = new RangerAdminJersey2RESTClient();
+ initWithMockClient(underTest, clientMock, conf);
+
+ Map cookies = new HashMap<>();
+ cookies.put(COOKIE_NAME, new NewCookie(COOKIE_NAME, "v"));
+
+ when(response200.getStatus()).thenReturn(200);
+ when(response200.readEntity(String.class)).thenReturn("{}");
+ when(response200.getCookies()).thenReturn(cookies);
+
+ when(response404.getStatus()).thenReturn(404);
+ when(response404.hasEntity()).thenReturn(true);
+ when(response404.readEntity(String.class)).thenReturn(RangerServiceNotFoundException.buildExceptionMsg(SERVICE));
+
+ when(clientMock.target(ArgumentMatchers.anyString())).thenReturn(targetMock);
+ when(targetMock.queryParam(ArgumentMatchers.anyString(), ArgumentMatchers.any())).thenReturn(targetMock);
+ when(targetMock.request(MediaType.APPLICATION_JSON_TYPE)).thenReturn(builderMock);
+ when(builderMock.cookie(ArgumentMatchers.nullable(Cookie.class))).thenReturn(builderMock);
+ when(builderMock.get()).thenReturn(response200, response404);
+
+ RangerRoles first = underTest.getRolesIfUpdated(1L, 2L);
+ assertNotNull(first);
+ assertThrows(RangerServiceNotFoundException.class, () -> underTest.getRolesIfUpdated(1L, 2L));
+ }
+
+ @Test
+ public void test20_roles_cookie_default_and_minus1() throws Exception {
+ Client clientMock = mock(Client.class);
+ WebTarget targetMock = mock(WebTarget.class);
+ Invocation.Builder builderMock = mock(Invocation.Builder.class);
+ Response response200 = mock(Response.class);
+ Response response500 = mock(Response.class);
+
+ Configuration conf = buildBaseConfig(BASE_URL);
+ RangerAdminJersey2RESTClient underTest = new RangerAdminJersey2RESTClient();
+ initWithMockClient(underTest, clientMock, conf);
+
+ Map cookies = new HashMap<>();
+ cookies.put(COOKIE_NAME, new NewCookie(COOKIE_NAME, "v"));
+
+ when(response200.getStatus()).thenReturn(200);
+ when(response200.readEntity(String.class)).thenReturn("{}");
+ when(response200.getCookies()).thenReturn(cookies);
+
+ when(response500.getStatus()).thenReturn(500);
+ when(response500.readEntity(String.class)).thenReturn("ERR");
+
+ when(clientMock.target(ArgumentMatchers.anyString())).thenReturn(targetMock);
+ when(targetMock.queryParam(ArgumentMatchers.anyString(), ArgumentMatchers.any())).thenReturn(targetMock);
+ when(targetMock.request(MediaType.APPLICATION_JSON_TYPE)).thenReturn(builderMock);
+ when(builderMock.cookie(ArgumentMatchers.nullable(Cookie.class))).thenReturn(builderMock);
+ when(builderMock.get()).thenReturn(response200, response500, null);
+
+ RangerRoles first = underTest.getRolesIfUpdated(1L, 2L);
+ assertNotNull(first);
+ RangerRoles second = underTest.getRolesIfUpdated(1L, 2L);
+ assertNull(second);
+ RangerRoles third = underTest.getRolesIfUpdated(1L, 2L);
+ assertNull(third);
+ }
+
+ @Test
+ public void test21_tags_cookie_404_throws_service_not_found() throws Exception {
+ Client clientMock = mock(Client.class);
+ WebTarget targetMock = mock(WebTarget.class);
+ Invocation.Builder builderMock = mock(Invocation.Builder.class);
+ Response response200 = mock(Response.class);
+ Response response404 = mock(Response.class);
+
+ Configuration conf = buildBaseConfig(BASE_URL);
+ RangerAdminJersey2RESTClient underTest = new RangerAdminJersey2RESTClient();
+ initWithMockClient(underTest, clientMock, conf);
+
+ Map cookies = new HashMap<>();
+ cookies.put(COOKIE_NAME, new NewCookie(COOKIE_NAME, "v"));
+
+ when(response200.getStatus()).thenReturn(200);
+ when(response200.readEntity(String.class)).thenReturn("{}");
+ when(response200.getCookies()).thenReturn(cookies);
+
+ when(response404.getStatus()).thenReturn(404);
+ when(response404.hasEntity()).thenReturn(true);
+ when(response404.readEntity(String.class)).thenReturn(RangerServiceNotFoundException.buildExceptionMsg(SERVICE));
+
+ when(clientMock.target(ArgumentMatchers.anyString())).thenReturn(targetMock);
+ when(targetMock.queryParam(ArgumentMatchers.anyString(), ArgumentMatchers.any())).thenReturn(targetMock);
+ when(targetMock.request(MediaType.APPLICATION_JSON_TYPE)).thenReturn(builderMock);
+ when(builderMock.cookie(ArgumentMatchers.nullable(Cookie.class))).thenReturn(builderMock);
+ when(builderMock.get()).thenReturn(response200, response404);
+
+ ServiceTags first = underTest.getServiceTagsIfUpdated(1L, 2L);
+ assertNotNull(first);
+ assertThrows(RangerServiceNotFoundException.class, () -> underTest.getServiceTagsIfUpdated(1L, 2L));
+ }
+
+ @Test
+ public void test22_tags_cookie_default_and_minus1() throws Exception {
+ Client clientMock = mock(Client.class);
+ WebTarget targetMock = mock(WebTarget.class);
+ Invocation.Builder builderMock = mock(Invocation.Builder.class);
+ Response response200 = mock(Response.class);
+ Response response500 = mock(Response.class);
+
+ Configuration conf = buildBaseConfig(BASE_URL);
+ RangerAdminJersey2RESTClient underTest = new RangerAdminJersey2RESTClient();
+ initWithMockClient(underTest, clientMock, conf);
+
+ Map cookies = new HashMap<>();
+ cookies.put(COOKIE_NAME, new NewCookie(COOKIE_NAME, "v"));
+
+ when(response200.getStatus()).thenReturn(200);
+ when(response200.readEntity(String.class)).thenReturn("{}");
+ when(response200.getCookies()).thenReturn(cookies);
+
+ when(response500.getStatus()).thenReturn(500);
+ when(response500.readEntity(String.class)).thenReturn("ERR");
+
+ when(clientMock.target(ArgumentMatchers.anyString())).thenReturn(targetMock);
+ when(targetMock.queryParam(ArgumentMatchers.anyString(), ArgumentMatchers.any())).thenReturn(targetMock);
+ when(targetMock.request(MediaType.APPLICATION_JSON_TYPE)).thenReturn(builderMock);
+ when(builderMock.cookie(ArgumentMatchers.nullable(Cookie.class))).thenReturn(builderMock);
+ when(builderMock.get()).thenReturn(response200, response500, null);
+
+ ServiceTags first = underTest.getServiceTagsIfUpdated(1L, 2L);
+ assertNotNull(first);
+ ServiceTags second = underTest.getServiceTagsIfUpdated(1L, 2L);
+ assertNull(second);
+ ServiceTags third = underTest.getServiceTagsIfUpdated(1L, 2L);
+ assertNull(third);
+ }
+}
diff --git a/knox-agent/src/test/java/org/apache/ranger/authorization/knox/TestKnoxRangerPlugin.java b/knox-agent/src/test/java/org/apache/ranger/authorization/knox/TestKnoxRangerPlugin.java
new file mode 100644
index 0000000000..704074fc33
--- /dev/null
+++ b/knox-agent/src/test/java/org/apache/ranger/authorization/knox/TestKnoxRangerPlugin.java
@@ -0,0 +1,99 @@
+/**
+ * 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.authorization.knox;
+
+import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
+import org.apache.ranger.plugin.policyengine.RangerAccessResource;
+import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl;
+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.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+* @generated by Cursor
+* @description
+*/
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestKnoxRangerPlugin {
+ @Test
+ public void test01_init_setsResultProcessorOnce() {
+ KnoxRangerPlugin plugin = new KnoxRangerPlugin();
+
+ Assertions.assertNull(plugin.getResultProcessor());
+ plugin.init();
+ Assertions.assertNotNull(plugin.getResultProcessor());
+
+ // second init should not reset and should not throw
+ plugin.init();
+ Assertions.assertNotNull(plugin.getResultProcessor());
+ }
+
+ @Test
+ public void test02_requestBuilder_verifyBuildable_throwsOnMissing() {
+ KnoxRangerPlugin.RequestBuilder b = new KnoxRangerPlugin.RequestBuilder();
+ Assertions.assertThrows(IllegalStateException.class, b::verifyBuildable);
+
+ b.service("svc");
+ Assertions.assertThrows(IllegalStateException.class, b::verifyBuildable);
+
+ b.topology("top");
+ Assertions.assertThrows(IllegalStateException.class, b::verifyBuildable);
+
+ b.user("u");
+ // no exception now
+ b.verifyBuildable();
+ }
+
+ @Test
+ public void test03_requestBuilder_build_setsAllFields() {
+ Set groups = new HashSet<>(Arrays.asList("g1", "g2"));
+ List fwd = Arrays.asList("1.1.1.1", "2.2.2.2");
+
+ KnoxRangerPlugin.RequestBuilder b = new KnoxRangerPlugin.RequestBuilder()
+ .service("svc")
+ .topology("top")
+ .user("user")
+ .groups(groups)
+ .clientIp("10.0.0.1")
+ .remoteIp("10.0.0.1")
+ .forwardedAddresses(fwd);
+
+ RangerAccessRequest req = b.build();
+ Assertions.assertEquals("user", req.getUser());
+ Assertions.assertEquals("allow", req.getAction());
+ Assertions.assertEquals("allow", req.getAccessType());
+ Assertions.assertEquals("10.0.0.1", req.getClientIPAddress());
+ Assertions.assertEquals(groups, req.getUserGroups());
+ Assertions.assertEquals(fwd, req.getForwardedAddresses());
+
+ RangerAccessResource res = req.getResource();
+ Assertions.assertTrue(res instanceof RangerAccessResourceImpl);
+ Assertions.assertEquals("svc", ((RangerAccessResourceImpl) res).getValue(KnoxRangerPlugin.KnoxConstants.ResourceName.Service));
+ Assertions.assertEquals("top", ((RangerAccessResourceImpl) res).getValue(KnoxRangerPlugin.KnoxConstants.ResourceName.Topology));
+ }
+}
diff --git a/knox-agent/src/test/java/org/apache/ranger/authorization/knox/TestRangerPDPKnoxFilter.java b/knox-agent/src/test/java/org/apache/ranger/authorization/knox/TestRangerPDPKnoxFilter.java
new file mode 100644
index 0000000000..12dd3ab88f
--- /dev/null
+++ b/knox-agent/src/test/java/org/apache/ranger/authorization/knox/TestRangerPDPKnoxFilter.java
@@ -0,0 +1,201 @@
+/**
+ * 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.authorization.knox;
+
+import org.apache.knox.gateway.filter.AbstractGatewayFilter;
+import org.apache.knox.gateway.security.GroupPrincipal;
+import org.apache.knox.gateway.security.ImpersonatedPrincipal;
+import org.apache.knox.gateway.security.PrimaryPrincipal;
+import org.apache.ranger.audit.provider.MiscUtil;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
+import org.apache.ranger.plugin.policyengine.RangerAccessResult;
+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 javax.security.auth.Subject;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.security.PrivilegedExceptionAction;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+* @generated by Cursor
+* @description
+*/
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestRangerPDPKnoxFilter {
+ @Test
+ public void test01_init_initializesPluginOnce() {
+ RangerPDPKnoxFilter filter = new RangerPDPKnoxFilter();
+ FilterConfig cfg = mock(FilterConfig.class);
+ when(cfg.getInitParameter("resource.role"))
+ .thenReturn("WEBHDFS");
+
+ try (MockedStatic mu = Mockito.mockStatic(MiscUtil.class)) {
+ filter.init(cfg);
+ // second init call should reuse existing plugin and not throw
+ filter.init(cfg);
+ }
+ }
+
+ @Test
+ public void test02_doFilter_accessAllowed_callsChain() throws Exception {
+ RangerPDPKnoxFilter filter = new RangerPDPKnoxFilter();
+
+ // prepare Subject with principals
+ Subject subject = new Subject();
+ subject.getPrincipals().add(new PrimaryPrincipal("primary"));
+ subject.getPrincipals().add(new ImpersonatedPrincipal("impersonated"));
+ subject.getPrincipals().add(new GroupPrincipal("g1"));
+ subject.getPrincipals().add(new GroupPrincipal("g2"));
+
+ ServletRequest req = mock(HttpServletRequest.class);
+ ServletResponse res = mock(HttpServletResponse.class);
+ FilterChain chain = mock(FilterChain.class);
+
+ when(req.getAttribute(AbstractGatewayFilter.SOURCE_REQUEST_CONTEXT_URL_ATTRIBUTE_NAME))
+ .thenReturn("/cluster/top1/path");
+ when(req.getRemoteAddr()).thenReturn("10.0.0.1");
+
+ // mock plugin
+ KnoxRangerPlugin plugin = Mockito.mock(KnoxRangerPlugin.class);
+ Field f = RangerPDPKnoxFilter.class.getDeclaredField("plugin");
+ f.setAccessible(true);
+ f.set(null, plugin);
+
+ RangerAccessResult allow = Mockito.mock(RangerAccessResult.class);
+ when(allow.getIsAllowed()).thenReturn(true);
+ when(plugin.isAccessAllowed(Mockito.any(RangerAccessRequest.class))).thenReturn(allow);
+
+ Subject.doAs(subject, (PrivilegedExceptionAction) () -> {
+ filter.doFilter(req, res, chain);
+ return null;
+ });
+ verify(chain, times(1)).doFilter(req, res);
+ }
+
+ @Test
+ public void test03_doFilter_accessDenied_sendsForbidden() throws Exception {
+ RangerPDPKnoxFilter filter = new RangerPDPKnoxFilter();
+ Subject subject = new Subject();
+ subject.getPrincipals().add(new PrimaryPrincipal("primary"));
+ ServletRequest req = mock(HttpServletRequest.class);
+ HttpServletResponse res = mock(HttpServletResponse.class);
+ FilterChain chain = mock(FilterChain.class);
+
+ when(req.getAttribute(AbstractGatewayFilter.SOURCE_REQUEST_CONTEXT_URL_ATTRIBUTE_NAME))
+ .thenReturn("/cluster/top1/path");
+ when(req.getRemoteAddr()).thenReturn("10.0.0.1");
+
+ KnoxRangerPlugin plugin = Mockito.mock(KnoxRangerPlugin.class);
+ Field f = RangerPDPKnoxFilter.class.getDeclaredField("plugin");
+ f.setAccessible(true);
+ f.set(null, plugin);
+
+ RangerAccessResult deny = Mockito.mock(RangerAccessResult.class);
+ when(deny.getIsAllowed()).thenReturn(false);
+ when(plugin.isAccessAllowed(Mockito.any(RangerAccessRequest.class))).thenReturn(deny);
+
+ Subject.doAs(subject, (PrivilegedExceptionAction) () -> {
+ filter.doFilter(req, res, chain);
+ return null;
+ });
+ verify(res, times(1)).sendError(403);
+ }
+
+ @Test
+ public void test04_getInitParameter_lowercasesName() {
+ RangerPDPKnoxFilter filter = new RangerPDPKnoxFilter();
+ FilterConfig cfg = mock(FilterConfig.class);
+ when(cfg.getInitParameter("resource.role")).thenReturn("WEBHDFS");
+ Assertions.assertEquals("WEBHDFS", invokeGetInitParameter(filter, cfg, "RESOURCE.ROLE"));
+ }
+
+ @Test
+ public void test05_getForwardedAddresses_parsesHeader() {
+ RangerPDPKnoxFilter filter = new RangerPDPKnoxFilter();
+ HttpServletRequest req = mock(HttpServletRequest.class);
+ when(req.getHeader("X-Forwarded-For")).thenReturn("1.1.1.1,2.2.2.2");
+ List addrs = invokeGetForwardedAddresses(filter, req);
+ Assertions.assertEquals(Arrays.asList("1.1.1.1", "2.2.2.2"), addrs);
+
+ when(req.getHeader("X-Forwarded-For")).thenReturn(null);
+ Assertions.assertNull(invokeGetForwardedAddresses(filter, req));
+ }
+
+ @Test
+ public void test06_getTopologyName_extractsThirdTokenOrNull() {
+ RangerPDPKnoxFilter filter = new RangerPDPKnoxFilter();
+ Assertions.assertNull(invokeGetTopologyName(filter, null));
+ Assertions.assertNull(invokeGetTopologyName(filter, "/one"));
+ Assertions.assertEquals("two", invokeGetTopologyName(filter, "/one/two/three"));
+ }
+
+ private String invokeGetInitParameter(RangerPDPKnoxFilter filter, FilterConfig cfg, String name) {
+ try {
+ Method m = RangerPDPKnoxFilter.class.getDeclaredMethod("getInitParameter", FilterConfig.class, String.class);
+ m.setAccessible(true);
+ return (String) m.invoke(filter, cfg, name);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private List invokeGetForwardedAddresses(RangerPDPKnoxFilter filter, ServletRequest req) {
+ try {
+ Method m = RangerPDPKnoxFilter.class.getDeclaredMethod("getForwardedAddresses", ServletRequest.class);
+ m.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ List ret = (List) m.invoke(filter, req);
+ return ret;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private String invokeGetTopologyName(RangerPDPKnoxFilter filter, String url) {
+ try {
+ Method m = RangerPDPKnoxFilter.class.getDeclaredMethod("getTopologyName", String.class);
+ m.setAccessible(true);
+ return (String) m.invoke(filter, url);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/knox-agent/src/test/java/org/apache/ranger/services/knox/TestRangerServiceKnox.java b/knox-agent/src/test/java/org/apache/ranger/services/knox/TestRangerServiceKnox.java
new file mode 100644
index 0000000000..e80b95a783
--- /dev/null
+++ b/knox-agent/src/test/java/org/apache/ranger/services/knox/TestRangerServiceKnox.java
@@ -0,0 +1,162 @@
+/**
+ * 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.knox;
+
+import org.apache.ranger.plugin.client.HadoopException;
+import org.apache.ranger.plugin.model.RangerPolicy;
+import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItem;
+import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemAccess;
+import org.apache.ranger.plugin.model.RangerService;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.service.ResourceLookupContext;
+import org.apache.ranger.services.knox.client.KnoxResourceMgr;
+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.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @generated by Cursor
+ * @description
+ */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestRangerServiceKnox {
+ private RangerServiceKnox createService(String serviceName, Map configs) {
+ RangerServiceKnox svc = new RangerServiceKnox();
+ RangerServiceDef def = new RangerServiceDef();
+ RangerService service = new RangerService("knox", serviceName, "desc", null, configs);
+ svc.init(def, service);
+ return svc;
+ }
+
+ @Test
+ public void test01_validateConfig_success() throws Exception {
+ Map cfg = new HashMap<>();
+ cfg.put("knox.url", "https://knox");
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+
+ RangerServiceKnox svc = createService("svc", cfg);
+
+ try (MockedStatic km = Mockito.mockStatic(KnoxResourceMgr.class)) {
+ Map expected = new HashMap<>();
+ expected.put("connectivityStatus", true);
+ km.when(() -> KnoxResourceMgr.validateConfig("svc", cfg)).thenReturn(expected);
+
+ Map resp = svc.validateConfig();
+ Assertions.assertEquals(Boolean.TRUE, resp.get("connectivityStatus"));
+ }
+ }
+
+ @Test
+ public void test02_validateConfig_exceptionPropagates() throws Exception {
+ Map cfg = new HashMap<>();
+ RangerServiceKnox svc = createService("svc", cfg);
+ try (MockedStatic km = Mockito.mockStatic(KnoxResourceMgr.class)) {
+ km.when(() -> KnoxResourceMgr.validateConfig("svc", cfg)).thenThrow(new HadoopException("boom"));
+ Assertions.assertThrows(HadoopException.class, svc::validateConfig);
+ }
+ }
+
+ @Test
+ public void test03_lookupResource_nullContext_returnsEmpty() throws Exception {
+ RangerServiceKnox svc = createService("svc", new HashMap<>());
+ List ret = svc.lookupResource(null);
+ Assertions.assertNotNull(ret);
+ Assertions.assertTrue(ret.isEmpty());
+ }
+
+ @Test
+ public void test04_lookupResource_success() throws Exception {
+ Map cfg = new HashMap<>();
+ RangerServiceKnox svc = createService("svc", cfg);
+ ResourceLookupContext ctx = new ResourceLookupContext();
+ ctx.setResourceName("topology");
+ ctx.setUserInput("adm");
+
+ try (MockedStatic km = Mockito.mockStatic(KnoxResourceMgr.class)) {
+ List expected = new ArrayList<>();
+ expected.add("admin");
+ km.when(() -> KnoxResourceMgr.getKnoxResources("svc", cfg, ctx)).thenReturn(expected);
+
+ List ret = svc.lookupResource(ctx);
+ Assertions.assertEquals(expected, ret);
+ }
+ }
+
+ @Test
+ public void test05_lookupResource_exceptionPropagates() throws Exception {
+ Map cfg = new HashMap<>();
+ RangerServiceKnox svc = createService("svc", cfg);
+ ResourceLookupContext ctx = new ResourceLookupContext();
+
+ try (MockedStatic km = Mockito.mockStatic(KnoxResourceMgr.class)) {
+ km.when(() -> KnoxResourceMgr.getKnoxResources("svc", cfg, ctx)).thenThrow(new HadoopException("boom"));
+ Assertions.assertThrows(HadoopException.class, () -> svc.lookupResource(ctx));
+ }
+ }
+
+ @Test
+ public void test06_getDefaultRangerPolicies_addsLookupUserPolicyItem() throws Exception {
+ Map cfg = new HashMap<>();
+ cfg.put("setup.additional.default.policies", "true");
+ cfg.put("default-policy.1.name", "all endpoints");
+ cfg.put("default-policy.1.resource.path", "/*");
+ cfg.put("default-policy.1.policyItem.1.users", "user1");
+ cfg.put("default-policy.1.policyItem.1.accessTypes", "read,write");
+
+ RangerServiceKnox svc = createService("svc", cfg);
+
+ // set lookUpUser via reflection to cover branch in RangerServiceKnox
+ Field f = org.apache.ranger.plugin.service.RangerBaseService.class.getDeclaredField("lookUpUser");
+ f.setAccessible(true);
+ f.set(svc, "lookupUser");
+
+ List policies = svc.getDefaultRangerPolicies();
+ Assertions.assertNotNull(policies);
+ Assertions.assertFalse(policies.isEmpty());
+
+ boolean foundAugmented = false;
+ for (RangerPolicy p : policies) {
+ if (p.getName() != null && p.getName().contains("all")) {
+ for (RangerPolicyItem item : p.getPolicyItems()) {
+ if (item.getUsers() != null && item.getUsers().contains("lookupUser")) {
+ List acc = item.getAccesses();
+ Assertions.assertEquals(1, acc.size());
+ Assertions.assertEquals(RangerServiceKnox.ACCESS_TYPE_ALLOW, acc.get(0).getType());
+ foundAugmented = true;
+ break;
+ }
+ }
+ }
+ }
+ Assertions.assertTrue(foundAugmented, "Expected policy item for lookupUser was not added");
+ }
+}
diff --git a/knox-agent/src/test/java/org/apache/ranger/services/knox/client/TestKnoxClient.java b/knox-agent/src/test/java/org/apache/ranger/services/knox/client/TestKnoxClient.java
new file mode 100644
index 0000000000..e70f7b6914
--- /dev/null
+++ b/knox-agent/src/test/java/org/apache/ranger/services/knox/client/TestKnoxClient.java
@@ -0,0 +1,438 @@
+/**
+ * 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.knox.client;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.WebResource.Builder;
+import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
+import org.apache.ranger.plugin.client.HadoopException;
+import org.apache.ranger.plugin.util.PasswordUtils;
+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.MockedConstruction;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.security.Permission;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * @generated by Cursor
+ * @description
+ */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestKnoxClient {
+ @Test
+ public void test01_connectionTest_success() {
+ Map configs = new HashMap<>();
+ configs.put("knox.url", "https://example:8443/gateway/admin/api/v1/topologies");
+ configs.put("username", "admin");
+ configs.put("password", "pwd");
+
+ try (MockedStatic knoxStatic = Mockito.mockStatic(KnoxClient.class)) {
+ KnoxClient dummy = new KnoxClient("u", "u", "p");
+ knoxStatic.when(() -> KnoxClient.connectionTest("svc", configs)).thenCallRealMethod();
+ knoxStatic.when(() -> KnoxClient.getKnoxClient("svc", configs)).thenReturn(dummy);
+ knoxStatic.when(() -> KnoxClient.getKnoxResources(Mockito.eq(dummy), anyString(), Mockito.isNull(),
+ Mockito.isNull(), Mockito.isNull())).thenReturn(Collections.singletonList("top1"));
+
+ Map resp = KnoxClient.connectionTest("svc", configs);
+
+ Assertions.assertNotNull(resp);
+ Assertions.assertEquals(Boolean.TRUE, resp.get("connectivityStatus"));
+ Assertions.assertEquals("ConnectionTest Successful", resp.get("message"));
+ Assertions.assertEquals("ConnectionTest Successful", resp.get("description"));
+ Assertions.assertNull(resp.get("objectId"));
+ Assertions.assertNull(resp.get("fieldName"));
+ }
+ }
+
+ @Test
+ public void test02_connectionTest_failure() {
+ Map configs = new HashMap<>();
+ configs.put("knox.url", "https://example:8443/gateway/admin/api/v1/topologies");
+ configs.put("username", "admin");
+ configs.put("password", "pwd");
+
+ try (MockedStatic knoxStatic = Mockito.mockStatic(KnoxClient.class)) {
+ KnoxClient dummy = new KnoxClient("u", "u", "p");
+ knoxStatic.when(() -> KnoxClient.connectionTest("svc", configs)).thenCallRealMethod();
+ knoxStatic.when(() -> KnoxClient.getKnoxClient("svc", configs)).thenReturn(dummy);
+ knoxStatic.when(() -> KnoxClient.getKnoxResources(Mockito.eq(dummy), anyString(), Mockito.isNull(),
+ Mockito.isNull(), Mockito.isNull())).thenReturn(Collections.emptyList());
+
+ Map resp = KnoxClient.connectionTest("svc", configs);
+
+ Assertions.assertNotNull(resp);
+ Assertions.assertEquals(Boolean.FALSE, resp.get("connectivityStatus"));
+ Assertions.assertEquals("Unable to retrieve any topologies/services using given parameters.",
+ resp.get("message"));
+ Assertions.assertTrue(String.valueOf(resp.get("description")).contains("You can still save"));
+ }
+ }
+
+ @Test
+ public void test03_getKnoxClient_validConfigs() {
+ Map configs = new HashMap<>();
+ configs.put("knox.url", "https://example");
+ configs.put("username", "admin");
+ configs.put("password", "pwd");
+
+ KnoxClient client = KnoxClient.getKnoxClient("svc", configs);
+ Assertions.assertNotNull(client);
+ }
+
+ @Test
+ public void test04_getKnoxClient_emptyConfigs_throws() {
+ Map configs = new HashMap<>();
+ HadoopException ex = Assertions.assertThrows(HadoopException.class,
+ () -> KnoxClient.getKnoxClient("svc", configs));
+ Assertions.assertTrue(ex.getResponseData().containsKey("connectivityStatus"));
+ Assertions.assertEquals(Boolean.FALSE, ex.getResponseData().get("connectivityStatus"));
+ Assertions.assertTrue(String.valueOf(ex.getResponseData().get("description")).contains("You can still save"));
+ }
+
+ @Test
+ public void test05_getKnoxResources_nullClient_throws() {
+ HadoopException ex = Assertions.assertThrows(HadoopException.class,
+ () -> KnoxClient.getKnoxResources(null, "t", null, null, null));
+ Assertions.assertEquals(Boolean.FALSE, ex.getResponseData().get("connectivityStatus"));
+ }
+
+ @Test
+ public void test06_getKnoxResources_callsTopologyList() throws Exception {
+ KnoxClient knoxClient = mock(KnoxClient.class);
+ List expected = Arrays.asList("topA", "topB");
+ when(knoxClient.getTopologyList(Mockito.eq("adm"), Mockito.isNull())).thenReturn(expected);
+
+ List result = KnoxClient.getKnoxResources(knoxClient, "adm", null, null, null);
+ Assertions.assertEquals(expected, result);
+ }
+
+ @Test
+ public void test07_getKnoxResources_callsServiceList() throws Exception {
+ KnoxClient knoxClient = mock(KnoxClient.class);
+ List expected = Arrays.asList("HIVE", "NAMENODE");
+ when(knoxClient.getServiceList(Mockito.anyList(), Mockito.eq("HI"), Mockito.isNull())).thenReturn(expected);
+
+ List topoList = new ArrayList<>();
+ List result = KnoxClient.getKnoxResources(knoxClient, null, "HI", topoList, null);
+ Assertions.assertEquals(expected, result);
+ }
+
+ @Test
+ public void test08_getKnoxResources_wrapsException() throws Exception {
+ KnoxClient knoxClient = mock(KnoxClient.class);
+ when(knoxClient.getTopologyList(Mockito.eq(""), Mockito.isNull())).thenThrow(new RuntimeException("boom"));
+
+ HadoopException ex = Assertions.assertThrows(HadoopException.class,
+ () -> KnoxClient.getKnoxResources(knoxClient, null, null, null, null));
+ Assertions.assertTrue(String.valueOf(ex.getResponseData().get("description")).contains("You can still save"));
+ }
+
+ @Test
+ public void test09_timedTask_success() throws Exception {
+ String value = KnoxClient.timedTask(new Callable() {
+ @Override
+ public String call() {
+ return "ok";
+ }
+ }, 1L, TimeUnit.SECONDS);
+ Assertions.assertEquals("ok", value);
+ }
+
+ @Test
+ public void test10_timedTask_throws() {
+ Assertions.assertThrows(Exception.class, () -> KnoxClient.timedTask(new Callable() {
+ @Override
+ public String call() throws Exception {
+ throw new Exception("x");
+ }
+ }, 1L, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void test11_getTopologyList_status200_and_filters() {
+ KnoxClient client = new KnoxClient("https://knox/topologies", "admin", "pwd");
+
+ try (MockedStatic pwStatic = Mockito.mockStatic(PasswordUtils.class);
+ MockedStatic clientStatic = Mockito.mockStatic(Client.class)) {
+ pwStatic.when(() -> PasswordUtils.decryptPassword("pwd")).thenReturn("dec");
+
+ Client jersey = mock(Client.class);
+ WebResource wr = mock(WebResource.class);
+ Builder builder = mock(Builder.class);
+ ClientResponse response = mock(ClientResponse.class);
+
+ clientStatic.when(Client::create).thenReturn(jersey);
+ Mockito.doNothing().when(jersey).addFilter(Mockito.any(HTTPBasicAuthFilter.class));
+ when(jersey.resource("https://knox/topologies")).thenReturn(wr);
+ when(wr.accept("application/json")).thenReturn(builder);
+ when(builder.get(ClientResponse.class)).thenReturn(response);
+
+ when(response.getStatus()).thenReturn(200);
+ String json = "{\"topology\":[{\"name\":\"admin\"},{\"name\":\"gateway\"}]}";
+ when(response.getEntity(String.class)).thenReturn(json);
+
+ List listAll = client.getTopologyList("*", null);
+ Assertions.assertEquals(2, listAll.size());
+
+ List listFiltered = client.getTopologyList("adm", null);
+ Assertions.assertEquals(1, listFiltered.size());
+
+ List skipDueToContains = client.getTopologyList("adm", Collections.singletonList("adm"));
+ Assertions.assertTrue(skipDueToContains.isEmpty());
+
+ verify(response, times(3)).close();
+ verify(jersey, times(3)).destroy();
+ }
+ }
+
+ @Test
+ public void test12_getTopologyList_statusNot200_returnsEmpty() {
+ KnoxClient client = new KnoxClient("https://knox/topologies", "admin", "pwd");
+
+ try (MockedStatic clientStatic = Mockito.mockStatic(Client.class)) {
+ Client jersey = mock(Client.class);
+ WebResource wr = mock(WebResource.class);
+ Builder builder = mock(Builder.class);
+ ClientResponse response = mock(ClientResponse.class);
+
+ clientStatic.when(Client::create).thenReturn(jersey);
+ Mockito.doNothing().when(jersey).addFilter(Mockito.any(HTTPBasicAuthFilter.class));
+ when(jersey.resource("https://knox/topologies")).thenReturn(wr);
+ when(wr.accept("application/json")).thenReturn(builder);
+ when(builder.get(ClientResponse.class)).thenReturn(response);
+
+ when(response.getStatus()).thenReturn(500);
+
+ List list = client.getTopologyList("*", null);
+ Assertions.assertTrue(list.isEmpty());
+ }
+ }
+
+ @Test
+ public void test13_getTopologyList_responseNull_throws() {
+ KnoxClient client = new KnoxClient("https://knox/topologies", "admin", "pwd");
+
+ try (MockedStatic clientStatic = Mockito.mockStatic(Client.class)) {
+ Client jersey = mock(Client.class);
+ WebResource wr = mock(WebResource.class);
+ Builder builder = mock(Builder.class);
+
+ clientStatic.when(Client::create).thenReturn(jersey);
+ Mockito.doNothing().when(jersey).addFilter(Mockito.any(HTTPBasicAuthFilter.class));
+ when(jersey.resource("https://knox/topologies")).thenReturn(wr);
+ when(wr.accept("application/json")).thenReturn(builder);
+ when(builder.get(ClientResponse.class)).thenReturn(null);
+
+ Assertions.assertThrows(HadoopException.class, () -> client.getTopologyList("*", null));
+ }
+ }
+
+ @Test
+ public void test14_getTopologyList_throwsWrapped() {
+ KnoxClient client = new KnoxClient("https://knox/topologies", "admin", "pwd");
+
+ try (MockedStatic clientStatic = Mockito.mockStatic(Client.class)) {
+ Client jersey = mock(Client.class);
+ clientStatic.when(Client::create).thenReturn(jersey);
+ Mockito.doNothing().when(jersey).addFilter(Mockito.any(HTTPBasicAuthFilter.class));
+ when(jersey.resource("https://knox/topologies")).thenThrow(new RuntimeException("boom"));
+
+ Assertions.assertThrows(HadoopException.class, () -> client.getTopologyList("*", null));
+ }
+ }
+
+ @Test
+ public void test15_getServiceList_happyPath_andFilters() {
+ KnoxClient client = new KnoxClient("https://knox/topologies", "admin", "pwd");
+
+ try (MockedStatic pwStatic = Mockito.mockStatic(PasswordUtils.class);
+ MockedStatic clientStatic = Mockito.mockStatic(Client.class)) {
+ pwStatic.when(() -> PasswordUtils.decryptPassword("pwd")).thenReturn("dec");
+
+ Client jersey = mock(Client.class);
+ WebResource wr = mock(WebResource.class);
+ Builder builder = mock(Builder.class);
+ ClientResponse response = mock(ClientResponse.class);
+
+ clientStatic.when(Client::create).thenReturn(jersey);
+ Mockito.doNothing().when(jersey).addFilter(Mockito.any(HTTPBasicAuthFilter.class));
+ when(jersey.resource("https://knox/topologies/top1")).thenReturn(wr);
+ when(wr.accept("application/json")).thenReturn(builder);
+ when(builder.get(ClientResponse.class)).thenReturn(response);
+
+ when(response.getStatus()).thenReturn(200);
+ String json = "{\"topology\":{\"service\":[{\"role\":\"HIVE\"},{\"role\":\"NAMENODE\"}]}}";
+ when(response.getEntity(String.class)).thenReturn(json);
+
+ List tops = Collections.singletonList("top1");
+ List all = client.getServiceList(tops, "*", null);
+ Assertions.assertEquals(2, all.size());
+
+ List filtered = client.getServiceList(tops, "HI", null);
+ Assertions.assertEquals(1, filtered.size());
+
+ List skip = client.getServiceList(tops, "HI", Collections.singletonList("HIVE"));
+ Assertions.assertTrue(skip.isEmpty());
+ }
+ }
+
+ @Test
+ public void test16_getServiceList_statusNot200_logsAndContinues() {
+ KnoxClient client = new KnoxClient("https://knox/topologies", "admin", "pwd");
+
+ try (MockedStatic clientStatic = Mockito.mockStatic(Client.class)) {
+ Client jersey = mock(Client.class);
+ WebResource wr = mock(WebResource.class);
+ Builder builder = mock(Builder.class);
+ ClientResponse response = mock(ClientResponse.class);
+
+ clientStatic.when(Client::create).thenReturn(jersey);
+ Mockito.doNothing().when(jersey).addFilter(Mockito.any(HTTPBasicAuthFilter.class));
+ when(jersey.resource("https://knox/topologies/top1")).thenReturn(wr);
+ when(wr.accept("application/json")).thenReturn(builder);
+ when(builder.get(ClientResponse.class)).thenReturn(response);
+
+ when(response.getStatus()).thenReturn(500);
+
+ List list = client.getServiceList(Collections.singletonList("top1"), "*", null);
+ Assertions.assertTrue(list.isEmpty());
+ }
+ }
+
+ @Test
+ public void test17_getServiceList_responseNull_throws() {
+ KnoxClient client = new KnoxClient("https://knox/topologies", "admin", "pwd");
+
+ try (MockedStatic clientStatic = Mockito.mockStatic(Client.class)) {
+ Client jersey = mock(Client.class);
+ WebResource wr = mock(WebResource.class);
+ Builder builder = mock(Builder.class);
+
+ clientStatic.when(Client::create).thenReturn(jersey);
+ Mockito.doNothing().when(jersey).addFilter(Mockito.any(HTTPBasicAuthFilter.class));
+ when(jersey.resource("https://knox/topologies/top1")).thenReturn(wr);
+ when(wr.accept("application/json")).thenReturn(builder);
+ when(builder.get(ClientResponse.class)).thenReturn(null);
+
+ Assertions.assertThrows(HadoopException.class,
+ () -> client.getServiceList(Collections.singletonList("top1"), "*", null));
+ }
+ }
+
+ @Test
+ public void test18_getServiceList_throwsWrapped() {
+ KnoxClient client = new KnoxClient("https://knox/topologies", "admin", "pwd");
+
+ try (MockedStatic clientStatic = Mockito.mockStatic(Client.class)) {
+ Client jersey = mock(Client.class);
+ clientStatic.when(Client::create).thenReturn(jersey);
+ Mockito.doNothing().when(jersey).addFilter(Mockito.any(HTTPBasicAuthFilter.class));
+ when(jersey.resource("https://knox/topologies/top1")).thenThrow(new RuntimeException("boom"));
+
+ Assertions.assertThrows(HadoopException.class,
+ () -> client.getServiceList(Collections.singletonList("top1"), "*", null));
+ }
+ }
+
+ @Test
+ public void test19_main_invalidArgs_exitsWithStatus1() {
+ SecurityManager originalSm = System.getSecurityManager();
+ class NoExitSecurityManager extends SecurityManager {
+ private Integer status;
+
+ @Override
+ public void checkPermission(Permission perm) {
+ // allow
+ }
+
+ @Override
+ public void checkPermission(Permission perm, Object context) {
+ // allow
+ }
+
+ @Override
+ public void checkExit(int status) {
+ this.status = status;
+ throw new SecurityException("exit");
+ }
+ }
+
+ NoExitSecurityManager sm = new NoExitSecurityManager();
+ PrintStream origErr = System.err;
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setErr(new PrintStream(baos));
+ System.setSecurityManager(sm);
+ try {
+ Assertions.assertThrows(SecurityException.class, () -> KnoxClient.main(new String[] {"only", "two"}));
+ Assertions.assertEquals(Integer.valueOf(1), sm.status);
+ String err = baos.toString();
+ Assertions.assertTrue(err.contains("USAGE: java "));
+ Assertions.assertTrue(err.contains("KnoxClient"));
+ } finally {
+ System.setErr(origErr);
+ System.setSecurityManager(originalSm);
+ }
+ }
+
+ @Test
+ public void test20_main_validArgs_happyPath_printsServices() {
+ String[] args = new String[] {"https://knox", "admin", "pwd"};
+ PrintStream origOut = System.out;
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+
+ try (MockedConstruction construction = Mockito.mockConstruction(KnoxClient.class,
+ (mock, context) -> {
+ when(mock.getTopologyList("", null)).thenReturn(Collections.singletonList("top1"));
+ when(mock.getServiceList(Collections.singletonList("top1"), "*", null))
+ .thenReturn(Collections.singletonList("HIVE"));
+ })) {
+ KnoxClient.main(args);
+ String out = baos.toString();
+ Assertions.assertTrue(out.contains("Found service for topology:"));
+ } finally {
+ System.setOut(origOut);
+ }
+ }
+}
diff --git a/knox-agent/src/test/java/org/apache/ranger/services/knox/client/TestKnoxConnectionMgr.java b/knox-agent/src/test/java/org/apache/ranger/services/knox/client/TestKnoxConnectionMgr.java
new file mode 100644
index 0000000000..2c8df80dc9
--- /dev/null
+++ b/knox-agent/src/test/java/org/apache/ranger/services/knox/client/TestKnoxConnectionMgr.java
@@ -0,0 +1,103 @@
+/**
+ * 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.knox.client;
+
+import org.apache.ranger.plugin.model.RangerService;
+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.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @generated by Cursor
+ * @description
+ */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestKnoxConnectionMgr {
+ @Test
+ public void test01_getKnoxClientbyService_nullService_returnsNull() {
+ KnoxConnectionMgr mgr = new KnoxConnectionMgr();
+ Assertions.assertNull(mgr.getKnoxClientbyService(null));
+ }
+
+ @Test
+ public void test02_getKnoxClientbyService_validService_returnsClient() {
+ RangerService svc = Mockito.mock(RangerService.class);
+ Map cfg = new HashMap<>();
+ cfg.put("knox.url", "https://knox");
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+ Mockito.when(svc.getConfigs()).thenReturn(cfg);
+
+ KnoxConnectionMgr mgr = new KnoxConnectionMgr();
+ KnoxClient client = mgr.getKnoxClientbyService(svc);
+ Assertions.assertNotNull(client);
+ }
+
+ @Test
+ public void test03_getKnoxClientByConfig_null_returnsNull() {
+ KnoxConnectionMgr mgr = new KnoxConnectionMgr();
+ Assertions.assertNull(mgr.getKnoxClientByConfig(null));
+ }
+
+ @Test
+ public void test04_getKnoxClientByConfig_valid_returnsClient() {
+ KnoxConnectionMgr mgr = new KnoxConnectionMgr();
+ Map cfg = new HashMap<>();
+ cfg.put("knox.url", "https://knox");
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+ Assertions.assertNotNull(mgr.getKnoxClientByConfig(cfg));
+ }
+
+ @Test
+ public void test05_getKnoxClient_nullConfig_returnsNull() {
+ KnoxConnectionMgr mgr = new KnoxConnectionMgr();
+ Assertions.assertNull(mgr.getKnoxClient("svc", null));
+ }
+
+ @Test
+ public void test06_getKnoxClient_validConfig_returnsClient() {
+ KnoxConnectionMgr mgr = new KnoxConnectionMgr();
+ Map cfg = new HashMap<>();
+ cfg.put("knox.url", "https://knox");
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+ Assertions.assertNotNull(mgr.getKnoxClient("svc", cfg));
+ }
+
+ @Test
+ public void test07_getKnoxClient_byValues_validatesInputs() {
+ KnoxConnectionMgr mgr = new KnoxConnectionMgr();
+ Assertions.assertNull(mgr.getKnoxClient(null, "u", "p"));
+ Assertions.assertNull(mgr.getKnoxClient("", "u", "p"));
+ Assertions.assertNull(mgr.getKnoxClient("https://knox", null, "p"));
+ Assertions.assertNull(mgr.getKnoxClient("https://knox", "", "p"));
+ Assertions.assertNull(mgr.getKnoxClient("https://knox", "u", null));
+ Assertions.assertNull(mgr.getKnoxClient("https://knox", "u", ""));
+ Assertions.assertNotNull(mgr.getKnoxClient("https://knox", "u", "p"));
+ }
+}
diff --git a/knox-agent/src/test/java/org/apache/ranger/services/knox/client/TestKnoxResourceMgr.java b/knox-agent/src/test/java/org/apache/ranger/services/knox/client/TestKnoxResourceMgr.java
new file mode 100644
index 0000000000..3beeb1aaa3
--- /dev/null
+++ b/knox-agent/src/test/java/org/apache/ranger/services/knox/client/TestKnoxResourceMgr.java
@@ -0,0 +1,157 @@
+/**
+ * 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.knox.client;
+
+import org.apache.ranger.plugin.client.HadoopException;
+import org.apache.ranger.plugin.service.ResourceLookupContext;
+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.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @generated by Cursor
+ * @description
+ */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestKnoxResourceMgr {
+ @Test
+ public void test01_validateConfig_success() {
+ Map cfg = new HashMap<>();
+ cfg.put("knox.url", "https://knox");
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+
+ try (MockedStatic kc = Mockito.mockStatic(KnoxClient.class)) {
+ kc.when(() -> KnoxClient.connectionTest("svc", cfg)).thenReturn(new HashMap() {
+ {
+ put("connectivityStatus", true);
+ put("message", "ok");
+ put("description", "ok");
+ }
+ });
+
+ Map resp = KnoxResourceMgr.validateConfig("svc", cfg);
+ Assertions.assertEquals(Boolean.TRUE, resp.get("connectivityStatus"));
+ }
+ }
+
+ @Test
+ public void test02_validateConfig_exceptionPropagates() {
+ Map cfg = new HashMap<>();
+ try (MockedStatic kc = Mockito.mockStatic(KnoxClient.class)) {
+ kc.when(() -> KnoxClient.connectionTest("svc", cfg)).thenThrow(new HadoopException("boom"));
+ Assertions.assertThrows(HadoopException.class, () -> KnoxResourceMgr.validateConfig("svc", cfg));
+ }
+ }
+
+ @Test
+ public void test03_getKnoxResources_missingConfig_returnsNull() {
+ ResourceLookupContext ctx = new ResourceLookupContext();
+ ctx.setUserInput("x");
+ ctx.setResourceName("topology");
+
+ Map cfg = new HashMap<>();
+ // missing all
+ Assertions.assertNull(KnoxResourceMgr.getKnoxResources("svc", cfg, ctx));
+
+ cfg.put("knox.url", "");
+ Assertions.assertNull(KnoxResourceMgr.getKnoxResources("svc", cfg, ctx));
+
+ cfg.put("knox.url", "https://knox");
+ Assertions.assertNull(KnoxResourceMgr.getKnoxResources("svc", cfg, ctx));
+
+ cfg.put("username", "u");
+ Assertions.assertNull(KnoxResourceMgr.getKnoxResources("svc", cfg, ctx));
+ }
+
+ @Test
+ public void test04_getKnoxResources_topologyFlow() {
+ ResourceLookupContext ctx = new ResourceLookupContext();
+ ctx.setUserInput("adm");
+ ctx.setResourceName("topology");
+
+ Map> resMap = new HashMap<>();
+ resMap.put("topology", new ArrayList<>());
+ ctx.setResources(resMap);
+
+ Map cfg = new HashMap<>();
+ cfg.put("knox.url", "https://knox");
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+
+ try (MockedStatic kc = Mockito.mockStatic(KnoxClient.class)) {
+ kc.when(() -> KnoxClient.getKnoxResources(Mockito.any(KnoxClient.class), Mockito.eq("adm"),
+ Mockito.isNull(), Mockito.anyList(), Mockito.isNull())).thenReturn(new ArrayList() {
+ {
+ add("admin");
+ }
+ });
+
+ // Use real call for connection mgr within KnoxResourceMgr
+ List result = KnoxResourceMgr.getKnoxResources("svc", cfg, ctx);
+ Assertions.assertNotNull(result);
+ Assertions.assertEquals(1, result.size());
+ }
+ }
+
+ @Test
+ public void test05_getKnoxResources_serviceFlow() {
+ ResourceLookupContext ctx = new ResourceLookupContext();
+ ctx.setUserInput("HI");
+ ctx.setResourceName("service");
+
+ Map> resMap = new HashMap<>();
+ resMap.put("service", new ArrayList<>());
+ resMap.put("topology", new ArrayList() {
+ {
+ add("top1");
+ }
+ });
+ ctx.setResources(resMap);
+
+ Map cfg = new HashMap<>();
+ cfg.put("knox.url", "https://knox");
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+
+ try (MockedStatic kc = Mockito.mockStatic(KnoxClient.class)) {
+ kc.when(() -> KnoxClient.getKnoxResources(Mockito.any(KnoxClient.class), Mockito.isNull(), Mockito.eq("HI"),
+ Mockito.anyList(), Mockito.anyList())).thenReturn(new ArrayList() {
+ {
+ add("HIVE");
+ }
+ });
+
+ List result = KnoxResourceMgr.getKnoxResources("svc", cfg, ctx);
+ Assertions.assertNotNull(result);
+ Assertions.assertEquals(1, result.size());
+ }
+ }
+}