This is an automated email from the ASF dual-hosted git repository.
dhavalshah9131 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/master by this push:
new 9403f5f16 RANGER-5344:Test Cases for hdfs-agent [ranger-hdfs-plugin]
Module (#690)
9403f5f16 is described below
commit 9403f5f16b9a87249bd6a55252839122ddb2acd4
Author: Bhaavesh Amol Amre <[email protected]>
AuthorDate: Fri Nov 28 18:44:53 2025 +0530
RANGER-5344:Test Cases for hdfs-agent [ranger-hdfs-plugin] Module (#690)
---
hdfs-agent/pom.xml | 12 +
.../hadoop/TestRangerAccessControlEnforcer.java | 493 +++++++++++++++++++++
.../TestRangerAccessControlException.java | 52 +++
.../ranger/services/hdfs/HDFSRangerTest.java | 66 +--
.../services/hdfs/RangerHdfsAuthorizerTest.java | 19 +-
.../services/hdfs/TestRangerServiceHdfs.java | 176 ++++++++
.../services/hdfs/client/HdfsClientTest.java | 354 ++++++++++++++-
.../hdfs/client/TestHdfsConnectionMgr.java | 108 +++++
.../services/hdfs/client/TestHdfsResourceMgr.java | 229 ++++++++++
9 files changed, 1479 insertions(+), 30 deletions(-)
diff --git a/hdfs-agent/pom.xml b/hdfs-agent/pom.xml
index 88370ad87..88be7bb6f 100644
--- a/hdfs-agent/pom.xml
+++ b/hdfs-agent/pom.xml
@@ -185,6 +185,18 @@
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-inline</artifactId>
+ <version>${mockito.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-junit-jupiter</artifactId>
+ <version>${mockito.version}</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
diff --git
a/hdfs-agent/src/test/java/org/apache/ranger/authorization/hadoop/TestRangerAccessControlEnforcer.java
b/hdfs-agent/src/test/java/org/apache/ranger/authorization/hadoop/TestRangerAccessControlEnforcer.java
new file mode 100644
index 000000000..e00da352f
--- /dev/null
+++
b/hdfs-agent/src/test/java/org/apache/ranger/authorization/hadoop/TestRangerAccessControlEnforcer.java
@@ -0,0 +1,493 @@
+/*
+ * 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.hadoop;
+
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.fs.permission.FsAction;
+import org.apache.hadoop.hdfs.server.namenode.INode;
+import
org.apache.hadoop.hdfs.server.namenode.INodeAttributeProvider.AccessControlEnforcer;
+import org.apache.hadoop.hdfs.server.namenode.INodeAttributes;
+import org.apache.hadoop.hdfs.server.namenode.INodeDirectory;
+import org.apache.hadoop.hdfs.server.namenode.INodeDirectoryAttributes;
+import org.apache.hadoop.hdfs.util.ReadOnlyList;
+import org.apache.hadoop.security.UserGroupInformation;
+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.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * @generated by Cursor
+ * @description : Unit Test cases for RangerAccessControlEnforcer
+ */
+
+@TestMethodOrder(MethodOrderer.MethodName.class)
+@ExtendWith(MockitoExtension.class)
+public class TestRangerAccessControlEnforcer {
+ @Test
+ public void
test01_subAccess_callsDefaultEnforcer_forTopAndChildren_trailingSlash() throws
Exception {
+ RangerHdfsPlugin plugin =
Mockito.mock(RangerHdfsPlugin.class);
+ AccessControlEnforcer defaultEnforcer =
Mockito.mock(AccessControlEnforcer.class);
+
+ Mockito.when(plugin.isHadoopAuthEnabled()).thenReturn(true);
+
Mockito.when(plugin.isUseLegacySubAccessAuthorization()).thenReturn(false);
+
Mockito.when(plugin.isOptimizeSubAccessAuthEnabled()).thenReturn(false);
+ Mockito.when(plugin.getHadoopModuleName()).thenReturn("hdfs");
+
Mockito.when(plugin.getExcludedUsers()).thenReturn(Collections.emptySet());
+ // Return NOT_DETERMINED results to force default authorizer path
+
Mockito.when(plugin.isAccessAllowed(Mockito.any(RangerAccessRequest.class),
Mockito.any())).thenAnswer(inv -> {
+ RangerAccessRequest req = inv.getArgument(0);
+ return new RangerAccessResult(0, "hdfs", null, req);
+ });
+
+ RangerAccessControlEnforcer enforcer = new
RangerAccessControlEnforcer(plugin, defaultEnforcer);
+
+ // Build inode hierarchy: root directory with two child directories
"a" and "b"
+ INodeDirectory rootDir = Mockito.mock(INodeDirectory.class);
+ INode rootNode = Mockito.mock(INode.class);
+ Mockito.when(rootNode.isDirectory()).thenReturn(true);
+ Mockito.when(rootNode.asDirectory()).thenReturn(rootDir);
+ Mockito.when(rootDir.getFullPathName()).thenReturn("/root");
+ Mockito.when(rootDir.getPathComponents()).thenReturn(new byte[][]
{"".getBytes()});
+ INodeDirectoryAttributes rootAttrs =
Mockito.mock(INodeDirectoryAttributes.class);
+
Mockito.when(rootAttrs.getLocalNameBytes()).thenReturn("root".getBytes());
+
Mockito.when(rootDir.getSnapshotINode(Mockito.anyInt())).thenReturn(rootAttrs);
+
+ INode childA = Mockito.mock(INode.class);
+ INode childB = Mockito.mock(INode.class);
+ INodeDirectory dirA = Mockito.mock(INodeDirectory.class);
+ INodeDirectory dirB = Mockito.mock(INodeDirectory.class);
+ Mockito.when(childA.isDirectory()).thenReturn(true);
+ Mockito.when(childB.isDirectory()).thenReturn(true);
+ Mockito.when(childA.asDirectory()).thenReturn(dirA);
+ Mockito.when(childB.asDirectory()).thenReturn(dirB);
+ Mockito.when(childA.getLocalName()).thenReturn("a");
+ Mockito.when(childB.getLocalName()).thenReturn("b");
+ INodeDirectoryAttributes attrA =
Mockito.mock(INodeDirectoryAttributes.class);
+ INodeDirectoryAttributes attrB =
Mockito.mock(INodeDirectoryAttributes.class);
+ Mockito.when(attrA.getLocalNameBytes()).thenReturn("a".getBytes());
+ Mockito.when(attrB.getLocalNameBytes()).thenReturn("b".getBytes());
+
Mockito.when(dirA.getSnapshotINode(Mockito.anyInt())).thenReturn(attrA);
+
Mockito.when(dirB.getSnapshotINode(Mockito.anyInt())).thenReturn(attrB);
+ Mockito.when(dirA.getPathComponents()).thenReturn(new byte[][]
{"".getBytes(), "root".getBytes(), "a".getBytes()});
+ Mockito.when(dirB.getPathComponents()).thenReturn(new byte[][]
{"".getBytes(), "root".getBytes(), "b".getBytes()});
+ ReadOnlyList<INode> emptyChildren = Mockito.mock(ReadOnlyList.class);
+ Mockito.when(emptyChildren.isEmpty()).thenReturn(true);
+ Mockito.when(emptyChildren.size()).thenReturn(0);
+
Mockito.when(emptyChildren.iterator()).thenReturn(Collections.emptyIterator());
+
Mockito.when(dirA.getChildrenList(Mockito.anyInt())).thenReturn(emptyChildren);
+
Mockito.when(dirB.getChildrenList(Mockito.anyInt())).thenReturn(emptyChildren);
+
+ List<INode> list = Arrays.asList(childA, childB);
+ ReadOnlyList<INode> children = Mockito.mock(ReadOnlyList.class);
+ Mockito.when(children.isEmpty()).thenReturn(list.isEmpty());
+ Mockito.when(children.size()).thenReturn(list.size());
+ Mockito.when(children.get(Mockito.eq(0))).thenReturn(list.get(0));
+ Mockito.when(children.get(Mockito.eq(1))).thenReturn(list.get(1));
+ Mockito.when(children.iterator()).thenReturn(list.iterator());
+
Mockito.when(rootDir.getChildrenList(Mockito.anyInt())).thenReturn(children);
+
+ // prepare arrays
+ INodeAttributes[] inodeAttrs = new INodeAttributes[] {rootAttrs};
+ INode[] inodes = new INode[] {rootNode};
+ byte[][] pathByNameArr = new byte[][] {"root".getBytes()};
+
+ UserGroupInformation ugi = Mockito.mock(UserGroupInformation.class);
+ Mockito.when(ugi.getShortUserName()).thenReturn("user");
+ Mockito.when(ugi.getGroupNames()).thenReturn(new String[] {"grp"});
+
+ // Call with resourcePath ending with separator
+ enforcer.checkPermission("owner", "super", ugi, inodeAttrs, inodes,
pathByNameArr, 0, "/root/", 0, false,
+ null, null, null, FsAction.READ_EXECUTE, false);
+
+ // Verify defaultEnforcer called for top dir and each child
+ ArgumentCaptor<INodeAttributes[]> attrsCaptor =
ArgumentCaptor.forClass(INodeAttributes[].class);
+ ArgumentCaptor<INode[]> inodesCaptor =
ArgumentCaptor.forClass(INode[].class);
+ Mockito.verify(defaultEnforcer,
Mockito.atLeast(2)).checkPermission(Mockito.anyString(), Mockito.anyString(),
Mockito.any(),
+ attrsCaptor.capture(), inodesCaptor.capture(), Mockito.any(),
Mockito.anyInt(), Mockito.anyString(), Mockito.anyInt(), Mockito.anyBoolean(),
+ Mockito.isNull(), Mockito.isNull(), Mockito.isNull(),
Mockito.isNull(), Mockito.anyBoolean());
+
+ // Ensure at least one invocation carried arrays augmented by 1 (child
path case)
+ boolean sawAugmented = attrsCaptor.getAllValues().stream().anyMatch(a
-> a.length == inodeAttrs.length + 1);
+ Assertions.assertTrue(sawAugmented);
+ boolean sawInodeAugmented =
inodesCaptor.getAllValues().stream().anyMatch(a -> a.length == inodes.length +
1);
+ Assertions.assertTrue(sawInodeAugmented);
+ }
+
+ @Test
+ public void
test02_subAccess_callsDefaultEnforcer_children_withoutTrailingSlash() throws
Exception {
+ RangerHdfsPlugin plugin =
Mockito.mock(RangerHdfsPlugin.class);
+ AccessControlEnforcer defaultEnforcer =
Mockito.mock(AccessControlEnforcer.class);
+
+ Mockito.when(plugin.isHadoopAuthEnabled()).thenReturn(true);
+
Mockito.when(plugin.isUseLegacySubAccessAuthorization()).thenReturn(false);
+
Mockito.when(plugin.isOptimizeSubAccessAuthEnabled()).thenReturn(false);
+ Mockito.when(plugin.getHadoopModuleName()).thenReturn("hdfs");
+
Mockito.when(plugin.getExcludedUsers()).thenReturn(Collections.emptySet());
+
Mockito.when(plugin.isAccessAllowed(Mockito.any(RangerAccessRequest.class),
Mockito.any())).thenAnswer(inv -> {
+ RangerAccessRequest req = inv.getArgument(0);
+ return new RangerAccessResult(0, "hdfs", null, req);
+ });
+
+ RangerAccessControlEnforcer enforcer = new
RangerAccessControlEnforcer(plugin, defaultEnforcer);
+
+ INodeDirectory rootDir = Mockito.mock(INodeDirectory.class);
+ INode rootNode = Mockito.mock(INode.class);
+ Mockito.when(rootNode.isDirectory()).thenReturn(true);
+ Mockito.when(rootNode.asDirectory()).thenReturn(rootDir);
+ Mockito.when(rootDir.getFullPathName()).thenReturn("/root");
+ Mockito.when(rootDir.getPathComponents()).thenReturn(new byte[][]
{"".getBytes()});
+ INodeDirectoryAttributes rootAttrs =
Mockito.mock(INodeDirectoryAttributes.class);
+
Mockito.when(rootAttrs.getLocalNameBytes()).thenReturn("root".getBytes());
+
Mockito.when(rootDir.getSnapshotINode(Mockito.anyInt())).thenReturn(rootAttrs);
+
+ INode childC = Mockito.mock(INode.class);
+ INodeDirectory dirC = Mockito.mock(INodeDirectory.class);
+ Mockito.when(childC.isDirectory()).thenReturn(true);
+ Mockito.when(childC.asDirectory()).thenReturn(dirC);
+ Mockito.when(childC.getLocalName()).thenReturn("c");
+ INodeDirectoryAttributes attrC =
Mockito.mock(INodeDirectoryAttributes.class);
+ Mockito.when(attrC.getLocalNameBytes()).thenReturn("c".getBytes());
+
Mockito.when(dirC.getSnapshotINode(Mockito.anyInt())).thenReturn(attrC);
+ Mockito.when(dirC.getPathComponents()).thenReturn(new byte[][]
{"".getBytes(), "root".getBytes(), "c".getBytes()});
+
+ List<INode> list = Collections.singletonList(childC);
+ ReadOnlyList<INode> children = Mockito.mock(ReadOnlyList.class);
+ Mockito.when(children.isEmpty()).thenReturn(list.isEmpty());
+ Mockito.when(children.size()).thenReturn(list.size());
+ Mockito.when(children.get(Mockito.eq(0))).thenReturn(list.get(0));
+ Mockito.when(children.iterator()).thenReturn(list.iterator());
+
Mockito.when(rootDir.getChildrenList(Mockito.anyInt())).thenReturn(children);
+ ReadOnlyList<INode> emptyChildren2 = Mockito.mock(ReadOnlyList.class);
+ Mockito.when(emptyChildren2.isEmpty()).thenReturn(true);
+ Mockito.when(emptyChildren2.size()).thenReturn(0);
+
Mockito.when(emptyChildren2.iterator()).thenReturn(Collections.emptyIterator());
+
Mockito.when(dirC.getChildrenList(Mockito.anyInt())).thenReturn(emptyChildren2);
+
+ INodeAttributes[] inodeAttrs = new INodeAttributes[] {rootAttrs};
+ INode[] inodes = new INode[] {rootNode};
+ byte[][] pathByNameArr = new byte[][] {"root".getBytes()};
+
+ UserGroupInformation ugi = Mockito.mock(UserGroupInformation.class);
+ Mockito.when(ugi.getShortUserName()).thenReturn("user");
+ Mockito.when(ugi.getGroupNames()).thenReturn(new String[] {"grp"});
+
+ // resourcePath without trailing separator forces adding
Path.SEPARATOR_CHAR when pushing children
+ enforcer.checkPermission("owner", "super", ugi, inodeAttrs, inodes,
pathByNameArr, 0, "/root", 0, false,
+ null, null, null, FsAction.READ_EXECUTE, false);
+
+ Mockito.verify(defaultEnforcer,
Mockito.atLeastOnce()).checkPermission(Mockito.anyString(),
Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(),
Mockito.any(), Mockito.anyInt(), Mockito.anyString(), Mockito.anyInt(),
Mockito.anyBoolean(), Mockito.isNull(), Mockito.isNull(), Mockito.isNull(),
Mockito.isNull(), Mockito.anyBoolean());
+ }
+
+ @Test
+ public void test03_traverseOnly_setsExecute_and_usesDefaultEnforcer()
throws Exception {
+ RangerHdfsPlugin plugin =
Mockito.mock(RangerHdfsPlugin.class);
+ AccessControlEnforcer defaultEnforcer =
Mockito.mock(AccessControlEnforcer.class);
+
+ Mockito.when(plugin.isHadoopAuthEnabled()).thenReturn(true);
+ Mockito.when(plugin.getHadoopModuleName()).thenReturn("hdfs");
+ Mockito.when(plugin.getExcludedUsers()).thenReturn(new
HashSet<String>());
+ // No policy evaluation in this path
+
+ RangerAccessControlEnforcer enforcer = new
RangerAccessControlEnforcer(plugin, defaultEnforcer);
+
+ INodeDirectory rootDir = Mockito.mock(INodeDirectory.class);
+ INode rootNode = Mockito.mock(INode.class);
+ Mockito.when(rootNode.isDirectory()).thenReturn(true);
+ Mockito.when(rootNode.asDirectory()).thenReturn(rootDir);
+ Mockito.when(rootDir.getFullPathName()).thenReturn("/root");
+ Mockito.when(rootDir.getPathComponents()).thenReturn(new byte[][]
{"".getBytes()});
+ INodeDirectoryAttributes rootAttrs =
Mockito.mock(INodeDirectoryAttributes.class);
+
Mockito.when(rootAttrs.getLocalNameBytes()).thenReturn("root".getBytes());
+
Mockito.when(rootDir.getSnapshotINode(Mockito.anyInt())).thenReturn(rootAttrs);
+
+ INodeAttributes[] inodeAttrs = new INodeAttributes[] {rootAttrs};
+ INode[] inodes = new INode[] {rootNode};
+ byte[][] pathByNameArr = new byte[][] {"root".getBytes()};
+
+ UserGroupInformation ugi = Mockito.mock(UserGroupInformation.class);
+ Mockito.when(ugi.getShortUserName()).thenReturn("user");
+ Mockito.when(ugi.getGroupNames()).thenReturn(new String[] {"grp"});
+
+ // All access parameters null -> traverseOnlyCheck true
+ enforcer.checkPermission("owner", "super", ugi, inodeAttrs, inodes,
pathByNameArr, 0, Path.SEPARATOR, 0, false,
+ null, null, null, null, false);
+
+ Mockito.verify(plugin,
Mockito.atLeastOnce()).isAccessAllowed(Mockito.any(RangerAccessRequest.class),
Mockito.isNull());
+ }
+
+ @Test
+ public void
test04_isAccessAllowedForHierarchy_notDetermined_and_pathHasWildcard() throws
Exception {
+ RangerHdfsPlugin plugin =
Mockito.mock(RangerHdfsPlugin.class);
+ AccessControlEnforcer defaultEnforcer =
Mockito.mock(AccessControlEnforcer.class);
+
+
Mockito.when(plugin.getRandomizedWildcardPathName()).thenReturn("*RAND*");
+
+ // Return result without isAccessDetermined => NOT_DETERMINED
+
Mockito.when(plugin.isAccessAllowed(Mockito.any(RangerAccessRequest.class),
Mockito.isNull())).thenAnswer(inv -> {
+ RangerAccessRequest req = inv.getArgument(0);
+ return new RangerAccessResult(0, "hdfs", null, req);
+ });
+
+ RangerAccessControlEnforcer enforcer = new
RangerAccessControlEnforcer(plugin, defaultEnforcer);
+
+ INode inode = Mockito.mock(INode.class);
+ INodeAttributes inodeAttrs = Mockito.mock(INodeAttributes.class);
+ Mockito.when(inodeAttrs.getUserName()).thenReturn("owner");
+
+ UserGroupInformation ugi = Mockito.mock(UserGroupInformation.class);
+ Mockito.when(ugi.getShortUserName()).thenReturn("user");
+ Mockito.when(ugi.getGroupNames()).thenReturn(new String[] {"grp"});
+
+ Class<?>[] inner =
RangerAccessControlEnforcer.class.getDeclaredClasses();
+ Class<?> authzCtxClass = null;
+ for (Class<?> c : inner) {
+ if (c.getSimpleName().equals("AuthzContext")) {
+ authzCtxClass = c;
+ break;
+ }
+ }
+ Constructor<?> ctor =
authzCtxClass.getDeclaredConstructor(UserGroupInformation.class, String.class,
boolean.class);
+ ctor.setAccessible(true);
+ Object authzCtx = ctor.newInstance(ugi, "op", false);
+
+ Method m =
RangerAccessControlEnforcer.class.getDeclaredMethod("isAccessAllowedForHierarchy",
INode.class, INodeAttributes.class, String.class, FsAction.class,
authzCtxClass);
+ m.setAccessible(true);
+
+ ArgumentCaptor<RangerAccessRequest> reqCaptor =
ArgumentCaptor.forClass(RangerAccessRequest.class);
+
+ Object ret = m.invoke(enforcer, inode, inodeAttrs, "/root/child",
FsAction.READ_EXECUTE, authzCtx);
+
+ Mockito.verify(plugin,
Mockito.atLeastOnce()).isAccessAllowed(reqCaptor.capture(), Mockito.isNull());
+ RangerAccessRequest sent = reqCaptor.getValue();
+
Assertions.assertTrue(sent.getResource().getAsString().startsWith("/root/child/"));
+
Assertions.assertTrue(sent.getResource().getAsString().contains("*RAND*"));
+ Assertions.assertEquals("NOT_DETERMINED", ret.toString());
+ }
+
+ @Test
+ public void test05_isAccessAllowedForHierarchy_allow_and_deny() throws
Exception {
+ RangerHdfsPlugin plugin =
Mockito.mock(RangerHdfsPlugin.class);
+ AccessControlEnforcer defaultEnforcer =
Mockito.mock(AccessControlEnforcer.class);
+
+ Mockito.when(plugin.getRandomizedWildcardPathName()).thenReturn("*W*");
+
+ RangerAccessControlEnforcer enforcer = new
RangerAccessControlEnforcer(plugin, defaultEnforcer);
+
+ INode inode = Mockito.mock(INode.class);
+ INodeAttributes inodeAttrs = Mockito.mock(INodeAttributes.class);
+
+ UserGroupInformation ugi = Mockito.mock(UserGroupInformation.class);
+ Mockito.when(ugi.getShortUserName()).thenReturn("user");
+ Mockito.when(ugi.getGroupNames()).thenReturn(new String[] {"grp"});
+
+ Class<?> authzCtxClass = null;
+ for (Class<?> c :
RangerAccessControlEnforcer.class.getDeclaredClasses()) {
+ if (c.getSimpleName().equals("AuthzContext")) {
+ authzCtxClass = c;
+ break;
+ }
+ }
+ Constructor<?> ctor =
authzCtxClass.getDeclaredConstructor(UserGroupInformation.class, String.class,
boolean.class);
+ ctor.setAccessible(true);
+ Object authzCtx = ctor.newInstance(ugi, "op", false);
+
+ Method m =
RangerAccessControlEnforcer.class.getDeclaredMethod("isAccessAllowedForHierarchy",
INode.class, INodeAttributes.class, String.class, FsAction.class,
authzCtxClass);
+ m.setAccessible(true);
+
+ // DENY
+
Mockito.when(plugin.isAccessAllowed(Mockito.any(RangerAccessRequest.class),
Mockito.isNull())).thenAnswer(inv -> {
+ RangerAccessRequest req = inv.getArgument(0);
+ RangerAccessResult rs = new RangerAccessResult(0, "hdfs", null,
req);
+ rs.setIsAllowed(false); // also sets determined
+ return rs;
+ });
+ Object retDeny = m.invoke(enforcer, inode, inodeAttrs, "/p",
FsAction.EXECUTE, authzCtx);
+ Assertions.assertEquals("DENY", retDeny.toString());
+
+ // ALLOW
+
Mockito.when(plugin.isAccessAllowed(Mockito.any(RangerAccessRequest.class),
Mockito.isNull())).thenAnswer(inv -> {
+ RangerAccessRequest req = inv.getArgument(0);
+ RangerAccessResult rs = new RangerAccessResult(0, "hdfs", null,
req);
+ rs.setIsAccessDetermined(true);
+ rs.setIsAllowed(true);
+ return rs;
+ });
+ Object retAllow = m.invoke(enforcer, inode, inodeAttrs, "/p",
FsAction.EXECUTE, authzCtx);
+ Assertions.assertEquals("ALLOW", retAllow.toString());
+ }
+
+ @Test
+ public void test06_operationOptimizer_delete_setsParentAccess_and_caches()
throws Exception {
+ RangerHdfsPlugin plugin =
Mockito.mock(RangerHdfsPlugin.class);
+ AccessControlEnforcer defaultEnforcer =
Mockito.mock(AccessControlEnforcer.class);
+ RangerAccessControlEnforcer enforcer = new
RangerAccessControlEnforcer(plugin, defaultEnforcer);
+
+ Class<?> ctxClz = null;
+ for (Class<?> c :
RangerAccessControlEnforcer.class.getDeclaredClasses()) {
+ if (c.getSimpleName().equals("OptimizedAuthzContext")) {
+ ctxClz = c;
+ }
+ }
+
+ INode inode = Mockito.mock(INode.class);
+ Mockito.when(inode.isDirectory()).thenReturn(true);
+ INodeAttributes attr = Mockito.mock(INodeAttributes.class);
+ Mockito.when(attr.getLocalNameBytes()).thenReturn("root".getBytes());
+ byte[][] components = new byte[][] {"root".getBytes()};
+ // consume stubs to satisfy strictness
+ inode.isDirectory();
+ attr.getLocalNameBytes();
+
+ OperationOptimizer optimizer = new
OperationOptimizer(enforcer, "delete", "/root", null, FsAction.READ, null,
null, components, new INodeAttributes[] {attr}, 0, null, inode, inode);
+ RangerAccessControlEnforcer.OptimizedAuthzContext ctx =
optimizer.optimize();
+ Assertions.assertNotNull(ctx);
+
+ // parentAccess should be WRITE_EXECUTE
+ Field parentAccessF = ctxClz.getDeclaredField("parentAccess");
+ parentAccessF.setAccessible(true);
+ Object parentAccessVal = parentAccessF.get(ctx);
+ Assertions.assertEquals(FsAction.WRITE_EXECUTE, parentAccessVal);
+ }
+
+ @Test
+ public void test07_operationOptimizer_create_bypass_whenNodeNull() throws
Exception {
+ RangerHdfsPlugin plugin =
Mockito.mock(RangerHdfsPlugin.class);
+ AccessControlEnforcer defaultEnforcer =
Mockito.mock(AccessControlEnforcer.class);
+ RangerAccessControlEnforcer enforcer = new
RangerAccessControlEnforcer(plugin, defaultEnforcer);
+
+ INodeAttributes attr = Mockito.mock(INodeAttributes.class);
+ Mockito.when(attr.getLocalNameBytes()).thenReturn("root".getBytes());
+ byte[][] components = new byte[][] {"root".getBytes()};
+ // consume stub
+ attr.getLocalNameBytes();
+ OperationOptimizer optimizer = new
OperationOptimizer(enforcer, "create", "/root", null, null, null, null,
components, new INodeAttributes[] {attr}, 0, null, null, null);
+ RangerAccessControlEnforcer.OptimizedAuthzContext ctx =
optimizer.optimize();
+
+ Assertions.assertSame(OperationOptimizer.OPT_BYPASS_AUTHZ, ctx);
+ }
+
+ @Test
+ public void
test08_operationOptimizer_listStatus_setsAccess_and_trimsPath() throws
Exception {
+ RangerHdfsPlugin plugin =
Mockito.mock(RangerHdfsPlugin.class);
+ AccessControlEnforcer defaultEnforcer =
Mockito.mock(AccessControlEnforcer.class);
+ RangerAccessControlEnforcer enforcer = new
RangerAccessControlEnforcer(plugin, defaultEnforcer);
+
+ Class<?> ctxClz = null;
+ for (Class<?> c :
RangerAccessControlEnforcer.class.getDeclaredClasses()) {
+ if (c.getSimpleName().equals("OptimizedAuthzContext")) {
+ ctxClz = c;
+ }
+ }
+ INode inode = Mockito.mock(INode.class);
+ Mockito.when(inode.isDirectory()).thenReturn(true);
+ INodeAttributes attr = Mockito.mock(INodeAttributes.class);
+ // consume stubbed isDirectory
+ inode.isDirectory();
+ byte[][] components = new
byte[][] {"root".getBytes()};
+ OperationOptimizer optimizer = new
OperationOptimizer(enforcer, "listStatus", "/root/", null, null, null, null,
components, new INodeAttributes[] {attr}, 0, null, null, inode);
+ RangerAccessControlEnforcer.OptimizedAuthzContext ctx =
optimizer.optimize();
+ Assertions.assertNotNull(ctx);
+
+ Field accessF = ctxClz.getDeclaredField("access");
+ accessF.setAccessible(true);
+ Assertions.assertEquals(FsAction.READ_EXECUTE, accessF.get(ctx));
+
+ Field pathF = ctxClz.getDeclaredField("path");
+ pathF.setAccessible(true);
+ Assertions.assertEquals("/root", pathF.get(ctx));
+ }
+
+ @Test
+ public void test09_operationOptimizer_isOptimizableOperation() throws
Exception {
+
Assertions.assertTrue(OperationOptimizer.isOptimizableOperation("create"));
+
Assertions.assertTrue(OperationOptimizer.isOptimizableOperation("delete"));
+
Assertions.assertTrue(OperationOptimizer.isOptimizableOperation("rename"));
+
Assertions.assertTrue(OperationOptimizer.isOptimizableOperation("mkdirs"));
+
Assertions.assertTrue(OperationOptimizer.isOptimizableOperation("listStatus"));
+
Assertions.assertTrue(OperationOptimizer.isOptimizableOperation("getEZForPath"));
+
Assertions.assertFalse(OperationOptimizer.isOptimizableOperation("randomOp"));
+ }
+
+ @Test
+ public void test10_operationOptimizer_create_fileNullAccess_returnsNull()
throws Exception {
+ RangerAccessControlEnforcer enforcer = new
RangerAccessControlEnforcer(Mockito.mock(RangerHdfsPlugin.class),
Mockito.mock(AccessControlEnforcer.class));
+ INode fileNode = Mockito.mock(INode.class);
+ Mockito.when(fileNode.isFile()).thenReturn(true);
+ INodeAttributes attr = Mockito.mock(INodeAttributes.class);
+ // consume stubbed isFile
+ fileNode.isFile();
+ byte[][] components = new
byte[][] {"f".getBytes()};
+ OperationOptimizer optimizer = new
OperationOptimizer(enforcer, "create", "/f", null, null, null, null,
components, new INodeAttributes[] {attr}, 0, null, null, fileNode);
+ RangerAccessControlEnforcer.OptimizedAuthzContext ctx =
optimizer.optimize();
+ Assertions.assertNull(ctx);
+ }
+
+ @Test
+ public void
test11_operationOptimizer_rename_parentDirectory_returnsContext() throws
Exception {
+ RangerAccessControlEnforcer enforcer = new
RangerAccessControlEnforcer(Mockito.mock(RangerHdfsPlugin.class),
Mockito.mock(AccessControlEnforcer.class));
+ Class<?> ctxClz = null;
+ for (Class<?> c :
RangerAccessControlEnforcer.class.getDeclaredClasses()) {
+ if (c.getSimpleName().equals("OptimizedAuthzContext")) {
+ ctxClz = c;
+ }
+ }
+ INode dirParent = Mockito.mock(INode.class);
+ Mockito.when(dirParent.isDirectory()).thenReturn(true);
+ INodeAttributes attr = Mockito.mock(INodeAttributes.class);
+ // consume stubbed isDirectory
+ dirParent.isDirectory();
+ byte[][] components = new
byte[][] {"".getBytes(), "p".getBytes(), "f".getBytes()};
+ OperationOptimizer optimizer = new
OperationOptimizer(enforcer, "rename", "/p/f", null, null, null, null,
components, new INodeAttributes[] {attr, attr, attr}, 0, null, dirParent, null);
+ RangerAccessControlEnforcer.OptimizedAuthzContext ctx =
optimizer.optimize();
+ Assertions.assertNotNull(ctx);
+ Field pathF = ctxClz.getDeclaredField("path");
+ pathF.setAccessible(true);
+ Assertions.assertEquals("/p", pathF.get(ctx));
+ }
+
+ @Test
+ public void test12_operationOptimizer_mkdirs_nodeIsFile_returnsNull()
throws Exception {
+ RangerAccessControlEnforcer enforcer = new
RangerAccessControlEnforcer(Mockito.mock(RangerHdfsPlugin.class),
Mockito.mock(AccessControlEnforcer.class));
+ INode file = Mockito.mock(INode.class);
+ Mockito.when(file.isFile()).thenReturn(true);
+ INodeAttributes attr = Mockito.mock(INodeAttributes.class);
+ // consume stubbed isFile
+ file.isFile();
+ byte[][] components = new
byte[][] {"d".getBytes()};
+ OperationOptimizer optimizer = new
OperationOptimizer(enforcer, "mkdirs", "/d", null, null, null, null,
components, new INodeAttributes[] {attr}, 0, null, null, file);
+ RangerAccessControlEnforcer.OptimizedAuthzContext ctx =
optimizer.optimize();
+ Assertions.assertNull(ctx);
+ }
+}
diff --git
a/hdfs-agent/src/test/java/org/apache/ranger/authorization/hadoop/exceptions/TestRangerAccessControlException.java
b/hdfs-agent/src/test/java/org/apache/ranger/authorization/hadoop/exceptions/TestRangerAccessControlException.java
new file mode 100644
index 000000000..6a5f7892e
--- /dev/null
+++
b/hdfs-agent/src/test/java/org/apache/ranger/authorization/hadoop/exceptions/TestRangerAccessControlException.java
@@ -0,0 +1,52 @@
+/*
+ * 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.hadoop.exceptions;
+
+import org.apache.hadoop.security.AccessControlException;
+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;
+
+/**
+ * @generated by Cursor
+ * @description : Unit Test cases for RangerAccessControlException
+ */
+
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestRangerAccessControlException {
+ @Test
+ public void test01_constructWithMessage_setsMessage() {
+ String message = "Permission denied: user=test,
access=READ, inode=/path";
+ RangerAccessControlException ex = new
RangerAccessControlException(message);
+
+ Assertions.assertEquals(message, ex.getMessage());
+ }
+
+ @Test
+ public void test02_isInstanceOfAccessControlException() {
+ RangerAccessControlException ex = new
RangerAccessControlException("msg");
+
+ Assertions.assertInstanceOf(AccessControlException.class, ex);
+ }
+}
diff --git
a/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/HDFSRangerTest.java
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/HDFSRangerTest.java
index 52bc4ab4a..1ba676b81 100644
---
a/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/HDFSRangerTest.java
+++
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/HDFSRangerTest.java
@@ -34,12 +34,20 @@
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.ranger.authorization.hadoop.RangerHdfsAuthorizer;
import org.junit.Assert;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.security.PrivilegedExceptionAction;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
/**
* Here we plug the Ranger AccessControlEnforcer into HDFS.
* <p>
@@ -51,6 +59,8 @@
* with the tag called "TmpdirTag". A "hdfs_path" entity was created in Apache
Atlas + then associated with the "TmpdirTag". This was
* then imported into Ranger using the TagSyncService. The policies were then
downloaded locally and saved for testing off-line.
*/
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
public class HDFSRangerTest {
private static final File baseDir = new
File("./target/hdfs/").getAbsoluteFile();
@@ -138,10 +148,10 @@ public void writeTest() throws Exception {
try {
fs.append(file);
- Assert.fail("Failure expected on an incorrect permission");
+ fail("Failure expected on an incorrect permission");
} catch (AccessControlException ex) {
// expected
-
Assert.assertEquals(AccessControlException.class.getName(),
ex.getClass().getName());
+ assertEquals(AccessControlException.class.getName(),
ex.getClass().getName());
}
}
@@ -181,7 +191,7 @@ public void executeTest() throws Exception {
try (FileSystem fs = FileSystem.get(conf)) {
RemoteIterator<LocatedFileStatus> iter =
fs.listFiles(file.getParent(), false);
- Assert.assertTrue(iter.hasNext());
+ assertTrue(iter.hasNext());
}
return null;
@@ -197,7 +207,7 @@ public void executeTest() throws Exception {
try (FileSystem fs = FileSystem.get(conf)) {
RemoteIterator<LocatedFileStatus> iter =
fs.listFiles(file.getParent(), false);
- Assert.assertTrue(iter.hasNext());
+ assertTrue(iter.hasNext());
}
return null;
@@ -215,11 +225,11 @@ public void executeTest() throws Exception {
try {
RemoteIterator<LocatedFileStatus> iter =
fs.listFiles(file.getParent(), false);
- Assert.assertTrue(iter.hasNext());
- Assert.fail("Failure expected on an incorrect permission");
+ assertTrue(iter.hasNext());
+ fail("Failure expected on an incorrect permission");
} catch (AccessControlException ex) {
// expected
-
Assert.assertEquals(AccessControlException.class.getName(),
ex.getClass().getName());
+ assertEquals(AccessControlException.class.getName(),
ex.getClass().getName());
}
}
@@ -259,9 +269,9 @@ public void readTestUsingTagPolicy() throws Exception {
IOUtils.copy(in, output);
- String content = new String(output.toByteArray());
+ String content = new String(output.toByteArray(),
StandardCharsets.UTF_8);
- Assert.assertTrue(content.startsWith("data0"));
+ assertTrue(content.startsWith("data0"));
}
return null;
@@ -282,9 +292,9 @@ public void readTestUsingTagPolicy() throws Exception {
IOUtils.copy(in, output);
- String content = new String(output.toByteArray());
+ String content = new String(output.toByteArray(),
StandardCharsets.UTF_8);
- Assert.assertTrue(content.startsWith("data0"));
+ assertTrue(content.startsWith("data0"));
}
return null;
@@ -303,10 +313,10 @@ public void readTestUsingTagPolicy() throws Exception {
try {
fs.open(file);
- Assert.fail("Failure expected on an incorrect permission");
+ fail("Failure expected on an incorrect permission");
} catch (AccessControlException ex) {
// expected
-
Assert.assertEquals(AccessControlException.class.getName(),
ex.getClass().getName());
+ assertEquals(AccessControlException.class.getName(),
ex.getClass().getName());
}
}
@@ -325,10 +335,10 @@ public void readTestUsingTagPolicy() throws Exception {
// Read the file
try {
fs.open(file);
- Assert.fail("Failure expected on an incorrect permission");
+ fail("Failure expected on an incorrect permission");
} catch (AccessControlException ex) {
// expected
-
Assert.assertEquals(AccessControlException.class.getName(),
ex.getClass().getName());
+ assertEquals(AccessControlException.class.getName(),
ex.getClass().getName());
}
}
@@ -386,9 +396,9 @@ void hdfsReadTest(String fileName) throws Exception {
IOUtils.copy(in, output);
- String content = new String(output.toByteArray());
+ String content = new String(output.toByteArray(),
StandardCharsets.UTF_8);
- Assert.assertTrue(content.startsWith("data0"));
+ assertTrue(content.startsWith("data0"));
}
return null;
@@ -409,9 +419,9 @@ void hdfsReadTest(String fileName) throws Exception {
IOUtils.copy(in, output);
- String content = new String(output.toByteArray());
+ String content = new String(output.toByteArray(),
StandardCharsets.UTF_8);
- Assert.assertTrue(content.startsWith("data0"));
+ assertTrue(content.startsWith("data0"));
}
return null;
@@ -429,10 +439,10 @@ void hdfsReadTest(String fileName) throws Exception {
try {
fs.open(file);
- Assert.fail("Failure expected on an incorrect permission");
+ fail("Failure expected on an incorrect permission");
} catch (AccessControlException ex) {
// expected
-
Assert.assertEquals(AccessControlException.class.getName(),
ex.getClass().getName());
+ assertEquals(AccessControlException.class.getName(),
ex.getClass().getName());
}
}
@@ -469,10 +479,10 @@ void hdfsReadFailTest(String fileName) throws Exception {
try {
fs.open(file);
- Assert.fail("Failure expected on an incorrect permission");
+ fail("Failure expected on an incorrect permission");
} catch (AccessControlException ex) {
// expected
-
Assert.assertEquals(AccessControlException.class.getName(),
ex.getClass().getName());
+ assertEquals(AccessControlException.class.getName(),
ex.getClass().getName());
}
}
@@ -492,10 +502,10 @@ void hdfsReadFailTest(String fileName) throws Exception {
try {
fs.open(file);
- Assert.fail("Failure expected on an incorrect permission");
+ fail("Failure expected on an incorrect permission");
} catch (AccessControlException ex) {
// expected
-
Assert.assertEquals(AccessControlException.class.getName(),
ex.getClass().getName());
+ assertEquals(AccessControlException.class.getName(),
ex.getClass().getName());
}
}
@@ -514,10 +524,10 @@ void hdfsReadFailTest(String fileName) throws Exception {
// Read the file
try {
fs.open(file);
- Assert.fail("Failure expected on an incorrect permission");
+ fail("Failure expected on an incorrect permission");
} catch (AccessControlException ex) {
// expected
-
Assert.assertEquals(AccessControlException.class.getName(),
ex.getClass().getName());
+ assertEquals(AccessControlException.class.getName(),
ex.getClass().getName());
}
}
@@ -547,7 +557,7 @@ void hdfsGetContentSummary(final String dirName) throws
Exception {
Assert.assertEquals("Found unexpected number of
directories; expected-count=3, actual-count=" + directoryCount, 3,
directoryCount);
} catch (Exception e) {
- Assert.fail("Failed to getContentSummary, exception=" + e);
+ fail("Failed to getContentSummary, exception=" + e);
}
}
diff --git
a/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/RangerHdfsAuthorizerTest.java
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/RangerHdfsAuthorizerTest.java
index 68884d811..11a1b739e 100644
---
a/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/RangerHdfsAuthorizerTest.java
+++
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/RangerHdfsAuthorizerTest.java
@@ -17,7 +17,7 @@
package org.apache.ranger.services.hdfs;
-import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.fs.permission.FsAction;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.hdfs.server.namenode.INode;
@@ -62,6 +62,8 @@ public static void setup() {
file.deleteOnExit();
+ String resourceDir =
RangerHdfsAuthorizerTest.class.getResource("/hdfs_version_3.0").getPath();
+
try (FileOutputStream outStream = new FileOutputStream(file);
OutputStreamWriter writer = new
OutputStreamWriter(outStream, StandardCharsets.UTF_8)) {
writer.write("<configuration>\n" +
@@ -73,6 +75,18 @@ public static void setup() {
"
<name>xasecure.add-hadoop-authorization</name>\n" +
" <value>true</value>\n" +
" </property>\n" +
+ " <property>\n" +
+ "
<name>ranger.plugin.hdfs.service.name</name>\n" +
+ " <value>cl1_hadoop</value>\n" +
+ " </property>\n" +
+ " <property>\n" +
+ "
<name>ranger.plugin.hdfs.policy.source.impl</name>\n" +
+ "
<value>org.apache.ranger.services.hdfs.RangerAdminClientImpl</value>\n" +
+ " </property>\n" +
+ " <property>\n" +
+ "
<name>ranger.plugin.hdfs.policy.cache.dir</name>\n" +
+ " <value>" + resourceDir + "</value>\n"
+
+ " </property>\n" +
"</configuration>\n");
}
@@ -86,6 +100,8 @@ public static void setup() {
AccessControlEnforcer accessControlEnforcer = null;
rangerControlEnforcer =
authorizer.getExternalAccessControlEnforcer(accessControlEnforcer);
+
+ Assert.assertNotNull("rangerControlEnforcer should not be null",
rangerControlEnforcer);
}
@AfterClass
@@ -213,6 +229,7 @@ private static INode createNode(String[] pathSegments, int
i, String owner, Stri
INode mock = Mockito.mock(INode.class);
when(mock.getLocalNameBytes()).thenReturn(name.getBytes(StandardCharsets.UTF_8));
+ when(mock.getLocalName()).thenReturn(name);
when(mock.getUserName()).thenReturn(owner);
when(mock.getGroupName()).thenReturn(group);
when(mock.getFullPathName()).thenReturn(fullPath);
diff --git
a/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/TestRangerServiceHdfs.java
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/TestRangerServiceHdfs.java
new file mode 100644
index 000000000..63b1d34df
--- /dev/null
+++
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/TestRangerServiceHdfs.java
@@ -0,0 +1,176 @@
+/*
+ * 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.hdfs;
+
+import org.apache.ranger.plugin.model.RangerPolicy;
+import org.apache.ranger.plugin.model.RangerService;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef;
+import org.apache.ranger.plugin.resourcematcher.RangerAbstractResourceMatcher;
+import org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher;
+import org.apache.ranger.plugin.service.ResourceLookupContext;
+import org.apache.ranger.services.hdfs.client.HdfsResourceMgr;
+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.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @generated by Cursor
+ * @description : Unit Test cases for RangerServiceHdfs
+ */
+
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestRangerServiceHdfs {
+ @Test
+ public void test01_validateConfig_delegatesToConnectionTest() throws
Exception {
+ RangerServiceHdfs svc = new RangerServiceHdfs();
+ RangerServiceDef def = buildMinimalServiceDef();
+ RangerService service = buildService("hdfs-test", "hdfs", new
HashMap<>());
+ svc.init(def, service);
+
+ Map<String, Object> expected = new HashMap<>();
+ expected.put("status", "ok");
+
+ try (MockedStatic<HdfsResourceMgr> mocked =
Mockito.mockStatic(HdfsResourceMgr.class)) {
+ mocked.when(() ->
HdfsResourceMgr.connectionTest(Mockito.eq("hdfs-test"),
Mockito.anyMap())).thenReturn(expected);
+
+ Map<String, Object> ret = svc.validateConfig();
+ Assertions.assertEquals(expected, ret);
+ mocked.verify(() ->
HdfsResourceMgr.connectionTest(Mockito.eq("hdfs-test"), Mockito.anyMap()));
+ }
+ }
+
+ @Test
+ public void test02_lookupResource_delegatesToGetHdfsResources() throws
Exception {
+ RangerServiceHdfs svc = new RangerServiceHdfs();
+ RangerServiceDef def = buildMinimalServiceDef();
+ RangerService service = buildService("hdfs-svc", "hdfs", new
HashMap<>());
+ svc.init(def, service);
+
+ ResourceLookupContext ctx = new ResourceLookupContext();
+ ctx.setUserInput("/tmp/");
+ ctx.setResourceName("path");
+ Map<String, List<String>> res = new HashMap<>();
+ res.put(HdfsResourceMgr.PATH, Collections.singletonList("/tmp"));
+ ctx.setResources(res);
+
+ List<String> expected = Arrays.asList("/tmp/a", "/tmp/b");
+
+ try (MockedStatic<HdfsResourceMgr> mocked =
Mockito.mockStatic(HdfsResourceMgr.class)) {
+ mocked.when(() ->
HdfsResourceMgr.getHdfsResources(Mockito.eq("hdfs-svc"), Mockito.eq("hdfs"),
Mockito.anyMap(), Mockito.eq(ctx))).thenReturn(expected);
+
+ List<String> ret = svc.lookupResource(ctx);
+ Assertions.assertEquals(expected, ret);
+ mocked.verify(() ->
HdfsResourceMgr.getHdfsResources(Mockito.eq("hdfs-svc"), Mockito.eq("hdfs"),
Mockito.anyMap(), Mockito.eq(ctx)));
+ }
+ }
+
+ @Test
+ public void
test03_getDefaultRangerPolicies_setsPathWildcard_andAddsAuditPolicies() throws
Exception {
+ RangerServiceHdfs svc = new RangerServiceHdfs();
+ RangerServiceDef def = buildMinimalServiceDef();
+ Map<String, String> cfg = new HashMap<>();
+ cfg.put("setup.additional.default.policies", "false");
+ RangerService service = buildService("hdfs-def", "hdfs", cfg);
+ svc.init(def, service);
+
+ List<RangerPolicy> policies = svc.getDefaultRangerPolicies();
+ Assertions.assertFalse(policies.isEmpty());
+
+ boolean foundAll = false;
+ boolean foundKms = false;
+ boolean foundHbaseArchive = false;
+
+ for (RangerPolicy p : policies) {
+ if (p.getName().contains("all")) {
+ RangerPolicy.RangerPolicyResource r =
p.getResources().get("path");
+ Assertions.assertNotNull(r);
+ Assertions.assertEquals(Collections.singletonList("/*"),
r.getValues());
+ foundAll = true;
+ }
+ if ("kms-audit-path".equals(p.getName())) {
+
Assertions.assertEquals(Collections.singletonList("/ranger/audit/kms"),
p.getResources().get("path").getValues());
+ Assertions.assertTrue(p.getPolicyItems().stream().anyMatch(it
-> it.getUsers() != null && it.getUsers().contains("keyadmin")));
+ foundKms = true;
+ }
+ if ("hbase-archive".equals(p.getName())) {
+
Assertions.assertEquals(Collections.singletonList("/hbase/archive"),
p.getResources().get("path").getValues());
+ Assertions.assertTrue(p.getPolicyItems().stream().anyMatch(it
-> it.getUsers() != null && it.getUsers().contains("hbase")));
+ foundHbaseArchive = true;
+ }
+ }
+
+ Assertions.assertTrue(foundAll);
+ Assertions.assertTrue(foundKms);
+ Assertions.assertTrue(foundHbaseArchive);
+ }
+
+ private RangerService buildService(String name, String type, Map<String,
String> cfg) {
+ RangerService svc = new RangerService();
+ svc.setName(name);
+ svc.setType(type);
+ svc.setConfigs(cfg);
+ return svc;
+ }
+
+ private RangerServiceDef buildMinimalServiceDef() {
+ RangerServiceDef def = new RangerServiceDef();
+ def.setName("hdfs");
+
+ RangerAccessTypeDef read = new RangerAccessTypeDef();
+ read.setName("read");
+ RangerAccessTypeDef write = new RangerAccessTypeDef();
+ write.setName("write");
+ def.setAccessTypes(Arrays.asList(read, write));
+
+ Map<String, String> options = new HashMap<>();
+ options.put("create.default.policy.per.hierarchy", "true");
+ def.setOptions(options);
+
+ RangerResourceDef path = new RangerResourceDef();
+ path.setName("path");
+ path.setMandatory(true);
+ path.setRecursiveSupported(true);
+ path.setMatcher(RangerPathResourceMatcher.class.getName());
+ Map<String, String> matcherOptions = new HashMap<>();
+ matcherOptions.put(RangerPathResourceMatcher.OPTION_PATH_SEPARATOR,
"/");
+ matcherOptions.put(RangerAbstractResourceMatcher.OPTION_WILD_CARD,
"true");
+ path.setMatcherOptions(matcherOptions);
+
+ List<RangerResourceDef> resources = new ArrayList<>();
+ resources.add(path);
+ def.setResources(resources);
+ return def;
+ }
+}
diff --git
a/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/HdfsClientTest.java
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/HdfsClientTest.java
index dbb8c4883..2026ab2e9 100644
---
a/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/HdfsClientTest.java
+++
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/HdfsClientTest.java
@@ -19,11 +19,41 @@
package org.apache.ranger.services.hdfs.client;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileStatus;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.security.SecureClientLogin;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.ranger.plugin.client.HadoopException;
import org.junit.Test;
-
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.MethodOrderer;
+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 javax.security.auth.Subject;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+/**
+ * @generated by Cursor
+ * @description : Unit Test cases for HdfsClient
+ */
+
+@TestMethodOrder(MethodOrderer.MethodName.class)
+@ExtendWith(MockitoExtension.class)
public class HdfsClientTest {
@Test(expected = IllegalArgumentException.class)
public void testUsernameNotSpecified() throws IllegalArgumentException {
@@ -178,4 +208,326 @@ public void testValidHaConfig() throws
IllegalArgumentException {
HdfsClient.validateConnectionConfigs(configs);
}
+
+ // ===== JUnit 5 additional tests appended (preserving existing code
above) =====
+
+ @Test
+ public void test_validate_valid_multi_nn_transforms_config() {
+ Map<String, String> configs = new HashMap<>();
+ configs.put("username", "hdfsuser");
+ configs.put("password", "hdfsuser");
+ configs.put("hadoop.security.authentication", "simple");
+ configs.put("fs.default.name",
"node-1.example.com:8020,node-2.example.com:8020");
+ HdfsClient.validateConnectionConfigs(configs);
+ Assertions.assertEquals("hdfscluster",
configs.get("dfs.nameservices"));
+ Assertions.assertEquals("hdfs://hdfscluster",
configs.get("fs.default.name"));
+
Assertions.assertEquals("org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider",
configs.get("dfs.client.failover.proxy.provider.hdfscluster"));
+ Assertions.assertEquals("namenode1,namenode2",
configs.get("dfs.ha.namenodes.hdfscluster"));
+ Assertions.assertEquals("node-1.example.com:8020",
configs.get("dfs.namenode.rpc-address.hdfscluster.namenode1"));
+ Assertions.assertEquals("node-2.example.com:8020",
configs.get("dfs.namenode.rpc-address.hdfscluster.namenode2"));
+ }
+
+ @Test
+ public void test01_validate_username_missing() {
+ Map<String, String> configs = new HashMap<>();
+ IllegalArgumentException ex =
Assertions.assertThrows(IllegalArgumentException.class, () ->
HdfsClient.validateConnectionConfigs(configs));
+ Assertions.assertTrue(ex.getMessage().contains("username"));
+ }
+
+ @Test
+ public void test02_validate_password_missing() {
+ Map<String, String> configs = new HashMap<>();
+ configs.put("username", "hdfsuser");
+ IllegalArgumentException ex =
Assertions.assertThrows(IllegalArgumentException.class, () ->
HdfsClient.validateConnectionConfigs(configs));
+ Assertions.assertTrue(ex.getMessage().contains("password"));
+ }
+
+ @Test
+ public void test03_validate_auth_missing() {
+ Map<String, String> configs = new HashMap<>();
+ configs.put("username", "hdfsuser");
+ configs.put("password", "hdfsuser");
+ IllegalArgumentException ex =
Assertions.assertThrows(IllegalArgumentException.class, () ->
HdfsClient.validateConnectionConfigs(configs));
+
Assertions.assertTrue(ex.getMessage().contains("hadoop.security.authentication"));
+ }
+
+ @Test
+ public void test04_validate_fsdefault_missing() {
+ Map<String, String> configs = new HashMap<>();
+ configs.put("username", "hdfsuser");
+ configs.put("password", "hdfsuser");
+ configs.put("hadoop.security.authentication", "simple");
+ IllegalArgumentException ex =
Assertions.assertThrows(IllegalArgumentException.class, () ->
HdfsClient.validateConnectionConfigs(configs));
+ Assertions.assertTrue(ex.getMessage().contains("fs.default.name"));
+ }
+
+ @Test
+ public void test05_validate_proxyProvider_missing() {
+ Map<String, String> configs = new HashMap<>();
+ configs.put("username", "hdfsuser");
+ configs.put("password", "hdfsuser");
+ configs.put("hadoop.security.authentication", "simple");
+ configs.put("fs.default.name", "hdfs://hwqe-1425428405");
+ configs.put("dfs.nameservices", "hwqe-1425428405");
+ IllegalArgumentException ex =
Assertions.assertThrows(IllegalArgumentException.class, () ->
HdfsClient.validateConnectionConfigs(configs));
+
Assertions.assertTrue(ex.getMessage().contains("dfs.client.failover.proxy.provider.hwqe-1425428405"));
+ }
+
+ @Test
+ public void test06_validate_nnElements_missing() {
+ Map<String, String> configs = new HashMap<>();
+ configs.put("username", "hdfsuser");
+ configs.put("password", "hdfsuser");
+ configs.put("hadoop.security.authentication", "simple");
+ configs.put("fs.default.name", "hdfs://hwqe-1425428405");
+ configs.put("dfs.nameservices", "hwqe-1425428405");
+ configs.put("dfs.client.failover.proxy.provider.hwqe-1425428405",
"org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider");
+ IllegalArgumentException ex =
Assertions.assertThrows(IllegalArgumentException.class, () ->
HdfsClient.validateConnectionConfigs(configs));
+
Assertions.assertTrue(ex.getMessage().contains("dfs.ha.namenodes.hwqe-1425428405"));
+ }
+
+ @Test
+ public void test07_validate_nn1_missing() {
+ Map<String, String> configs = new HashMap<>();
+ configs.put("username", "hdfsuser");
+ configs.put("password", "hdfsuser");
+ configs.put("hadoop.security.authentication", "simple");
+ configs.put("fs.default.name", "hdfs://hwqe-1425428405");
+ configs.put("dfs.nameservices", "hwqe-1425428405");
+ configs.put("dfs.client.failover.proxy.provider.hwqe-1425428405",
"org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider");
+ configs.put("dfs.ha.namenodes.hwqe-1425428405", "nn1,nn2");
+ configs.put("dfs.namenode.rpc-address.hwqe-1425428405.nn2",
"node-2.example.com:8020");
+ IllegalArgumentException ex =
Assertions.assertThrows(IllegalArgumentException.class, () ->
HdfsClient.validateConnectionConfigs(configs));
+
Assertions.assertTrue(ex.getMessage().contains("dfs.namenode.rpc-address.hwqe-1425428405.nn1"));
+ }
+
+ @Test
+ public void test08_validate_nn2_missing() {
+ Map<String, String> configs = new HashMap<>();
+ configs.put("username", "hdfsuser");
+ configs.put("password", "hdfsuser");
+ configs.put("hadoop.security.authentication", "simple");
+ configs.put("fs.default.name", "hdfs://hwqe-1425428405");
+ configs.put("dfs.nameservices", "hwqe-1425428405");
+ configs.put("dfs.client.failover.proxy.provider.hwqe-1425428405",
"org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider");
+ configs.put("dfs.ha.namenodes.hwqe-1425428405", "nn1,nn2");
+ configs.put("dfs.namenode.rpc-address.hwqe-1425428405.nn1",
"node-1.example.com:8020");
+ IllegalArgumentException ex =
Assertions.assertThrows(IllegalArgumentException.class, () ->
HdfsClient.validateConnectionConfigs(configs));
+
Assertions.assertTrue(ex.getMessage().contains("dfs.namenode.rpc-address.hwqe-1425428405.nn2"));
+ }
+
+ @Test
+ public void test09_validate_valid_non_ha() {
+ Map<String, String> configs = new HashMap<>();
+ configs.put("username", "hdfsuser");
+ configs.put("password", "hdfsuser");
+ configs.put("hadoop.security.authentication", "simple");
+ configs.put("fs.default.name", "hdfs://node-2.example.com:8020");
+ HdfsClient.validateConnectionConfigs(configs);
+ }
+
+ @Test
+ public void test10_validate_valid_multi_nn_transforms_config() {
+ Map<String, String> configs = new HashMap<>();
+ configs.put("username", "hdfsuser");
+ configs.put("password", "hdfsuser");
+ configs.put("hadoop.security.authentication", "simple");
+ configs.put("fs.default.name",
"node-1.example.com:8020,node-2.example.com:8020");
+ HdfsClient.validateConnectionConfigs(configs);
+ Assertions.assertEquals("hdfscluster",
configs.get("dfs.nameservices"));
+ Assertions.assertEquals("hdfs://hdfscluster",
configs.get("fs.default.name"));
+
Assertions.assertEquals("org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider",
configs.get("dfs.client.failover.proxy.provider.hdfscluster"));
+ Assertions.assertEquals("namenode1,namenode2",
configs.get("dfs.ha.namenodes.hdfscluster"));
+ Assertions.assertEquals("node-1.example.com:8020",
configs.get("dfs.namenode.rpc-address.hdfscluster.namenode1"));
+ Assertions.assertEquals("node-2.example.com:8020",
configs.get("dfs.namenode.rpc-address.hdfscluster.namenode2"));
+ }
+
+ @Test
+ public void test11_connectionTest_success() throws Exception {
+ Map<String, String> cfg = new HashMap<>();
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+ cfg.put("hadoop.security.authentication", "simple");
+ cfg.put("fs.default.name", "hdfs://node-1:8020");
+
+ try (MockedStatic<SecureClientLogin> mockedLogin =
Mockito.mockStatic(SecureClientLogin.class);
+ MockedStatic<UserGroupInformation> mockedUGI =
Mockito.mockStatic(UserGroupInformation.class);
+ MockedStatic<FileSystem> mockedFS =
Mockito.mockStatic(FileSystem.class)) {
+ mockedLogin.when(() ->
SecureClientLogin.getPrincipal(Mockito.anyString(),
Mockito.anyString())).thenReturn(null);
+ mockedLogin.when(() ->
SecureClientLogin.login(Mockito.anyString())).thenReturn(new Subject());
+
+ try (MockedConstruction<HdfsClient> mockedConstruct =
Mockito.mockConstruction(HdfsClient.class, (mock, ctx) -> {
+ Mockito.when(mock.listFiles(Mockito.eq("/"), Mockito.isNull(),
Mockito.isNull())).thenReturn(Collections.singletonList("/a"));
+ })) {
+ Map<String, Object> ret = HdfsClient.connectionTest("svc",
cfg);
+ Assertions.assertEquals(Boolean.TRUE,
ret.get("connectivityStatus"));
+ }
+ }
+ }
+
+ @Test
+ public void test12_connectionTest_unableToListFiles() throws Exception {
+ Map<String, String> cfg = new HashMap<>();
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+ cfg.put("hadoop.security.authentication", "simple");
+ cfg.put("fs.default.name", "hdfs://node-1:8020");
+
+ try (MockedStatic<SecureClientLogin> mockedLogin =
Mockito.mockStatic(SecureClientLogin.class);
+ MockedStatic<UserGroupInformation> mockedUGI =
Mockito.mockStatic(UserGroupInformation.class);
+ MockedStatic<FileSystem> mockedFS =
Mockito.mockStatic(FileSystem.class)) {
+ mockedLogin.when(() ->
SecureClientLogin.getPrincipal(Mockito.anyString(),
Mockito.anyString())).thenReturn(null);
+ mockedLogin.when(() ->
SecureClientLogin.login(Mockito.anyString())).thenReturn(new Subject());
+
+ try (MockedConstruction<HdfsClient> mockedConstruct =
Mockito.mockConstruction(HdfsClient.class, (mock, ctx) -> {
+ Mockito.when(mock.listFiles(Mockito.eq("/"), Mockito.isNull(),
Mockito.isNull())).thenReturn(Collections.emptyList());
+ })) {
+ Map<String, Object> ret = HdfsClient.connectionTest("svc",
cfg);
+ Assertions.assertEquals(Boolean.FALSE,
ret.get("connectivityStatus"));
+ }
+ }
+ }
+
+ @Test
+ public void test13_connectionTest_propagates_hadoop_exception() throws
Exception {
+ Map<String, String> cfg = new HashMap<>();
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+ cfg.put("hadoop.security.authentication", "simple");
+ cfg.put("fs.default.name", "hdfs://node-1:8020");
+
+ try (MockedStatic<SecureClientLogin> mockedLogin =
Mockito.mockStatic(SecureClientLogin.class);
+ MockedStatic<UserGroupInformation> mockedUGI =
Mockito.mockStatic(UserGroupInformation.class);
+ MockedStatic<FileSystem> mockedFS =
Mockito.mockStatic(FileSystem.class)) {
+ mockedLogin.when(() ->
SecureClientLogin.getPrincipal(Mockito.anyString(),
Mockito.anyString())).thenReturn(null);
+ mockedLogin.when(() ->
SecureClientLogin.login(Mockito.anyString())).thenReturn(new Subject());
+
+ try (MockedConstruction<HdfsClient> mockedConstruct =
Mockito.mockConstruction(HdfsClient.class, (mock, ctx) -> {
+ Mockito.when(mock.listFiles(Mockito.eq("/"), Mockito.isNull(),
Mockito.isNull())).thenThrow(new HadoopException("x", new
RuntimeException("y")));
+ })) {
+ Assertions.assertThrows(HadoopException.class, () ->
HdfsClient.connectionTest("svc", cfg));
+ }
+ }
+ }
+
+ @Test
+ public void test14_listFiles_returns_base_when_empty_listing_and_exists()
throws Exception {
+ Map<String, String> cfg = baseCfg();
+ try (MockedStatic<SecureClientLogin> mockedLogin =
Mockito.mockStatic(SecureClientLogin.class);
+ MockedStatic<UserGroupInformation> mockedUGI =
Mockito.mockStatic(UserGroupInformation.class);
+ MockedStatic<FileSystem> mockedFS =
Mockito.mockStatic(FileSystem.class)) {
+ mockedLogin.when(() ->
SecureClientLogin.getPrincipal(Mockito.anyString(),
Mockito.anyString())).thenReturn(null);
+ mockedLogin.when(() ->
SecureClientLogin.login(Mockito.anyString())).thenReturn(new Subject());
+
+ FileSystem fs = Mockito.mock(FileSystem.class);
+ mockedFS.when(() ->
FileSystem.get(Mockito.any(Configuration.class))).thenReturn(fs);
+
+ Path base = new Path("/base");
+ Mockito.when(fs.listStatus(Mockito.eq(base))).thenReturn(new
FileStatus[0]);
+ Mockito.when(fs.exists(Mockito.eq(base))).thenReturn(true);
+
+ HdfsClient client = new HdfsClient("svc", cfg);
+ List<String> out = client.listFiles("/base", null, null);
+ Assertions.assertEquals(Collections.singletonList("/base"), out);
+ }
+ }
+
+ @Test
+ public void test15_listFiles_filters_and_skips_duplicates() throws
Exception {
+ Map<String, String> cfg = baseCfg();
+ try (MockedStatic<SecureClientLogin> mockedLogin =
Mockito.mockStatic(SecureClientLogin.class);
+ MockedStatic<UserGroupInformation> mockedUGI =
Mockito.mockStatic(UserGroupInformation.class);
+ MockedStatic<FileSystem> mockedFS =
Mockito.mockStatic(FileSystem.class)) {
+ mockedLogin.when(() ->
SecureClientLogin.getPrincipal(Mockito.anyString(),
Mockito.anyString())).thenReturn(null);
+ mockedLogin.when(() ->
SecureClientLogin.login(Mockito.anyString())).thenReturn(new Subject());
+
+ FileSystem fs = Mockito.mock(FileSystem.class);
+ mockedFS.when(() ->
FileSystem.get(Mockito.any(Configuration.class))).thenReturn(fs);
+
+ Path base = new Path("/base");
+ FileStatus stA = Mockito.mock(FileStatus.class);
+ FileStatus stB = Mockito.mock(FileStatus.class);
+ Mockito.when(stA.getPath()).thenReturn(new Path("/base/a"));
+ Mockito.when(stB.getPath()).thenReturn(new Path("/base/b"));
+ Mockito.when(fs.listStatus(Mockito.eq(base))).thenReturn(new
FileStatus[] {stA, stB});
+ Mockito.when(fs.exists(Mockito.eq(base))).thenReturn(true);
+
+ HdfsClient client = new HdfsClient("svc", cfg);
+ List<String> outNoFilter = client.listFiles("/base", null,
Collections.singletonList("/base/a"));
+ Assertions.assertEquals(Collections.singletonList("/base/b"),
outNoFilter);
+
+ List<String> outWithFilter = client.listFiles("/base", "*.txt",
new ArrayList<>());
+ // for filter, set statuses to .txt and .log
+ FileStatus stTxt = Mockito.mock(FileStatus.class);
+ FileStatus stLog = Mockito.mock(FileStatus.class);
+ Mockito.when(stTxt.getPath()).thenReturn(new
Path("/base/file.txt"));
+ Mockito.when(stLog.getPath()).thenReturn(new
Path("/base/file.log"));
+ Mockito.when(fs.listStatus(Mockito.eq(base))).thenReturn(new
FileStatus[] {stTxt, stLog});
+ List<String> filtered = client.listFiles("/base", "*.txt", new
ArrayList<>());
+
Assertions.assertEquals(Collections.singletonList("/base/file.txt"), filtered);
+ }
+ }
+
+ @Test
+ public void test16_listFiles_wraps_unknown_host() throws Exception {
+ Map<String, String> cfg = baseCfg();
+ try (MockedStatic<SecureClientLogin> mockedLogin =
Mockito.mockStatic(SecureClientLogin.class);
+ MockedStatic<UserGroupInformation> mockedUGI =
Mockito.mockStatic(UserGroupInformation.class);
+ MockedStatic<FileSystem> mockedFS =
Mockito.mockStatic(FileSystem.class)) {
+ mockedLogin.when(() ->
SecureClientLogin.getPrincipal(Mockito.anyString(),
Mockito.anyString())).thenReturn(null);
+ mockedLogin.when(() ->
SecureClientLogin.login(Mockito.anyString())).thenReturn(new Subject());
+
+ mockedFS.when(() ->
FileSystem.get(Mockito.any(Configuration.class))).thenThrow(new
UnknownHostException("unresolvable"));
+
+ HdfsClient client = new HdfsClient("svc", cfg);
+ Assertions.assertThrows(HadoopException.class, () ->
client.listFiles("/base", null, null));
+ }
+ }
+
+ @Test
+ public void test17_listFiles_wraps_file_not_found() throws Exception {
+ Map<String, String> cfg = baseCfg();
+ try (MockedStatic<SecureClientLogin> mockedLogin =
Mockito.mockStatic(SecureClientLogin.class);
+ MockedStatic<UserGroupInformation> mockedUGI =
Mockito.mockStatic(UserGroupInformation.class);
+ MockedStatic<FileSystem> mockedFS =
Mockito.mockStatic(FileSystem.class)) {
+ mockedLogin.when(() ->
SecureClientLogin.getPrincipal(Mockito.anyString(),
Mockito.anyString())).thenReturn(null);
+ mockedLogin.when(() ->
SecureClientLogin.login(Mockito.anyString())).thenReturn(new Subject());
+
+ FileSystem fs = Mockito.mock(FileSystem.class);
+ mockedFS.when(() ->
FileSystem.get(Mockito.any(Configuration.class))).thenReturn(fs);
+
+ Mockito.when(fs.listStatus(Mockito.any(Path.class))).thenThrow(new
FileNotFoundException("missing"));
+
+ HdfsClient client = new HdfsClient("svc", cfg);
+ Assertions.assertThrows(HadoopException.class, () ->
client.listFiles("/base", null, null));
+ }
+ }
+
+ @Test
+ public void test18_listFiles_wraps_io_exception() throws Exception {
+ Map<String, String> cfg = baseCfg();
+ try (MockedStatic<SecureClientLogin> mockedLogin =
Mockito.mockStatic(SecureClientLogin.class);
+ MockedStatic<UserGroupInformation> mockedUGI =
Mockito.mockStatic(UserGroupInformation.class);
+ MockedStatic<FileSystem> mockedFS =
Mockito.mockStatic(FileSystem.class)) {
+ mockedLogin.when(() ->
SecureClientLogin.getPrincipal(Mockito.anyString(),
Mockito.anyString())).thenReturn(null);
+ mockedLogin.when(() ->
SecureClientLogin.login(Mockito.anyString())).thenReturn(new Subject());
+
+ FileSystem fs = Mockito.mock(FileSystem.class);
+ mockedFS.when(() ->
FileSystem.get(Mockito.any(Configuration.class))).thenReturn(fs);
+
+ Mockito.when(fs.listStatus(Mockito.any(Path.class))).thenThrow(new
IOException("io"));
+
+ HdfsClient client = new HdfsClient("svc", cfg);
+ Assertions.assertThrows(HadoopException.class, () ->
client.listFiles("/base", null, null));
+ }
+ }
+
+ private Map<String, String> baseCfg() {
+ Map<String, String> cfg = new HashMap<>();
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+ cfg.put("hadoop.security.authentication", "simple");
+ cfg.put("fs.default.name", "hdfs://node-1:8020");
+ return cfg;
+ }
}
diff --git
a/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/TestHdfsConnectionMgr.java
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/TestHdfsConnectionMgr.java
new file mode 100644
index 000000000..796a2daee
--- /dev/null
+++
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/TestHdfsConnectionMgr.java
@@ -0,0 +1,108 @@
+/*
+ * 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.hdfs.client;
+
+import org.apache.ranger.plugin.util.TimedEventUtil;
+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.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+
+import static org.mockito.Mockito.eq;
+
+/**
+ * @generated by Cursor
+ * @description : Unit Test cases for HdfsConnectionMgr
+ */
+
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestHdfsConnectionMgr {
+ @Test
+ public void test01_cacheMiss_withConfigs_constructsClient() throws
Exception {
+ Map<String, String> cfg = new HashMap<>();
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+ HdfsConnectionMgr mgr = new HdfsConnectionMgr();
+
+ try (MockedStatic<TimedEventUtil> timed =
Mockito.mockStatic(TimedEventUtil.class);
+ MockedConstruction<HdfsClient> constructed =
Mockito.mockConstruction(HdfsClient.class, (mock, ctx) -> {
+ Mockito.when(mock.listFiles(eq("/"), eq("*"),
Mockito.isNull())).thenReturn(Collections.singletonList("/a"));
+ })) {
+ timed.when(() ->
TimedEventUtil.timedTask(Mockito.any(Callable.class), eq(5L),
eq(TimeUnit.SECONDS))).then(inv -> {
+ Callable<HdfsClient> c = inv.getArgument(0);
+ return c.call();
+ });
+
+ HdfsClient client = mgr.getHadoopConnection("svc", "hdfs", cfg);
+ Assertions.assertNotNull(client);
+ }
+ }
+
+ @Test
+ public void test02_cacheHit_successfulListFiles() throws Exception {
+ Map<String, String> cfg = new HashMap<>();
+ HdfsConnectionMgr mgr = new HdfsConnectionMgr();
+
+ try (MockedStatic<TimedEventUtil> timed =
Mockito.mockStatic(TimedEventUtil.class);
+ MockedConstruction<HdfsClient> constructed =
Mockito.mockConstruction(HdfsClient.class, (mock, ctx) -> {
+ Mockito.when(mock.listFiles(eq("/"), eq("*"),
Mockito.isNull())).thenReturn(Collections.singletonList("/a"));
+ })) {
+ timed.when(() ->
TimedEventUtil.timedTask(Mockito.any(Callable.class), Mockito.anyLong(),
Mockito.eq(TimeUnit.SECONDS))).then(inv -> {
+ Callable<HdfsClient> c = inv.getArgument(0);
+ return c.call();
+ });
+
+ HdfsClient first = mgr.getHadoopConnection("svc2", "hdfs", cfg);
+ HdfsClient second = mgr.getHadoopConnection("svc2", "hdfs", cfg);
+ Assertions.assertSame(first, second);
+ }
+ }
+
+ @Test
+ public void test03_cacheHit_nullListFiles_triggersReconnection() throws
Exception {
+ Map<String, String> cfg = new HashMap<>();
+ HdfsConnectionMgr mgr = new HdfsConnectionMgr();
+
+ try (MockedStatic<TimedEventUtil> timed =
Mockito.mockStatic(TimedEventUtil.class);
+ MockedConstruction<HdfsClient> constructed =
Mockito.mockConstruction(HdfsClient.class, (mock, ctx) -> {
+ Mockito.when(mock.listFiles(eq("/"), eq("*"),
Mockito.isNull())).thenReturn(null).thenReturn(Collections.singletonList("/a"));
+ })) {
+ timed.when(() ->
TimedEventUtil.timedTask(Mockito.any(Callable.class), Mockito.anyLong(),
Mockito.eq(TimeUnit.SECONDS))).then(inv -> {
+ Callable<HdfsClient> c = inv.getArgument(0);
+ return c.call();
+ });
+
+ HdfsClient client = mgr.getHadoopConnection("svc3", "hdfs", cfg);
+ Assertions.assertNotNull(client);
+ }
+ }
+}
diff --git
a/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/TestHdfsResourceMgr.java
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/TestHdfsResourceMgr.java
new file mode 100644
index 000000000..0c8bd39e4
--- /dev/null
+++
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/TestHdfsResourceMgr.java
@@ -0,0 +1,229 @@
+/*
+ * 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.hdfs.client;
+
+import org.apache.ranger.plugin.client.HadoopException;
+import org.apache.ranger.plugin.service.ResourceLookupContext;
+import org.apache.ranger.plugin.util.TimedEventUtil;
+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.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.Mockito.eq;
+
+/**
+ * @generated by Cursor
+ * @description : Unit Test cases for HdfsResourceMgr
+ */
+
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestHdfsResourceMgr {
+ @Test
+ public void test01_connectionTest_delegatesToHdfsClient() throws Exception
{
+ Map<String, String> cfg = new HashMap<>();
+ cfg.put("username", "u");
+
+ Map<String, Object> expected = new HashMap<>();
+ expected.put("connectivityStatus", true);
+
+ try (MockedStatic<HdfsClient> mocked =
Mockito.mockStatic(HdfsClient.class)) {
+ mocked.when(() -> HdfsClient.connectionTest(eq("svc"),
eq(cfg))).thenReturn(expected);
+
+ Map<String, Object> ret = HdfsResourceMgr.connectionTest("svc",
cfg);
+ Assertions.assertEquals(expected, ret);
+ mocked.verify(() -> HdfsClient.connectionTest(eq("svc"), eq(cfg)));
+ }
+ }
+
+ @Test
+ public void test02_getHdfsResources_userInputNoSlash() throws Exception {
+ ResourceLookupContext ctx = new ResourceLookupContext();
+ ctx.setUserInput("abc");
+ ctx.setResourceName("path");
+ Map<String, List<String>> res = new HashMap<>();
+ res.put(HdfsResourceMgr.PATH, Collections.singletonList("/alt/skip"));
+ ctx.setResources(res);
+
+ Map<String, String> cfg = new HashMap<>();
+ HdfsClient client = Mockito.mock(HdfsClient.class);
+ List<String> expected = Arrays.asList("/x", "/y");
+ Mockito.when(client.listFiles(eq("/"), eq("abc*"),
eq(Collections.singletonList("/alt/skip")))).thenReturn(expected);
+
+ try (MockedStatic<TimedEventUtil> timed =
Mockito.mockStatic(TimedEventUtil.class);
+ MockedConstruction<HdfsConnectionMgr> conn =
Mockito.mockConstruction(HdfsConnectionMgr.class, (mock, context) -> {
+ Mockito.when(mock.getHadoopConnection(eq("svc"),
eq("hdfs"), eq(cfg))).thenReturn(client);
+ })) {
+ timed.when(() ->
TimedEventUtil.timedTask(Mockito.any(Callable.class), eq(5L),
eq(TimeUnit.SECONDS))).then(inv -> {
+ Callable<List<String>> c = inv.getArgument(0);
+ return c.call();
+ });
+
+ List<String> out = HdfsResourceMgr.getHdfsResources("svc", "hdfs",
cfg, ctx);
+ Assertions.assertEquals(expected, out);
+ }
+ }
+
+ @Test
+ public void test03_getHdfsResources_userInputRootOnly() throws Exception {
+ ResourceLookupContext ctx = new ResourceLookupContext();
+ ctx.setUserInput("/");
+ ctx.setResourceName("path");
+ ctx.setResources(Collections.singletonMap(HdfsResourceMgr.PATH,
Collections.singletonList("/p")));
+
+ Map<String, String> cfg = new HashMap<>();
+ HdfsClient client = Mockito.mock(HdfsClient.class);
+ List<String> expected = Collections.singletonList("/a");
+ Mockito.when(client.listFiles(eq("/"), Mockito.isNull(),
eq(Collections.singletonList("/p")))).thenReturn(expected);
+
+ try (MockedStatic<TimedEventUtil> timed =
Mockito.mockStatic(TimedEventUtil.class);
+ MockedConstruction<HdfsConnectionMgr> conn =
Mockito.mockConstruction(HdfsConnectionMgr.class, (mock, context) -> {
+ Mockito.when(mock.getHadoopConnection(eq("svc"),
eq("hdfs"), eq(cfg))).thenReturn(client);
+ })) {
+ timed.when(() ->
TimedEventUtil.timedTask(Mockito.any(Callable.class), eq(5L),
eq(TimeUnit.SECONDS))).then(inv -> ((Callable<List<String>>)
inv.getArgument(0)).call());
+
+ List<String> out = HdfsResourceMgr.getHdfsResources("svc", "hdfs",
cfg, ctx);
+ Assertions.assertEquals(expected, out);
+ }
+ }
+
+ @Test
+ public void test04_getHdfsResources_userInputTrailingSlash() throws
Exception {
+ ResourceLookupContext ctx = new ResourceLookupContext();
+ ctx.setUserInput("/tmp/");
+ ctx.setResourceName("path");
+ ctx.setResources(Collections.singletonMap(HdfsResourceMgr.PATH,
Collections.singletonList("/ignore")));
+
+ Map<String, String> cfg = new HashMap<>();
+ HdfsClient client = Mockito.mock(HdfsClient.class);
+ List<String> expected = Arrays.asList("/tmp/a", "/tmp/b");
+ Mockito.when(client.listFiles(eq("/tmp/"), Mockito.isNull(),
eq(Collections.singletonList("/ignore")))).thenReturn(expected);
+
+ try (MockedStatic<TimedEventUtil> timed =
Mockito.mockStatic(TimedEventUtil.class);
+ MockedConstruction<HdfsConnectionMgr> conn =
Mockito.mockConstruction(HdfsConnectionMgr.class, (mock, context) -> {
+ Mockito.when(mock.getHadoopConnection(eq("svc"),
eq("hdfs"), eq(cfg))).thenReturn(client);
+ })) {
+ timed.when(() ->
TimedEventUtil.timedTask(Mockito.any(Callable.class), eq(5L),
eq(TimeUnit.SECONDS))).then(inv -> ((Callable<List<String>>)
inv.getArgument(0)).call());
+
+ List<String> out = HdfsResourceMgr.getHdfsResources("svc", "hdfs",
cfg, ctx);
+ Assertions.assertEquals(expected, out);
+ }
+ }
+
+ @Test
+ public void test05_getHdfsResources_userInputWithComponent() throws
Exception {
+ ResourceLookupContext ctx = new ResourceLookupContext();
+ ctx.setUserInput("/tmp/dir");
+ ctx.setResourceName("path");
+ ctx.setResources(Collections.singletonMap(HdfsResourceMgr.PATH,
Collections.singletonList("/tmp/a")));
+
+ Map<String, String> cfg = new HashMap<>();
+ HdfsClient client = Mockito.mock(HdfsClient.class);
+ List<String> expected = Arrays.asList("/tmp/dir1", "/tmp/dir2");
+ Mockito.when(client.listFiles(eq("/tmp/"), eq("dir*"),
eq(Collections.singletonList("/tmp/a")))).thenReturn(expected);
+
+ try (MockedStatic<TimedEventUtil> timed =
Mockito.mockStatic(TimedEventUtil.class);
+ MockedConstruction<HdfsConnectionMgr> conn =
Mockito.mockConstruction(HdfsConnectionMgr.class, (mock, context) -> {
+ Mockito.when(mock.getHadoopConnection(eq("svc"),
eq("hdfs"), eq(cfg))).thenReturn(client);
+ })) {
+ timed.when(() ->
TimedEventUtil.timedTask(Mockito.any(Callable.class), eq(5L),
eq(TimeUnit.SECONDS))).then(inv -> ((Callable<List<String>>)
inv.getArgument(0)).call());
+
+ List<String> out = HdfsResourceMgr.getHdfsResources("svc", "hdfs",
cfg, ctx);
+ Assertions.assertEquals(expected, out);
+ }
+ }
+
+ @Test
+ public void test06_getHdfsResources_nullServiceOrUserInput_returnsNull()
throws Exception {
+ ResourceLookupContext ctx1 = new ResourceLookupContext();
+ ctx1.setUserInput("/tmp");
+ ctx1.setResourceName("path");
+ ctx1.setResources(Collections.singletonMap(HdfsResourceMgr.PATH,
Collections.singletonList("/p")));
+
+ ResourceLookupContext ctx2 = new ResourceLookupContext();
+ ctx2.setUserInput(null);
+ ctx2.setResourceName("path");
+ ctx2.setResources(Collections.singletonMap(HdfsResourceMgr.PATH,
Collections.singletonList("/p")));
+
+ Map<String, String> cfg = new HashMap<>();
+
+ Assertions.assertNull(HdfsResourceMgr.getHdfsResources(null, "hdfs",
cfg, ctx1));
+ Assertions.assertNull(HdfsResourceMgr.getHdfsResources("svc", "hdfs",
cfg, ctx2));
+ }
+
+ @Test
+ public void test07_getHdfsResources_returnsNullWhenHdfsClientNull() throws
Exception {
+ ResourceLookupContext ctx = new ResourceLookupContext();
+ ctx.setUserInput("/tmp");
+ ctx.setResourceName("path");
+ ctx.setResources(Collections.singletonMap(HdfsResourceMgr.PATH,
Collections.singletonList("/p")));
+
+ Map<String, String> cfg = new HashMap<>();
+
+ try (MockedStatic<TimedEventUtil> timed =
Mockito.mockStatic(TimedEventUtil.class);
+ MockedConstruction<HdfsConnectionMgr> conn =
Mockito.mockConstruction(HdfsConnectionMgr.class, (mock, context) -> {
+ Mockito.when(mock.getHadoopConnection(eq("svc"),
eq("hdfs"), eq(cfg))).thenReturn(null);
+ })) {
+ // timedTask should not be called when client is null; but we
still set a safe default
+ timed.when(() ->
TimedEventUtil.timedTask(Mockito.any(Callable.class), Mockito.anyLong(),
Mockito.any(TimeUnit.class))).thenThrow(new AssertionError("timedTask should
not be invoked"));
+
+ List<String> out = HdfsResourceMgr.getHdfsResources("svc", "hdfs",
cfg, ctx);
+ Assertions.assertNull(out);
+ }
+ }
+
+ @Test
+ public void test08_getHdfsResources_propagatesHadoopException() throws
Exception {
+ ResourceLookupContext ctx = new ResourceLookupContext();
+ ctx.setUserInput("/tmp");
+ ctx.setResourceName("path");
+ ctx.setResources(Collections.singletonMap(HdfsResourceMgr.PATH,
Collections.singletonList("/p")));
+
+ Map<String, String> cfg = new HashMap<>();
+ HdfsClient client = Mockito.mock(HdfsClient.class);
+ Mockito.when(client.listFiles(eq("/"), eq("tmp*"),
eq(Collections.singletonList("/p")))).thenThrow(new HadoopException("boom", new
RuntimeException("x")));
+
+ try (MockedStatic<TimedEventUtil> timed =
Mockito.mockStatic(TimedEventUtil.class);
+ MockedConstruction<HdfsConnectionMgr> conn =
Mockito.mockConstruction(HdfsConnectionMgr.class, (mock, context) -> {
+ Mockito.when(mock.getHadoopConnection(eq("svc"),
eq("hdfs"), eq(cfg))).thenReturn(client);
+ })) {
+ timed.when(() ->
TimedEventUtil.timedTask(Mockito.any(Callable.class), eq(5L),
eq(TimeUnit.SECONDS))).then(inv -> {
+ Callable<List<String>> c = inv.getArgument(0);
+ return c.call();
+ });
+
+ Assertions.assertThrows(HadoopException.class, () ->
HdfsResourceMgr.getHdfsResources("svc", "hdfs", cfg, ctx));
+ }
+ }
+}